From 9ddcd066e37e4c72685817c042c30897786ece05 Mon Sep 17 00:00:00 2001 From: Mikko Rasa Date: Mon, 12 Apr 2010 20:49:54 +0000 Subject: [PATCH] Foundations of using physics simulation for trains --- locos.dat | 3 + source/engineer/engineer.cpp | 4 +- source/engineer/trainpanel.cpp | 41 +++--- source/engineer/trainpanel.h | 3 +- source/libmarklin/aicontrol.cpp | 94 +++++++++++++ source/libmarklin/aicontrol.h | 45 ++++++ source/libmarklin/controlmodel.h | 36 +++++ source/libmarklin/simplephysics.cpp | 61 ++++++++ source/libmarklin/simplephysics.h | 38 +++++ source/libmarklin/track.cpp | 5 + source/libmarklin/track.h | 1 + source/libmarklin/train.cpp | 210 +++++++++++++++------------- source/libmarklin/train.h | 25 ++-- source/libmarklin/traincontrol.cpp | 76 ++++++++++ source/libmarklin/traincontrol.h | 44 ++++++ source/libmarklin/vehicle.h | 2 + source/network/client.cpp | 2 +- source/network/client.h | 4 +- source/network/packets.h | 6 +- source/network/protocol.cpp | 4 +- source/network/server.cpp | 35 ++--- source/network/server.h | 7 +- source/network/train.cpp | 38 ++--- source/network/train.h | 14 +- source/remote/remote.cpp | 4 +- source/remote/remote.h | 4 +- source/remote/trainpanel.cpp | 34 +++-- source/remote/trainpanel.h | 10 +- source/serial/serial.cpp | 11 +- source/serial/serial.h | 1 + 30 files changed, 630 insertions(+), 232 deletions(-) create mode 100644 source/libmarklin/aicontrol.cpp create mode 100644 source/libmarklin/aicontrol.h create mode 100644 source/libmarklin/controlmodel.h create mode 100644 source/libmarklin/simplephysics.cpp create mode 100644 source/libmarklin/simplephysics.h create mode 100644 source/libmarklin/traincontrol.cpp create mode 100644 source/libmarklin/traincontrol.h diff --git a/locos.dat b/locos.dat index e2f5756..3c045ba 100644 --- a/locos.dat +++ b/locos.dat @@ -1,5 +1,8 @@ /* $Id$ */ +scale 1 87; +gauge 16.5; + locomotive 37844 { name "BR 50"; diff --git a/source/engineer/engineer.cpp b/source/engineer/engineer.cpp index d1a123b..92f0f0f 100644 --- a/source/engineer/engineer.cpp +++ b/source/engineer/engineer.cpp @@ -114,7 +114,7 @@ Engineer::~Engineer() { const map &trains = layout.get_trains(); for(map::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) @@ -433,7 +433,7 @@ void Engineer::sighandler(int sig) IO::print(IO::cerr, "Fatal signal received, terminating\n"); const map &trains = layout.get_trains(); for(map::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); } diff --git a/source/engineer/trainpanel.cpp b/source/engineer/trainpanel.cpp index dce1387..1fd4abe 100644 --- a/source/engineer/trainpanel.cpp +++ b/source/engineer/trainpanel.cpp @@ -5,6 +5,7 @@ Copyright © 2006-2010 Mikkosoft Productions, Mikko Rasa Distributed under the GPL */ +#include #include #include #include "libmarklin/locotype.h" @@ -25,6 +26,8 @@ TrainPanel::TrainPanel(Engineer &e, const GLtk::Resources &r, Train &t): { 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)); @@ -34,23 +37,21 @@ TrainPanel::TrainPanel(Engineer &e, const GLtk::Resources &r, Train &t): 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")))); @@ -99,20 +100,24 @@ TrainPanel::TrainPanel(Engineer &e, const GLtk::Resources &r, Train &t): btn->signal_clicked.connect(sigc::mem_fun(this, &TrainPanel::route_clicked)); } -void TrainPanel::speed_slider_changed(double v) -{ - train.set_speed(static_cast(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) @@ -163,9 +168,9 @@ void TrainPanel::goto_clicked() 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) diff --git a/source/engineer/trainpanel.h b/source/engineer/trainpanel.h index b4bd386..12aa8fc 100644 --- a/source/engineer/trainpanel.h +++ b/source/engineer/trainpanel.h @@ -37,8 +37,7 @@ public: 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 &); diff --git a/source/libmarklin/aicontrol.cpp b/source/libmarklin/aicontrol.cpp new file mode 100644 index 0000000..ae47d41 --- /dev/null +++ b/source/libmarklin/aicontrol.cpp @@ -0,0 +1,94 @@ +/* $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_distset_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 diff --git a/source/libmarklin/aicontrol.h b/source/libmarklin/aicontrol.h new file mode 100644 index 0000000..7c5bf5d --- /dev/null +++ b/source/libmarklin/aicontrol.h @@ -0,0 +1,45 @@ +/* $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 +#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 diff --git a/source/libmarklin/controlmodel.h b/source/libmarklin/controlmodel.h new file mode 100644 index 0000000..d37ef63 --- /dev/null +++ b/source/libmarklin/controlmodel.h @@ -0,0 +1,36 @@ +/* $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 +#include + +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 diff --git a/source/libmarklin/simplephysics.cpp b/source/libmarklin/simplephysics.cpp new file mode 100644 index 0000000..3dc8e2e --- /dev/null +++ b/source/libmarklin/simplephysics.cpp @@ -0,0 +1,61 @@ +/* $Id$ + +This file is part of the MSP Märklin suite +Copyright © 2010 Mikkosoft Productions, Mikko Rasa +Distributed under the GPL +*/ + +#include +#include +#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(speedtarget_speed.value) + speed = target_speed.value; + } + else if(speed>target_speed.value) + { + speed -= secs*accel; + if(speed +#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 diff --git a/source/libmarklin/track.cpp b/source/libmarklin/track.cpp index 030af70..208d40f 100644 --- a/source/libmarklin/track.cpp +++ b/source/libmarklin/track.cpp @@ -293,6 +293,11 @@ unsigned Track::traverse(unsigned i, unsigned path) const 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); diff --git a/source/libmarklin/track.h b/source/libmarklin/track.h index 008b81d..34d484c 100644 --- a/source/libmarklin/track.h +++ b/source/libmarklin/track.h @@ -80,6 +80,7 @@ public: const std::vector &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; diff --git a/source/libmarklin/train.cpp b/source/libmarklin/train.cpp index f057992..8ddb66b 100644 --- a/source/libmarklin/train.cpp +++ b/source/libmarklin/train.cpp @@ -9,10 +9,13 @@ Distributed under the GPL #include #include #include +#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" @@ -27,8 +30,10 @@ Train::Train(Layout &l, const LocoType &t, unsigned a): 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), @@ -36,7 +41,6 @@ Train::Train(Layout &l, const LocoType &t, unsigned a): end_of_route(false), status("Unplaced"), travel_dist(0), - travel_speed(0), pure_speed(false), real_speed(15) { @@ -81,44 +85,30 @@ const Vehicle &Train::get_vehicle(unsigned i) const 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(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) @@ -164,8 +154,8 @@ void Train::set_route(const Route *r) } } - if(target_speed && reserve_more()<2) - update_speed(); + if(active) + reserve_more(); signal_route_changed.emit(route); } @@ -175,7 +165,7 @@ void Train::go_to(const Track &to) for(list::const_iterator i=cur_blocks.begin(); i!=cur_blocks.end(); ++i) if(i->block->get_tracks().count(const_cast(&to))) { - set_speed(0); + signal_arrived.emit(); set_route(0); return; } @@ -198,12 +188,14 @@ void Train::go_to(const Track &to) 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"); @@ -224,8 +216,6 @@ void Train::place(Block &block, unsigned entry) 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) @@ -238,7 +228,6 @@ 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()) @@ -259,17 +248,89 @@ int Train::get_entry_to_block(Block &block) const 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::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; @@ -308,15 +369,12 @@ void Train::save(list &st) const 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; } } @@ -349,12 +407,12 @@ void Train::sensor_event(unsigned addr, bool state) { // Compute speed and update related state float travel_time_secs = (Time::now()-last_entry_time)/Time::sec; - travel_speed = static_cast(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; @@ -399,16 +457,14 @@ void Train::sensor_event(unsigned addr, bool state) 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(); } } } @@ -577,52 +633,9 @@ unsigned Train::reserve_more() 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::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) @@ -679,6 +692,13 @@ unsigned Train::find_speed(float real) const return static_cast(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(round(speed/scale*3.6/5))*5; +} + void Train::set_status(const string &s) { status = s; diff --git a/source/libmarklin/train.h b/source/libmarklin/train.h index 5a20bd0..4289d3c 100644 --- a/source/libmarklin/train.h +++ b/source/libmarklin/train.h @@ -15,6 +15,7 @@ Distributed under the GPL namespace Marklin { +class ControlModel; class LocoType; class Route; class Vehicle; @@ -38,11 +39,10 @@ public: }; sigc::signal signal_name_changed; - sigc::signal signal_target_speed_changed; - sigc::signal signal_speed_changed; - sigc::signal signal_reverse_changed; + sigc::signal signal_control_changed; sigc::signal signal_function_changed; sigc::signal signal_route_changed; + sigc::signal signal_arrived; sigc::signal signal_status_changed; private: @@ -72,8 +72,10 @@ private: std::list cur_blocks; std::list 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; @@ -84,7 +86,6 @@ private: Msp::Time::TimeStamp last_entry_time; float travel_dist; - unsigned travel_speed; bool pure_speed; std::vector real_speed; @@ -92,20 +93,21 @@ public: 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; } @@ -116,6 +118,7 @@ public: 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; } @@ -129,9 +132,9 @@ private: 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 &); void release_blocks(std::list &, std::list::iterator, std::list::iterator); diff --git a/source/libmarklin/traincontrol.cpp b/source/libmarklin/traincontrol.cpp new file mode 100644 index 0000000..a9a5a84 --- /dev/null +++ b/source/libmarklin/traincontrol.cpp @@ -0,0 +1,76 @@ +/* $Id$ + +This file is part of the MSP Märklin suite +Copyright © 2010 Mikkosoft Productions, Mikko Rasa +Distributed under the GPL +*/ + +#include +#include +#include "traincontrol.h" + +using namespace std; +using namespace Msp; + +namespace Marklin { + +void TrainControl::set(float v) +{ + if(vmax_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 + +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 diff --git a/source/libmarklin/vehicle.h b/source/libmarklin/vehicle.h index d24bebe..149adda 100644 --- a/source/libmarklin/vehicle.h +++ b/source/libmarklin/vehicle.h @@ -58,6 +58,8 @@ public: 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: diff --git a/source/network/client.cpp b/source/network/client.cpp index 2e00384..50db7df 100644 --- a/source/network/client.cpp +++ b/source/network/client.cpp @@ -58,7 +58,7 @@ void Client::receive(const TrainInfoPacket &pkt) signal_train_added.emit(*train); } -void Client::receive(const TrainSpeedPacket &pkt) +void Client::receive(const TrainControlPacket &pkt) { get_train(pkt.address).process_packet(pkt); } diff --git a/source/network/client.h b/source/network/client.h index 39c7eb0..2ab8722 100644 --- a/source/network/client.h +++ b/source/network/client.h @@ -18,8 +18,8 @@ Distributed under the GPL namespace Marklin { class Client: public Msp::Net::PacketReceiver, - Msp::Net::PacketReceiver, Msp::Net::PacketReceiver, + Msp::Net::PacketReceiver, Msp::Net::PacketReceiver, Msp::Net::PacketReceiver, Msp::Net::PacketReceiver, @@ -56,7 +56,7 @@ public: 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 &); diff --git a/source/network/packets.h b/source/network/packets.h index 31fd56d..8bbaf8e 100644 --- a/source/network/packets.h +++ b/source/network/packets.h @@ -19,11 +19,11 @@ struct TrainInfoPacket std::string name; }; -struct TrainSpeedPacket +struct TrainControlPacket { unsigned address; - unsigned speed; - char reverse; + std::string control; + float value; }; struct TrainFunctionPacket diff --git a/source/network/protocol.cpp b/source/network/protocol.cpp index f8e13be..623eadb 100644 --- a/source/network/protocol.cpp +++ b/source/network/protocol.cpp @@ -14,8 +14,8 @@ Protocol::Protocol() { add() (&TrainInfoPacket::address) (&TrainInfoPacket::loco_type) (&TrainInfoPacket::name); - add() (&TrainSpeedPacket::address) - (&TrainSpeedPacket::speed) (&TrainSpeedPacket::reverse); + add() (&TrainControlPacket::address) + (&TrainControlPacket::control) (&TrainControlPacket::value); add() (&TrainFunctionPacket::address) (&TrainFunctionPacket::functions); add() (&TrainStatusPacket::address) diff --git a/source/network/server.cpp b/source/network/server.cpp index 2e3248f..dd0dd8b 100644 --- a/source/network/server.cpp +++ b/source/network/server.cpp @@ -47,8 +47,7 @@ void Server::incoming_connection() 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))); @@ -60,21 +59,12 @@ void Server::train_added(Train &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); } @@ -149,13 +139,7 @@ void Server::Connection::handshake_done() 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(); @@ -184,15 +168,12 @@ void Server::Connection::end_of_file() 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) { diff --git a/source/network/server.h b/source/network/server.h index 41bb8e9..056b0dd 100644 --- a/source/network/server.h +++ b/source/network/server.h @@ -21,7 +21,7 @@ namespace Marklin { class Server { private: - struct Connection: private Msp::Net::PacketReceiver, + struct Connection: private Msp::Net::PacketReceiver, private Msp::Net::PacketReceiver, private Msp::Net::PacketReceiver { @@ -35,7 +35,7 @@ private: 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 &); @@ -54,8 +54,7 @@ private: 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 &); diff --git a/source/network/train.cpp b/source/network/train.cpp index 025dcf5..5c4b457 100644 --- a/source/network/train.cpp +++ b/source/network/train.cpp @@ -17,32 +17,18 @@ NetTrain::NetTrain(Client &c, const TrainInfoPacket &pkt): 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); } @@ -69,18 +55,10 @@ void NetTrain::set_route(const string &r) 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) diff --git a/source/network/train.h b/source/network/train.h index 0a6a8a8..74c6ccb 100644 --- a/source/network/train.h +++ b/source/network/train.h @@ -20,8 +20,7 @@ class NetTrain { public: sigc::signal signal_name_changed; - sigc::signal signal_speed_changed; - sigc::signal signal_reverse_changed; + sigc::signal signal_control_changed; sigc::signal signal_function_changed; sigc::signal signal_route_changed; sigc::signal signal_status_changed; @@ -31,8 +30,7 @@ private: const LocoType &loco_type; unsigned address; std::string name; - unsigned speed; - bool reverse; + std::map controls; unsigned functions; std::string route; std::string status; @@ -43,16 +41,14 @@ public: 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 &); diff --git a/source/remote/remote.cpp b/source/remote/remote.cpp index c6afc6d..a193b2b 100644 --- a/source/remote/remote.cpp +++ b/source/remote/remote.cpp @@ -1,7 +1,7 @@ /* $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 */ @@ -52,7 +52,7 @@ void Remote::tick() 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; diff --git a/source/remote/remote.h b/source/remote/remote.h index a4bfc42..6d6be33 100644 --- a/source/remote/remote.h +++ b/source/remote/remote.h @@ -1,7 +1,7 @@ /* $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 */ @@ -31,6 +31,8 @@ private: public: Remote(int argc, char **argv); + + const Marklin::Catalogue &get_catalogue() const { return catalogue; } private: void tick(); diff --git a/source/remote/trainpanel.cpp b/source/remote/trainpanel.cpp index ce46fa1..37d75af 100644 --- a/source/remote/trainpanel.cpp +++ b/source/remote/trainpanel.cpp @@ -1,24 +1,25 @@ /* $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 #include #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)); @@ -34,8 +35,8 @@ TrainPanel::TrainPanel(Marklin::Client &c, Marklin::NetTrain &t): 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)); @@ -80,14 +81,14 @@ void TrainPanel::status_changed(const string &status) 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) @@ -110,12 +111,15 @@ void TrainPanel::route_changed(const string &route) void TrainPanel::ui_speed_changed() { - train.set_speed(static_cast(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) diff --git a/source/remote/trainpanel.h b/source/remote/trainpanel.h index b969af4..e1464e3 100644 --- a/source/remote/trainpanel.h +++ b/source/remote/trainpanel.h @@ -1,7 +1,7 @@ /* $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 */ @@ -16,6 +16,8 @@ Distributed under the GPL #include "network/client.h" #include "network/train.h" +class Remote; + class TrainPanel: public Gtk::Expander { private: @@ -26,6 +28,7 @@ private: RouteRecord(); }; + Remote &remote; Marklin::Client &client; Marklin::NetTrain &train; Gtk::Scale *scl_speed; @@ -36,12 +39,11 @@ private: std::map 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(); diff --git a/source/serial/serial.cpp b/source/serial/serial.cpp index 2766752..4b94034 100644 --- a/source/serial/serial.cpp +++ b/source/serial/serial.cpp @@ -20,6 +20,7 @@ Serial::Serial(int, char **argv): client(catalogue), serial_port(argv[2]), train(0), + reverse(false), rx_fill(0) { DataFile::load(catalogue, "locos.dat"); @@ -97,7 +98,8 @@ void Serial::data_available() 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(); @@ -121,9 +123,10 @@ void Serial::data_available() } 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; } } diff --git a/source/serial/serial.h b/source/serial/serial.h index b5427e4..e03bb50 100644 --- a/source/serial/serial.h +++ b/source/serial/serial.h @@ -20,6 +20,7 @@ private: Marklin::Client client; Msp::IO::Serial serial_port; Marklin::NetTrain *train; + bool reverse; char rx_buf[3]; unsigned rx_fill; -- 2.45.2