]> git.tdb.fi Git - r2c2.git/blobdiff - source/libr2c2/train.cpp
Split block allocation to a separate class
[r2c2.git] / source / libr2c2 / train.cpp
index d78e6276ec14bd98780badaef96ddece39ed2be7..aa6cc481855ff3655ad2e29ff7ad65bdc5db66d1 100644 (file)
@@ -32,9 +32,7 @@ Train::Train(Layout &l, const VehicleType &t, unsigned a, const string &p):
        address(a),
        protocol(p),
        preceding_train(0),
-       cur_blocks_end(blocks.end()),
-       pending_block(0),
-       reserving(false),
+       allocator(*this),
        advancing(false),
        controller(new SimpleController),
        active(false),
@@ -63,16 +61,10 @@ Train::Train(Layout &l, const VehicleType &t, unsigned a, const string &p):
        layout.get_driver().signal_loco_speed.connect(sigc::mem_fun(this, &Train::loco_speed_event));
        layout.get_driver().signal_loco_function.connect(sigc::mem_fun(this, &Train::loco_func_event));
 
-       layout.signal_block_reserved.connect(sigc::mem_fun(this, &Train::block_reserved));
        layout.signal_block_state_changed.connect(sigc::mem_fun(this, &Train::block_state_changed));
 
        layout.get_driver().signal_halt.connect(sigc::mem_fun(this, &Train::halt_event));
 
-       const set<Track *> &tracks = layout.get_tracks();
-       for(set<Track *>::const_iterator i=tracks.begin(); i!=tracks.end(); ++i)
-               if((*i)->get_turnout_id())
-                       (*i)->signal_path_changed.connect(sigc::hide(sigc::bind(sigc::mem_fun(this, &Train::turnout_path_changed), sigc::ref(**i))));
-
        controller->signal_control_changed.connect(sigc::mem_fun(this, &Train::control_changed));
 }
 
@@ -145,7 +137,7 @@ void Train::set_active(bool a)
        if(active)
        {
                stop_timeout = Time::TimeStamp();
-               reserve_more();
+               allocator.reserve_more();
        }
        else
                stop_timeout = Time::now()+2*Time::sec;
@@ -205,17 +197,10 @@ void Train::place(Block &block, unsigned entry)
        if(controller->get_speed())
                throw logic_error("moving");
 
-       release_blocks();
-
        set_active(false);
        accurate_position = false;
 
-       blocks.push_back(BlockIter(&block, entry));
-       if(!block.reserve(this))
-       {
-               blocks.pop_back();
-               return;
-       }
+       allocator.start_from(block, entry);
 
        if(reverse)
        {
@@ -234,7 +219,7 @@ void Train::unplace()
        if(controller->get_speed())
                throw logic_error("moving");
 
-       release_blocks();
+       allocator.clear();
 
        set_active(false);
        accurate_position = false;
@@ -245,9 +230,9 @@ void Train::unplace()
 
 void Train::stop_at(Block *block)
 {
-       stop_at_block = block;
-       if(active && !stop_at_block)
-               reserve_more();
+       allocator.stop_at(block);
+       if(active && !block)
+               allocator.reserve_more();
 }
 
 bool Train::free_block(Block &block)
@@ -255,31 +240,17 @@ bool Train::free_block(Block &block)
        if(get_reserved_distance_until(&block, false)<controller->get_braking_distance()*1.3)
                return false;
 
-       unsigned nsens = 0;
-       for(BlockList::iterator i=cur_blocks_end; i!=blocks.end(); ++i)
-       {
-               if(i->block()==&block)
-               {
-                       if(nsens<1)
-                               return false;
-                       release_blocks(i, blocks.end());
-                       return true;
-               }
-               else if((*i)->get_sensor_id())
-                       ++nsens;
-       }
-
-       return false;
+       return allocator.release_from(block);
 }
 
 void Train::free_noncritical_blocks()
 {
-       if(blocks.empty())
+       if(allocator.empty())
                return;
 
        if(controller->get_speed()==0)
        {
-               release_blocks(cur_blocks_end, blocks.end());
+               allocator.release_noncurrent();
                return;
        }
 
@@ -289,13 +260,17 @@ void Train::free_noncritical_blocks()
        Vehicle &veh = *(reverse ? vehicles.back() : vehicles.front());
 
        TrackIter track(veh.get_track(), veh.get_entry());
-       BlockList::iterator block = blocks.begin();
+       BlockIter block = allocator.first();
+       const BlockIter &last_cur = allocator.last_current();
+       const BlockIter &last = allocator.last();
        bool in_rsv = false;
-       while(block!=blocks.end() && !(*block)->has_track(*track))
+       while(!block->has_track(*track))
        {
-               ++block;
-               if(block==cur_blocks_end)
+               if(&*block==&*last_cur)
                        in_rsv = true;
+               if(&*block==&*last)
+                       break;
+               block = block.next();
        }
 
        float dist = veh.get_offset();
@@ -310,21 +285,21 @@ void Train::free_noncritical_blocks()
        {
                track = track.next();
 
-               if(!(*block)->has_track(*track))
+               if(!block->has_track(*track))
                {
-                       ++block;
-                       if(block==cur_blocks_end)
+                       if(&*block==&*last_cur)
                                in_rsv = true;
-                       if(block==blocks.end())
+                       if(&*block==&*last)
                                return;
+                       block = block.next();
 
                        if(dist>min_dist && nsens>0)
                        {
-                               release_blocks(block, blocks.end());
+                               allocator.release_from(*block);
                                return;
                        }
 
-                       if(in_rsv && (*block)->get_sensor_id())
+                       if(in_rsv && block->get_sensor_id())
                                ++nsens;
                }
 
@@ -332,46 +307,29 @@ void Train::free_noncritical_blocks()
        }
 }
 
-const BlockIter &Train::get_head_block() const
-{
-       if(blocks.empty())
-               throw logic_error("no blocks");
-       return blocks.back();
-}
-
-const BlockIter &Train::get_tail_block() const
-{
-       if(blocks.empty())
-               throw logic_error("no blocks");
-       return blocks.front();
-}
-
-int Train::get_entry_to_block(const Block &block) const
-{
-       for(BlockList::const_iterator i=blocks.begin(); i!=blocks.end(); ++i)
-               if(i->block()==&block)
-                       return i->entry();
-       return -1;
-}
-
 float Train::get_reserved_distance() const
 {
-       if(blocks.empty())
+       if(allocator.empty())
                return 0;
 
        float margin = 0;
-       TrackIter next = blocks.back().next().track_iter();
+       TrackIter next = allocator.last().next().track_iter();
        if(next && next->get_type().is_turnout())
                margin = 15*layout.get_catalogue().get_scale();
 
-       return max(get_reserved_distance_until(pending_block, false)-margin, 0.0f);
+       return max(get_reserved_distance_until(0, false)-margin, 0.0f);
+}
+
+void Train::reserve_more()
+{
+       allocator.reserve_more();
 }
 
 void Train::tick(const Time::TimeStamp &t, const Time::TimeDelta &dt)
 {
        if(!active && stop_timeout && t>=stop_timeout)
        {
-               release_blocks(cur_blocks_end, blocks.end());
+               allocator.release_noncurrent();
                stop_timeout = Time::TimeStamp();
        }
 
@@ -391,10 +349,9 @@ void Train::tick(const Time::TimeStamp &t, const Time::TimeDelta &dt)
                        r = !r;
                driver.set_loco_reverse(address, r);
 
-               release_blocks(cur_blocks_end, blocks.end());
-               reverse_blocks(blocks);
-
-               reserve_more();
+               allocator.reverse();
+               if(active)
+                       allocator.reserve_more();
        }
 
        if(speed_quantizer)
@@ -419,9 +376,15 @@ void Train::tick(const Time::TimeStamp &t, const Time::TimeDelta &dt)
                Vehicle &vehicle = *(reverse ? vehicles.back() : vehicles.front());
                Track *track = vehicle.get_track();
 
+               BlockIter first = allocator.first();
+               BlockIter last_current = allocator.last_current();
                bool ok = false;
-               for(BlockList::const_iterator i=blocks.begin(); (!ok && i!=cur_blocks_end); ++i)
-                       ok = (*i)->has_track(*track);
+               for(BlockIter i=first; !ok; i=i.next())
+               {
+                       ok = i->has_track(*track);
+                       if(i==last_current)
+                               break;
+               }
 
                float d = speed*(dt/Time::sec);
                if(ok)
@@ -440,16 +403,12 @@ void Train::tick(const Time::TimeStamp &t, const Time::TimeDelta &dt)
                }
        }
 
-       if(!blocks.empty() && !blocks.front()->get_sensor_id())
+       if(!allocator.empty() && !allocator.first()->get_sensor_id())
        {
-               float dist = get_reserved_distance_until(&*blocks.front(), true);
+               float dist = get_reserved_distance_until(&*allocator.first(), true);
 
                if(dist>10*layout.get_catalogue().get_scale())
-               {
-                       Block &block = *blocks.front();
-                       blocks.pop_front();
-                       block.reserve(0);
-               }
+                       allocator.release_until(*allocator.first());
        }
 }
 
@@ -468,17 +427,10 @@ void Train::save(list<DataFile::Statement> &st) const
                st.push_back(ss);
        }
 
-       if(!blocks.empty() && cur_blocks_end!=blocks.begin())
        {
-               BlockList blks(blocks.begin(), BlockList::const_iterator(cur_blocks_end));
-               if(reverse)
-                       reverse_blocks(blks);
-
-               BlockIter prev = blks.front().flip();
-               st.push_back((DataFile::Statement("block_hint"), prev->get_id()));
-
-               for(BlockList::const_iterator i=blks.begin(); i!=blks.end(); ++i)
-                       st.push_back((DataFile::Statement("block"), (*i)->get_id()));
+               DataFile::Statement ss("blocks");
+               allocator.save(ss.sub);
+               st.push_back(ss);
        }
 
        // XXX Need more generic way of saving AI state
@@ -536,26 +488,18 @@ void Train::block_state_changed(Block &block, Block::State state)
 {
        if(state==Block::MAYBE_ACTIVE)
        {
-               // Find the first sensor block from our reserved blocks that isn't this sensor
-               BlockList::iterator end;
-               unsigned result = 0;
-               for(end=cur_blocks_end; end!=blocks.end(); ++end)
-                       if((*end)->get_sensor_id())
-                       {
-                               if(&**end!=&block)
-                               {
-                                       if(result==0)
-                                               result = 2;
-                                       else if(result==1)
-                                               break;
-                               }
-                               else if(result==0)
-                                       result = 1;
-                               else if(result==2)
-                                       result = 3;
-                       }
+               const BlockIter &first = allocator.first();
+               const BlockIter &last_cur = allocator.last_current();
+               bool valid = false;
+               for(BlockIter i=first; !valid; i=i.next())
+               {
+                       if(&*i==&block)
+                               valid = true;
+                       if(&*i==&*last_cur)
+                               break;
+               }
 
-               if(result==1)
+               if(valid)
                {
                        // Compute speed and update related state
                        float travel_time_secs = (Time::now()-last_entry_time)/Time::sec;
@@ -564,13 +508,14 @@ void Train::block_state_changed(Block &block, Block::State state)
                                speed_quantizer->learn(current_speed_step, travel_dist/travel_time_secs, travel_time_secs);
 
                        travel_dist = 0;
-                       for(BlockList::iterator j=cur_blocks_end; j!=end; ++j)
+                       unsigned entry = allocator.get_entry_to_block(block);
+                       for(BlockIter i(&block, entry);; i=i.next())
                        {
-                               travel_dist += (*j)->get_path_length(j->entry());
+                               travel_dist += i->get_path_length(i.entry());
 
-                               if(&**j==&block && !advancing)
+                               if(&*i==&block && !advancing && vehicles.front()->get_track())
                                {
-                                       TrackIter track = j->track_iter();
+                                       TrackIter track = i.track_iter();
                                        if(reverse)
                                        {
                                                track = track.flip();
@@ -579,182 +524,27 @@ void Train::block_state_changed(Block &block, Block::State state)
                                        else
                                                vehicles.front()->place(*track, track.entry(), 0, Vehicle::FRONT_AXLE);
                                }
+
+                               if(i==last_cur)
+                                       break;
                        }
                        last_entry_time = Time::now();
                        pure_speed = true;
                        accurate_position = true;
                        overshoot_dist = 0;
-
-                       // Move blocks up to the next sensor to our current blocks
-                       for(BlockList::iterator j=cur_blocks_end; j!=end; ++j)
-                               signal_advanced.emit(**j);
-                       cur_blocks_end = end;
-
-                       // Try to get more blocks if we're moving
-                       if(active)
-                               reserve_more();
-               }
-               else if(result==3)
-                       layout.emergency("Sensor for "+name+" triggered out of order");
-       }
-       else if(state==Block::INACTIVE)
-       {
-               const Vehicle &veh = *(reverse ? vehicles.front() : vehicles.back());
-
-               // Find the first sensor in our current blocks that's still active
-               BlockList::iterator end = blocks.begin();
-               for(BlockList::iterator i=blocks.begin(); i!=cur_blocks_end; ++i)
-               {
-                       if((*i)->has_track(*veh.get_track()))
-                               break;
-                       if((*i)->get_sensor_id())
-                       {
-                               if(layout.get_driver().get_sensor((*i)->get_sensor_id()))
-                                       break;
-                               else
-                               {
-                                       end = i;
-                                       ++end;
-                               }
-                       }
                }
-               
-               if(end!=blocks.begin() && end!=cur_blocks_end)
-                       // Free blocks up to the last inactive sensor
-                       release_blocks(blocks.begin(), end);
        }
 }
 
-void Train::turnout_path_changed(Track &track)
-{
-       for(list<BlockIter>::iterator i=blocks.begin(); i!=blocks.end(); ++i)
-               if((*i)->get_turnout_id()==track.get_turnout_id() && !reserving && &**i==pending_block)
-                       reserve_more();
-}
-
 void Train::halt_event(bool h)
 {
        if(h)
                accurate_position = false;
 }
 
-void Train::block_reserved(const Block &block, const Train *train)
-{
-       if(&block==pending_block && !train && !reserving)
-               reserve_more();
-}
-
-void Train::reserve_more()
-{
-       if(!active || blocks.empty())
-               return;
-
-       BlockIter start = blocks.back();
-       if(&*start==stop_at_block)
-               return;
-       else if(&*start==pending_block)
-       {
-               TrackIter track = start.track_iter();
-               if(!track.endpoint().has_path(track->get_active_path()))
-                       return;
-       }
-
-       pending_block = 0;
-       preceding_train = 0;
-
-       // See how many sensor blocks and how much track we already have
-       unsigned nsens = 0;
-       float dist = 0;
-       for(BlockList::const_iterator i=cur_blocks_end; i!=blocks.end(); ++i)
-       {
-               if((*i)->get_sensor_id())
-                       ++nsens;
-               if(nsens>0)
-                       dist += (*i)->get_path_length(i->entry());
-       }
-
-       float approach_margin = 50*layout.get_catalogue().get_scale();
-       float min_dist = controller->get_braking_distance()*1.3+approach_margin*2;
-
-       BlockIter block = start;
-
-       SetFlag setf(reserving);
-
-       while(1)
-       {
-               BlockIter last = block;
-               block = block.next();
-               if(!block || block->get_endpoints().size()<2)
-                       // The track ends here
-                       break;
-
-               if(block->get_turnout_id() && !last->get_turnout_id())
-               {
-                       /* We are arriving at a turnout.  See if we have enough blocks and
-                       distance reserved. */
-                       if(nsens>=3 && dist>=min_dist)
-                               break;
-               }
-
-               blocks.push_back(block);
-               if(!block->reserve(this))
-               {
-                       blocks.pop_back();
-                       pending_block = &*block;
-                       break;
-               }
-
-               if(cur_blocks_end==blocks.end())
-                       --cur_blocks_end;
-
-               TrackIter track = block.track_iter();
-               if(track->is_path_changing())
-               {
-                       pending_block = &*block;
-                       break;
-               }
-               else
-               {
-                       const TrackType::Endpoint &entry_ep = track.endpoint();
-                       unsigned path = track->get_active_path();
-                       if(!entry_ep.has_path(path))
-                       {
-                               const TrackType::Endpoint &exit_ep = track.reverse().endpoint();
-                               if(entry_ep.has_common_paths(exit_ep))
-                               {
-                                       unsigned mask = entry_ep.paths&exit_ep.paths;
-                                       for(path=0; mask>1; ++path, mask>>=1) ;
-
-                                       track->set_active_path(path);
-                                       if(track->is_path_changing())
-                                       {
-                                               pending_block = &*block;
-                                               break;
-                                       }
-                               }
-                               else
-                                       // XXX Do something here
-                                       break;
-                       }
-               }
-
-               if(&*block==stop_at_block)
-                       break;
-
-               if(block->get_sensor_id())
-                       ++nsens;
-               if(nsens>0)
-                       dist += block->get_path_length(block.entry());
-       }
-
-       // Make any sensorless blocks at the beginning immediately current
-       while(cur_blocks_end!=blocks.end() && !(*cur_blocks_end)->get_sensor_id())
-               ++cur_blocks_end;
-}
-
 float Train::get_reserved_distance_until(const Block *until_block, bool back) const
 {
-       if(blocks.empty())
+       if(allocator.empty())
                return 0;
 
        Vehicle &veh = *(reverse!=back ? vehicles.back() : vehicles.front());
@@ -764,12 +554,21 @@ float Train::get_reserved_distance_until(const Block *until_block, bool back) co
        if(!track)  // XXX Probably unnecessary
                return 0;
 
-       BlockList::const_iterator block = blocks.begin();
-       while(block!=blocks.end() && !(*block)->has_track(*track))
-               ++block;
-       if(block==blocks.end() || &**block==until_block)
+       const BlockIter &first = allocator.first();
+       const BlockIter &last = allocator.last();
+       BlockIter block = first;
+       while(!block->has_track(*track))
+       {
+               if(&*block==&*last)
+                       return 0;
+               block = block.next();
+       }
+       if(&*block==until_block)
                return 0;
 
+       if(back)
+               block = block.reverse();
+
        float result = veh.get_offset();
        if(reverse!=back)
                track = track.reverse();
@@ -783,22 +582,21 @@ float Train::get_reserved_distance_until(const Block *until_block, bool back) co
                if(!track)
                        break;
 
-               if(!(*block)->has_track(*track))
+               if(!block->has_track(*track))
                {
                        if(back)
                        {
-                               if(block==blocks.begin())
+                               if(&*block==&*first)
                                        break;
-                               --block;
                        }
                        else
                        {
-                               ++block;
-                               if(block==blocks.end())
+                               if(&*block==&*last)
                                        break;
                        }
+                       block = block.next();
 
-                       if(&**block==until_block)
+                       if(&*block==until_block)
                                break;
                }
 
@@ -808,39 +606,13 @@ float Train::get_reserved_distance_until(const Block *until_block, bool back) co
        return result;
 }
 
-void Train::release_blocks()
-{
-       release_blocks(blocks.begin(), blocks.end());
-}
-
-void Train::release_blocks(BlockList::iterator begin, BlockList::iterator end)
-{
-       while(begin!=end)
-       {
-               if(begin==cur_blocks_end)
-                       cur_blocks_end = end;
-
-               Block &block = **begin;
-               blocks.erase(begin++);
-               block.reserve(0);
-       }
-}
-
-void Train::reverse_blocks(BlockList &blks) const
-{
-       blks.reverse();
-       for(BlockList::iterator i=blks.begin(); i!=blks.end(); ++i)
-               *i = i->reverse();
-}
-
 
 Train::Loader::Loader(Train &t):
        DataFile::ObjectLoader<Train>(t),
        prev_block(0),
        blocks_valid(true)
 {
-       add("block",       &Loader::block);
-       add("block_hint",  &Loader::block_hint);
+       add("blocks",      &Loader::blocks);
        add("name",        &Loader::name);
        add("quantized_speed",  &Loader::quantized_speed);
        add("router",      &Loader::router);
@@ -850,55 +622,17 @@ Train::Loader::Loader(Train &t):
 
 void Train::Loader::finish()
 {
-       if(!obj.blocks.empty())
+       if(!obj.allocator.empty())
        {
-               TrackIter track = obj.blocks.front().track_iter();
+               TrackIter track = obj.allocator.first().track_iter();
                float offset = 2*obj.layout.get_catalogue().get_scale();
                obj.vehicles.back()->place(*track, track.entry(), offset, Vehicle::BACK_BUFFER);
        }
 }
 
-void Train::Loader::block(unsigned id)
+void Train::Loader::blocks()
 {
-       if(!blocks_valid)
-               return;
-
-       Block *blk;
-       try
-       {
-               blk = &obj.layout.get_block(id);
-       }
-       catch(const key_error &)
-       {
-               blocks_valid = false;
-               return;
-       }
-
-       int entry = -1;
-       if(prev_block)
-               entry = blk->get_endpoint_by_link(*prev_block);
-       if(entry<0)
-               entry = 0;
-
-       obj.blocks.push_back(BlockIter(blk, entry));
-       blk->reserve(&obj);
-
-       if(blk->get_sensor_id())
-               obj.layout.get_driver().set_sensor(blk->get_sensor_id(), true);
-
-       prev_block = blk;
-}
-
-void Train::Loader::block_hint(unsigned id)
-{
-       try
-       {
-               prev_block = &obj.layout.get_block(id);
-       }
-       catch(const key_error &)
-       {
-               blocks_valid = false;
-       }
+       load_sub(obj.allocator);
 }
 
 void Train::Loader::name(const string &n)