From ca9d278f9472206ad9a01190dcef9f0eb1bcc274 Mon Sep 17 00:00:00 2001 From: Mikko Rasa Date: Sat, 1 Jun 2013 01:06:20 +0300 Subject: [PATCH] Split block allocation to a separate class --- source/libr2c2/blockallocator.cpp | 401 ++++++++++++++++++++++++++ source/libr2c2/blockallocator.h | 74 +++++ source/libr2c2/signal.cpp | 2 +- source/libr2c2/train.cpp | 452 ++++++------------------------ source/libr2c2/train.h | 27 +- source/libr2c2/trainrouter.cpp | 13 +- 6 files changed, 582 insertions(+), 387 deletions(-) create mode 100644 source/libr2c2/blockallocator.cpp create mode 100644 source/libr2c2/blockallocator.h diff --git a/source/libr2c2/blockallocator.cpp b/source/libr2c2/blockallocator.cpp new file mode 100644 index 0000000..ac840d3 --- /dev/null +++ b/source/libr2c2/blockallocator.cpp @@ -0,0 +1,401 @@ +#include +#include +#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 &tracks = layout.get_tracks(); + for(set::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::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 &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(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 index 0000000..3ef3fa0 --- /dev/null +++ b/source/libr2c2/blockallocator.h @@ -0,0 +1,74 @@ +#ifndef LIBR2C2_BLOCKALLOCATOR_H_ +#define LIBR2C2_BLOCKALLOCATOR_H_ + +#include +#include +#include "block.h" +#include "blockiter.h" + +namespace R2C2 { + +class Train; + +class BlockAllocator +{ +public: + class Loader: public Msp::DataFile::ObjectLoader + { + private: + Block *prev_block; + bool valid; + + public: + Loader(BlockAllocator &); + + private: + void block(unsigned); + void hint(unsigned); + }; + +private: + typedef std::list 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 &) const; +}; + +} // namepsace R2C2 + +#endif diff --git a/source/libr2c2/signal.cpp b/source/libr2c2/signal.cpp index 0139b1f..7ccb238 100644 --- a/source/libr2c2/signal.cpp +++ b/source/libr2c2/signal.cpp @@ -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(train_entry)==entry) { if(train_conn) diff --git a/source/libr2c2/train.cpp b/source/libr2c2/train.cpp index d78e627..aa6cc48 100644 --- a/source/libr2c2/train.cpp +++ b/source/libr2c2/train.cpp @@ -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 &tracks = layout.get_tracks(); - for(set::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)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 &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::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(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) diff --git a/source/libr2c2/train.h b/source/libr2c2/train.h index 461a27e..94348e1 100644 --- a/source/libr2c2/train.h +++ b/source/libr2c2/train.h @@ -6,7 +6,7 @@ #include #include #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 signal_advanced; private: - typedef std::list BlockList; - Layout &layout; const VehicleType &loco_type; unsigned address; @@ -55,11 +52,7 @@ private: std::string name; const Train *preceding_train; std::vector 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 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 diff --git a/source/libr2c2/trainrouter.cpp b/source/libr2c2/trainrouter.cpp index 77d6f71..8cfedee 100644 --- a/source/libr2c2/trainrouter.cpp +++ b/source/libr2c2/trainrouter.cpp @@ -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 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 &btracks = i->get_tracks(); for(set::const_iterator j=btracks.begin(); j!=btracks.end(); ++j) -- 2.43.0