#include <msp/time/units.h>
#include <msp/time/utils.h>
#include "aicontrol.h"
+#include "beamgate.h"
#include "block.h"
#include "catalogue.h"
#include "driver.h"
#include "route.h"
#include "simplecontroller.h"
#include "speedquantizer.h"
-#include "timetable.h"
#include "trackcircuit.h"
#include "trackiter.h"
#include "tracktype.h"
allocator(*this),
advancing(false),
controller(new SimpleController),
- active(false),
current_speed_step(0),
speed_changing(false),
reverse(false),
speed_quantizer = new SpeedQuantizer(speed_steps);
vehicles.push_back(new Vehicle(layout, loco_type));
+ vehicles.back()->set_train(this);
layout.add_train(*this);
- layout.get_driver().add_loco(address, protocol, loco_type);
+ loco_id = layout.get_driver().add_loco(address, protocol, loco_type);
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));
Vehicle *veh = new Vehicle(layout, vt);
vehicles.back()->attach_back(*veh);
vehicles.push_back(veh);
+ veh->set_train(this);
+ signal_vehicle_added.emit(vehicles.size()-1, *veh);
}
void Train::remove_vehicle(unsigned i)
throw out_of_range("Train::remove_vehicle");
if(i==0)
throw logic_error("can't remove locomotive");
- delete vehicles[i];
+
+ Vehicle *veh = vehicles[i];
vehicles.erase(vehicles.begin()+i);
+ veh->detach_front();
if(i<vehicles.size())
+ {
+ veh->detach_back();
vehicles[i-1]->attach_back(*vehicles[i]);
+ }
+ signal_vehicle_removed.emit(i, *veh);
+ delete veh;
}
unsigned Train::get_n_vehicles() const
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::TimeStamp();
- allocator.reserve_more();
- }
- else
- stop_timeout = Time::now()+2*Time::sec;
-}
-
void Train::set_function(unsigned func, bool state)
{
if(!loco_type.get_functions().count(func))
throw invalid_argument("Train::set_function");
- layout.get_driver().set_loco_function(address, func, state);
+ layout.get_driver().set_loco_function(loco_id, func, state);
}
float Train::get_control(const string &ctrl) const
(*i)->message(msg);
}
-void Train::place(const BlockIter &block)
+bool Train::place(const BlockIter &block)
{
if(!block)
throw invalid_argument("Train::place");
if(controller->get_speed())
throw logic_error("moving");
- set_active(false);
accurate_position = false;
+ last_entry_block = BlockIter();
- allocator.start_from(block);
-
- if(reverse)
- vehicles.front()->place(block.reverse().track_iter(), 0, Vehicle::FRONT_BUFFER);
+ if(allocator.start_from(block))
+ {
+ if(reverse)
+ vehicles.front()->place(block.reverse().track_iter(), VehiclePlacement::FRONT_BUFFER);
+ else
+ vehicles.back()->place(block.track_iter(), VehiclePlacement::BACK_BUFFER);
+ return true;
+ }
else
- vehicles.back()->place(block.track_iter(), 0, Vehicle::BACK_BUFFER);
+ {
+ unplace();
+ return false;
+ }
}
void Train::unplace()
throw logic_error("moving");
allocator.clear();
-
- set_active(false);
accurate_position = false;
+ last_entry_block = BlockIter();
for(vector<Vehicle *>::iterator i=vehicles.begin(); i!=vehicles.end(); ++i)
(*i)->unplace();
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
{
if(allocator.empty())
- return;
+ return BlockIter();
+
+ BlockIter i = allocator.last_current().next();
if(controller->get_speed()==0)
- {
- allocator.release_noncurrent();
- return;
- }
+ return i;
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)
- {
- allocator.release_from(*i);
- return;
- }
+ return i;
dist += i->get_path_length(i.entry());
- if(i->get_sensor_id())
+ if(i->get_sensor_address())
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
return max(get_reserved_distance_until(0)-margin, 0.0f);
}
-void Train::reserve_more()
+void Train::tick(const Time::TimeDelta &dt)
{
- allocator.reserve_more();
-}
-
-void Train::tick(const Time::TimeStamp &t, const Time::TimeDelta &dt)
-{
- if(!active && stop_timeout && t>=stop_timeout)
+ if(stop_timeout)
{
- allocator.release_noncurrent();
- stop_timeout = Time::TimeStamp();
+ stop_timeout -= dt;
+ if(stop_timeout<=Time::zero)
+ {
+ allocator.set_active(false);
+ stop_timeout = Time::TimeDelta();
+ }
}
+ travel_time += dt;
+
Driver &driver = layout.get_driver();
+ bool intent_to_move = false;
for(list<TrainAI *>::iterator i=ais.begin(); i!=ais.end(); ++i)
- (*i)->tick(t, dt);
+ {
+ (*i)->tick(dt);
+ if((*i)->has_intent_to_move())
+ intent_to_move = true;
+ }
+
controller->tick(dt);
float speed = controller->get_speed();
bool moving = speed>0;
bool r = reverse;
if(loco_type.get_swap_direction())
r = !r;
- driver.set_loco_reverse(address, r);
+ driver.set_loco_reverse(loco_id, r);
allocator.reverse();
- if(active)
- allocator.reserve_more();
+ last_entry_block = BlockIter();
}
if(speed_quantizer)
if(speed_step!=current_speed_step && !speed_changing && !driver.is_halted() && driver.get_power())
{
speed_changing = true;
- driver.set_loco_speed(address, speed_step);
+ driver.set_loco_speed(loco_id, speed_step);
pure_speed = false;
}
if(moving)
{
- if(!active)
- set_active(true);
+ if(!allocator.is_active())
+ allocator.set_active(true);
Vehicle &vehicle = *(reverse ? vehicles.back() : vehicles.front());
float d = speed*(dt/Time::sec);
- if(allocator.is_block_current(vehicle.get_track()->get_block()))
+ if(allocator.is_block_current(vehicle.get_placement().get_position(reverse ? VehiclePlacement::BACK_AXLE : VehiclePlacement::FRONT_AXLE)->get_block()))
{
SetFlag setf(advancing);
vehicle.advance(reverse ? -d : d);
}
}
}
+ else if(intent_to_move && !allocator.is_active())
+ allocator.set_active(true);
+ else if(allocator.is_active() && !intent_to_move && !stop_timeout)
+ stop_timeout = 2*Time::sec;
}
void Train::save(list<DataFile::Statement> &st) const
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 rev)
+void Train::loco_speed_event(unsigned id, unsigned speed, bool rev)
{
- if(addr==address)
+ if(id==loco_id)
{
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);
+ layout.get_driver().set_loco_reverse(loco_id, r);
speed_changing = false;
pure_speed = false;
}
}
-void Train::loco_func_event(unsigned addr, unsigned func, bool state)
+void Train::loco_func_event(unsigned id, unsigned func, bool state)
{
- if(addr==address)
+ if(id==loco_id)
{
if(state)
functions |= 1<<func;
void Train::sensor_state_changed(Sensor &sensor, Sensor::State state)
{
- Block *block = 0;
- if(TrackCircuit *tc = dynamic_cast<TrackCircuit *>(&sensor))
- block = &tc->get_block();
- else
+ if(!current_speed_step || state!=Sensor::MAYBE_ACTIVE)
return;
- if(block->get_train()==this && state==Sensor::MAYBE_ACTIVE)
+ Block *block = sensor.get_block();
+ if(!block || block->get_train()!=this)
+ return;
+
+ if(last_entry_block && &*last_entry_block!=block)
+ {
+ for(BlockIter i=last_entry_block.next(); (i && &*i!=block); i=i.next())
+ if(i->get_train()!=this || i->get_sensor_address())
+ return;
+ }
+
+ if(dynamic_cast<TrackCircuit *>(&sensor))
{
- if(last_entry_block)
+ if(last_entry_block && pure_speed && speed_quantizer)
{
- float travel_distance = -1;
- if(pure_speed && speed_quantizer && current_speed_step>0)
- travel_distance = 0;
+ float travel_distance = 0;
for(BlockIter i=last_entry_block; &*i!=block; i=i.next())
- {
- if(i->get_sensor_id())
- return;
- if(travel_distance>=0)
- travel_distance += i->get_path_length(i.entry());
- }
+ travel_distance += i->get_path_length(i.entry());
if(travel_distance>0)
{
- float travel_time_secs = (Time::now()-last_entry_time)/Time::sec;
+ float travel_time_secs = travel_time/Time::sec;
if(travel_time_secs>=2)
speed_quantizer->learn(current_speed_step, travel_distance/travel_time_secs, travel_time_secs);
}
last_entry_block = allocator.iter_for(*block);
- last_entry_time = Time::now();
+ travel_time = Time::zero;
pure_speed = true;
accurate_position = true;
overshoot_dist = 0;
- if(!advancing && vehicles.front()->get_track())
+ if(!advancing && vehicles.front()->is_placed())
{
TrackIter track = last_entry_block.track_iter();
if(reverse)
{
track = track.flip();
- vehicles.back()->place(track, 0, Vehicle::BACK_AXLE);
+ vehicles.back()->place(track, VehiclePlacement::BACK_AXLE);
}
else
- vehicles.front()->place(track, 0, Vehicle::FRONT_AXLE);
+ vehicles.front()->place(track, VehiclePlacement::FRONT_AXLE);
+ }
+ }
+ else if(BeamGate *gate = dynamic_cast<BeamGate *>(&sensor))
+ {
+ if(!advancing && vehicles.front()->is_placed())
+ {
+ TrackIter track = allocator.iter_for(*block).track_iter();
+ for(; (track && &track->get_block()==block); track=track.next())
+ if(track.track()==gate->get_track())
+ {
+ if(reverse)
+ track = track.reverse();
+ float offset = gate->get_offset_from_endpoint(track.entry());
+ if(reverse)
+ vehicles.back()->place(TrackOffsetIter(track, offset), VehiclePlacement::BACK_BUFFER);
+ else
+ vehicles.front()->place(TrackOffsetIter(track, offset), VehiclePlacement::FRONT_BUFFER);
+ break;
+ }
}
}
}
Vehicle &veh = *(reverse ? vehicles.back() : vehicles.front());
- TrackIter track = veh.get_track_iter();
+ TrackOffsetIter track = veh.get_placement().get_position(reverse ? VehiclePlacement::BACK_AXLE : VehiclePlacement::FRONT_AXLE);
if(!track) // XXX Probably unnecessary
return 0;
- BlockIter block = track.block_iter();
- if(&*block==until_block)
+ if(&track->get_block()==until_block)
return 0;
// Account for the vehicle's offset on its current track
- float result = veh.get_offset();
+ float result = track.offset();
if(reverse)
track = track.reverse();
else
- result = track->get_type().get_path_length(track->get_active_path())-result;
+ result = track->get_path_length()-result;
result -= veh.get_type().get_length()/2;
+ BlockIter block = track.block_iter();
+
// Count remaining distance in the vehicle's current block
for(track=track.next(); &track->get_block()==&*block; track=track.next())
- result += track->get_type().get_path_length(track->get_active_path());
+ result += track->get_path_length();
const BlockIter &last = allocator.last();
if(&*block==&*last)
add("name", &Loader::name);
add("quantized_speed", &Loader::quantized_speed);
add("router", &Loader::router);
- add("timetable", &Loader::timetable);
add("vehicle", &Loader::vehicle);
}
{
TrackIter track = obj.allocator.first().track_iter();
float offset = 2*obj.layout.get_catalogue().get_scale();
- obj.vehicles.back()->place(track, offset, Vehicle::BACK_BUFFER);
+ obj.vehicles.back()->place(TrackOffsetIter(track, offset), VehiclePlacement::BACK_BUFFER);
}
}
load_sub(*rtr);
}
-void Train::Loader::timetable()
-{
- Timetable *ttbl = new Timetable(obj);
- load_sub(*ttbl);
-}
-
void Train::Loader::vehicle(ArticleNumber art_nr)
{
- const VehicleType &vtype = obj.layout.get_catalogue().get_vehicle(art_nr);
+ const VehicleType &vtype = obj.layout.get_catalogue().get<VehicleType>(art_nr);
Vehicle *veh = new Vehicle(obj.layout, vtype);
obj.vehicles.back()->attach_back(*veh);
obj.vehicles.push_back(veh);
+ veh->set_train(&obj);
}
} // namespace R2C2