]> git.tdb.fi Git - r2c2.git/commitdiff
Split block allocation to a separate class
authorMikko Rasa <tdb@tdb.fi>
Fri, 31 May 2013 22:06:20 +0000 (01:06 +0300)
committerMikko Rasa <tdb@tdb.fi>
Fri, 31 May 2013 22:06:20 +0000 (01:06 +0300)
source/libr2c2/blockallocator.cpp [new file with mode: 0644]
source/libr2c2/blockallocator.h [new file with mode: 0644]
source/libr2c2/signal.cpp
source/libr2c2/train.cpp
source/libr2c2/train.h
source/libr2c2/trainrouter.cpp

diff --git a/source/libr2c2/blockallocator.cpp b/source/libr2c2/blockallocator.cpp
new file mode 100644 (file)
index 0000000..ac840d3
--- /dev/null
@@ -0,0 +1,401 @@
+#include <msp/core/maputils.h>
+#include <msp/core/raii.h>
+#include "blockallocator.h"
+#include "catalogue.h"
+#include "driver.h"
+#include "layout.h"
+#include "trackiter.h"
+#include "train.h"
+#include "vehicle.h"
+
+using namespace std;
+using namespace Msp;
+
+namespace R2C2 {
+
+BlockAllocator::BlockAllocator(Train &t):
+       train(t),
+       cur_blocks_end(blocks.end()),
+       pending_block(0),
+       stop_at_block(0),
+       reserving(false)
+{
+       Layout &layout = train.get_layout();
+       layout.signal_block_reserved.connect(sigc::mem_fun(this, &BlockAllocator::block_reserved));
+       layout.signal_block_state_changed.connect(sigc::mem_fun(this, &BlockAllocator::block_state_changed));
+
+       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, &BlockAllocator::turnout_path_changed), sigc::ref(**i))));
+}
+
+void BlockAllocator::start_from(Block &block, unsigned entry)
+{
+       release_blocks(blocks.begin(), blocks.end());
+
+       blocks.push_back(BlockIter(&block, entry));
+       if(!block.reserve(&train))
+       {
+               blocks.pop_back();
+               return;
+       }
+}
+
+void BlockAllocator::clear()
+{
+       release_blocks(blocks.begin(), blocks.end());
+       pending_block = 0;
+       stop_at_block = 0;
+}
+
+void BlockAllocator::stop_at(const Block *block)
+{
+       stop_at_block = block;
+}
+
+const BlockIter &BlockAllocator::first() const
+{
+       if(blocks.empty())
+               throw logic_error("no blocks");
+       return blocks.front();
+}
+
+const BlockIter &BlockAllocator::last() const
+{
+       if(blocks.empty())
+               throw logic_error("no blocks");
+       BlockList::const_iterator i = --blocks.end();
+       if(i->block()==pending_block)
+               --i;
+       return *i;
+}
+
+const BlockIter &BlockAllocator::last_current() const
+{
+       if(blocks.empty())
+               throw logic_error("no blocks");
+       if(cur_blocks_end==blocks.begin())
+               throw logic_error("internal error (no current blocks)");
+       BlockList::const_iterator i = cur_blocks_end;
+       return *--i;
+}
+
+int BlockAllocator::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;
+}
+
+void BlockAllocator::reserve_more()
+{
+       if(blocks.empty())
+               throw logic_error("no blocks");
+
+       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;
+
+       // 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*train.get_layout().get_catalogue().get_scale();
+       float min_dist = train.get_controller().get_braking_distance()*1.3+approach_margin*2;
+
+       BlockIter block = start;
+
+       SetFlag setf(reserving);
+
+       while(1)
+       {
+               BlockIter prev = block;
+               block = block.next();
+               if(!block || block->get_endpoints().size()<2)
+                       // The track ends here
+                       break;
+
+               if(block->get_turnout_id() && !prev->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(&train))
+               {
+                       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;
+}
+
+void BlockAllocator::release_until(const Block &block)
+{
+       for(BlockList::iterator i=blocks.begin(); i!=cur_blocks_end; ++i)
+               if(i->block()==&block)
+               {
+                       if(++i!=cur_blocks_end)
+                               release_blocks(blocks.begin(), i);
+                       return;
+               }
+}
+
+bool BlockAllocator::release_from(const Block &block)
+{
+       bool have_sensor = false;
+       for(BlockList::iterator i=cur_blocks_end; i!=blocks.end(); ++i)
+       {
+               if(i->block()==&block)
+               {
+                       if(have_sensor)
+                               release_blocks(i, blocks.end());
+                       return have_sensor;
+               }
+               else if((*i)->get_sensor_id())
+                       have_sensor = true;
+       }
+
+       return false;
+}
+
+void BlockAllocator::release_noncurrent()
+{
+       release_blocks(cur_blocks_end, blocks.end());
+}
+
+void BlockAllocator::release_blocks(const BlockList::iterator &b, const BlockList::iterator &e)
+{
+       for(BlockList::iterator i=b; i!=e; )
+       {
+               if(cur_blocks_end==i)
+                       cur_blocks_end = e;
+
+               Block &block = **i;
+               blocks.erase(i++);
+               block.reserve(0);
+       }
+}
+
+void BlockAllocator::reverse()
+{
+       release_noncurrent();
+       blocks.reverse();
+       for(BlockList::iterator i=blocks.begin(); i!=blocks.end(); ++i)
+               *i = i->reverse();
+}
+
+void BlockAllocator::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 BlockAllocator::block_reserved(Block &block, const Train *tr)
+{
+       if(&block==pending_block && !tr && !reserving)
+               reserve_more();
+}
+
+void BlockAllocator::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;
+                       }
+
+               if(result==1)
+               {
+                       // Move blocks up to the next sensor to our current blocks
+                       for(BlockList::iterator j=cur_blocks_end; j!=end; ++j)
+                               train.signal_advanced.emit(**j);
+                       cur_blocks_end = end;
+               }
+               else if(result==3)
+                       train.get_layout().emergency("Sensor for "+train.get_name()+" triggered out of order");
+       }
+       else if(state==Block::INACTIVE)
+       {
+               const Vehicle &veh = train.get_controller().get_reverse() ? train.get_vehicle(0) : train.get_vehicle(train.get_n_vehicles()-1);
+
+               // 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(train.get_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 BlockAllocator::save(list<DataFile::Statement> &st) const
+{
+       if(!blocks.empty() && cur_blocks_end!=blocks.begin())
+       {
+               BlockList cur_blocks(blocks.begin(), BlockList::const_iterator(cur_blocks_end));
+               BlockIter prev;
+               if(train.get_controller().get_reverse())
+               {
+                       cur_blocks.reverse();
+                       prev = cur_blocks.front().next();
+               }
+               else
+                       prev = cur_blocks.front().flip();
+
+               st.push_back((DataFile::Statement("hint"), prev->get_id()));
+
+               for(BlockList::const_iterator i=cur_blocks.begin(); i!=cur_blocks.end(); ++i)
+                       st.push_back((DataFile::Statement("block"), (*i)->get_id()));
+       }
+}
+
+
+BlockAllocator::Loader::Loader(BlockAllocator &ba):
+       DataFile::ObjectLoader<BlockAllocator>(ba),
+       valid(true)
+{
+       add("block", &Loader::block);
+       add("hint",  &Loader::hint);
+}
+
+void BlockAllocator::Loader::block(unsigned id)
+{
+       if(!valid)
+               return;
+
+       Block *blk;
+       try
+       {
+               blk = &obj.train.get_layout().get_block(id);
+       }
+       catch(const key_error &)
+       {
+               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.train);
+
+       if(blk->get_sensor_id())
+               obj.train.get_layout().get_driver().set_sensor(blk->get_sensor_id(), true);
+
+       prev_block = blk;
+}
+
+void BlockAllocator::Loader::hint(unsigned id)
+{
+       try
+       {
+               prev_block = &obj.train.get_layout().get_block(id);
+       }
+       catch(const key_error &)
+       {
+               valid = false;
+       }
+}
+
+} // namespace R2C2
diff --git a/source/libr2c2/blockallocator.h b/source/libr2c2/blockallocator.h
new file mode 100644 (file)
index 0000000..3ef3fa0
--- /dev/null
@@ -0,0 +1,74 @@
+#ifndef LIBR2C2_BLOCKALLOCATOR_H_
+#define LIBR2C2_BLOCKALLOCATOR_H_
+
+#include <list>
+#include <msp/datafile/objectloader.h>
+#include "block.h"
+#include "blockiter.h"
+
+namespace R2C2 {
+
+class Train;
+
+class BlockAllocator
+{
+public:
+       class Loader: public Msp::DataFile::ObjectLoader<BlockAllocator>
+       {
+       private:
+               Block *prev_block;
+               bool valid;
+
+       public:
+               Loader(BlockAllocator &);
+
+       private:
+               void block(unsigned);
+               void hint(unsigned);
+       };
+
+private:
+       typedef std::list<BlockIter> BlockList;
+
+       Train &train;
+       BlockList blocks;
+       BlockList::iterator cur_blocks_end;
+       Block *pending_block;
+       const Block *stop_at_block;
+       bool reserving;
+
+public:
+       BlockAllocator(Train &);
+
+       void start_from(Block &, unsigned);
+       void clear();
+       bool empty() const { return blocks.empty(); }
+       void stop_at(const Block *);
+
+       const BlockIter &first() const;
+       const BlockIter &last() const;
+       const BlockIter &last_current() const;
+
+       int get_entry_to_block(const Block &) const;
+
+       void reserve_more();
+       void release_until(const Block &);
+       bool release_from(const Block &);
+       void release_noncurrent();
+private:
+       void release_blocks(const BlockList::iterator &, const BlockList::iterator &);
+public:
+       void reverse();
+
+private:
+       void turnout_path_changed(Track &);
+       void block_reserved(Block &, const Train *);
+       void block_state_changed(Block &, Block::State);
+
+public:
+       void save(std::list<Msp::DataFile::Statement> &) const;
+};
+
+} // namepsace R2C2
+
+#endif
index 0139b1fbdaa9cf73ff6a3b25cdaff0b7f63eb984..7ccb23874a7feb6c5659556b513ab4ecaea90a25 100644 (file)
@@ -163,7 +163,7 @@ void Signal::block_reserved(const Block &b, Train *t)
        {
                if(t)
                {
-                       int train_entry = t->get_entry_to_block(*block);
+                       int train_entry = t->get_block_allocator().get_entry_to_block(*block);
                        if(train_entry>=0 && static_cast<unsigned>(train_entry)==entry)
                        {
                                if(train_conn)
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)
index 461a27e583146bde1080e64acbb6384b0c33a3e6..94348e105c551a76edcd0730af57aac7ed3dd18b 100644 (file)
@@ -6,7 +6,7 @@
 #include <msp/datafile/objectloader.h>
 #include <msp/time/timestamp.h>
 #include "block.h"
-#include "blockiter.h"
+#include "blockallocator.h"
 #include "controller.h"
 #include "trainai.h"
 
@@ -30,8 +30,7 @@ public:
                Loader(Train &);
        private:
                virtual void finish();
-               void block(unsigned);
-               void block_hint(unsigned);
+               void blocks();
                void name(const std::string &);
                void quantized_speed();
                void router();
@@ -46,8 +45,6 @@ public:
        sigc::signal<void, Block &> signal_advanced;
 
 private:
-       typedef std::list<BlockIter> BlockList;
-
        Layout &layout;
        const VehicleType &loco_type;
        unsigned address;
@@ -55,11 +52,7 @@ private:
        std::string name;
        const Train *preceding_train;
        std::vector<Vehicle *> vehicles;
-       BlockList blocks;
-       BlockList::iterator cur_blocks_end;
-       Block *pending_block;
-       Block *stop_at_block;
-       bool reserving;
+       BlockAllocator allocator;
        bool advancing;
        Controller *controller;
        std::list<TrainAI *> ais;
@@ -122,13 +115,12 @@ public:
 
        void place(Block &, unsigned);
        void unplace();
-       bool is_placed() const { return !blocks.empty(); }
+       bool is_placed() const { return !allocator.empty(); }
        void stop_at(Block *);
        bool free_block(Block &);
        void free_noncritical_blocks();
-       const BlockIter &get_head_block() const;
-       const BlockIter &get_tail_block() const;
-       int get_entry_to_block(const Block &) const;
+       void reserve_more();
+       const BlockAllocator &get_block_allocator() const { return allocator; }
        float get_reserved_distance() const;
 
        void tick(const Msp::Time::TimeStamp &, const Msp::Time::TimeDelta &);
@@ -139,16 +131,9 @@ private:
        void loco_speed_event(unsigned, unsigned, bool);
        void loco_func_event(unsigned, unsigned, bool);
        void block_state_changed(Block &, Block::State);
-       void turnout_path_changed(Track &);
        void halt_event(bool);
        void block_reserved(const Block &, const Train *);
-public:
-       void reserve_more();
-private:
        float get_reserved_distance_until(const Block *, bool) const;
-       void release_blocks();
-       void release_blocks(BlockList::iterator, BlockList::iterator);
-       void reverse_blocks(BlockList &) const;
 };
 
 } // namespace R2C2
index 77d6f71c8a082f9108921b3d3051b58b4fada51f..8cfedee8f28623013cfa719f436704ec2963af5f 100644 (file)
@@ -35,8 +35,9 @@ bool TrainRouter::set_route(const Route *r)
        Route *lead = 0;
        if(r && train.is_placed())
        {
-               TrackIter first = train.get_tail_block().track_iter();
-               TrackIter next = train.get_head_block().next().track_iter();
+               const BlockAllocator &allocator = train.get_block_allocator();
+               TrackIter first = allocator.first().track_iter();
+               TrackIter next = allocator.last().next().track_iter();
                if(!r->has_track(*next))
                {
                        lead = Route::find(next, *r);
@@ -175,7 +176,7 @@ void TrainRouter::block_reserved(Block &block, Train *t)
                return;
        }
 
-       BlockIter b_iter(&block, t->get_entry_to_block(block));
+       BlockIter b_iter(&block, t->get_block_allocator().get_entry_to_block(block));
 
        RouteList::iterator route = routes.begin();
        if(advance_route(route, block))
@@ -208,7 +209,7 @@ void TrainRouter::train_advanced(Block &block)
        // Check if we've reached the next route
        if(routes.size()>1)
        {
-               unsigned entry = train.get_entry_to_block(block);
+               unsigned entry = train.get_block_allocator().get_entry_to_block(block);
                Track &track = *block.get_endpoint(entry).track;
                const Route &route = **++routes.begin();
                if(route.has_track(track))
@@ -221,7 +222,7 @@ void TrainRouter::train_advanced(Block &block)
 
        if(!routes.empty())
        {
-               BlockIter iter(&block, train.get_entry_to_block(block));
+               BlockIter iter(&block, train.get_block_allocator().get_entry_to_block(block));
                iter = iter.next();
                if(iter && !is_on_route(*iter))
                        arriving = true;
@@ -249,7 +250,7 @@ Route *TrainRouter::create_lead_route(Route *lead, const Route *target)
        }
 
        set<Track *> tracks;
-       for(BlockIter i=train.get_tail_block(); (i && i->get_train()==&train); i=i.next())
+       for(BlockIter i=train.get_block_allocator().first(); (i && i->get_train()==&train); i=i.next())
        {
                const set<Track *> &btracks = i->get_tracks();
                for(set<Track *>::const_iterator j=btracks.begin(); j!=btracks.end(); ++j)