-/* $Id$
-
-This file is part of R²C²
-Copyright © 2006-2010 Mikkosoft Productions, Mikko Rasa
-Distributed under the GPL
-*/
-
+#include <algorithm>
#include <cmath>
-#include <msp/strings/formatter.h>
+#include <msp/core/maputils.h>
+#include <msp/strings/format.h>
#include <msp/time/units.h>
#include <msp/time/utils.h>
#include "aicontrol.h"
#include "layout.h"
#include "route.h"
#include "simplecontroller.h"
+#include "speedquantizer.h"
#include "timetable.h"
#include "trackiter.h"
#include "tracktype.h"
#include "train.h"
+#include "trainrouter.h"
#include "vehicle.h"
#include "vehicletype.h"
#include "zone.h"
loco_type(t),
address(a),
protocol(p),
- priority(0),
- yielding_to(0),
preceding_train(0),
cur_blocks_end(blocks.end()),
- clear_blocks_end(blocks.end()),
pending_block(0),
reserving(false),
advancing(false),
- controller(new AIControl(*this, new SimpleController)),
- timetable(0),
+ controller(new SimpleController),
active(false),
current_speed_step(0),
speed_changing(false),
reverse(false),
functions(0),
- end_of_route(false),
- status("Unplaced"),
travel_dist(0),
pure_speed(false),
- real_speed(layout.get_driver().get_protocol_speed_steps(protocol)+1),
+ speed_quantizer(0),
accurate_position(false),
overshoot_dist(false)
{
if(!loco_type.is_locomotive())
- throw InvalidParameterValue("Initial vehicle must be a locomotive");
+ throw invalid_argument("Train::Train");
+
+ unsigned speed_steps = layout.get_driver().get_protocol_speed_steps(protocol);
+ if(speed_steps)
+ speed_quantizer = new SpeedQuantizer(speed_steps);
vehicles.push_back(new Vehicle(layout, loco_type));
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.get_driver().signal_sensor.connect(sigc::mem_fun(this, &Train::sensor_event));
+ 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));
Train::~Train()
{
delete controller;
- delete timetable;
for(vector<Vehicle *>::iterator i=vehicles.begin(); i!=vehicles.end(); ++i)
delete *i;
layout.remove_train(*this);
signal_name_changed.emit(name);
}
-void Train::set_priority(int p)
-{
- priority = p;
-}
-
-void Train::yield_to(const Train &t)
-{
- yielding_to = &t;
-}
-
void Train::add_vehicle(const VehicleType &vt)
{
Vehicle *veh = new Vehicle(layout, vt);
void Train::remove_vehicle(unsigned i)
{
if(i>=vehicles.size())
- throw InvalidParameterValue("Vehicle index out of range");
+ throw out_of_range("Train::remove_vehicle");
if(i==0)
- throw InvalidParameterValue("Can't remove the locomotive");
+ throw logic_error("can't remove locomotive");
delete vehicles[i];
vehicles.erase(vehicles.begin()+i);
if(i<vehicles.size())
Vehicle &Train::get_vehicle(unsigned i)
{
if(i>=vehicles.size())
- throw InvalidParameterValue("Vehicle index out of range");
+ throw out_of_range("Train::get_vehicle");
return *vehicles[i];
}
const Vehicle &Train::get_vehicle(unsigned i) const
{
if(i>=vehicles.size())
- throw InvalidParameterValue("Vehicle index out of range");
+ throw out_of_range("Train::get_vehicle");
return *vehicles[i];
}
if(a==active)
return;
if(!a && controller->get_speed())
- throw InvalidState("Can't deactivate while moving");
+ throw logic_error("moving");
active = a;
if(active)
reserve_more();
}
else
- {
stop_timeout = Time::now()+2*Time::sec;
- set_status("Stopped");
- }
}
void Train::set_function(unsigned func, bool state)
{
if(!loco_type.get_functions().count(func))
- throw InvalidParameterValue("Invalid function");
+ throw invalid_argument("Train::set_function");
layout.get_driver().set_loco_function(address, func, state);
}
return controller->get_speed();
}
-bool Train::get_function(unsigned func) const
-{
- return (functions>>func)&1;
-}
-
-void Train::set_timetable(Timetable *tt)
+float Train::get_quantized_speed() const
{
- delete timetable;
- timetable = tt;
-}
-
-bool Train::set_route(const Route *r)
-{
- free_noncritical_blocks();
-
- Route *lead = 0;
- if(r && !blocks.empty())
- {
- TrackIter first = blocks.front().track_iter();
- TrackIter next = blocks.back().next().track_iter();
- if(!r->has_track(*next))
- {
- lead = Route::find(next, *r);
- if(!lead)
- return false;
- create_lead_route(lead, lead);
- routes.push_front(lead);
- }
- else if(!r->has_track(*first))
- lead = create_lead_route(0, r);
- }
-
- routes.clear();
- if(lead)
- routes.push_back(lead);
- if(r)
- routes.push_back(r);
- end_of_route = false;
-
- reserve_more();
-
- signal_route_changed.emit(get_route());
-
- return true;
+ if(speed_quantizer)
+ return speed_quantizer->quantize_speed(controller->get_speed());
+ else
+ return controller->get_speed();
}
-bool Train::go_to(Track &to)
+bool Train::get_function(unsigned func) const
{
- for(BlockList::const_iterator i=blocks.begin(); i!=cur_blocks_end; ++i)
- if((*i)->has_track(to))
- {
- signal_arrived.emit();
- return set_route(0);
- }
-
- free_noncritical_blocks();
-
- TrackIter next = blocks.back().next().track_iter();
-
- Route *route = Route::find(next, to);
- if(!route)
- return false;
- create_lead_route(route, route);
- return set_route(route);
+ return (functions>>func)&1;
}
-bool Train::go_to(const Zone &to)
+void Train::add_ai(TrainAI &ai)
{
- set<Track *> tracks;
- for(BlockList::const_iterator i=blocks.begin(); i!=blocks.end(); ++i)
- tracks.insert((*i)->get_tracks().begin(), (*i)->get_tracks().end());
-
- const Zone::TrackSet &ztracks = to.get_tracks();
- unsigned union_size = 0;
- for(Zone::TrackSet::const_iterator i=ztracks.begin(); i!=ztracks.end(); ++i)
- union_size += tracks.count(*i);
-
- if(union_size==tracks.size() || union_size==ztracks.size())
- {
- signal_arrived.emit();
- return set_route(0);
- }
-
- free_noncritical_blocks();
-
- TrackIter next = blocks.back().next().track_iter();
-
- Route *route = Route::find(next, to);
- if(!route)
- return false;
- create_lead_route(route, route);
- route->add_tracks(ztracks);
- return set_route(route);
+ ais.push_back(&ai);
+ ai.signal_event.connect(sigc::bind<0>(signal_ai_event, sigc::ref(ai)));
}
-bool Train::divert(Track &from)
+void Train::remove_ai(TrainAI &ai)
{
- if(!from.get_turnout_id())
- throw InvalidParameterValue("Can't divert from a non-turnout");
- if(routes.empty())
- return false;
-
- unsigned path = 0;
- unsigned entry = 0;
- list<RouteRef>::iterator route = routes.begin();
-
- // Follow our routes to find out where we're entering the turnout
- for(TrackLoopIter track = blocks.front().track_iter();;)
- {
- if(!advance_route(route, *track))
- return false;
-
- if(&*track==&from)
- {
- Block &block = track->get_block();
- if(block.get_train()==this && !free_block(block))
- return false;
-
- int route_path = route->route->get_turnout(from.get_turnout_id());
-
- // Check that more than one path is available
- unsigned ep_paths = track.endpoint().paths;
- if(!(ep_paths&(ep_paths-1)))
- return false;
-
- // Choose some other path
- for(int i=0; ep_paths>>i; ++i)
- if((ep_paths&(1<<i)) && i!=route_path)
- {
- path = i;
- break;
- }
-
- entry = track.entry();
- break;
- }
-
- track = track.next(route->route->get_path(*track));
-
- if(!track || track.looped())
- return false;
- }
-
- TrackIter track = TrackIter(&from, entry).next(path);
- if(!track)
- return false;
-
- set<Track *> tracks;
- for(list<RouteRef>::iterator i=routes.begin(); i!=routes.end(); ++i)
- tracks.insert(i->route->get_tracks().begin(), i->route->get_tracks().end());
- RefPtr<Route> diversion = Route::find(track, tracks);
- if(!diversion)
- return false;
-
- diversion->set_name("Diversion");
- diversion->add_track(from);
- diversion->set_turnout(from.get_turnout_id(), path);
-
- if(!is_valid_diversion(*diversion, TrackIter(&from, entry)))
- return false;
-
- // Follow the diversion route until we get back to the original route
- list<RouteRef>::iterator end = routes.end();
- while(1)
- {
- for(list<RouteRef>::iterator i=route; (end==routes.end() && i!=routes.end()); ++i)
- if(i->route->has_track(*track))
- end = i;
-
- if(end!=routes.end())
- break;
- else if(!diversion->has_track(*track))
- throw LogicError("Pathfinder returned a bad route");
-
- track = track.next(diversion->get_path(*track));
- }
-
- if(route==end)
- // We are rejoining the same route we diverted from, duplicate it
- routes.insert(end, *route);
- else
- {
- ++route;
- routes.erase(route, end);
- }
- routes.insert(end, RouteRef(diversion.release(), from.get_turnout_id()));
-
- return true;
+ list<TrainAI *>::iterator i = find(ais.begin(), ais.end(), &ai);
+ if(i!=ais.end())
+ ais.erase(i);
}
-const Route *Train::get_route() const
+void Train::ai_message(const TrainAI::Message &msg)
{
- if(routes.empty())
- return 0;
- return routes.front().route;
+ for(list<TrainAI *>::iterator i=ais.begin(); i!=ais.end(); ++i)
+ (*i)->message(msg);
}
void Train::place(Block &block, unsigned entry)
{
if(controller->get_speed())
- throw InvalidState("Must be stopped before placing");
+ throw logic_error("moving");
release_blocks();
set_active(false);
accurate_position = false;
+ blocks.push_back(BlockIter(&block, entry));
if(!block.reserve(this))
{
- set_status("Unplaced");
+ blocks.pop_back();
return;
}
- blocks.push_back(BlockIter(&block, entry));
if(reverse)
{
TrackIter track = BlockIter(&block, entry).reverse().track_iter();
void Train::unplace()
{
if(controller->get_speed())
- throw InvalidState("Must be stopped before unplacing");
+ throw logic_error("moving");
release_blocks();
for(vector<Vehicle *>::iterator i=vehicles.begin(); i!=vehicles.end(); ++i)
(*i)->unplace();
+}
- set_status("Unplaced");
+void Train::stop_at(Block *block)
+{
+ stop_at_block = block;
+ if(active && !stop_at_block)
+ reserve_more();
}
bool Train::free_block(Block &block)
{
- float margin = 10*layout.get_catalogue().get_scale();
- if(get_reserved_distance_until(&block, false)<controller->get_braking_distance()*1.3+margin)
+ if(get_reserved_distance_until(&block, false)<controller->get_braking_distance()*1.3)
return false;
unsigned nsens = 0;
}
}
-int Train::get_entry_to_block(Block &block) const
+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)
float Train::get_reserved_distance() const
{
- return get_reserved_distance_until(0, false);
+ if(blocks.empty())
+ return 0;
+
+ float margin = 0;
+ TrackIter next = blocks.back().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);
}
void Train::tick(const Time::TimeStamp &t, const Time::TimeDelta &dt)
Driver &driver = layout.get_driver();
- if(timetable)
- timetable->tick(t);
+ for(list<TrainAI *>::iterator i=ais.begin(); i!=ais.end(); ++i)
+ (*i)->tick(t, dt);
controller->tick(dt);
float speed = controller->get_speed();
- unsigned speed_step = find_speed_step(speed);
+ bool moving = speed>0;
if(controller->get_reverse()!=reverse)
{
reverse = controller->get_reverse();
- driver.set_loco_reverse(address, reverse);
+ bool r = reverse;
+ if(loco_type.get_swap_direction())
+ r = !r;
+ driver.set_loco_reverse(address, r);
release_blocks(cur_blocks_end, blocks.end());
reverse_blocks(blocks);
reserve_more();
}
- if(speed_step!=current_speed_step && !speed_changing && !driver.is_halted() && driver.get_power())
+
+ if(speed_quantizer)
{
- speed_changing = true;
- driver.set_loco_speed(address, speed_step);
+ unsigned speed_step = speed_quantizer->find_speed_step(speed);
+ if(speed_step!=current_speed_step && !speed_changing && !driver.is_halted() && driver.get_power())
+ {
+ speed_changing = true;
+ driver.set_loco_speed(address, speed_step);
- pure_speed = false;
+ pure_speed = false;
+ }
- if(speed_step)
- set_status(format("Traveling %d kmh", get_travel_speed()));
- else
- set_status("Waiting");
+ speed = speed_quantizer->get_speed(current_speed_step);
}
- if(speed)
+ if(moving)
{
if(!active)
set_active(true);
for(BlockList::const_iterator i=blocks.begin(); (!ok && i!=cur_blocks_end); ++i)
ok = (*i)->has_track(*track);
- float d;
- if(real_speed.size()>1)
- d = get_real_speed(current_speed_step)*(dt/Time::sec);
- else
- d = speed*(dt/Time::sec);
+ float d = speed*(dt/Time::sec);
if(ok)
{
SetFlag setf(advancing);
}
}
}
- else if(end_of_route && cur_blocks_end==blocks.end())
- {
- set_active(false);
- signal_arrived.emit();
- set_route(0);
- }
if(!blocks.empty() && !blocks.front()->get_sensor_id())
{
if(dist>10*layout.get_catalogue().get_scale())
{
- blocks.front()->reserve(0);
+ Block &block = *blocks.front();
blocks.pop_front();
+ block.reserve(0);
}
}
}
{
st.push_back((DataFile::Statement("name"), name));
- st.push_back((DataFile::Statement("priority"), priority));
-
for(vector<Vehicle *>::const_iterator i=vehicles.begin(); i!=vehicles.end(); ++i)
if(i!=vehicles.begin())
st.push_back((DataFile::Statement("vehicle"), (*i)->get_type().get_article_number()));
- for(unsigned i=0; i<real_speed.size(); ++i)
- if(real_speed[i].weight)
- st.push_back((DataFile::Statement("real_speed"), i, real_speed[i].speed, real_speed[i].weight));
+ if(speed_quantizer)
+ {
+ DataFile::Statement ss("quantized_speed");
+ speed_quantizer->save(ss.sub);
+ st.push_back(ss);
+ }
if(!blocks.empty() && cur_blocks_end!=blocks.begin())
{
st.push_back((DataFile::Statement("block"), (*i)->get_id()));
}
- if(!routes.empty())
+ // XXX Need more generic way of saving AI state
+ for(list<TrainAI *>::const_iterator i=ais.begin(); i!=ais.end(); ++i)
{
- list<RouteRef>::const_iterator i = routes.begin();
- for(; (i!=routes.end() && i->route->is_temporary()); ++i) ;
- if(i!=routes.end())
- st.push_back((DataFile::Statement("route"), i->route->get_name()));
- }
-
- if(timetable)
- {
- DataFile::Statement ss("timetable");
- timetable->save(ss.sub);
- st.push_back(ss);
+ if(TrainRouter *router = dynamic_cast<TrainRouter *>(*i))
+ {
+ DataFile::Statement ss("router");
+ router->save(ss.sub);
+ st.push_back(ss);
+ }
+ else if(Timetable *timetable = dynamic_cast<Timetable *>(*i))
+ {
+ DataFile::Statement ss("timetable");
+ timetable->save(ss.sub);
+ st.push_back(ss);
+ }
}
}
signal_control_changed.emit(ctrl.name, ctrl.value);
}
-void Train::loco_speed_event(unsigned addr, unsigned speed, bool)
+void Train::loco_speed_event(unsigned addr, unsigned speed, bool rev)
{
if(addr==address)
{
current_speed_step = speed;
+ bool r = reverse;
+ if(loco_type.get_swap_direction())
+ r = !r;
+ if(rev!=r)
+ layout.get_driver().set_loco_reverse(address, r);
speed_changing = false;
pure_speed = false;
}
}
}
-void Train::sensor_event(unsigned addr, bool state)
+void Train::block_state_changed(Block &block, Block::State state)
{
- if(state)
+ if(state==Block::MAYBE_ACTIVE)
{
// Find the first sensor block from our reserved blocks that isn't this sensor
BlockList::iterator end;
for(end=cur_blocks_end; end!=blocks.end(); ++end)
if((*end)->get_sensor_id())
{
- if((*end)->get_sensor_id()!=addr)
+ if(&**end!=&block)
{
if(result==0)
result = 2;
// Compute speed and update related state
float travel_time_secs = (Time::now()-last_entry_time)/Time::sec;
- if(pure_speed)
- {
- if(current_speed_step>0)
- {
- RealSpeed &rs = real_speed[current_speed_step];
- rs.add(travel_dist/travel_time_secs, travel_time_secs);
- }
- set_status(format("Traveling %d kmh", get_travel_speed()));
- }
+ if(pure_speed && speed_quantizer && current_speed_step>0 && travel_time_secs>=2)
+ 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)
{
travel_dist += (*j)->get_path_length(j->entry());
- if((*j)->get_sensor_id()==addr && !advancing)
+ if(&**j==&block && !advancing)
{
TrackIter track = j->track_iter();
if(reverse)
accurate_position = true;
overshoot_dist = 0;
- // Check if we've reached the next route
- if(routes.size()>1)
- {
- const Route &route = *(++routes.begin())->route;
- for(BlockList::iterator j=cur_blocks_end; j!=end; ++j)
- if(route.has_track(*j->track_iter()))
- {
- routes.pop_front();
- // XXX Exceptions?
- signal_route_changed.emit(routes.front().route);
- break;
- }
- }
-
// 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
else if(result==3)
layout.emergency("Sensor for "+name+" triggered out of order");
}
- else
+ else if(state==Block::INACTIVE)
{
const Vehicle &veh = *(reverse ? vehicles.front() : vehicles.back());
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)
- check_turnout_paths(false);
+ if((*i)->get_turnout_id()==track.get_turnout_id() && !reserving && &**i==pending_block)
+ reserve_more();
}
void Train::halt_event(bool h)
void Train::reserve_more()
{
- if(!active || blocks.empty() || end_of_route)
+ 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;
dist += (*i)->get_path_length(i->entry());
}
- list<RouteRef>::iterator cur_route = routes.begin();
- advance_route(cur_route, *start.track_iter());
-
float approach_margin = 50*layout.get_catalogue().get_scale();
float min_dist = controller->get_braking_distance()*1.3+approach_margin*2;
BlockIter block = start;
- list<BlockIter>::iterator good_end = blocks.end();
- Track *divert_track = 0;
- bool try_divert = false;
- Train *blocking_train = 0;
- BlockList contested_blocks;
SetFlag setf(reserving);
while(1)
{
BlockIter last = block;
- block = block.next(cur_route!=routes.end() ? cur_route->route : 0);
+ block = block.next();
if(!block || block->get_endpoints().size()<2)
- {
- if(!blocking_train)
- good_end = blocks.end();
+ // The track ends here
break;
- }
-
- TrackIter track = block.track_iter();
- if(cur_route!=routes.end())
+ if(block->get_turnout_id() && !last->get_turnout_id())
{
- if(!advance_route(cur_route, *track))
- {
- // Keep the blocks if we arrived at the end of the route
- if(!blocking_train)
- {
- good_end = blocks.end();
- end_of_route = true;
- }
+ /* We are arriving at a turnout. See if we have enough blocks and
+ distance reserved. */
+ if(nsens>=3 && dist>=min_dist)
break;
- }
}
- if(block->get_turnout_id() && !last->get_turnout_id())
+ blocks.push_back(block);
+ if(!block->reserve(this))
{
- /* 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;
+ blocks.pop_back();
+ pending_block = &*block;
+ break;
}
- if(blocking_train)
+ if(cur_blocks_end==blocks.end())
+ --cur_blocks_end;
+
+ TrackIter track = block.track_iter();
+ if(track->is_path_changing())
+ {
+ pending_block = &*block;
+ break;
+ }
+ else
{
- if(block->get_train()!=blocking_train)
+ const TrackType::Endpoint &entry_ep = track.endpoint();
+ unsigned path = track->get_active_path();
+ if(!entry_ep.has_path(path))
{
- if(blocking_train->free_block(*contested_blocks.back()))
+ const TrackType::Endpoint &exit_ep = track.reverse().endpoint();
+ if(entry_ep.has_common_paths(exit_ep))
{
- // Roll back and start actually reserving the blocks
- block = blocks.back();
- cur_route = routes.begin();
- advance_route(cur_route, *block.track_iter().track());
- if(blocking_train->get_priority()==priority)
- blocking_train->yield_to(*this);
- blocking_train = 0;
- continue;
+ 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
- {
- yield_to(*blocking_train);
- pending_block = contested_blocks.front().block();
- try_divert = divert_track;
+ // XXX Do something here
break;
- }
- }
- else
- {
- contested_blocks.push_back(block);
- continue;
- }
- }
-
- bool reserved = block->reserve(this);
- if(!reserved)
- {
- /* We've found another train. If it wants to exit the block from the
- same endpoint we're trying to enter from or the other way around,
- treat it as coming towards us. Otherwise treat it as going in the
- same direction. */
- Train *other_train = block->get_train();
- int other_entry = other_train->get_entry_to_block(*block);
- if(other_entry<0)
- throw LogicError("Block reservation inconsistency");
-
- unsigned exit = block.reverse().entry();
- unsigned other_exit = BlockIter(block.block(), other_entry).reverse().entry();
- 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_end = blocks.end();
-
- if(static_cast<unsigned>(other_entry)==block.entry())
- preceding_train = other_train;
- }
-
- int other_prio = other_train->get_priority();
-
- if(!entry_conflict && !exit_conflict && other_prio<priority)
- {
- /* Ask a lesser priority train going to the same direction to free
- the block for us */
- if(other_train->free_block(*block))
- reserved = block->reserve(this);
- }
- else if(other_train!=yielding_to && (other_prio<priority || (other_prio==priority && entry_conflict)))
- {
- /* A lesser priority train is coming at us, we must ask it to free
- enough blocks to get clear of it to avoid a potential deadlock */
- blocking_train = other_train;
- contested_blocks.clear();
- contested_blocks.push_back(block);
- continue;
- }
- else if(divert_track && (entry_conflict || exit_conflict))
- // We are blocked, but there's a diversion possibility
- try_divert = true;
-
- if(!reserved)
- {
- pending_block = &*block;
- break;
}
}
- if(block->get_turnout_id())
- {
- const TrackType::Endpoint &track_ep = track.endpoint();
- bool multiple_paths = (track_ep.paths&(track_ep.paths-1));
-
- 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 */
- divert_track = &*track;
- }
-
- if(!contested_blocks.empty() && contested_blocks.front()==block)
- 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==stop_at_block)
+ break;
if(block->get_sensor_id())
++nsens;
dist += block->get_path_length(block.entry());
}
- // Unreserve blocks that were not good
- release_blocks(good_end, blocks.end());
-
- 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!=clear_blocks_end && !(*cur_blocks_end)->get_sensor_id())
+ while(cur_blocks_end!=blocks.end() && !(*cur_blocks_end)->get_sensor_id())
++cur_blocks_end;
-
- if(try_divert && divert(*divert_track))
- 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);
-
- /* 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!=clear_blocks_end && !(*block)->has_track(*track))
+ while(block!=blocks.end() && !(*block)->has_track(*track))
++block;
- if(block==clear_blocks_end || &**block==until_block)
+ if(block==blocks.end() || &**block==until_block)
return 0;
float result = veh.get_offset();
else
{
++block;
- if(block==clear_blocks_end)
+ if(block==blocks.end())
break;
}
return result;
}
-float Train::get_real_speed(unsigned i) const
-{
- if(i==0)
- return 0;
- if(real_speed[i].weight)
- return real_speed[i].speed;
-
- unsigned low;
- unsigned high;
- for(low=i; low>0; --low)
- if(real_speed[low].weight)
- break;
- for(high=i; high+1<real_speed.size(); ++high)
- if(real_speed[high].weight)
- break;
-
- if(real_speed[high].weight)
- {
- if(real_speed[low].weight)
- {
- float f = float(i-low)/(high-low);
- return real_speed[low].speed*(1-f)+real_speed[high].speed*f;
- }
- else
- return real_speed[high].speed*float(i)/high;
- }
- else if(real_speed[low].weight)
- return real_speed[low].speed*float(i)/low;
- else
- return 0;
-}
-
-unsigned Train::find_speed_step(float real) const
-{
- if(real_speed.size()<=1)
- return 0;
- if(real<=real_speed[1].speed*0.5)
- return 0;
-
- unsigned low = 0;
- unsigned high = 0;
- unsigned last = 0;
- for(unsigned i=0; (!high && i<real_speed.size()); ++i)
- if(real_speed[i].weight)
- {
- last = i;
- if(real_speed[i].speed<real)
- low = i;
- else
- high = i;
- }
- if(!high)
- {
- unsigned limit = real_speed.size()/5;
- if(!low)
- {
- if(real)
- return limit;
- else
- return 0;
- }
- return min(min(static_cast<unsigned>(low*real/real_speed[low].speed), real_speed.size()-1), last+limit);
- }
-
- float f = (real-real_speed[low].speed)/(real_speed[high].speed-real_speed[low].speed);
- return static_cast<unsigned>(low*(1-f)+high*f+0.5);
-}
-
-float Train::get_travel_speed() const
-{
- float speed = get_real_speed(current_speed_step);
- float scale = layout.get_catalogue().get_scale();
- return static_cast<int>(round(speed/scale*3.6/5))*5;
-}
-
-void Train::set_status(const string &s)
-{
- status = s;
- signal_status_changed.emit(s);
-}
-
void Train::release_blocks()
{
release_blocks(blocks.begin(), blocks.end());
{
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;
}
}
*i = i->reverse();
}
-bool Train::advance_route(list<RouteRef>::iterator &iter, Track &track)
-{
- while(iter!=routes.end() && !iter->route->has_track(track))
- ++iter;
- if(iter==routes.end())
- return false;
-
- list<RouteRef>::iterator next = iter;
- ++next;
- if(next!=routes.end() && next->diversion && next->route->has_track(track))
- iter = next;
-
- return true;
-}
-
-Route *Train::create_lead_route(Route *lead, const Route *target)
-{
- if(!lead)
- {
- lead = new Route(layout);
- lead->set_name("Lead");
- lead->set_temporary(true);
- }
-
- set<Track *> tracks;
- for(BlockList::iterator i=blocks.begin(); i!=blocks.end(); ++i)
- {
- const set<Track *> &btracks = (*i)->get_tracks();
- for(set<Track *>::const_iterator j=btracks.begin(); j!=btracks.end(); ++j)
- if(!target || !target->has_track(**j))
- tracks.insert(*j);
- }
-
- lead->add_tracks(tracks);
-
- return lead;
-}
-
-bool Train::is_valid_diversion(const Route &diversion, const TrackIter &from)
-{
- float diversion_len = 0;
- TrackLoopIter track1 = from;
- while(diversion.has_track(*track1))
- {
- unsigned path = diversion.get_path(*track1);
- diversion_len += track1->get_type().get_path_length(path);
-
- track1 = track1.next(path);
-
- if(track1.looped())
- return false;
- }
-
- list<RouteRef>::iterator route = routes.begin();
- if(!advance_route(route, *from))
- return false;
-
- float route_len = 0;
- TrackLoopIter track2 = from;
- while(1)
- {
- unsigned path = route->route->get_path(*track2);
- route_len += track2->get_type().get_path_length(path);
-
- bool ok = (track2!=from && diversion.has_track(*track2));
-
- track2 = track2.next(path);
-
- if(ok)
- break;
-
- if(track2.looped())
- return false;
-
- if(!advance_route(route, *track2))
- return false;
- }
-
- // Must end up at the same place through both routes
- if(track2!=track1)
- return false;
-
- return diversion_len<route_len*1.2;
-}
-
-
-Train::RouteRef::RouteRef(const Route *r, unsigned d):
- route(r),
- diversion(d)
-{ }
-
-
-Train::RealSpeed::RealSpeed():
- speed(0),
- weight(0)
-{ }
-
-void Train::RealSpeed::add(float s, float w)
-{
- speed = (speed*weight+s*w)/(weight+w);
- weight = min(weight+w, 300.0f);
-}
-
Train::Loader::Loader(Train &t):
- DataFile::BasicLoader<Train>(t),
+ DataFile::ObjectLoader<Train>(t),
prev_block(0),
blocks_valid(true)
{
add("block", &Loader::block);
add("block_hint", &Loader::block_hint);
add("name", &Loader::name);
- add("priority", &Train::priority);
- add("real_speed", &Loader::real_speed);
- add("route", &Loader::route);
+ add("quantized_speed", &Loader::quantized_speed);
+ add("router", &Loader::router);
add("timetable", &Loader::timetable);
add("vehicle", &Loader::vehicle);
}
TrackIter track = obj.blocks.front().track_iter();
float offset = 2*obj.layout.get_catalogue().get_scale();
obj.vehicles.back()->place(*track, track.entry(), offset, Vehicle::BACK_BUFFER);
-
- obj.set_status("Stopped");
}
}
{
blk = &obj.layout.get_block(id);
}
- catch(const KeyError &)
+ catch(const key_error &)
{
blocks_valid = false;
return;
if(entry<0)
entry = 0;
- blk->reserve(&obj);
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 = &obj.layout.get_block(id);
}
- catch(const KeyError &)
+ catch(const key_error &)
{
blocks_valid = false;
}
obj.set_name(n);
}
-void Train::Loader::real_speed(unsigned i, float speed, float weight)
+void Train::Loader::quantized_speed()
{
- if(i>=obj.real_speed.size())
- return;
- obj.real_speed[i].speed = speed;
- obj.real_speed[i].weight = weight;
+ if(obj.speed_quantizer)
+ load_sub(*obj.speed_quantizer);
}
-void Train::Loader::route(const string &n)
+void Train::Loader::router()
{
- obj.set_route(&obj.layout.get_route(n));
+ TrainRouter *rtr = new TrainRouter(obj);
+ load_sub(*rtr);
}
void Train::Loader::timetable()
{
- if(obj.timetable)
- throw InvalidState("A timetable has already been loaded");
-
- obj.timetable = new Timetable(obj);
- load_sub(*obj.timetable);
+ Timetable *ttbl = new Timetable(obj);
+ load_sub(*ttbl);
}
void Train::Loader::vehicle(ArticleNumber art_nr)