/* $Id$ */
+scale 1 87;
+gauge 16.5;
+
locomotive 37844
{
name "BR 50";
{
const map<unsigned, Train *> &trains = layout.get_trains();
for(map<unsigned, Train *>::const_iterator i=trains.begin(); i!=trains.end(); ++i)
- i->second->set_speed(0);
+ layout.get_driver().set_loco_speed(i->first, 0);
layout.get_driver().flush();
if(!options.simulate)
IO::print(IO::cerr, "Fatal signal received, terminating\n");
const map<unsigned, Train *> &trains = layout.get_trains();
for(map<unsigned, Train *>::const_iterator i=trains.begin(); i!=trains.end(); ++i)
- i->second->set_speed(0);
+ layout.get_driver().set_loco_speed(i->first, 0);
layout.get_driver().flush();
raise(sig);
}
Distributed under the GPL
*/
+#include <cmath>
#include <msp/gltk/button.h>
#include <msp/strings/formatter.h>
#include "libmarklin/locotype.h"
{
set_size(200, 170);
+ train.signal_control_changed.connect(sigc::mem_fun(this, &TrainPanel::train_control_changed));
+
add(*(lbl_addr=new GLtk::Label(res, format("%2d", train.get_address()))));
lbl_addr->set_style("digital");
lbl_addr->set_geometry(GLtk::Geometry(10, geom.h-34, 35, 24));
lbl_name->set_geometry(GLtk::Geometry(45, geom.h-34, geom.w-55, 24));
train.signal_name_changed.connect(sigc::mem_fun(lbl_name, &GLtk::Label::set_text));
- add(*(lbl_speed=new GLtk::Label(res, format("%2d", train.get_speed()))));
+ add(*(lbl_speed=new GLtk::Label(res, " 0")));
lbl_speed->set_style("digital");
lbl_speed->set_geometry(GLtk::Geometry(10, geom.h-58, 35, 24));
- train.signal_target_speed_changed.connect(sigc::mem_fun(this, &TrainPanel::train_speed_changed));
add(*(sld_speed=new GLtk::HSlider(res)));
sld_speed->set_geometry(GLtk::Geometry(50, geom.h-51, geom.w-80, 10));
- sld_speed->set_range(0, 14);
- sld_speed->set_step(1);
+ sld_speed->set_range(0, 200);
+ sld_speed->set_step(5);
sld_speed->signal_value_changed.connect(sigc::mem_fun(this, &TrainPanel::speed_slider_changed));
add(*(tgl_forward=new GLtk::Toggle(res)));
tgl_forward->set_text("Fwd");
tgl_forward->set_geometry(GLtk::Geometry(geom.w-30, geom.h-59, 20, 27));
- tgl_forward->set_value(!train.get_reverse());
+ tgl_forward->set_value(true);
tgl_forward->signal_toggled.connect(sigc::mem_fun(this, &TrainPanel::forward_toggled));
- train.signal_reverse_changed.connect(sigc::mem_fun(this, &TrainPanel::train_reverse_changed));
const Route *route = train.get_route();
add(*(lbl_route=new GLtk::Label(res, (route ? route->get_name() : "Free run"))));
btn->signal_clicked.connect(sigc::mem_fun(this, &TrainPanel::route_clicked));
}
-void TrainPanel::speed_slider_changed(double v)
-{
- train.set_speed(static_cast<unsigned>(v));
-}
-
-void TrainPanel::train_speed_changed(unsigned speed)
+void TrainPanel::speed_slider_changed(double value)
{
- lbl_speed->set_text(format("%2d", speed));
- sld_speed->set_value(speed);
+ 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_reverse_changed(bool reverse)
+void TrainPanel::train_control_changed(const string &control, float value)
{
- tgl_forward->set_value(!reverse);
+ if(control=="speed")
+ {
+ float speed = abs(value)/engineer.get_layout().get_catalogue().get_scale()*3.6;
+ sld_speed->set_value(speed);
+ lbl_speed->set_text(format("%03.0f", speed));
+ if(value)
+ tgl_forward->set_value(value>0);
+ }
}
void TrainPanel::train_function_changed(unsigned func, bool value)
pick_conn = engineer.signal_pick_done.connect(sigc::mem_fun(this, &TrainPanel::go_to));
}
-void TrainPanel::forward_toggled(bool value)
+void TrainPanel::forward_toggled(bool /*value*/)
{
- train.set_reverse(!value);
+ train.set_control("speed", 0);
}
void TrainPanel::func_toggled(bool value, unsigned func)
TrainPanel(Engineer &, const Msp::GLtk::Resources &, Marklin::Train &);
private:
void speed_slider_changed(double);
- void train_speed_changed(unsigned);
- void train_reverse_changed(bool);
+ void train_control_changed(const std::string &, float);
void train_function_changed(unsigned, bool);
void train_route_changed(const Marklin::Route *);
void train_status_changed(const std::string &);
--- /dev/null
+/* $Id$
+
+This file is part of the MSP Märklin suite
+Copyright © 2010 Mikkosoft Productions, Mikko Rasa
+Distributed under the GPL
+*/
+
+#include "aicontrol.h"
+#include "catalogue.h"
+#include "layout.h"
+#include "train.h"
+
+using namespace std;
+using namespace Msp;
+
+namespace Marklin {
+
+AIControl::AIControl(Train &t, ControlModel *n):
+ train(t),
+ next_model(n),
+ target_speed(TrainControl::continuous("speed", -1000, 1000)),
+ blocked(false)
+{
+ target_speed.set(0);
+
+ train.signal_arrived.connect(sigc::mem_fun(this, &AIControl::arrived));
+}
+
+AIControl::~AIControl()
+{
+ delete next_model;
+}
+
+void AIControl::set_control(const string &n, float v)
+{
+ if(n=="speed")
+ {
+ if(v && !train.is_active())
+ train.set_active(true);
+
+ target_speed.set(v);
+ if(!blocked)
+ next_model->set_control("speed", target_speed.value);
+ }
+ else
+ next_model->set_control(n, v);
+}
+
+const TrainControl &AIControl::get_control(const string &n) const
+{
+ if(n=="speed")
+ return target_speed;
+ else
+ return next_model->get_control(n);
+}
+
+float AIControl::get_speed() const
+{
+ return next_model->get_speed();
+}
+
+float AIControl::get_braking_distance() const
+{
+ return next_model->get_braking_distance();
+}
+
+void AIControl::tick(const Time::TimeDelta &dt)
+{
+ 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();
+ if(!blocked && rsv_dist<brake_dist+margin)
+ {
+ blocked = true;
+ next_model->set_control("speed", 0);
+ }
+ else if(blocked && rsv_dist>brake_dist+margin*3)
+ {
+ blocked = false;
+ next_model->set_control("speed", target_speed.value);
+ }
+
+ next_model->tick(dt);
+
+ if(!target_speed.value && !next_model->get_speed() && train.is_active())
+ train.set_active(false);
+}
+
+void AIControl::arrived()
+{
+ set_control("speed", 0);
+}
+
+} // namespace Marklin
--- /dev/null
+/* $Id$
+
+This file is part of the MSP Märklin suite
+Copyright © 2010 Mikkosoft Productions, Mikko Rasa
+Distributed under the GPL
+*/
+
+#ifndef LIBMARKLIN_AICONTROL_H_
+#define LIBMARKLIN_AICONTROL_H_
+
+#include <sigc++/trackable.h>
+#include "controlmodel.h"
+#include "traincontrol.h"
+
+namespace Marklin {
+
+class Train;
+
+class AIControl: public ControlModel, public sigc::trackable
+{
+private:
+ Train &train;
+ ControlModel *next_model;
+ TrainControl target_speed;
+ bool blocked;
+
+public:
+ AIControl(Train &, ControlModel *);
+ virtual ~AIControl();
+
+ virtual void set_control(const std::string &, float);
+ virtual const TrainControl &get_control(const std::string &) const;
+
+ virtual float get_speed() const;
+ virtual float get_braking_distance() const;
+
+ virtual void tick(const Msp::Time::TimeDelta &);
+
+private:
+ void arrived();
+};
+
+} // namespace Marklin
+
+#endif
--- /dev/null
+/* $Id$
+
+This file is part of the MSP Märklin suite
+Copyright © 2010 Mikkosoft Productions, Mikko Rasa
+Distributed under the GPL
+*/
+
+#ifndef LIBMARKLIN_CONTROLMODEL_H_
+#define LIBMARKLIN_CONTROLMODEL_H_
+
+#include <string>
+#include <msp/time/timedelta.h>
+
+namespace Marklin {
+
+class TrainControl;
+
+class ControlModel
+{
+protected:
+ ControlModel() { }
+public:
+ virtual ~ControlModel() { }
+
+ virtual void set_control(const std::string &, float) = 0;
+ virtual const TrainControl &get_control(const std::string &) const = 0;
+
+ virtual float get_speed() const = 0;
+ virtual float get_braking_distance() const = 0;
+
+ virtual void tick(const Msp::Time::TimeDelta &) = 0;
+};
+
+} // namespace Marklin
+
+#endif
--- /dev/null
+/* $Id$
+
+This file is part of the MSP Märklin suite
+Copyright © 2010 Mikkosoft Productions, Mikko Rasa
+Distributed under the GPL
+*/
+
+#include <msp/core/except.h>
+#include <msp/time/units.h>
+#include "simplephysics.h"
+
+using namespace std;
+using namespace Msp;
+
+namespace Marklin {
+
+SimplePhysics::SimplePhysics():
+ target_speed(TrainControl::continuous("speed", -1000, 1000)),
+ accel(0.07),
+ speed(0)
+{
+ target_speed.set(0);
+}
+
+void SimplePhysics::set_control(const string &name, float v)
+{
+ if(name=="speed")
+ target_speed.set(v);
+}
+
+const TrainControl &SimplePhysics::get_control(const string &name) const
+{
+ if(name=="speed")
+ return target_speed;
+ else
+ throw KeyError("Unknown control", name);
+}
+
+float SimplePhysics::get_braking_distance() const
+{
+ return speed*speed/(2*accel);
+}
+
+void SimplePhysics::tick(const Time::TimeDelta &dt)
+{
+ float secs = dt/Time::sec;
+ if(speed<target_speed.value)
+ {
+ speed += secs*accel;
+ if(speed>target_speed.value)
+ speed = target_speed.value;
+ }
+ else if(speed>target_speed.value)
+ {
+ speed -= secs*accel;
+ if(speed<target_speed.value)
+ speed = target_speed.value;
+ }
+}
+
+} // namespace Marklin
--- /dev/null
+/* $Id$
+
+This file is part of the MSP Märklin suite
+Copyright © 2010 Mikkosoft Productions, Mikko Rasa
+Distributed under the GPL
+*/
+
+#ifndef LIBMARKLIN_SIMPLEPHYSICS_H_
+#define LIBMARKLIN_SIMPLEPHYSICS_H_
+
+#include <string>
+#include "controlmodel.h"
+#include "traincontrol.h"
+
+namespace Marklin {
+
+class SimplePhysics: public ControlModel
+{
+private:
+ TrainControl target_speed;
+ float accel;
+ float speed;
+
+public:
+ SimplePhysics();
+
+ virtual void set_control(const std::string &, float);
+ virtual const TrainControl &get_control(const std::string &) const;
+
+ virtual float get_speed() const { return speed; }
+ virtual float get_braking_distance() const;
+
+ virtual void tick(const Msp::Time::TimeDelta &);
+};
+
+} // namespace Marklin
+
+#endif
throw Exception("Track endpoint did not have a counterpart");
}
+unsigned Track::traverse(unsigned i) const
+{
+ return traverse(i, active_path);
+}
+
TrackPoint Track::get_point(unsigned epi, unsigned path, float d) const
{
TrackPoint p = type.get_point(epi, path, d);
const std::vector<Track *> &get_links() const { return links; }
Track *get_link(unsigned) const;
unsigned traverse(unsigned, unsigned) const;
+ unsigned traverse(unsigned) const;
TrackPoint get_point(unsigned, unsigned, float) const;
TrackPoint get_point(unsigned, float) const;
#include <msp/strings/formatter.h>
#include <msp/time/units.h>
#include <msp/time/utils.h>
+#include "aicontrol.h"
+#include "catalogue.h"
#include "driver.h"
#include "layout.h"
#include "locotype.h"
#include "route.h"
+#include "simplephysics.h"
#include "tracktype.h"
#include "train.h"
#include "vehicle.h"
loco_type(t),
address(a),
pending_block(0),
- target_speed(0),
+ control(new AIControl(*this, new SimplePhysics)),
+ active(false),
current_speed(0),
+ speed_changing(false),
reverse(false),
functions(0),
route(0),
end_of_route(false),
status("Unplaced"),
travel_dist(0),
- travel_speed(0),
pure_speed(false),
real_speed(15)
{
return *vehicles[i];
}
-void Train::set_speed(unsigned speed)
+void Train::set_control(const string &n, float v)
{
- if(speed==target_speed)
- return;
- travel_speed = static_cast<int>(round(get_real_speed(speed)*87*3.6/5))*5;
-
- target_speed = speed;
- if(!target_speed)
- {
- pending_block = 0;
- stop_timeout = Time::now()+(800+current_speed*150)*Time::msec;
- }
- else
- reserve_more();
-
- signal_target_speed_changed.emit(target_speed);
-
- update_speed();
- pure_speed = false;
+ control->set_control(n, v);
+ signal_control_changed.emit(n, control->get_control(n).value);
}
-void Train::set_reverse(bool rev)
+void Train::set_active(bool a)
{
- if(rev==reverse)
+ if(a==active)
return;
+ if(!a && control->get_speed())
+ throw InvalidState("Can't deactivate while moving");
- if(target_speed)
+ active = a;
+ if(active)
{
- set_speed(0);
- return;
+ stop_timeout = Time::TimeStamp();
+ reserve_more();
+ }
+ else
+ {
+ stop_timeout = Time::now()+2*Time::sec;
+ set_status("Stopped");
}
- else if(stop_timeout)
- return;
-
- layout.get_driver().set_loco_reverse(address, rev);
-
- release_blocks(rsv_blocks);
- reverse_blocks(cur_blocks);
}
void Train::set_function(unsigned func, bool state)
}
}
- if(target_speed && reserve_more()<2)
- update_speed();
+ if(active)
+ reserve_more();
signal_route_changed.emit(route);
}
for(list<BlockRef>::const_iterator i=cur_blocks.begin(); i!=cur_blocks.end(); ++i)
if(i->block->get_tracks().count(const_cast<Track *>(&to)))
{
- set_speed(0);
+ signal_arrived.emit();
set_route(0);
return;
}
void Train::place(Block &block, unsigned entry)
{
- if(target_speed)
- set_speed(0);
+ if(control->get_speed())
+ throw InvalidState("Must be stopped before placing");
release_blocks(rsv_blocks);
release_blocks(cur_blocks);
+ set_active(false);
+
if(!block.reserve(this))
{
set_status("Unplaced");
const Block::Endpoint &bep = block.get_endpoints()[entry];
vehicles.front()->place(bep.track, bep.track_ep, 0, Vehicle::BACK_BUFFER);
}
-
- set_status("Stopped");
}
bool Train::free_block(Block &block)
if(nsens<1)
return false;
release_blocks(rsv_blocks, i, rsv_blocks.end());
- update_speed();
return true;
}
else if(i->block->get_sensor_id())
return -1;
}
+float Train::get_reserved_distance() const
+{
+ Vehicle &veh = *(reverse ? vehicles.back() : vehicles.front());
+ const VehicleType &vtype = veh.get_type();
+
+ Track *track = veh.get_track();
+ unsigned entry = veh.get_entry();
+
+ float result = -vtype.get_length()/2;
+ if(reverse)
+ {
+ entry = track->traverse(entry);
+ result += veh.get_offset();
+ }
+ else
+ result -= veh.get_offset();
+
+ bool first = true;
+ list<BlockRef>::const_iterator block = cur_blocks.begin();
+ while(1)
+ {
+ if(!first || !reverse)
+ result += track->get_type().get_path_length(track->get_active_path());
+ first = false;
+
+ unsigned exit = track->traverse(entry);
+ Track *next = track->get_link(exit);
+
+ while(!block->block->get_tracks().count(next))
+ {
+ ++block;
+ if(block==cur_blocks.end())
+ block = rsv_blocks.begin();
+ if(block==rsv_blocks.end())
+ return result;
+ }
+
+ entry = next->get_endpoint_by_link(*track);
+ track = next;
+ }
+}
+
void Train::tick(const Time::TimeStamp &t, const Time::TimeDelta &dt)
{
- if(stop_timeout && t>=stop_timeout)
+ if(!active && stop_timeout && t>=stop_timeout)
{
release_blocks(rsv_blocks);
end_of_route = false;
stop_timeout = Time::TimeStamp();
}
- if(current_speed)
+ control->tick(dt);
+ float speed = control->get_speed();
+ unsigned speed_notch = find_speed(abs(speed));
+
+ if(speed && (speed<0)!=reverse)
+ {
+ layout.get_driver().set_loco_reverse(address, speed<0);
+ reverse = speed<0;
+
+ release_blocks(rsv_blocks);
+ reverse_blocks(cur_blocks);
+
+ reserve_more();
+ }
+ if(speed_notch!=current_speed && !speed_changing)
+ {
+ speed_changing = true;
+ layout.get_driver().set_loco_speed(address, speed_notch);
+
+ pure_speed = false;
+
+ if(speed_notch)
+ set_status(format("Traveling %d kmh", get_travel_speed()));
+ else
+ set_status("Waiting");
+ }
+
+ if(speed)
{
+ if(!active)
+ set_active(true);
+
Track *track = vehicles[0]->get_track();
bool ok = false;
st.push_back((DataFile::Statement("route"), route->get_name()));
}
-void Train::loco_speed_event(unsigned addr, unsigned speed, bool rev)
+void Train::loco_speed_event(unsigned addr, unsigned speed, bool)
{
if(addr==address)
{
current_speed = speed;
- reverse = rev;
-
- signal_speed_changed.emit(current_speed);
- signal_reverse_changed.emit(reverse);
+ speed_changing = false;
}
}
{
// Compute speed and update related state
float travel_time_secs = (Time::now()-last_entry_time)/Time::sec;
- travel_speed = static_cast<int>(round(travel_dist/travel_time_secs*87*3.6/5))*5;
if(pure_speed)
{
RealSpeed &rs = real_speed[current_speed];
rs.add(travel_dist/travel_time_secs, travel_time_secs);
+ set_status(format("Traveling %d kmh", get_travel_speed()));
}
travel_dist = 0;
cur_blocks.splice(cur_blocks.end(), rsv_blocks, rsv_blocks.begin(), i);
// Try to get more blocks if we're moving
- if(target_speed)
+ if(active)
{
unsigned nsens = reserve_more();
if(!nsens && end_of_route)
{
- set_speed(0);
+ signal_arrived.emit();
set_route(0);
}
- else if(nsens<2)
- update_speed();
}
}
}
last = &rsv_blocks.back();
}
- if(got_more)
- update_speed();
-
return good_sens;
}
-void Train::update_speed()
-{
- Driver &driver = layout.get_driver();
-
- unsigned speed;
- if(!target_speed)
- {
- speed = 0;
- set_status("Stopped");
- }
- else
- {
- unsigned nsens = 0;
- for(list<BlockRef>::const_iterator i=rsv_blocks.begin(); i!=rsv_blocks.end(); ++i)
- if(i->block->get_sensor_id())
- ++nsens;
-
- unsigned slow_speed = find_speed(0.1); // 31.3 km/h
- if(nsens==0)
- {
- speed = 0;
- pure_speed = false;
- set_status("Blocked");
- }
- else if(nsens==1 && target_speed>slow_speed)
- {
- speed = slow_speed;
- pure_speed = false;
- set_status("Slow");
- }
- else
- {
- speed = target_speed;
- set_status(format("Traveling %d kmh", travel_speed));
- }
- }
-
- driver.set_loco_speed(address, speed);
-}
-
float Train::get_real_speed(unsigned i) const
{
if(real_speed[i].weight)
return static_cast<unsigned>(low*(1-f)+high*f+0.5);
}
+float Train::get_travel_speed() const
+{
+ float speed = get_real_speed(current_speed);
+ 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;
namespace Marklin {
+class ControlModel;
class LocoType;
class Route;
class Vehicle;
};
sigc::signal<void, const std::string &> signal_name_changed;
- sigc::signal<void, unsigned> signal_target_speed_changed;
- sigc::signal<void, unsigned> signal_speed_changed;
- sigc::signal<void, bool> signal_reverse_changed;
+ sigc::signal<void, const std::string &, float> signal_control_changed;
sigc::signal<void, unsigned, bool> signal_function_changed;
sigc::signal<void, const Route *> signal_route_changed;
+ sigc::signal<void> signal_arrived;
sigc::signal<void, const std::string &> signal_status_changed;
private:
std::list<BlockRef> cur_blocks;
std::list<BlockRef> rsv_blocks;
Block *pending_block;
- unsigned target_speed;
+ ControlModel *control;
+ bool active;
unsigned current_speed;
+ bool speed_changing;
bool reverse;
Msp::Time::TimeStamp stop_timeout;
unsigned functions;
Msp::Time::TimeStamp last_entry_time;
float travel_dist;
- unsigned travel_speed;
bool pure_speed;
std::vector<RealSpeed> real_speed;
Train(Layout &, const LocoType &, unsigned);
~Train();
+ Layout &get_layout() const { return layout; }
const LocoType &get_locomotive_type() const { return loco_type; }
unsigned get_address() const { return address; }
void set_name(const std::string &);
const std::string &get_name() const { return name; }
+ ControlModel &get_control() const { return *control; }
Vehicle &get_vehicle(unsigned);
const Vehicle &get_vehicle(unsigned) const;
- void set_speed(unsigned);
- void set_reverse(bool);
+ void set_control(const std::string &, float);
+ void set_active(bool);
void set_function(unsigned, bool);
- unsigned get_target_speed() const { return target_speed; }
- unsigned get_speed() const { return current_speed; }
- bool get_reverse() const { return reverse; }
+ float get_control(const std::string &) const;
+ bool is_active() const { return active; }
bool get_function(unsigned) const;
unsigned get_functions() const { return functions; }
bool is_placed() const { return !cur_blocks.empty(); }
bool free_block(Block &);
int get_entry_to_block(Block &) const;
+ float get_reserved_distance() const;
const std::string &get_status() const { return status; }
void turnout_event(unsigned, bool);
void block_reserved(const Block &, const Train *);
unsigned reserve_more();
- void update_speed();
float get_real_speed(unsigned) const;
unsigned find_speed(float) const;
+ float get_travel_speed() const;
void set_status(const std::string &);
void release_blocks(std::list<BlockRef> &);
void release_blocks(std::list<BlockRef> &, std::list<BlockRef>::iterator, std::list<BlockRef>::iterator);
--- /dev/null
+/* $Id$
+
+This file is part of the MSP Märklin suite
+Copyright © 2010 Mikkosoft Productions, Mikko Rasa
+Distributed under the GPL
+*/
+
+#include <cmath>
+#include <msp/core/except.h>
+#include "traincontrol.h"
+
+using namespace std;
+using namespace Msp;
+
+namespace Marklin {
+
+void TrainControl::set(float v)
+{
+ if(v<min_value)
+ v = min_value;
+ else if(v>max_value)
+ v = max_value;
+ else if(type==BINARY)
+ value = v ? 1 : 0;
+ else if(type==DISCRETE)
+ value = min_value+floor((v-min_value)/step)*step;
+ else if(type==CONTINUOUS)
+ value = v;
+}
+
+TrainControl TrainControl::binary(const string &n)
+{
+ TrainControl tc;
+ tc.name = n;
+ tc.type = BINARY;
+ tc.min_value = 0;
+ tc.max_value = 1;
+ tc.step = 1;
+ tc.value = 0;
+
+ return tc;
+}
+
+TrainControl TrainControl::discrete(const string &n, float m, float x, float s)
+{
+ if(x<m)
+ throw InvalidParameterValue("Max value must be greater than min value");
+
+ TrainControl tc;
+ tc.name = n;
+ tc.type = DISCRETE;
+ tc.min_value = m;
+ tc.max_value = m+floor((x-m)/s)*s;
+ tc.step = s;
+ tc.value = m;
+
+ return tc;
+}
+
+TrainControl TrainControl::continuous(const string &n, float m, float x)
+{
+ if(x<m)
+ throw InvalidParameterValue("Max value must be greater than min value");
+
+ TrainControl tc;
+ tc.name = n;
+ tc.type = CONTINUOUS;
+ tc.min_value = m;
+ tc.max_value = x;
+ tc.step = 0;
+ tc.value = m;
+
+ return tc;
+}
+
+} // namespace Marklin
--- /dev/null
+/* $Id$
+
+This file is part of the MSP Märklin suite
+Copyright © 2010 Mikkosoft Productions, Mikko Rasa
+Distributed under the GPL
+*/
+
+#ifndef LIBMARKLIN_TRAINCONTROL_H_
+#define LIBMARKLIN_TRAINCONTROL_H_
+
+#include <string>
+
+namespace Marklin {
+
+struct TrainControl
+{
+ enum Type
+ {
+ BINARY,
+ DISCRETE,
+ CONTINUOUS
+ };
+
+ std::string name;
+ Type type;
+ float min_value;
+ float max_value;
+ float step;
+ float value;
+
+private:
+ TrainControl() { }
+
+public:
+ void set(float);
+
+ static TrainControl binary(const std::string &);
+ static TrainControl discrete(const std::string &, float, float, float);
+ static TrainControl continuous(const std::string &, float, float);
+};
+
+} // namespace Marklin
+
+#endif
void place(Track *, unsigned, float, PlaceMode = CENTER);
void advance(float);
Track *get_track() const { return track_pos.track; }
+ unsigned get_entry() const { return track_pos.ep; }
+ float get_offset() const { return track_pos.offs; }
const Point &get_position() const { return position; }
float get_direction() const { return direction; }
private:
signal_train_added.emit(*train);
}
-void Client::receive(const TrainSpeedPacket &pkt)
+void Client::receive(const TrainControlPacket &pkt)
{
get_train(pkt.address).process_packet(pkt);
}
namespace Marklin {
class Client: public Msp::Net::PacketReceiver<TrainInfoPacket>,
- Msp::Net::PacketReceiver<TrainSpeedPacket>,
Msp::Net::PacketReceiver<TrainFunctionPacket>,
+ Msp::Net::PacketReceiver<TrainControlPacket>,
Msp::Net::PacketReceiver<TrainStatusPacket>,
Msp::Net::PacketReceiver<RouteInfoPacket>,
Msp::Net::PacketReceiver<TrainRoutePacket>,
private:
virtual void receive(const TrainInfoPacket &);
- virtual void receive(const TrainSpeedPacket &);
+ virtual void receive(const TrainControlPacket &);
virtual void receive(const TrainFunctionPacket &);
virtual void receive(const TrainStatusPacket &);
virtual void receive(const RouteInfoPacket &);
std::string name;
};
-struct TrainSpeedPacket
+struct TrainControlPacket
{
unsigned address;
- unsigned speed;
- char reverse;
+ std::string control;
+ float value;
};
struct TrainFunctionPacket
{
add<TrainInfoPacket>() (&TrainInfoPacket::address)
(&TrainInfoPacket::loco_type) (&TrainInfoPacket::name);
- add<TrainSpeedPacket>() (&TrainSpeedPacket::address)
- (&TrainSpeedPacket::speed) (&TrainSpeedPacket::reverse);
+ add<TrainControlPacket>() (&TrainControlPacket::address)
+ (&TrainControlPacket::control) (&TrainControlPacket::value);
add<TrainFunctionPacket>() (&TrainFunctionPacket::address)
(&TrainFunctionPacket::functions);
add<TrainStatusPacket>() (&TrainStatusPacket::address)
void Server::train_added(Train &train)
{
- train.signal_target_speed_changed.connect(sigc::bind<0>(sigc::mem_fun(this, &Server::train_speed_changed), sigc::ref(train)));
- train.signal_reverse_changed.connect(sigc::bind<0>(sigc::mem_fun(this, &Server::train_reverse_changed), sigc::ref(train)));
+ train.signal_control_changed.connect(sigc::bind<0>(sigc::mem_fun(this, &Server::train_control_changed), sigc::ref(train)));
train.signal_function_changed.connect(sigc::bind<0>(sigc::mem_fun(this, &Server::train_function_changed), sigc::ref(train)));
train.signal_route_changed.connect(sigc::bind<0>(sigc::mem_fun(this, &Server::train_route_changed), sigc::ref(train)));
train.signal_status_changed.connect(sigc::bind<0>(sigc::mem_fun(this, &Server::train_status_changed), sigc::ref(train)));
send(pkt);
}
-void Server::train_speed_changed(const Train &train, unsigned speed)
+void Server::train_control_changed(const Train &train, const string &control, float value)
{
- TrainSpeedPacket pkt;
+ TrainControlPacket pkt;
pkt.address = train.get_address();
- pkt.speed = speed;
- pkt.reverse = train.get_reverse();
- send(pkt);
-}
-
-void Server::train_reverse_changed(const Train &train, bool reverse)
-{
- TrainSpeedPacket pkt;
- pkt.address = train.get_address();
- pkt.speed = train.get_target_speed();
- pkt.reverse = reverse;
+ pkt.control = control;
+ pkt.value = value;
send(pkt);
}
pkt.name = train.get_name();
comm.send(pkt);
}
- {
- TrainSpeedPacket pkt;
- pkt.address = train.get_address();
- pkt.speed = train.get_target_speed();
- pkt.reverse = train.get_reverse();
- comm.send(pkt);
- }
+ // XXX Need control enumeration to send control packets
{
TrainFunctionPacket pkt;
pkt.address = train.get_address();
stale = true;
}
-void Server::Connection::receive(const TrainSpeedPacket &pkt)
+void Server::Connection::receive(const TrainControlPacket &pkt)
{
try
{
Train &train = server.layout.get_train(pkt.address);
- if(pkt.reverse!=train.get_reverse())
- train.set_reverse(pkt.reverse);
- else
- train.set_speed(pkt.speed);
+ train.set_control(pkt.control, pkt.value);
}
catch(const Exception &e)
{
class Server
{
private:
- struct Connection: private Msp::Net::PacketReceiver<TrainSpeedPacket>,
+ struct Connection: private Msp::Net::PacketReceiver<TrainControlPacket>,
private Msp::Net::PacketReceiver<TrainFunctionPacket>,
private Msp::Net::PacketReceiver<TrainRoutePacket>
{
void handshake_done();
void end_of_file();
- virtual void receive(const TrainSpeedPacket &);
+ virtual void receive(const TrainControlPacket &);
virtual void receive(const TrainFunctionPacket &);
virtual void receive(const TrainRoutePacket &);
void error(const std::string &);
void incoming_connection();
void train_added(Train &);
- void train_speed_changed(const Train &, unsigned);
- void train_reverse_changed(const Train &, bool);
+ void train_control_changed(const Train &, const std::string &, float);
void train_function_changed(const Train &, unsigned, bool);
void train_route_changed(const Train &, const Route *);
void train_status_changed(const Train &, const std::string &);
loco_type(client.get_catalogue().get_locomotive(pkt.loco_type)),
address(pkt.address),
name(pkt.name),
- speed(0),
- reverse(0),
functions(0)
{ }
-void NetTrain::set_speed(unsigned s)
+void NetTrain::set_control(const string &c, float v)
{
- if(s==speed)
+ if(v==controls[c])
return;
- TrainSpeedPacket pkt;
+ TrainControlPacket pkt;
pkt.address = address;
- pkt.speed = s;
- pkt.reverse = reverse;
- client.send(pkt);
-}
-
-void NetTrain::set_reverse(bool r)
-{
- if(r==reverse)
- return;
-
- TrainSpeedPacket pkt;
- pkt.address = address;
- pkt.speed = speed;
- pkt.reverse = r;
+ pkt.control = c;
+ pkt.value = v;
client.send(pkt);
}
client.send(pkt);
}
-void NetTrain::process_packet(const TrainSpeedPacket &pkt)
+void NetTrain::process_packet(const TrainControlPacket &pkt)
{
- if(pkt.speed!=speed)
- {
- speed = pkt.speed;
- signal_speed_changed.emit(speed);
- }
- if(pkt.reverse!=reverse)
- {
- reverse = pkt.reverse;
- signal_reverse_changed.emit(reverse);
- }
+ controls[pkt.control] = pkt.value;
+ signal_control_changed.emit(pkt.control, pkt.value);
}
void NetTrain::process_packet(const TrainFunctionPacket &pkt)
{
public:
sigc::signal<void, const std::string &> signal_name_changed;
- sigc::signal<void, unsigned> signal_speed_changed;
- sigc::signal<void, bool> signal_reverse_changed;
+ sigc::signal<void, const std::string &, float> signal_control_changed;
sigc::signal<void, unsigned, bool> signal_function_changed;
sigc::signal<void, const std::string &> signal_route_changed;
sigc::signal<void, const std::string &> signal_status_changed;
const LocoType &loco_type;
unsigned address;
std::string name;
- unsigned speed;
- bool reverse;
+ std::map<std::string, float> controls;
unsigned functions;
std::string route;
std::string status;
const LocoType &get_loco_type() const { return loco_type; }
unsigned get_address() const { return address; }
const std::string &get_name() const { return name; }
- void set_speed(unsigned);
- unsigned get_speed() const { return speed; }
- void set_reverse(bool);
- bool get_reverse() const { return reverse; }
+ void set_control(const std::string &, float);
+ float get_control(const std::string &) const;
void set_function(unsigned, bool);
bool get_function(unsigned i) const { return (functions>>i)&1; }
void set_route(const std::string &);
const std::string &get_route() const { return route; }
- void process_packet(const TrainSpeedPacket &);
+ void process_packet(const TrainControlPacket &);
void process_packet(const TrainFunctionPacket &);
void process_packet(const TrainRoutePacket &);
void process_packet(const TrainStatusPacket &);
/* $Id$
This file is part of the MSP Märklin suite
-Copyright © 2009 Mikkosoft Productions, Mikko Rasa
+Copyright © 2009-2010 Mikkosoft Productions, Mikko Rasa
Distributed under the GPL
*/
void Remote::train_added(Marklin::NetTrain &t)
{
- TrainPanel *panel = new TrainPanel(client, t);
+ TrainPanel *panel = new TrainPanel(*this, client, t);
if(!train_panels.empty())
{
Gtk::HSeparator *sep = new Gtk::HSeparator;
/* $Id$
This file is part of the MSP Märklin suite
-Copyright © 2009 Mikkosoft Productions, Mikko Rasa
+Copyright © 2009-2010 Mikkosoft Productions, Mikko Rasa
Distributed under the GPL
*/
public:
Remote(int argc, char **argv);
+
+ const Marklin::Catalogue &get_catalogue() const { return catalogue; }
private:
void tick();
/* $Id$
This file is part of the MSP Märklin suite
-Copyright © 2009 Mikkosoft Productions, Mikko Rasa
+Copyright © 2009-2010 Mikkosoft Productions, Mikko Rasa
Distributed under the GPL
*/
#include <gtkmm/box.h>
#include <gtkmm/liststore.h>
#include "libmarklin/locotype.h"
+#include "remote.h"
#include "trainpanel.h"
using namespace std;
-TrainPanel::TrainPanel(Marklin::Client &c, Marklin::NetTrain &t):
+TrainPanel::TrainPanel(Remote &r, Marklin::Client &c, Marklin::NetTrain &t):
+ remote(r),
client(c),
train(t)
{
train.signal_name_changed.connect(sigc::mem_fun(this, &TrainPanel::name_changed));
- train.signal_speed_changed.connect(sigc::mem_fun(this, &TrainPanel::speed_changed));
- train.signal_reverse_changed.connect(sigc::mem_fun(this, &TrainPanel::reverse_changed));
+ train.signal_control_changed.connect(sigc::mem_fun(this, &TrainPanel::control_changed));
train.signal_function_changed.connect(sigc::mem_fun(this, &TrainPanel::function_changed));
train.signal_route_changed.connect(sigc::mem_fun(this, &TrainPanel::route_changed));
train.signal_status_changed.connect(sigc::mem_fun(this, &TrainPanel::status_changed));
hbox->add(*manage(scl_speed = new Gtk::HScale));
scl_speed->set_digits(0);
- scl_speed->set_range(0, 14);
- scl_speed->set_increments(1, 1);
+ scl_speed->set_range(0, 200);
+ scl_speed->set_increments(5, 5);
scl_speed->set_size_request(210, -1);
scl_speed->signal_value_changed().connect(sigc::mem_fun(this, &TrainPanel::ui_speed_changed));
lbl_status->set_text(status);
}
-void TrainPanel::speed_changed(unsigned speed)
+void TrainPanel::control_changed(const string &control, float value)
{
- scl_speed->set_value(speed);
-}
-
-void TrainPanel::reverse_changed(bool rev)
-{
- chk_reverse->set_active(rev);
+ if(control=="speed")
+ {
+ // XXX It would be better to make the LocoType give us the catalogue
+ scl_speed->set_value(abs(value)*3.6/remote.get_catalogue().get_scale());
+ chk_reverse->set_active(value<0);
+ }
}
void TrainPanel::function_changed(unsigned func, bool set)
void TrainPanel::ui_speed_changed()
{
- train.set_speed(static_cast<unsigned>(scl_speed->get_value()));
+ float speed = scl_speed->get_value()/3.6*remote.get_catalogue().get_scale();
+ if(chk_reverse->get_active())
+ speed = -speed;
+ train.set_control("speed", speed);
}
void TrainPanel::ui_reverse_changed()
{
- train.set_reverse(chk_reverse->get_active());
+ train.set_control("speed", 0);
}
void TrainPanel::ui_function_changed(unsigned func)
/* $Id$
This file is part of the MSP Märklin suite
-Copyright © 2009 Mikkosoft Productions, Mikko Rasa
+Copyright © 2009-2010 Mikkosoft Productions, Mikko Rasa
Distributed under the GPL
*/
#include "network/client.h"
#include "network/train.h"
+class Remote;
+
class TrainPanel: public Gtk::Expander
{
private:
RouteRecord();
};
+ Remote &remote;
Marklin::Client &client;
Marklin::NetTrain &train;
Gtk::Scale *scl_speed;
std::map<unsigned, Gtk::CheckButton *> chk_funcs;
public:
- TrainPanel(Marklin::Client &, Marklin::NetTrain &);
+ TrainPanel(Remote &, Marklin::Client &, Marklin::NetTrain &);
private:
void name_changed(const std::string &);
void status_changed(const std::string &);
- void speed_changed(unsigned);
- void reverse_changed(bool);
+ void control_changed(const std::string &, float);
void function_changed(unsigned, bool);
void route_changed(const std::string &);
void ui_speed_changed();
client(catalogue),
serial_port(argv[2]),
train(0),
+ reverse(false),
rx_fill(0)
{
DataFile::load(catalogue, "locos.dat");
else if(c=='R')
{
IO::print("Reverse\n");
- train->set_reverse(!train->get_reverse());
+ reverse = !reverse;
+ train->set_control("speed", 0);
}
else if(c=='N')
next_train();
}
else if(rx_buf[0]=='S' && rx_fill==3)
{
- unsigned speed = (rx_buf[1]-'0')*10+(rx_buf[2]-'0');
- IO::print("Set speed %d\n", speed);
- train->set_speed(speed);
+ // XXX The firmware is still coded for speed step based control
+ float speed = ((rx_buf[1]-'0')*10+(rx_buf[2]-'0'))*10/3.6*catalogue.get_scale();
+ IO::print("Set speed %g\n", speed);
+ train->set_control("speed", speed);
rx_fill = 0;
}
}
Marklin::Client client;
Msp::IO::Serial serial_port;
Marklin::NetTrain *train;
+ bool reverse;
char rx_buf[3];
unsigned rx_fill;