priority(0),
yielding_to(0),
cur_blocks_end(blocks.end()),
+ clear_blocks_end(blocks.end()),
pending_block(0),
reserving(false),
advancing(false),
layout.signal_block_reserved.connect(sigc::mem_fun(this, &Train::block_reserved));
layout.get_driver().signal_sensor.connect(sigc::mem_fun(this, &Train::sensor_event));
- layout.get_driver().signal_turnout.connect(sigc::mem_fun(this, &Train::turnout_event));
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));
}
int route_path = route->route->get_turnout(from.get_turnout_id());
// Check that more than one path is available
- unsigned ep_paths = track->get_type().get_endpoints()[track.entry()].paths;
+ unsigned ep_paths = track.endpoint().paths;
if(!(ep_paths&(ep_paths-1)))
return false;
}
else
{
- const Block::Endpoint &bep = block.get_endpoints()[entry];
+ const Block::Endpoint &bep = block.get_endpoint(entry);
vehicles.back()->place(*bep.track, bep.track_ep, 0, Vehicle::BACK_BUFFER);
}
}
if(!active && stop_timeout && t>=stop_timeout)
{
release_blocks(cur_blocks_end, blocks.end());
- end_of_route = false;
stop_timeout = Time::TimeStamp();
}
// Check if we've reached the next route
if(routes.size()>1)
{
- const set<Track *> &rtracks = (++routes.begin())->route->get_tracks();
+ const Route &route = *(++routes.begin())->route;
for(BlockList::iterator j=cur_blocks_end; j!=end; ++j)
- if(rtracks.count((*j)->get_endpoints()[j->entry()].track))
+ if(route.has_track(*j->track_iter()))
{
routes.pop_front();
// XXX Exceptions?
}
}
-void Train::turnout_event(unsigned addr, bool)
+void Train::turnout_path_changed(Track &track)
{
- if(pending_block && (!pending_block->get_train() || pending_block->get_train()==this))
- {
- unsigned pending_addr = pending_block->get_turnout_id();
- bool double_addr = (*pending_block->get_tracks().begin())->get_type().is_double_address();
- if(addr==pending_addr || (double_addr && addr==pending_addr+1))
- {
- if(reserving)
- pending_block = 0;
- else
- reserve_more();
- }
- }
+ for(list<BlockIter>::iterator i=blocks.begin(); i!=blocks.end(); ++i)
+ if((*i)->get_turnout_id()==track.get_turnout_id() && !reserving)
+ check_turnout_paths(false);
}
void Train::halt_event(bool h)
reserve_more();
}
-unsigned Train::reserve_more()
+void Train::reserve_more()
{
- if(!active || blocks.empty())
- return 0;
+ if(!active || blocks.empty() || end_of_route)
+ return;
BlockIter start = blocks.back();
if(nsens>0)
dist += (*i)->get_path_length(i->entry());
}
-
- if(end_of_route)
- return nsens;
list<RouteRef>::iterator cur_route = routes.begin();
advance_route(cur_route, *start.track_iter());
float min_dist = controller->get_braking_distance()*1.3+approach_margin*2;
BlockIter block = start;
- BlockIter good = start;
+ list<BlockIter>::iterator good_end = blocks.end();
Track *divert_track = 0;
bool try_divert = false;
- unsigned good_sens = nsens;
- float good_dist = dist;
Train *blocking_train = 0;
BlockList contested_blocks;
SetFlag setf(reserving);
- while(good_sens<3 || good_dist<min_dist || !contested_blocks.empty())
+ while(1)
{
BlockIter last = block;
block = block.next(cur_route!=routes.end() ? cur_route->route : 0);
- if(!block)
+ if(!block || block->get_endpoints().size()<2)
+ {
+ if(!blocking_train)
+ good_end = blocks.end();
break;
+ }
TrackIter track = block.track_iter();
// Keep the blocks if we arrived at the end of the route
if(!blocking_train)
{
- good = last;
- good_sens = nsens;
- good_dist = dist;
+ good_end = blocks.end();
end_of_route = true;
}
break;
}
}
- else if(!routes.empty() && routes.front().route->has_track(*track))
- cur_route = routes.begin();
- if(block->get_endpoints().size()<2)
+ if(block->get_turnout_id() && !last->get_turnout_id())
{
- if(!blocking_train)
- {
- good = last;
- good_sens = nsens;
- good_dist = dist;
- }
- break;
+ /* We can keep the blocks if we arrive at a turnout from a non-turnout
+ block. Having a turnout block as our last reserved block is not good
+ as it would limit our diversion possibilities for little benefit. */
+ good_end = blocks.end();
+ if(nsens>=3 && dist>=min_dist)
+ break;
}
if(blocking_train)
bool entry_conflict = (block.entry()==other_exit);
bool exit_conflict = (exit==static_cast<unsigned>(other_entry));
if(!entry_conflict && !last->get_turnout_id())
- {
/* The other train is not coming to the blocks we're holding, so we
can keep them. */
- good = last;
- good_sens = nsens;
- good_dist = dist;
- }
+ good_end = blocks.end();
int other_prio = other_train->get_priority();
if(block->get_turnout_id())
{
- const TrackType::Endpoint &track_ep = track->get_type().get_endpoints()[track.entry()];
+ const TrackType::Endpoint &track_ep = track.endpoint();
bool multiple_paths = (track_ep.paths&(track_ep.paths-1));
- if(multiple_paths || !last->get_turnout_id())
- {
- /* We can keep the blocks reserved so far if we are facing the
- points or if there was no turnout immediately before this one.
- With multiple successive turnouts (as is common in crossovers) it's
- best to hold at one we can divert from. */
- good = last;
- good_sens = nsens;
- good_dist = dist;
- }
-
- // Figure out what path we'd like to take on the turnout
- int path = -1;
- for(list<RouteRef>::iterator i=cur_route; (path<0 && i!=routes.end()); ++i)
- path = i->route->get_turnout(block->get_turnout_id());
- if(path<0)
- path = track->get_active_path();
- if(!(track_ep.paths&(1<<path)))
- {
- for(unsigned i=0; track_ep.paths>>i; ++i)
- if(track_ep.paths&(1<<i))
- path = i;
- }
-
- if(path!=static_cast<int>(track->get_active_path()))
- {
- // The turnout is set to wrong path - switch and wait for it
- pending_block = &*block;
- track->set_active_path(path);
- if(pending_block)
- {
- block->reserve(0);
- break;
- }
- }
-
if(multiple_paths && cur_route!=routes.end() && cur_route->diversion!=block->get_turnout_id())
/* There's multiple paths to be taken and we are on a route - take
note of the diversion possibility */
contested_blocks.pop_front();
blocks.push_back(block);
+
if(cur_blocks_end==blocks.end())
--cur_blocks_end;
+ if(clear_blocks_end==blocks.end())
+ --clear_blocks_end;
+ if(good_end==blocks.end())
+ --good_end;
+
if(block->get_sensor_id())
++nsens;
if(nsens>0)
}
// Unreserve blocks that were not good
- while(blocks.end()!=cur_blocks_end && blocks.back()!=good)
- {
- if(--blocks.end()==cur_blocks_end)
- cur_blocks_end = blocks.end();
- blocks.back()->reserve(0);
- blocks.pop_back();
- }
+ release_blocks(good_end, blocks.end());
- if(!blocks.back()!=start)
+ if(blocks.back()!=start)
// We got some new blocks, so no longer need to yield
yielding_to = 0;
+ check_turnout_paths(true);
+
// Make any sensorless blocks at the beginning immediately current
- while(cur_blocks_end!=blocks.end() && !(*cur_blocks_end)->get_sensor_id())
+ while(cur_blocks_end!=clear_blocks_end && !(*cur_blocks_end)->get_sensor_id())
++cur_blocks_end;
if(try_divert && divert(*divert_track))
- return reserve_more();
+ reserve_more();
+}
+
+void Train::check_turnout_paths(bool set)
+{
+ if(clear_blocks_end==blocks.end())
+ return;
+
+ for(list<BlockIter>::iterator i=clear_blocks_end; i!=blocks.end(); ++i)
+ {
+ if((*i)->get_turnout_id())
+ {
+ TrackIter track = i->track_iter();
+ const TrackType::Endpoint &track_ep = track.endpoint();
+
+ unsigned path = 0;
+ list<BlockIter>::iterator j = i;
+ if(++j!=blocks.end())
+ {
+ TrackIter rev = j->track_iter().flip();
+ unsigned mask = rev.endpoint().paths&track_ep.paths;
+ for(path=0; mask>1; mask>>=1, ++path) ;
+ }
+ else
+ return;
+
+ if(path!=track->get_active_path())
+ {
+ if(set)
+ track->set_active_path(path);
- return good_sens;
+ /* Check again, in case the driver was able to service the request
+ instantly */
+ if(!set || path!=track->get_active_path())
+ continue;
+ }
+ }
+
+ if(i==clear_blocks_end)
+ ++clear_blocks_end;
+ }
}
float Train::get_reserved_distance_until(const Block *until_block, bool back) const
return 0;
BlockList::const_iterator block = blocks.begin();
- while(block!=blocks.end() && !(*block)->has_track(*track))
+ while(block!=clear_blocks_end && !(*block)->has_track(*track))
++block;
- if(block==blocks.end() || &**block==until_block)
+ if(block==clear_blocks_end || &**block==until_block)
return 0;
float result = veh.get_offset();
else
{
++block;
- if(block==blocks.end())
+ if(block==clear_blocks_end)
break;
}
{
if(begin==cur_blocks_end)
cur_blocks_end = end;
+ if(begin==clear_blocks_end)
+ clear_blocks_end = end;
Block &block = **begin;
blocks.erase(begin++);
block.reserve(0);
+
+ if(begin==blocks.end())
+ end_of_route = false;
}
}