btn->signal_clicked.connect(sigc::mem_fun(this, &TrainPanel::timetable_clicked));
}
-void TrainPanel::speed_slider_changed(double value)
-{
- float speed = value/3.6*engineer.get_layout().get_catalogue().get_scale();
- if(!tgl_forward->get_value())
- speed = -speed;
- train.set_control("speed", speed);
-}
-
void TrainPanel::train_control_changed(const string &control, float value)
{
if(control=="speed")
{
- float speed = abs(value)/engineer.get_layout().get_catalogue().get_scale()*3.6;
+ float speed = value/engineer.get_layout().get_catalogue().get_scale()*3.6;
sld_speed->set_value(speed);
lbl_speed->set_text(format("%3.0f", speed));
- if(value)
- tgl_forward->set_value(value>0);
}
+ else if(control=="reverse")
+ tgl_forward->set_value(value==0);
}
void TrainPanel::train_function_changed(unsigned func, bool value)
dialog->set_position(geom.x+geom.w, geom.y+geom.h-dialog->get_geometry().h);
}
-void TrainPanel::forward_toggled(bool /*value*/)
+void TrainPanel::speed_slider_changed(double value)
{
- train.set_control("speed", 0);
+ float speed = value/3.6*engineer.get_layout().get_catalogue().get_scale();
+ train.set_control("speed", speed);
+}
+
+void TrainPanel::forward_toggled(bool value)
+{
+ if(train.get_speed() || sld_speed->get_value())
+ {
+ train.set_control("speed", 0);
+ tgl_forward->set_value(!train.get_control("reverse"));
+ }
+ else
+ train.set_control("reverse", !value);
}
void TrainPanel::func_toggled(bool value, unsigned func)
public:
TrainPanel(Engineer &, const Msp::GLtk::Resources &, Marklin::Train &);
private:
- void speed_slider_changed(double);
void train_control_changed(const std::string &, float);
void train_function_changed(unsigned, bool);
void train_route_changed(const Marklin::Route *);
void route_clicked();
void goto_clicked();
void timetable_clicked();
+ void speed_slider_changed(double);
void forward_toggled(bool);
void func_toggled(bool, unsigned);
void place(Marklin::Track *, unsigned);
AIControl::AIControl(Train &t, ControlModel *n):
train(t),
next_model(n),
- target_speed(TrainControl::continuous("speed", -1000, 1000)),
- blocked(false)
+ target_speed(TrainControl::continuous("speed", 0, 1000)),
+ blocked(false),
+ approach(false)
{
target_speed.set(0);
train.signal_arrived.connect(sigc::mem_fun(this, &AIControl::arrived));
+ next_model->signal_control_changed.connect(sigc::mem_fun(this, &AIControl::control_changed));
}
AIControl::~AIControl()
target_speed.set(v);
if(!blocked)
- next_model->set_control("speed", target_speed.value);
-
- signal_control_changed.emit(n, target_speed.value);
+ {
+ float approach_speed = 5*train.get_layout().get_catalogue().get_scale();
+ if(approach && target_speed.value>approach_speed)
+ next_model->set_control("speed", approach_speed);
+ else
+ next_model->set_control("speed", target_speed.value);
+ }
+
+ signal_control_changed.emit(target_speed);
}
else
next_model->set_control(n, v);
bool AIControl::get_reverse() const
{
- if(float ns = next_model->get_speed())
- return ns<0;
- else
- return target_speed.value<0;
+ return next_model->get_reverse();
}
float AIControl::get_braking_distance() const
void AIControl::tick(const Time::TimeDelta &dt)
{
+ float scale = train.get_layout().get_catalogue().get_scale();
float rsv_dist = train.get_reserved_distance();
- float brake_dist = next_model->get_braking_distance()*1.15;
- float margin = 25*train.get_layout().get_catalogue().get_scale();
+ float brake_dist = next_model->get_braking_distance();
+ float approach_margin = 50*scale;
+ float approach_speed = 5*scale;
+ float margin = 10*scale;
+
if(!blocked && rsv_dist<brake_dist+margin)
{
blocked = true;
next_model->set_control("speed", 0);
}
- else if(blocked && rsv_dist>brake_dist+margin*3)
+ else if((!approach && rsv_dist<brake_dist*1.3+approach_margin) || (blocked && rsv_dist>brake_dist+margin*2))
+ {
+ blocked = false;
+ approach = true;
+ if(target_speed.value>approach_speed)
+ next_model->set_control("speed", approach_speed);
+ else
+ next_model->set_control("speed", target_speed.value);
+ }
+ else if((blocked || approach) && rsv_dist>brake_dist*1.3+approach_margin*2)
{
blocked = false;
+ approach = false;
next_model->set_control("speed", target_speed.value);
}
train.set_active(false);
}
-void AIControl::control_changed(const string &n, float v)
+void AIControl::control_changed(const TrainControl &ctrl)
{
- if(n!="speed")
- signal_control_changed.emit(n, v);
+ if(ctrl.name!="speed")
+ signal_control_changed.emit(ctrl);
}
void AIControl::arrived()
ControlModel *next_model;
TrainControl target_speed;
bool blocked;
+ bool approach;
public:
AIControl(Train &, ControlModel *);
virtual void tick(const Msp::Time::TimeDelta &);
private:
- void control_changed(const std::string &, float);
+ void control_changed(const TrainControl &);
void arrived();
};
namespace Marklin {
-class TrainControl;
+struct TrainControl;
+/**
+Interface class for train control models. Takes input through a uniform named
+control interface. Provides information about train movement on output.
+*/
class ControlModel
{
public:
- sigc::signal<void, const std::string &, float> signal_control_changed;
+ sigc::signal<void, const TrainControl &> signal_control_changed;
protected:
ControlModel() { }
virtual void set_control(const std::string &, float) = 0;
virtual const TrainControl &get_control(const std::string &) const = 0;
+ /** Returns the current speed. Always non-negative. */
virtual float get_speed() const = 0;
+
+ /** Returns true if traveling in reverse. */
virtual bool get_reverse() const = 0;
+
+ /** Determines the distance required to come to a full stop. */
virtual float get_braking_distance() const = 0;
virtual void tick(const Msp::Time::TimeDelta &) = 0;
namespace Marklin {
SimplePhysics::SimplePhysics():
- target_speed(TrainControl::continuous("speed", -1000, 1000)),
+ target_speed(TrainControl::continuous("speed", 0, 1000)),
+ reverse(TrainControl::binary("reverse")),
accel(0.07),
speed(0)
{
if(name=="speed")
{
target_speed.set(v);
- signal_control_changed.emit(name, target_speed.value);
+ signal_control_changed.emit(target_speed);
+ }
+ else if(name=="reverse")
+ {
+ if(target_speed.value || speed)
+ throw InvalidState("Must be stopped to change reverse");
+ reverse.set(v);
+ signal_control_changed.emit(reverse);
}
}
{
if(name=="speed")
return target_speed;
+ else if(name=="reverse")
+ return reverse;
else
throw KeyError("Unknown control", name);
}
{
private:
TrainControl target_speed;
+ TrainControl reverse;
float accel;
float speed;
virtual const TrainControl &get_control(const std::string &) const;
virtual float get_speed() const { return speed; }
- virtual bool get_reverse() const { return speed<0; }
+ virtual bool get_reverse() const { return reverse.value; }
virtual float get_braking_distance() const;
virtual void tick(const Msp::Time::TimeDelta &);
layout.get_driver().signal_halt.connect(sigc::mem_fun(this, &Train::halt_event));
- control->signal_control_changed.connect(signal_control_changed);
+ control->signal_control_changed.connect(sigc::mem_fun(this, &Train::control_changed));
}
Train::~Train()
layout.get_driver().set_loco_function(address+1, func-4, state);
}
+float Train::get_control(const string &ctrl) const
+{
+ return control->get_control(ctrl).value;
+}
+
+float Train::get_speed() const
+{
+ return control->get_speed();
+}
+
bool Train::get_function(unsigned func) const
{
return (functions>>func)&1;
timetable->tick(t);
control->tick(dt);
float speed = control->get_speed();
- unsigned speed_notch = find_speed(abs(speed));
+ unsigned speed_notch = find_speed(speed);
if(control->get_reverse()!=reverse)
{
}
}
}
- else if(end_of_route)
+ else if(end_of_route && rsv_blocks.empty())
+ {
+ signal_arrived.emit();
set_route(0);
+ }
if(!cur_blocks.empty() && !cur_blocks.front().block->get_sensor_id())
{
}
}
+void Train::control_changed(const TrainControl &ctrl)
+{
+ signal_control_changed.emit(ctrl.name, ctrl.value);
+}
+
void Train::loco_speed_event(unsigned addr, unsigned speed, bool)
{
if(addr==address)
// Try to get more blocks if we're moving
if(active)
- {
- unsigned nsens = reserve_more();
- if(!nsens && end_of_route)
- signal_arrived.emit();
- }
+ reserve_more();
}
else if(result==3)
layout.emergency("Sensor for "+name+" triggered out of order");
class Timetable;
class Vehicle;
class VehicleType;
+struct TrainControl;
class Train: public sigc::trackable
{
const std::string &get_name() const { return name; }
void set_priority(int);
int get_priority() const { return priority; }
- ControlModel &get_control() const { return *control; }
+ ControlModel &get_control_model() const { return *control; }
void add_vehicle(const VehicleType &);
void remove_vehicle(unsigned);
void set_active(bool);
void set_function(unsigned, bool);
float get_control(const std::string &) const;
+ float get_speed() const;
bool is_active() const { return active; }
bool get_function(unsigned) const;
unsigned get_functions() const { return functions; }
void save(std::list<Msp::DataFile::Statement> &) const;
private:
+ void control_changed(const TrainControl &);
void loco_speed_event(unsigned, unsigned, bool);
void loco_func_event(unsigned, unsigned, bool);
void sensor_event(unsigned, bool);