It was getting rather messy and there was one or two bugs where block
allocation didn't resume correctly. The flag now resides in
BlockAllocator. Rather than poking at it directly, AIs can express an
intention of moving the train. This should help keep things in sync
with each other.
if(track->get_turnout_id())
{
Block &block = track->get_block();
if(track->get_turnout_id())
{
Block &block = track->get_block();
- if(block.get_train() && !block.get_train()->free_block(block))
+ if(block.get_train() && block.get_train()->is_block_critical(block))
set_status("Turnout is busy");
else
{
set_status("Turnout is busy");
else
{
void AIControl::set_target_speed(float s)
{
void AIControl::set_target_speed(float s)
{
- if(s && !train.is_active())
- train.set_active(true);
-
target_speed = s;
need_update = true;
signal_event.emit(Message("target-speed-changed", target_speed));
target_speed = s;
need_update = true;
signal_event.emit(Message("target-speed-changed", target_speed));
speed_limit = 0;
else if(state==APPROACH)
speed_limit = approach_speed;
speed_limit = 0;
else if(state==APPROACH)
speed_limit = approach_speed;
- else if(state==FOLLOW && train.get_preceding_train()->is_active())
+ else if(state==FOLLOW && train.get_preceding_train()->get_block_allocator().is_active())
speed_limit = train.get_preceding_train()->get_speed();
if(speed_limit>=0 && target_speed>speed_limit)
speed_limit = train.get_preceding_train()->get_speed();
if(speed_limit>=0 && target_speed>speed_limit)
reverse = pending_reverse;
train.set_control("reverse", reverse);
}
reverse = pending_reverse;
train.set_control("reverse", reverse);
}
- if(!target_speed && !train.get_controller().get_speed() && train.is_active())
- train.set_active(false);
+bool AIControl::has_intent_to_move() const
+{
+ return target_speed;
}
void AIControl::event(TrainAI &, const Message &ev)
}
void AIControl::event(TrainAI &, const Message &ev)
virtual void message(const Message &);
virtual void tick(const Msp::Time::TimeDelta &);
virtual void message(const Message &);
virtual void tick(const Msp::Time::TimeDelta &);
+ virtual bool has_intent_to_move() const;
private:
void event(TrainAI &, const Message &);
private:
void event(TrainAI &, const Message &);
const set<Track *> &tracks = layout.get_all<Track>();
for(set<Track *>::const_iterator i=tracks.begin(); i!=tracks.end(); ++i)
if((*i)->get_turnout_id())
const set<Track *> &tracks = layout.get_all<Track>();
for(set<Track *>::const_iterator i=tracks.begin(); i!=tracks.end(); ++i)
if((*i)->get_turnout_id())
+ {
+ (*i)->signal_path_changing.connect(sigc::hide(sigc::bind(sigc::mem_fun(this, &BlockAllocator::turnout_path_changing), sigc::ref(**i))));
(*i)->signal_path_changed.connect(sigc::hide(sigc::bind(sigc::mem_fun(this, &BlockAllocator::turnout_path_changed), sigc::ref(**i))));
(*i)->signal_path_changed.connect(sigc::hide(sigc::bind(sigc::mem_fun(this, &BlockAllocator::turnout_path_changed), sigc::ref(**i))));
+ }
+}
+
+void BlockAllocator::set_active(bool a)
+{
+ active = a;
+ if(active)
+ reserve_more();
+ else
+ {
+ release_blocks_end(cur_blocks_end);
+ pending_block = 0;
+ }
}
void BlockAllocator::start_from(const BlockIter &block)
}
void BlockAllocator::start_from(const BlockIter &block)
+void BlockAllocator::rewind_to(const Block &block)
+{
+ if(!active)
+ return;
+
+ BlockList::iterator i = find_if(cur_blocks_end, blocks.end(), BlockMatch(block));
+ if(i!=blocks.end())
+ {
+ release_blocks_end(i);
+ reserve_more();
+ }
+}
+
void BlockAllocator::clear()
{
void BlockAllocator::clear()
{
release_blocks_begin(blocks.end());
pending_block = 0;
stop_at_block = 0;
release_blocks_begin(blocks.end());
pending_block = 0;
stop_at_block = 0;
void BlockAllocator::stop_at(const Block *block)
{
stop_at_block = block;
void BlockAllocator::stop_at(const Block *block)
{
stop_at_block = block;
+ if(active && !block)
+ reserve_more();
}
const BlockIter &BlockAllocator::first() const
}
const BlockIter &BlockAllocator::first() const
-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_end(i);
- return have_sensor;
- }
- else if((*i)->get_sensor_id())
- have_sensor = true;
- }
-
- return false;
-}
-
-void BlockAllocator::release_noncurrent()
-{
- release_blocks_end(cur_blocks_end);
-}
-
void BlockAllocator::release_blocks_begin(const BlockList::iterator &end)
{
for(BlockList::iterator i=blocks.begin(); i!=end; )
void BlockAllocator::release_blocks_begin(const BlockList::iterator &end)
{
for(BlockList::iterator i=blocks.begin(); i!=end; )
void BlockAllocator::reverse()
{
void BlockAllocator::reverse()
{
+ release_blocks_end(cur_blocks_end);
blocks.reverse();
for(BlockList::iterator i=blocks.begin(); i!=blocks.end(); ++i)
*i = i->reverse();
blocks.reverse();
for(BlockList::iterator i=blocks.begin(); i!=blocks.end(); ++i)
*i = i->reverse();
+
+ if(active)
+ reserve_more();
+}
+
+void BlockAllocator::turnout_path_changing(Track &track)
+{
+ BlockList::iterator i = find_if(blocks.begin(), blocks.end(), BlockMatch(track.get_block()));
+ if(i!=blocks.end())
+ {
+ ++i;
+ release_blocks_end(i);
+ pending_block = &track.get_block();
+ }
}
void BlockAllocator::turnout_path_changed(Track &track)
}
void BlockAllocator::turnout_path_changed(Track &track)
for(BlockList::iterator j=cur_blocks_end; j!=end; ++j)
train.signal_advanced.emit(**j);
cur_blocks_end = end;
for(BlockList::iterator j=cur_blocks_end; j!=end; ++j)
train.signal_advanced.emit(**j);
cur_blocks_end = end;
+
+ if(active)
+ reserve_more();
}
else if(result==3)
train.get_layout().emergency("Sensor for "+train.get_name()+" triggered out of order");
}
else if(result==3)
train.get_layout().emergency("Sensor for "+train.get_name()+" triggered out of order");
typedef std::list<BlockIter> BlockList;
Train &train;
typedef std::list<BlockIter> BlockList;
Train &train;
BlockList blocks;
BlockList::iterator cur_blocks_end;
Block *pending_block;
BlockList blocks;
BlockList::iterator cur_blocks_end;
Block *pending_block;
public:
BlockAllocator(Train &);
public:
BlockAllocator(Train &);
+ void set_active(bool);
+ bool is_active() const { return active; }
+
void start_from(const BlockIter &);
void start_from(const BlockIter &);
+ void rewind_to(const Block &);
void clear();
bool empty() const { return blocks.empty(); }
void stop_at(const Block *);
void clear();
bool empty() const { return blocks.empty(); }
void stop_at(const Block *);
bool has_block(const Block &) const;
bool is_block_current(const Block &) const;
bool has_block(const Block &) const;
bool is_block_current(const Block &) const;
bool reserve_block(const BlockIter &);
bool reserve_block(const BlockIter &);
-public:
- bool release_from(const Block &);
- void release_noncurrent();
-private:
void release_blocks_begin(const BlockList::iterator &);
void release_blocks_end(const BlockList::iterator &);
void release_block(const BlockList::iterator &);
void release_blocks_begin(const BlockList::iterator &);
void release_blocks_end(const BlockList::iterator &);
void release_block(const BlockList::iterator &);
+ void turnout_path_changing(Track &);
void turnout_path_changed(Track &);
void block_reserved(Block &, const Train *);
void sensor_state_changed(Sensor &, Sensor::State);
void turnout_path_changed(Track &);
void block_reserved(Block &, const Train *);
void sensor_state_changed(Sensor &, Sensor::State);
allocator(*this),
advancing(false),
controller(new SimpleController),
allocator(*this),
advancing(false),
controller(new SimpleController),
current_speed_step(0),
speed_changing(false),
reverse(false),
current_speed_step(0),
speed_changing(false),
reverse(false),
controller->set_control(n, v);
}
controller->set_control(n, v);
}
-void Train::set_active(bool a)
-{
- if(a==active)
- return;
- if(!a && controller->get_speed())
- throw logic_error("moving");
-
- active = a;
- if(active)
- {
- stop_timeout = Time::zero;
- allocator.reserve_more();
- }
- else
- stop_timeout = 2*Time::sec;
-}
-
void Train::set_function(unsigned func, bool state)
{
if(!loco_type.get_functions().count(func))
void Train::set_function(unsigned func, bool state)
{
if(!loco_type.get_functions().count(func))
if(controller->get_speed())
throw logic_error("moving");
if(controller->get_speed())
throw logic_error("moving");
- set_active(false);
- accurate_position = false;
-
allocator.start_from(block);
allocator.start_from(block);
+ accurate_position = false;
if(reverse)
vehicles.front()->place(block.reverse().track_iter(), 0, Vehicle::FRONT_BUFFER);
if(reverse)
vehicles.front()->place(block.reverse().track_iter(), 0, Vehicle::FRONT_BUFFER);
throw logic_error("moving");
allocator.clear();
throw logic_error("moving");
allocator.clear();
accurate_position = false;
for(vector<Vehicle *>::iterator i=vehicles.begin(); i!=vehicles.end(); ++i)
accurate_position = false;
for(vector<Vehicle *>::iterator i=vehicles.begin(); i!=vehicles.end(); ++i)
void Train::stop_at(Block *block)
{
allocator.stop_at(block);
void Train::stop_at(Block *block)
{
allocator.stop_at(block);
- if(active && !block)
- allocator.reserve_more();
-bool Train::free_block(Block &block)
+bool Train::is_block_critical(const Block &block) const
- if(get_reserved_distance_until(&block)<controller->get_braking_distance()*1.3)
- return false;
-
- return allocator.release_from(block);
+ return get_reserved_distance_until(&block)<controller->get_braking_distance()*1.3;
-void Train::free_noncritical_blocks()
+BlockIter Train::get_first_noncritical_block() const
+ return BlockIter();
+
+ BlockIter i = allocator.last_current().next();
if(controller->get_speed()==0)
if(controller->get_speed()==0)
- {
- allocator.release_noncurrent();
- return;
- }
float margin = 10*layout.get_catalogue().get_scale();
float min_dist = controller->get_braking_distance()*1.3+margin;
float margin = 10*layout.get_catalogue().get_scale();
float min_dist = controller->get_braking_distance()*1.3+margin;
- BlockIter i = allocator.last_current().next();
float dist = 0;
bool sensor_seen = false;
for(; i->get_train()==this; i=i.next())
{
if(dist>min_dist && sensor_seen)
float dist = 0;
bool sensor_seen = false;
for(; i->get_train()==this; i=i.next())
{
if(dist>min_dist && sensor_seen)
- {
- allocator.release_from(*i);
- return;
- }
dist += i->get_path_length(i.entry());
if(i->get_sensor_id())
sensor_seen = true;
}
dist += i->get_path_length(i.entry());
if(i->get_sensor_id())
sensor_seen = true;
}
+
+ return i;
+}
+
+void Train::refresh_blocks_from(Block &block)
+{
+ if(is_block_critical(block))
+ allocator.rewind_to(*get_first_noncritical_block());
+ else
+ allocator.rewind_to(block);
}
float Train::get_reserved_distance() const
}
float Train::get_reserved_distance() const
return max(get_reserved_distance_until(0)-margin, 0.0f);
}
return max(get_reserved_distance_until(0)-margin, 0.0f);
}
-void Train::reserve_more()
-{
- allocator.reserve_more();
-}
-
void Train::tick(const Time::TimeDelta &dt)
{
void Train::tick(const Time::TimeDelta &dt)
{
- if(!active && stop_timeout)
{
stop_timeout -= dt;
if(stop_timeout<=Time::zero)
{
{
stop_timeout -= dt;
if(stop_timeout<=Time::zero)
{
- allocator.release_noncurrent();
+ allocator.set_active(false);
stop_timeout = Time::TimeDelta();
}
}
stop_timeout = Time::TimeDelta();
}
}
Driver &driver = layout.get_driver();
Driver &driver = layout.get_driver();
+ bool intent_to_move = false;
for(list<TrainAI *>::iterator i=ais.begin(); i!=ais.end(); ++i)
for(list<TrainAI *>::iterator i=ais.begin(); i!=ais.end(); ++i)
+ if((*i)->has_intent_to_move())
+ intent_to_move = true;
+ }
+
controller->tick(dt);
float speed = controller->get_speed();
bool moving = speed>0;
controller->tick(dt);
float speed = controller->get_speed();
bool moving = speed>0;
driver.set_loco_reverse(address, r);
allocator.reverse();
driver.set_loco_reverse(address, r);
allocator.reverse();
- if(active)
- allocator.reserve_more();
- if(!active)
- set_active(true);
+ if(!allocator.is_active())
+ allocator.set_active(true);
Vehicle &vehicle = *(reverse ? vehicles.back() : vehicles.front());
Vehicle &vehicle = *(reverse ? vehicles.back() : vehicles.front());
+ else if(allocator.is_active() && !intent_to_move && !stop_timeout)
+ stop_timeout = 2*Time::sec;
}
void Train::save(list<DataFile::Statement> &st) const
}
void Train::save(list<DataFile::Statement> &st) const
bool advancing;
Controller *controller;
std::list<TrainAI *> ais;
bool advancing;
Controller *controller;
std::list<TrainAI *> ais;
unsigned current_speed_step;
bool speed_changing;
bool reverse;
unsigned current_speed_step;
bool speed_changing;
bool reverse;
const Vehicle &get_vehicle(unsigned) const;
void set_control(const std::string &, float);
const Vehicle &get_vehicle(unsigned) const;
void set_control(const std::string &, float);
void set_function(unsigned, bool);
float get_control(const std::string &) const;
float get_speed() const;
float get_quantized_speed() const;
unsigned get_speed_step() const { return current_speed_step; }
void set_function(unsigned, bool);
float get_control(const std::string &) const;
float get_speed() const;
float get_quantized_speed() const;
unsigned get_speed_step() const { return current_speed_step; }
- bool is_active() const { return active; }
bool get_function(unsigned) const;
unsigned get_functions() const { return functions; }
bool get_function(unsigned) const;
unsigned get_functions() const { return functions; }
void unplace();
bool is_placed() const { return !allocator.empty(); }
void stop_at(Block *);
void unplace();
bool is_placed() const { return !allocator.empty(); }
void stop_at(Block *);
- bool free_block(Block &);
- void free_noncritical_blocks();
- void reserve_more();
+ bool is_block_critical(const Block &) const;
+ BlockIter get_first_noncritical_block() const;
+ void refresh_blocks_from(Block &);
const BlockAllocator &get_block_allocator() const { return allocator; }
float get_reserved_distance() const;
const BlockAllocator &get_block_allocator() const { return allocator; }
float get_reserved_distance() const;
virtual void message(const Message &) { }
virtual void tick(const Msp::Time::TimeDelta &) = 0;
virtual void message(const Message &) { }
virtual void tick(const Msp::Time::TimeDelta &) = 0;
+ virtual bool has_intent_to_move() const { return false; }
bool TrainRouter::set_route(const Route *r)
{
bool TrainRouter::set_route(const Route *r)
{
- train.free_noncritical_blocks();
+ BlockIter fncb = train.get_first_noncritical_block();
Route *lead = 0;
if(r && train.is_placed())
{
const BlockAllocator &allocator = train.get_block_allocator();
TrackIter first = allocator.first().track_iter();
Route *lead = 0;
if(r && train.is_placed())
{
const BlockAllocator &allocator = train.get_block_allocator();
TrackIter first = allocator.first().track_iter();
- TrackIter next = allocator.last().next().track_iter();
+ TrackIter next = fncb.track_iter();
if(!r->has_track(*next))
{
lead = Route::find(next, *r);
if(!r->has_track(*next))
{
lead = Route::find(next, *r);
train.stop_at(0);
arriving = false;
train.stop_at(0);
arriving = false;
+ train.refresh_blocks_from(*fncb);
const Route *route = get_route();
signal_route_changed.emit(route);
const Route *route = get_route();
signal_route_changed.emit(route);
if(arriving && !train.get_speed())
{
if(arriving && !train.get_speed())
{
- train.set_active(false);
signal_arrived.emit();
signal_event.emit(Message("arrived"));
set_route(0);
signal_arrived.emit();
signal_event.emit(Message("arrived"));
set_route(0);
{
float scale = train.get_layout().get_catalogue().get_scale();
int s = static_cast<int>(train.get_quantized_speed()*3.6/scale+0.5);
{
float scale = train.get_layout().get_catalogue().get_scale();
int s = static_cast<int>(train.get_quantized_speed()*3.6/scale+0.5);
- if(s==0 && train.is_active())
+ if(s==0 && train.get_block_allocator().is_active())