Create a Driver abstraction to hide the code that talks to the physical equipment
Remove TrafficManager and put Blocks and Trains in Layout
Dynamically update Blocks as the layout changes
Remove Locomotive, Turnout and Sensor classes
Tracks now have an active_path property
Trains now allows setting functions
Move option parsing in Engineer to a separate class
Many minor changes
#include <msp/gl/select.h>
#include <msp/gl/texture.h>
#include <msp/datafile/parser.h>
-#include "libmarklin/trafficmanager.h"
#include "layout.h"
using namespace std;
{
layout.signal_track_added.connect(sigc::mem_fun(this, &Layout3D::track_added));
layout.signal_track_removed.connect(sigc::mem_fun(this, &Layout3D::track_removed));
+ layout.signal_train_added.connect(sigc::mem_fun(this, &Layout3D::train_added));
const set<Track *> <racks = layout.get_tracks();
for(set<Track *>::iterator i=ltracks.begin(); i!=ltracks.end(); ++i)
return track;
}
-void Layout3D::set_traffic_manager(TrafficManager &tm)
-{
- tm.signal_train_added.connect(sigc::mem_fun(this, &Layout3D::train_added));
-}
-
void Layout3D::add_train(Train3D &t)
{
trains.push_back(&t);
#ifndef MARKLIN3D_LAYOUT_H_
#define MARKLIN3D_LAYOUT_H_
+#include <sigc++/trackable.h>
#include <msp/gl/scene.h>
#include "libmarklin/layout.h"
#include "catalogue.h"
namespace Marklin {
-class Layout3D
+class Layout3D: public sigc::trackable
{
private:
Layout &layout;
Track3D &get_track(const Track &) const;
Track3D *pick_track(float, float, float) const;
- void set_traffic_manager(TrafficManager &);
void add_train(Train3D &);
void remove_train(Train3D &);
Train3D &get_train(const Train &) const;
#include <msp/strings/utils.h>
#include <msp/time/units.h>
#include <msp/time/utils.h>
+#include "libmarklin/route.h"
#include "libmarklin/tracktype.h"
#include "designer.h"
#include "input.h"
void Designer::set_turnout_id()
{
Track *track = selection.get_track();
- if(selection.size()==1 && track->get_type().get_n_paths()>1)
+ if(selection.size()==1 && track->get_type().is_turnout())
{
InputDialog *input = new InputDialog(*this, "Turnout ID", lexical_cast(track->get_turnout_id()));
input->signal_accept.connect(sigc::mem_fun(this, &Designer::turnout_id_accept));
int id = -1;
for(set<Track *>::const_iterator i=tracks.begin(); i!=tracks.end(); ++i)
{
- if((*i)->get_type().get_n_paths()==1)
+ if(!(*i)->get_type().is_turnout())
ok = true;
if(static_cast<int>((*i)->get_sensor_id())!=id)
{
Track3D *ctrack = pick_track(x, y);
if(ctrack)
{
- Track *track = ctrack->get_track().copy();
+ Track *track = new Track(*layout, ctrack->get_track().get_type());
track->set_position(ground);
- layout->add_track(*track);
selection.clear();
selection.add_track(track);
/* $Id$
This file is part of the MSP Märklin suite
-Copyright © 2006-2008 Mikkosoft Productions, Mikko Rasa
+Copyright © 2006-2010 Mikkosoft Productions, Mikko Rasa
Distributed under the GPL
*/
list<Track *> new_tracks;
for(vector<MTrack>::iterator i=tracks.begin(); i!=tracks.end(); ++i)
{
- Track *track = i->track->copy();
- designer.get_layout()->add_track(*track);
+ Track *track = new Track(*designer.get_layout(), i->track->get_type());
new_tracks.push_back(track);
}
glPopMatrix();
}
-/*** private ***/
-
void Manipulator::selection_changed()
{
if(mode)
/* $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 <msp/gltk/button.h>
#include <msp/strings/formatter.h>
+#include "libmarklin/route.h"
#include "designer.h"
#include "toolbar.h"
#include <limits>
#include <signal.h>
#include <msp/core/except.h>
-#include <msp/core/getopt.h>
#include <msp/fs/stat.h>
#include <msp/gbase/display.h>
#include <msp/gbase/window.h>
#include <msp/gl/blend.h>
#include <msp/gl/framebuffer.h>
-#include <msp/gl/immediate.h>
#include <msp/gl/matrix.h>
#include <msp/gl/misc.h>
-#include <msp/gl/projection.h>
#include <msp/gl/tests.h>
#include <msp/gl/transform.h>
#include <msp/io/print.h>
#include <msp/strings/formatter.h>
-#include <msp/strings/lexicalcast.h>
-#include <msp/strings/regex.h>
#include <msp/time/units.h>
-#include "libmarklin/except.h"
+#include "libmarklin/driver.h"
#include "libmarklin/tracktype.h"
#include "engineer.h"
#include "mainpanel.h"
using namespace Msp;
Engineer::Engineer(int argc, char **argv):
- layout(catalogue),
+ options(argc, argv),
+ window(options.screen_w, options.screen_h, options.fullscreen),
+ layout(catalogue, (options.driver.empty() ? 0 : Driver::create(options.driver))),
layout_3d(layout),
server(0),
+ pipeline(window.get_width(), window.get_height(), false),
placing_train(0),
placing_block(0),
- placing_entry(0),
- simulate(false)
+ placing_entry(0)
{
- // Parse options
- unsigned screen_w = 1280;
- unsigned screen_h = 960;
- bool fullscreen = false;
- string res;
- bool debug = false;
- string device = "/dev/ttyS0";
- bool network = false;
-
- GetOpt getopt;
- getopt.add_option('r', "resolution", res, GetOpt::REQUIRED_ARG);
- getopt.add_option('f', "fullscreen", fullscreen, GetOpt::NO_ARG);
- getopt.add_option('g', "debug", debug, GetOpt::NO_ARG);
- getopt.add_option('d', "device", device, GetOpt::REQUIRED_ARG);
- getopt.add_option('s', "simulate", simulate, GetOpt::NO_ARG);
- getopt.add_option('n', "network", network, GetOpt::NO_ARG);
- getopt(argc, argv);
-
- const vector<string> &args = getopt.get_args();
- if(args.empty())
- throw UsageError("No layout given");
-
- if(!res.empty())
- {
- if(RegMatch m=Regex("([1-9][0-9]*)x([1-9][0-9]*)").match(res))
- {
- screen_w = lexical_cast<unsigned>(m[1].str);
- screen_h = lexical_cast<unsigned>(m[2].str);
- }
- else
- throw UsageError("Invalid resolution");
- }
-
// Setup GUI
- window = new Graphics::SimpleGLWindow(screen_w, screen_h, fullscreen);
- window->set_title("Railroad Engineer");
- window->signal_close.connect(sigc::bind(sigc::mem_fun(this, &Engineer::exit), 0));
+ window.set_title("Railroad Engineer");
+ window.signal_close.connect(sigc::bind(sigc::mem_fun(this, &Engineer::exit), 0));
DataFile::load(ui_res, "marklin.res");
- root = new GLtk::Root(ui_res, *window);
+ root = new GLtk::Root(ui_res, window);
root->signal_button_press.connect(sigc::mem_fun(this, &Engineer::button_press));
root->signal_pointer_motion.connect(sigc::mem_fun(this, &Engineer::pointer_motion));
root->set_visible(true);
main_panel = new MainPanel(*this, ui_res);
root->add(*main_panel);
- main_panel->set_position(0, window->get_height()-main_panel->get_geometry().h);
+ main_panel->set_position(0, window.get_height()-main_panel->get_geometry().h);
main_panel->set_visible(true);
// Setup railroad control
DataFile::load(catalogue, "tracks.dat");
DataFile::load(catalogue, "locos.dat");
- DataFile::load(layout, args.front());
-
- if(device!="none")
- control.open(device);
+ DataFile::load(layout, options.layout_fn);
- control.set_debug(debug);
-
- trfc_mgr = new TrafficManager(control, layout);
- layout_3d.set_traffic_manager(*trfc_mgr);
- trfc_mgr->signal_train_added.connect(sigc::mem_fun(this, &Engineer::train_added));
- trfc_mgr->signal_block_reserved.connect(sigc::mem_fun(this, &Engineer::block_reserved));
+ layout.signal_train_added.connect(sigc::mem_fun(this, &Engineer::train_added));
+ layout.signal_block_reserved.connect(sigc::mem_fun(this, &Engineer::block_reserved));
if(FS::exists("engineer.state"))
- DataFile::load(*trfc_mgr, "engineer.state");
+ DataFile::load(layout, "engineer.state");
- if(network)
+ if(options.network)
{
- server = new Server(*trfc_mgr);
+ server = new Server(layout);
server->use_event_dispatcher(event_disp);
}
- const map<unsigned, Sensor *> &sensors = control.get_sensors();
- for(map<unsigned, Sensor *>::const_iterator i=sensors.begin(); i!=sensors.end(); ++i)
- i->second->signal_state_changed.connect(sigc::bind(sigc::mem_fun(this, &Engineer::sensor_event), i->second));
+ layout.get_driver().signal_sensor.connect(sigc::mem_fun(this, &Engineer::sensor_event));
// Setup 3D view
DataFile::load(arrow_mesh, "arrow.mesh");
- overlay = new Overlay3D(*window, camera, ui_res.get_default_font());
+ overlay = new Overlay3D(window, camera, ui_res.get_default_font());
- pipeline = new GL::Pipeline(window->get_width(), window->get_height(), false);
- pipeline->set_camera(&camera);
- pipeline->add_renderable(layout_3d.get_scene());
+ pipeline.set_camera(&camera);
+ pipeline.add_renderable(layout_3d.get_scene());
light.set_position(0, -0.259, 0.966, 0);
lighting.attach(0, light);
- GL::PipelinePass &pass = pipeline->add_pass(0);
+ GL::PipelinePass &pass = pipeline.add_pass(0);
pass.depth_test = &GL::DepthTest::lequal();
pass.lighting = &lighting;
Engineer::~Engineer()
{
- const list<Train *> &trains = trfc_mgr->get_trains();
- for(list<Train *>::const_iterator i=trains.begin(); i!=trains.end(); ++i)
- (*i)->set_speed(0);
-
- while(control.get_queue_length())
- control.tick();
+ 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().flush();
- if(!simulate)
- trfc_mgr->save("engineer.state");
+ if(!options.simulate)
+ layout.save_trains("engineer.state");
- delete pipeline;
delete overlay;
delete root;
- delete window;
- delete trfc_mgr;
delete server;
}
int Engineer::main()
{
- window->show();
+ window.show();
return Application::main();
}
void Engineer::tick()
{
- window->get_display().tick();
+ window.get_display().tick();
- control.tick();
- trfc_mgr->tick();
+ layout.tick();
event_disp.tick(Time::zero);
for(list<Train *>::iterator i=new_trains.begin(); i!=new_trains.end(); ++i)
GL::clear(GL::COLOR_BUFFER_BIT|GL::DEPTH_BUFFER_BIT);
- pipeline->render_all();
+ pipeline.render_all();
{
GL::Bind blend(GL::Blend::alpha());
overlay->render(0);
GL::Texture::unbind();
}
- window->swap_buffers();
+ window.swap_buffers();
}
void Engineer::button_press(int x, int y, unsigned btn, unsigned)
}
else
{
- Track3D *track = pick_track(x, window->get_height()-y-1);
- if(track)
+ Track3D *t3d = pick_track(x, window.get_height()-y-1);
+ if(t3d)
{
- if(unsigned tid=track->get_track().get_turnout_id())
+ Track &track = t3d->get_track();
+ if(track.get_turnout_id())
{
- Turnout &turnout = control.get_turnout(tid);
- try
+ Block &block = layout.get_block_by_track(track);
+ if(block.get_train() && !block.get_train()->free_block(block))
+ main_panel->set_status_text("Turnout busy");
+ else
{
- turnout.set_path((turnout.get_path()+1)%track->get_track().get_type().get_n_paths());
- main_panel->set_status_text(format("Turnout %d switched", turnout.get_address()));
- }
- catch(const TurnoutBusy &e)
- {
- main_panel->set_status_text(e.what());
+ unsigned paths = track.get_type().get_paths();
+ unsigned i = track.get_active_path()+1;
+ while(!(paths&(1<<i)))
+ {
+ if(!(paths>>i))
+ i = 0;
+ else
+ ++i;
+ }
+ track.set_active_path(i);
}
}
- else if(simulate)
+ /*else if(simulate)
{
if(unsigned sid=track->get_track().get_sensor_id())
{
Sensor &sensor = control.get_sensor(sid);
control.signal_sensor_event.emit(sid, !sensor.get_state());
}
- }
+ }*/
}
}
}
{
if(placing_train)
{
- Track3D *track = pick_track(x, window->get_height()-y-1);
+ Track3D *track = pick_track(x, window.get_height()-y-1);
if(track)
{
- Block &block = trfc_mgr->get_block_by_track(track->get_track());
+ Block &block = layout.get_block_by_track(track->get_track());
if(&block!=placing_block)
{
if(placing_block)
{
const list<Track3D *> &tracks = layout_3d.get_tracks();
- float view_aspect = float(window->get_width()-200)/window->get_height();
+ float view_aspect = float(window.get_width()-200)/window.get_height();
float view_height = tan(camera.get_field_of_view()/2)*2;
float best_score = 0;
GL::Vector3 pos;
float size = max(width/view_aspect, height);
float c = cos(angle);
float s = sin(angle);
- float x = (min_x+max_x)/2-size*105/window->get_height();
+ float x = (min_x+max_x)/2-size*105/window.get_height();
float y = (min_y+max_y)/2;
float z = max(size*1.05/view_height, 0.15);
void Engineer::set_block_color(const Block &block, const GL::Color &color)
{
- (void)block;
- (void)color;
+ (void)block; (void)color;
}
void Engineer::reset_block_color(const Block &block)
{
if(unsigned sid=block.get_sensor_id())
{
- Sensor &sensor = control.get_sensor(sid);
- if(sensor.get_state())
+ if(layout.get_driver().get_sensor(sid))
{
set_block_color(block, GL::Color(1, 0.5, 0.3));
return;
set_block_color(block, GL::Color(1, 1, 1));
}
-void Engineer::sensor_event(bool, Sensor *sensor)
+void Engineer::sensor_event(unsigned addr, bool)
{
- const list<Block *> &blocks = trfc_mgr->get_blocks();
- for(list<Block *>::const_iterator i=blocks.begin(); i!=blocks.end(); ++i)
- if((*i)->get_sensor_id()==sensor->get_address())
+ const set<Block *> &blocks = layout.get_blocks();
+ for(set<Block *>::const_iterator i=blocks.begin(); i!=blocks.end(); ++i)
+ if((*i)->get_sensor_id()==addr)
reset_block_color(**i);
}
Track3D *Engineer::pick_track(int x, int y)
{
float view_height = tan(camera.get_field_of_view()/2)*2;
- float xx = ((float(x)-window->get_width()/2)/window->get_height())*view_height;
- float yy = (float(y)/window->get_height()-0.5)*view_height;
- float size = 4.0/window->get_height()*view_height;
+ float xx = ((float(x)-window.get_width()/2)/window.get_height())*view_height;
+ float yy = (float(y)/window.get_height()-0.5)*view_height;
+ float size = 4.0/window.get_height()*view_height;
camera.apply();
{
signal(sig, SIG_DFL);
IO::print(IO::cerr, "Fatal signal received, terminating\n");
- const map<unsigned, Locomotive *> &locos = control.get_locomotives();
- for(map<unsigned, Locomotive *>::const_iterator i=locos.begin(); i!=locos.end(); ++i)
+ 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);
- control.flush();
+ layout.get_driver().flush();
raise(sig);
}
else if(sig==SIGTERM || sig==SIGINT)
#include <msp/gltk/resources.h>
#include <msp/gltk/root.h>
#include "libmarklin/catalogue.h"
-#include "libmarklin/control.h"
-#include "libmarklin/locotype.h"
-#include "libmarklin/trafficmanager.h"
#include "libmarklin/train.h"
#include "3d/layout.h"
#include "3d/overlay.h"
#include "network/server.h"
+#include "options.h"
class MainPanel;
class TrainPanel;
class Engineer: public Msp::Application
{
private:
- Msp::Graphics::SimpleGLWindow *window;
+ Options options;
+
+ Msp::Graphics::SimpleGLWindow window;
Msp::GLtk::Resources ui_res;
Msp::GLtk::Root *root;
Marklin::Catalogue catalogue;
Marklin::Layout layout;
Marklin::Layout3D layout_3d;
- Marklin::Control control;
- Marklin::TrafficManager *trfc_mgr;
Marklin::Server *server;
Msp::IO::EventDispatcher event_disp;
Marklin::Overlay3D *overlay;
Msp::GL::Camera camera;
Msp::GL::Lighting lighting;
Msp::GL::Light light;
- Msp::GL::Pipeline *pipeline;
+ Msp::GL::Pipeline pipeline;
Msp::GL::Mesh arrow_mesh;
MainPanel *main_panel;
Marklin::Block *placing_block;
unsigned placing_entry;
- bool simulate;
-
public:
Engineer(int argc, char **argv);
~Engineer();
const Msp::GLtk::Resources &get_ui_resources() const { return ui_res; }
Msp::GLtk::Root &get_root() const { return *root; }
const Marklin::Catalogue &get_catalogue() const { return catalogue; }
- const Marklin::Layout &get_layout() const { return layout; }
- Marklin::Control &get_control() { return control; }
- Marklin::TrafficManager &get_traffic_manager() { return *trfc_mgr; }
+ Marklin::Layout &get_layout() { return layout; }
void place_train(Marklin::Train &);
int main();
void quit() { exit(0); }
void view_all();
void set_block_color(const Marklin::Block &, const Msp::GL::Color &);
void reset_block_color(const Marklin::Block &);
- void sensor_event(bool, Marklin::Sensor *);
+ void sensor_event(unsigned, bool);
void block_reserved(const Marklin::Block &, const Marklin::Train *);
Marklin::Track3D *pick_track(int, int);
void train_added(Marklin::Train &);
/* $Id$
This file is part of the MSP Märklin suite
-Copyright © 2006-2009 Mikkosoft Productions, Mikko Rasa
+Copyright © 2006-2010 Mikkosoft Productions, Mikko Rasa
Distributed under the GPL
*/
#include <msp/gltk/button.h>
+#include "libmarklin/driver.h"
#include "engineer.h"
#include "mainpanel.h"
#include "trainproperties.h"
lbl_status->set_geometry(GLtk::Geometry(10, 10, 180, 24));
lbl_status->set_style("digital");
- if(engineer.get_control().get_power())
+ Marklin::Driver &driver = engineer.get_layout().get_driver();
+ if(driver.get_power())
ind_on->set_active(true);
else
ind_off->set_active(true);
- engineer.get_control().signal_power_event.connect(sigc::mem_fun(this, &MainPanel::power_event));
+ driver.signal_power.connect(sigc::mem_fun(this, &MainPanel::power_event));
}
void MainPanel::set_status_text(const string &txt)
void MainPanel::power_on()
{
- engineer.get_control().set_power(true);
- ind_on->set_active(true);
- ind_off->set_active(false);
+ engineer.get_layout().get_driver().set_power(true);
}
void MainPanel::power_off()
{
- engineer.get_control().set_power(false);
- ind_on->set_active(false);
- ind_off->set_active(true);
+ engineer.get_layout().get_driver().set_power(false);
}
void MainPanel::new_loc()
--- /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/getopt.h>
+#include <msp/strings/lexicalcast.h>
+#include <msp/strings/regex.h>
+#include "options.h"
+
+using namespace std;
+using namespace Msp;
+
+Options::Options(int argc, char **argv):
+ screen_w(1280),
+ screen_h(960),
+ fullscreen(false),
+ debug(false),
+ network(false),
+ simulate(false)
+{
+ string res;
+
+ GetOpt getopt;
+ getopt.add_option('r', "resolution", res, GetOpt::REQUIRED_ARG);
+ getopt.add_option('f', "fullscreen", fullscreen, GetOpt::NO_ARG);
+ getopt.add_option('g', "debug", debug, GetOpt::NO_ARG);
+ getopt.add_option('d', "driver", driver, GetOpt::REQUIRED_ARG);
+ getopt.add_option('s', "simulate", simulate, GetOpt::NO_ARG);
+ getopt.add_option('n', "network", network, GetOpt::NO_ARG);
+ getopt(argc, argv);
+
+ if(!res.empty())
+ {
+ if(RegMatch m=Regex("([1-9][0-9]*)x([1-9][0-9]*)").match(res))
+ {
+ screen_w = lexical_cast<unsigned>(m[1].str);
+ screen_h = lexical_cast<unsigned>(m[2].str);
+ }
+ else
+ throw UsageError("Invalid resolution");
+ }
+
+ const vector<string> &args = getopt.get_args();
+ if(args.empty())
+ throw UsageError("No layout given");
+
+ layout_fn = args[0];
+}
--- /dev/null
+/* $Id$
+
+This file is part of the MSP Märklin suite
+Copyright © 2010 Mikkosoft Productions, Mikko Rasa
+Distributed under the GPL
+*/
+
+#ifndef OPTIONS_H_
+#define OPTIONS_H_
+
+#include <string>
+
+struct Options
+{
+ unsigned screen_w;
+ unsigned screen_h;
+ bool fullscreen;
+ bool debug;
+ std::string driver;
+ bool network;
+ bool simulate;
+ std::string layout_fn;
+
+ Options(int, char **);
+};
+
+#endif
/* $Id$
This file is part of the MSP Märklin suite
-Copyright © 2006-2009 Mikkosoft Productions, Mikko Rasa
+Copyright © 2006-2010 Mikkosoft Productions, Mikko Rasa
Distributed under the GPL
*/
#include <msp/gltk/button.h>
#include <msp/strings/formatter.h>
-#include "libmarklin/locomotive.h"
+#include "libmarklin/locotype.h"
#include "engineer.h"
#include "routeselect.h"
#include "trainpanel.h"
{
set_size(200, 170);
- add(*(lbl_addr=new GLtk::Label(res, format("%2d", train.get_locomotive().get_address()))));
+ 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_locomotive().get_speed()))));
+ add(*(lbl_speed=new GLtk::Label(res, format("%2d", train.get_speed()))));
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(*(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_locomotive().get_reverse());
+ tgl_forward->set_value(!train.get_reverse());
tgl_forward->signal_toggled.connect(sigc::mem_fun(this, &TrainPanel::forward_toggled));
- train.get_locomotive().signal_reverse_changed.connect(sigc::mem_fun(this, &TrainPanel::train_reverse_changed));
+ 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"))));
lbl_status->set_geometry(GLtk::Geometry(10, 34, geom.w-20, 24));
train.signal_status_changed.connect(sigc::mem_fun(this, &TrainPanel::train_status_changed));
- const map<unsigned, string> &funcs = train.get_locomotive().get_type().get_functions();
+ const map<unsigned, string> &funcs = train.get_locomotive_type().get_functions();
unsigned x = 10;
for(map<unsigned, string>::const_iterator i=funcs.begin(); i!=funcs.end(); ++i, x+=36)
{
add(*(tgl=new GLtk::Toggle(res)));
tgl->set_text(fname);
tgl->set_geometry(GLtk::Geometry(x, geom.h-85, 36, 27));
- tgl->set_value(train.get_locomotive().get_function(i->first));
+ tgl->set_value(train.get_function(i->first));
tgl->signal_toggled.connect(sigc::bind(sigc::mem_fun(this, &TrainPanel::func_toggled), i->first));
tgl_funcs[i->first] = tgl;
}
- train.get_locomotive().signal_function_changed.connect(sigc::mem_fun(this, &TrainPanel::loco_function_changed));
+ train.signal_function_changed.connect(sigc::mem_fun(this, &TrainPanel::train_function_changed));
GLtk::Button *btn;
tgl_forward->set_value(!reverse);
}
-void TrainPanel::loco_function_changed(unsigned func, bool value)
+void TrainPanel::train_function_changed(unsigned func, bool value)
{
map<unsigned, GLtk::Toggle *>::iterator i = tgl_funcs.find(func);
if(i!=tgl_funcs.end())
void TrainPanel::func_toggled(bool value, unsigned func)
{
- train.get_locomotive().set_function(func, value);
+ train.set_function(func, value);
}
/* $Id$
This file is part of the MSP Märklin suite
-Copyright © 2006-2009 Mikkosoft Productions, Mikko Rasa
+Copyright © 2006-2010 Mikkosoft Productions, Mikko Rasa
Distributed under the GPL
*/
void speed_slider_changed(double);
void train_speed_changed(unsigned);
void train_reverse_changed(bool);
- void loco_function_changed(unsigned, bool);
+ void train_function_changed(unsigned, bool);
void train_route_changed(const Marklin::Route *);
void train_status_changed(const std::string &);
void place_clicked();
/* $Id$
This file is part of the MSP Märklin suite
-Copyright © 2006-2009 Mikkosoft Productions, Mikko Rasa
+Copyright © 2006-2010 Mikkosoft Productions, Mikko Rasa
Distributed under the GPL
*/
#include <msp/gltk/label.h>
#include <msp/strings/formatter.h>
#include <msp/strings/lexicalcast.h>
+#include "libmarklin/locotype.h"
#include "engineer.h"
#include "trainproperties.h"
for(map<unsigned, LocoType *>::const_iterator i=locos.begin(); i!=locos.end(); ++i, ++n)
{
drp_type->append(format("%d %s", i->second->get_article_number(), i->second->get_name()));
- if(train && i->second==&train->get_locomotive().get_type())
+ if(train && i->second==&train->get_locomotive_type())
drp_type->set_selected_index(n);
}
if(train)
{
- ent_addr->set_text(lexical_cast(train->get_locomotive().get_address()));
+ ent_addr->set_text(lexical_cast(train->get_address()));
ent_name->set_text(train->get_name());
}
else
- ent_name->set_text(format("Train %d", engineer.get_traffic_manager().get_trains().size()+1));
+ ent_name->set_text(format("Train %d", engineer.get_layout().get_trains().size()+1));
}
void TrainProperties::on_ok_clicked()
advance(i, drp_type->get_selected_index());
unsigned addr = lexical_cast<unsigned>(ent_addr->get_text());
- Locomotive *loco = new Locomotive(*i->second, engineer.get_control(), addr);
- train = new Train(engineer.get_traffic_manager(), *loco);
+ train = new Train(engineer.get_layout(), *i->second, addr);
engineer.place_train(*train);
}
/* $Id$
This file is part of the MSP Märklin suite
-Copyright © 2006-2009 Mikkosoft Productions, Mikko Rasa
+Copyright © 2006-2010 Mikkosoft Productions, Mikko Rasa
Distributed under the GPL
*/
#include <algorithm>
-#include "control.h"
#include "block.h"
+#include "layout.h"
#include "tracktype.h"
-#include "trafficmanager.h"
-#include "turnout.h"
using namespace std;
using namespace Msp;
namespace Marklin {
-Block::Block(TrafficManager &tm, Track &start):
- trfc_mgr(tm),
+Block::Block(Layout &l, Track &start):
+ layout(l),
id(0),
sensor_id(start.get_sensor_id()),
turnout_id(start.get_turnout_id()),
}
}
- if(sensor_id)
- id = 0x1000|sensor_id;
- else if(turnout_id)
- id = 0x2000|turnout_id;
+ determine_id();
for(unsigned i=0; i<endpoints.size(); ++i)
{
set<Track *> visited;
find_paths(*endpoints[i].track, endpoints[i].track_ep, path, visited);
}
+
+ layout.add_block(*this);
+}
+
+Block::~Block()
+{
+ for(vector<Endpoint>::iterator i=endpoints.begin(); i!=endpoints.end(); ++i)
+ if(Block *blk = i->link)
+ {
+ i->link = 0;
+ blk->break_link(*this);
+ }
+
+ layout.remove_block(*this);
}
int Block::get_endpoint_by_link(const Block &other) const
while(1)
{
- unsigned cur_path = 0;
- unsigned tid = track->get_turnout_id();
- if(tid)
- {
- Turnout &turnout = trfc_mgr.get_control().get_turnout(tid);
- cur_path = turnout.get_path();
- }
+ unsigned cur_path = track->get_active_path();
if(len)
*len += track->get_type().get_path_length(cur_path);
{
i->link = &other;
j->link = this;
+
+ determine_id();
+ other.determine_id();
}
}
+}
- if(!sensor_id && !turnout_id && endpoints.size()==2)
- {
- unsigned id1 = endpoints[0].link ? endpoints[0].link->get_id() : 1;
- unsigned id2 = endpoints[1].link ? endpoints[1].link->get_id() : 1;
- if(id2<id1)
- swap(id1, id2);
- id = (id1<<16)|id2;
- }
+void Block::break_link(Block &other)
+{
+ for(vector<Endpoint>::iterator i=endpoints.begin(); i!=endpoints.end(); ++i)
+ if(i->link==&other)
+ {
+ i->link = 0;
+ other.break_link(*this);
+ determine_id();
+ }
}
Block *Block::get_link(unsigned epi) const
return endpoints[epi].link;
}
-bool Block::reserve(const Train *t)
+bool Block::reserve(Train *t)
{
if(!t || !train)
{
train = t;
- trfc_mgr.signal_block_reserved.emit(*this, train);
+ layout.signal_block_reserved.emit(*this, train);
return true;
}
else
}
}
+void Block::determine_id()
+{
+ if(sensor_id)
+ id = 0x1000|sensor_id;
+ else if(turnout_id)
+ id = 0x2000|turnout_id;
+ else if(endpoints.size()==2)
+ {
+ unsigned id1 = endpoints[0].link ? endpoints[0].link->get_id() : 1;
+ unsigned id2 = endpoints[1].link ? endpoints[1].link->get_id() : 1;
+ if(id2<id1)
+ swap(id1, id2);
+ id = (id1<<16)|id2;
+ }
+}
+
Block::Endpoint::Endpoint(Track *t, unsigned e):
track(t),
/* $Id$
This file is part of the MSP Märklin suite
-Copyright © 2006-2009 Mikkosoft Productions, Mikko Rasa
+Copyright © 2006-2010 Mikkosoft Productions, Mikko Rasa
Distributed under the GPL
*/
-#ifndef MARKLIN_3D_BLOCK_H_
-#define MARKLIN_3D_BLOCK_H_
+#ifndef LIBMARKLIN_BLOCK_H_
+#define LIBMARKLIN_BLOCK_H_
#include <list>
#include <set>
namespace Marklin {
+class Layout;
class Train;
-class TrafficManager;
class Block
{
};
private:
- TrafficManager &trfc_mgr;
+ Layout &layout;
unsigned id;
unsigned sensor_id;
unsigned turnout_id;
std::set<Track *> tracks;
std::vector<Endpoint> endpoints;
- const Train *train;
+ Train *train;
public:
- Block(TrafficManager &, Track &);
+ Block(Layout &, Track &);
+ ~Block();
unsigned get_id() const { return id; }
unsigned get_sensor_id() const { return sensor_id; }
int get_endpoint_by_link(const Block &) const;
unsigned traverse(unsigned, float * =0) const;
void check_link(Block &);
+ void break_link(Block &);
Block *get_link(unsigned) const;
- bool reserve(const Train *);
- const Train *get_train() const { return train; }
+ bool reserve(Train *);
+ Train *get_train() const { return train; }
void print_debug();
private:
void find_paths(Track &, unsigned, unsigned, std::set<Track *> &);
+ void determine_id();
};
} // namespace Marklin
+++ /dev/null
-/* $Id$
-
-This file is part of the MSP Märklin suite
-Copyright © 2006-2008 Mikkosoft Productions, Mikko Rasa
-Distributed under the GPL
-*/
-
-#include <cstring>
-#include <msp/strings/formatter.h>
-#include "command.h"
-
-using namespace std;
-using namespace Msp;
-
-namespace Marklin {
-
-Command::Command(Cmd c, const unsigned char *d, unsigned l):
- cmd(c),
- len(1),
- sent(false)
-{
- data[0]=cmd;
- if(d)
- {
- memcpy(data+1, d, min(l, 127U));
- len+=min(l, 127U);
- }
-}
-
-void Command::send(int fd)
-{
- write(fd, data, len);
- sent=true;
-}
-
-ostream &operator<<(ostream &out, const Command &cmd)
-{
- out<<cmd.cmd;
- for(unsigned i=1; i<cmd.len; ++i)
- out<<format(" %02X", static_cast<int>(cmd.data[i]));
-
- return out;
-}
-
-} // namespace Marklin
+++ /dev/null
-/* $Id$
-
-This file is part of the MSP Märklin suite
-Copyright © 2007-2008 Mikkosoft Productions, Mikko Rasa
-Distributed under the GPL
-*/
-
-#ifndef COMMAND_H_
-#define COMMAND_H_
-
-#include <ostream>
-#include <string>
-#include <sigc++/sigc++.h>
-#include "constants.h"
-
-namespace Marklin {
-
-class Reply;
-
-class Command
-{
-public:
- sigc::signal<void, const Reply &> signal_done;
-
-private:
- Cmd cmd;
- unsigned char data[128];
- unsigned len;
- bool sent;
-
-public:
- Command(Cmd, const unsigned char *, unsigned);
-
- void send(int);
- bool is_sent() const { return sent; }
- Cmd get_command() const { return cmd; }
-
- friend std::ostream &operator<<(std::ostream &, const Command &);
-};
-
-} // namespace Marklin
-
-#endif
+++ /dev/null
-/* $Id$
-
-This file is part of the MSP Märklin suite
-Copyright © 2008-2009 Mikkosoft Productions, Mikko Rasa
-Distributed under the GPL
-*/
-
-#include "constants.h"
-
-using namespace std;
-
-namespace Marklin {
-
-ostream &operator<<(ostream &out, const Error &err)
-{
- switch(err)
- {
- case ERR_NO_ERROR: out<<"ERR_NO_ERROR"; break;
- case ERR_SYS_ERROR: out<<"ERR_SYS_ERROR"; break;
- case ERR_BAD_PARAM: out<<"ERR_BAD_PARAM"; break;
- case ERR_POWER_OFF: out<<"ERR_POWER_OFF"; break;
- case ERR_NO_LOK_SPACE: out<<"ERR_NO_LOK_SPACE"; break;
- case ERR_NO_TURNOUT_SPACE: out<<"ERR_NO_TURNOUT_SPACE"; break;
- case ERR_NO_DATA: out<<"ERR_NO_DATA"; break;
- case ERR_NO_SLOT: out<<"ERR_NO_SLOT"; break;
- case ERR_BAD_LOK_ADDR: out<<"ERR_BAD_LOK_ADDR"; break;
- case ERR_LOK_BUSY: out<<"ERR_LOK_BUSY"; break;
- case ERR_BAD_TURNOUT_ADDR: out<<"ERR_BAD_TURNOUT_ADDR"; break;
- case ERR_BAD_SO_VALUE: out<<"ERR_BAD_SO_VALUE"; break;
- case ERR_NO_I2C_SPACE: out<<"ERR_NO_I2C_SPACE"; break;
- case ERR_LOW_TURNOUT_SPACE: out<<"ERR_LOW_TURNOUT_SPACE"; break;
- case ERR_LOK_HALTED: out<<"ERR_LOK_HALTED"; break;
- case ERR_LOK_POWER_OFF: out<<"ERR_LOK_POWER_OFF"; break;
- case ERR_UNKNOWN_ERROR: out<<"ERR_UNKNOWN_ERROR"; break;
- default: out<<"Err("<<static_cast<int>(err)<<')'; break;
- }
-
- return out;
-}
-
-ostream &operator<<(ostream &out, const Cmd &cmd)
-{
- switch(cmd)
- {
- case CMD_LOK: out<<"CMD_LOK"; break;
- case CMD_LOK_STATUS: out<<"CMD_LOK_STATUS"; break;
- case CMD_LOK_CONFIG: out<<"CMD_LOK_CONFIG"; break;
- case CMD_FUNC: out<<"CMD_FUNC"; break;
- case CMD_FUNC_STATUS: out<<"CMD_FUNC_STATUS"; break;
- case CMD_TURNOUT: out<<"CMD_TURNOUT"; break;
- case CMD_TURNOUT_FREE: out<<"CMD_TURNOUT_FREE"; break;
- case CMD_TURNOUT_STATUS: out<<"CMD_TURNOUT_STATUS"; break;
- case CMD_TURNOUT_GROUP_STATUS: out<<"CMD_TURNOUT_GROUP_STATUS"; break;
- case CMD_SENSOR_STATUS: out<<"CMD_SENSOR_STATUS"; break;
- case CMD_SENSOR_REPORT: out<<"CMD_SENSOR_REPORT"; break;
- case CMD_SENSOR_PARAM_SET: out<<"CMD_SENSOR_PARAM_SET"; break;
- case CMD_STATUS: out<<"CMD_STATUS"; break;
- case CMD_POWER_OFF: out<<"CMD_POWER_OFF"; break;
- case CMD_POWER_ON: out<<"CMD_POWER_ON"; break;
- case CMD_NOP: out<<"CMD_NOP"; break;
- case CMD_EVENT: out<<"CMD_EVENT"; break;
- case CMD_EVENT_LOK: out<<"CMD_EVENT_LOK"; break;
- case CMD_EVENT_TURNOUT: out<<"CMD_EVENT_TURNOUT"; break;
- case CMD_EVENT_SENSOR: out<<"CMD_EVENT_SENSOR"; break;
- default: out<<"Cmd("<<static_cast<int>(cmd)<<')'; break;
- }
-
- return out;
-}
-
-} // namespace Marklin
+++ /dev/null
-/* $Id$
-
-This file is part of the MSP Märklin suite
-Copyright © 2006-2008 Mikkosoft Productions, Mikko Rasa
-Distributed under the GPL
-*/
-
-#ifndef ERROR_H_
-#define ERROR_H_
-
-#include <ostream>
-
-namespace Marklin {
-
-enum Error
-{
- ERR_NO_ERROR=0,
- ERR_SYS_ERROR,
- ERR_BAD_PARAM,
- ERR_POWER_OFF=0x6,
- ERR_NO_LOK_SPACE=0x8, // No space in lok command buffer
- ERR_NO_TURNOUT_SPACE, // No space in turnout command buffer
- ERR_NO_DATA, // "no Lok status available (Lok is not in a slot)"
- ERR_NO_SLOT, // "there is no slot available"
- ERR_BAD_LOK_ADDR,
- ERR_LOK_BUSY,
- ERR_BAD_TURNOUT_ADDR,
- ERR_BAD_SO_VALUE,
- ERR_NO_I2C_SPACE,
- ERR_LOW_TURNOUT_SPACE=0x40,
- ERR_LOK_HALTED,
- ERR_LOK_POWER_OFF,
- ERR_UNKNOWN_ERROR=0xFF
-};
-
-std::ostream &operator<<(std::ostream &, const Error &);
-
-enum Cmd
-{
- CMD_LOK=0x80,
- CMD_LOK_STATUS=0x84,
- CMD_LOK_CONFIG=0x85,
- CMD_FUNC=0x88,
- CMD_FUNC_STATUS=0x8C,
- CMD_TURNOUT=0x90,
- CMD_TURNOUT_FREE=0x93,
- CMD_TURNOUT_STATUS=0x94,
- CMD_TURNOUT_GROUP_STATUS=0x95,
- CMD_SENSOR_STATUS=0x98,
- CMD_SENSOR_REPORT=0x99,
- CMD_SENSOR_PARAM_SET=0x9D,
- CMD_STATUS=0xA2,
- CMD_POWER_OFF=0xA6,
- CMD_POWER_ON=0xA7,
- CMD_NOP=0xC4,
- CMD_EVENT=0xC8,
- CMD_EVENT_LOK=0xC9,
- CMD_EVENT_TURNOUT=0xCA,
- CMD_EVENT_SENSOR=0xCB
-};
-
-std::ostream &operator<<(std::ostream &, const Cmd &);
-
-} // namespace Marklin
-
-#endif
+++ /dev/null
-/* $Id$
-
-This file is part of the MSP Märklin suite
-Copyright © 2007-2009 Mikkosoft Productions, Mikko Rasa
-Distributed under the GPL
-*/
-
-#include <fcntl.h>
-#include <termios.h>
-#include <sys/poll.h>
-#include <msp/core/except.h>
-#include <msp/io/print.h>
-#include <msp/time/units.h>
-#include <msp/time/utils.h>
-#include "command.h"
-#include "control.h"
-#include "reply.h"
-
-using namespace std;
-using namespace Msp;
-
-namespace Marklin {
-
-Control::Control():
- serial_fd(-1),
- power(true),
- poll_sensors(false),
- debug(false)
-{ }
-
-Control::~Control()
-{
- for(map<unsigned, Sensor *>::iterator i=sensors.begin(); i!=sensors.end(); ++i)
- delete i->second;
- for(map<unsigned, Turnout *>::iterator i=turnouts.begin(); i!=turnouts.end(); ++i)
- delete i->second;
- for(map<unsigned, Locomotive *>::iterator i=locomotives.begin(); i!=locomotives.end(); ++i)
- delete i->second;
- if(serial_fd>=0)
- close(serial_fd);
-}
-
-void Control::open(const string &dev)
-{
- serial_fd = ::open(dev.c_str(), O_RDWR);
- if(serial_fd<0)
- throw Exception("Couldn't open serial port\n");
-
- static unsigned baud[]=
- {
- 2400, B2400,
- 4800, B4800,
- 9600, B9600,
- 19200, B19200,
- 0
- };
-
- termios attr;
- tcgetattr(serial_fd, &attr);
- cfmakeraw(&attr);
- attr.c_cflag |= CSTOPB;
-
- bool ok = false;
- bool p50 = false;
- for(unsigned i=0; baud[i]; i+=2)
- {
- cfsetospeed(&attr, baud[i+1]);
- tcsetattr(serial_fd, TCSADRAIN, &attr);
-
- write(serial_fd, "\xC4", 1);
-
- pollfd pfd = { serial_fd, POLLIN, 0 };
- if(poll(&pfd, 1, 500)>0)
- {
- IO::print("IB detected at %d bits/s\n", baud[i]);
- char buf[2];
- p50 = (read(serial_fd, buf, 2)==2);
- ok = true;
- break;
- }
- }
-
- if(!ok)
- throw Exception("IB not detected");
-
- if(p50)
- write(serial_fd, "xZzA1\r", 6);
-
- command(CMD_STATUS).signal_done.connect(sigc::mem_fun(this, &Control::status_done));
-}
-
-void Control::set_debug(bool d)
-{
- debug = d;
-}
-
-void Control::set_power(bool p)
-{
- power = p;
- if(power)
- command(CMD_POWER_ON);
- else
- command(CMD_POWER_OFF);
-
- signal_power_event.emit(power);
-}
-
-Command &Control::command(Cmd cmd)
-{
- queue.push_back(Command(cmd, 0, 0));
- return queue.back();
-}
-
-Command &Control::command(Cmd cmd, unsigned char data)
-{
- queue.push_back(Command(cmd, &data, 1));
- return queue.back();
-}
-
-Command &Control::command(Cmd cmd, const unsigned char *data, unsigned len)
-{
- queue.push_back(Command(cmd, data, len));
- return queue.back();
-}
-
-void Control::flush()
-{
- for(list<Command>::iterator i=queue.begin(); i!=queue.end(); ++i)
- i->send(serial_fd);
-}
-
-void Control::add_turnout(Turnout &t)
-{
- turnouts[t.get_address()] = &t;
-}
-
-Turnout &Control::get_turnout(unsigned id) const
-{
- map<unsigned, Turnout *>::const_iterator i = turnouts.find(id);
- if(i==turnouts.end())
- throw KeyError("Unknown turnout");
-
- return *i->second;
-}
-
-void Control::add_locomotive(Locomotive &l)
-{
- locomotives[l.get_address()] = &l;
-}
-
-Locomotive &Control::get_locomotive(unsigned id) const
-{
- map<unsigned, Locomotive *>::const_iterator i = locomotives.find(id);
- if(i==locomotives.end())
- throw KeyError("Unknown locomotive");
-
- return *i->second;
-}
-
-void Control::add_sensor(Sensor &s)
-{
- sensors[s.get_address()] = &s;
- poll_sensors = true;
-}
-
-Sensor &Control::get_sensor(unsigned id) const
-{
- map<unsigned, Sensor *>::const_iterator i = sensors.find(id);
- if(i==sensors.end())
- throw KeyError("Unknown sensor");
-
- return *i->second;
-}
-
-void Control::tick()
-{
- const Time::TimeStamp t = Time::now();
-
- for(map<unsigned, Sensor *>::const_iterator i=sensors.begin(); i!=sensors.end(); ++i)
- i->second->tick();
-
- timer.tick(false);
-
- if(t>next_event_query)
- {
- next_event_query = t+200*Time::msec;
- command(CMD_EVENT).signal_done.connect(sigc::mem_fun(this, &Control::event_query_done));
- }
-
- if(poll_sensors)
- {
- unsigned max_addr = (--sensors.end())->first;
- unsigned char data[2];
- data[0] = 0;
- data[1] = (max_addr+7)/8;
- command(CMD_SENSOR_PARAM_SET, data, 2);
- command(CMD_SENSOR_REPORT);
- poll_sensors = false;
- }
-
- if(!queue.empty() && queue.front().is_sent())
- {
- pollfd pfd = { serial_fd, POLLIN, 0 };
- if(poll(&pfd, 1, 0)>0)
- {
- Reply reply = Reply::read(serial_fd, queue.front().get_command());
- if(debug)
- IO::print("R: %s\n", reply);
-
- queue.front().signal_done.emit(reply);
- queue.erase(queue.begin());
- }
- else
- return;
- }
-
- if(!queue.empty())
- {
- if(debug)
- IO::print("W: %s\n", queue.front());
-
- if(serial_fd>=0)
- queue.front().send(serial_fd);
- else
- {
- Reply reply = Reply::simulate(queue.front().get_command());
- queue.front().signal_done.emit(reply);
- queue.erase(queue.begin());
- }
- }
-}
-
-Time::Timer::Slot &Control::set_timer(const Time::TimeDelta &dt)
-{
- return timer.add(dt);
-}
-
-void Control::status_done(const Reply &reply)
-{
- power = ((reply.get_data()[0]&0x08)!=0);
- signal_power_event.emit(power);
-}
-
-void Control::event_query_done(const Reply &reply)
-{
- const unsigned char *data = reply.get_data();
- if(data[0]&0x01)
- command(CMD_EVENT_LOK);
- if(data[0]&0x20)
- command(CMD_EVENT_TURNOUT).signal_done.connect(sigc::mem_fun(this, &Control::turnout_event_done));
- if(data[0]&0x04)
- command(CMD_EVENT_SENSOR).signal_done.connect(sigc::mem_fun(this, &Control::sensor_event_done));
- if((data[0]&0x80) && (data[1]&0x40))
- command(CMD_STATUS).signal_done.connect(sigc::mem_fun(this, &Control::status_done));
-}
-
-void Control::turnout_event_done(const Reply &reply)
-{
- const unsigned char *data = reply.get_data();
- unsigned count = data[0];
- for(unsigned i=0; i<count; ++i)
- {
- unsigned addr = (data[i*2+1])+((data[i*2+2]&7)<<8);
- bool status = !(data[i*2+2]&0x80);
- IO::print("Turnout %d, status %d\n", addr, status);
- signal_turnout_event.emit(addr, status);
- }
-}
-
-void Control::sensor_event_done(const Reply &reply)
-{
- const unsigned char *data = reply.get_data();
- for(unsigned i=0; data[i]; i+=3)
- {
- unsigned module = data[i];
-
- IO::print("S88 module %d, status %08b%08b\n", module, data[1], data[2]);
-
- for(unsigned j=0; j<16; ++j)
- signal_sensor_event.emit(module*16+j-15, (data[i+1+j/8]>>(7-j%8))&1);
- }
-}
-
-} // namespace Marklin
+++ /dev/null
-/* $Id$
-
-This file is part of the MSP Märklin suite
-Copyright © 2006-2009 Mikkosoft Productions, Mikko Rasa
-Distributed under the GPL
-*/
-
-#ifndef LIBMARKLIN_CONTROL_H_
-#define LIBMARKLIN_CONTROL_H_
-
-#include <list>
-#include <string>
-#include <msp/time/timer.h>
-#include <msp/time/timestamp.h>
-#include "constants.h"
-#include "sensor.h"
-#include "locomotive.h"
-#include "turnout.h"
-
-namespace Marklin {
-
-class Command;
-class Reply;
-
-class Control
-{
-public:
- sigc::signal<void, bool> signal_power_event;
- sigc::signal<void, unsigned, bool> signal_turnout_event;
- sigc::signal<void, unsigned, bool> signal_sensor_event;
-
-private:
- int serial_fd;
- bool power;
- std::list<Command> queue;
- std::map<unsigned, Turnout *> turnouts;
- std::map<unsigned, Locomotive *> locomotives;
- std::map<unsigned, Sensor *> sensors;
- Msp::Time::TimeStamp next_event_query;
- bool poll_sensors;
- bool debug;
- Msp::Time::Timer timer;
-
-public:
- Control();
- ~Control();
-
- void open(const std::string &);
- void set_debug(bool);
- void set_power(bool);
- bool get_power() const { return power; }
- Command &command(Cmd);
- Command &command(Cmd, unsigned char);
- Command &command(Cmd, const unsigned char *, unsigned);
- unsigned get_queue_length() const { return queue.size(); }
- void flush();
-
- void add_turnout(Turnout &);
- Turnout &get_turnout(unsigned) const;
- const std::map<unsigned, Turnout *> &get_turnouts() const { return turnouts; }
- void add_locomotive(Locomotive &);
- Locomotive &get_locomotive(unsigned) const;
- const std::map<unsigned, Locomotive *> &get_locomotives() const { return locomotives; }
- void add_sensor(Sensor &);
- Sensor &get_sensor(unsigned) const;
- const std::map<unsigned, Sensor *> &get_sensors() const { return sensors; }
-
- void tick();
- Msp::Time::Timer::Slot &set_timer(const Msp::Time::TimeDelta &);
-private:
- void status_done(const Reply &);
- void event_query_done(const Reply &);
- void turnout_event_done(const Reply &);
- void sensor_event_done(const Reply &);
-};
-
-} // 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 "driver.h"
+#include "dummy.h"
+#include "intellibox.h"
+
+using namespace std;
+
+namespace Marklin {
+
+Driver *Driver::create(const string &str)
+{
+ string::size_type colon = str.find(':');
+ string type = str.substr(0, colon);
+ string params;
+
+ if(colon!=string::npos)
+ params = str.substr(colon+1);
+
+ if(type=="ib" || type=="intellibox")
+ return new Intellibox(params);
+ else if(type=="dummy")
+ return new Dummy;
+
+ throw Msp::InvalidParameterValue("Unknown driver");
+}
+
+} // 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_DRIVER_H_
+#define LIBMARKLIN_DRIVER_H_
+
+#include <string>
+#include <sigc++/signal.h>
+
+namespace Marklin {
+
+class Driver
+{
+public:
+ sigc::signal<void, bool> signal_power;
+ sigc::signal<void, unsigned, unsigned, bool> signal_loco_speed;
+ sigc::signal<void, unsigned, unsigned, bool> signal_loco_function;
+ sigc::signal<void, unsigned, bool> signal_turnout;
+ sigc::signal<void, unsigned, bool> signal_sensor;
+
+protected:
+ Driver() { }
+public:
+ virtual ~Driver() { }
+
+ virtual void set_power(bool) = 0;
+ virtual bool get_power() const = 0;
+
+ virtual void add_loco(unsigned) = 0;
+ virtual void set_loco_speed(unsigned, unsigned) = 0;
+ virtual void set_loco_reverse(unsigned, bool) = 0;
+ virtual void set_loco_function(unsigned, unsigned, bool) = 0;
+
+ virtual void add_turnout(unsigned) = 0;
+ virtual void set_turnout(unsigned, bool) = 0;
+ virtual bool get_turnout(unsigned) const = 0;
+
+ virtual void add_sensor(unsigned) = 0;
+ virtual bool get_sensor(unsigned) const = 0;
+
+ virtual void tick() = 0;
+ virtual void flush() = 0;
+
+ static Driver *create(const std::string &);
+};
+
+} // 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 "dummy.h"
+
+using namespace std;
+
+namespace Marklin {
+
+Dummy::Dummy():
+ power(true)
+{ }
+
+void Dummy::set_power(bool p)
+{
+ power = p;
+ signal_power.emit(power);
+}
+
+void Dummy::add_turnout(unsigned addr)
+{
+ turnouts[addr];
+}
+
+void Dummy::set_turnout(unsigned addr, bool state)
+{
+ turnouts[addr] = state;
+ signal_turnout.emit(addr, state);
+}
+
+bool Dummy::get_turnout(unsigned addr) const
+{
+ map<unsigned, bool>::const_iterator i = turnouts.find(addr);
+ if(i!=turnouts.end())
+ return i->second;
+ return false;
+}
+
+} // 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_DUMMY_H_
+#define LIBMARKLIN_DUMMY_H_
+
+#include <map>
+#include "driver.h"
+
+namespace Marklin {
+
+class Dummy: public Driver
+{
+private:
+ bool power;
+ std::map<unsigned, bool> turnouts;
+
+public:
+ Dummy();
+
+ virtual void set_power(bool);
+ virtual bool get_power() const { return power; }
+
+ virtual void add_loco(unsigned) { }
+ virtual void set_loco_speed(unsigned, unsigned) { }
+ virtual void set_loco_reverse(unsigned, bool) { }
+ virtual void set_loco_function(unsigned, unsigned, bool) { }
+
+ virtual void add_turnout(unsigned);
+ virtual void set_turnout(unsigned, bool);
+ virtual bool get_turnout(unsigned) const;
+
+ virtual void add_sensor(unsigned) { }
+ virtual bool get_sensor(unsigned) const { return false; }
+
+ virtual void tick() { }
+ virtual void flush() { }
+};
+
+} // namespace Marklin
+
+#endif
+++ /dev/null
-/* $Id$
-
-This file is part of the MSP Märklin suite
-Copyright © 2009 Mikkosoft Productions, Mikko Rasa
-Distributed under the GPL
-*/
-
-#ifndef LIBMARKLIN_EXCEPT_H_
-#define LIBMARKLIN_EXCEPT_H_
-
-#include <msp/core/except.h>
-
-namespace Marklin {
-
-class Train;
-
-class TurnoutBusy: public Msp::Exception
-{
-private:
- Train *train;
-
-public:
- TurnoutBusy(Train *t): Exception("Turnout is busy"), train(t) { }
- virtual ~TurnoutBusy() throw() { }
-
- Train *get_train() const throw() { return train; }
-};
-
-}
-
-#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 <fcntl.h>
+#include <termios.h>
+#include <sys/poll.h>
+#include <msp/io/print.h>
+#include <msp/time/units.h>
+#include <msp/time/utils.h>
+#include "intellibox.h"
+
+using namespace std;
+using namespace Msp;
+
+namespace Marklin {
+
+Intellibox::Intellibox(const string &dev):
+ power(false),
+ update_sensors(false),
+ command_sent(false)
+{
+ serial_fd = ::open(dev.c_str(), O_RDWR);
+ if(serial_fd<0)
+ throw Exception("Couldn't open serial port\n");
+
+ static unsigned baud[]=
+ {
+ 2400, B2400,
+ 4800, B4800,
+ 9600, B9600,
+ 19200, B19200,
+ 0
+ };
+
+ termios attr;
+ tcgetattr(serial_fd, &attr);
+ cfmakeraw(&attr);
+ attr.c_cflag |= CSTOPB;
+
+ bool ok = false;
+ bool p50 = false;
+ for(unsigned i=0; baud[i]; i+=2)
+ {
+ cfsetospeed(&attr, baud[i+1]);
+ tcsetattr(serial_fd, TCSADRAIN, &attr);
+
+ write(serial_fd, "\xC4", 1);
+
+ pollfd pfd = { serial_fd, POLLIN, 0 };
+ if(poll(&pfd, 1, 500)>0)
+ {
+ IO::print("IB detected at %d bits/s\n", baud[i]);
+ char buf[2];
+ p50 = (read(serial_fd, buf, 2)==2);
+ ok = true;
+ break;
+ }
+ }
+
+ if(!ok)
+ throw Exception("IB not detected");
+
+ if(p50)
+ write(serial_fd, "xZzA1\r", 6);
+
+ command(CMD_STATUS);
+}
+
+void Intellibox::set_power(bool p)
+{
+ power = p;
+ if(power)
+ command(CMD_POWER_ON);
+ else
+ command(CMD_POWER_OFF);
+ signal_power.emit(power);
+}
+
+void Intellibox::add_loco(unsigned addr)
+{
+ if(!locos.count(addr))
+ {
+ locos[addr];
+
+ unsigned char data[2];
+ data[0] = addr&0xFF;
+ data[1] = (addr>>8)&0xFF;
+ command(CMD_LOK_STATUS, addr, data, 2);
+ }
+}
+
+void Intellibox::set_loco_speed(unsigned addr, unsigned speed)
+{
+ Locomotive &loco = locos[addr];
+ loco.speed = speed;
+ loco_command(addr, speed, loco.reverse, loco.funcs|0x100);
+ signal_loco_speed.emit(addr, speed, loco.reverse);
+}
+
+void Intellibox::set_loco_reverse(unsigned addr, bool rev)
+{
+ Locomotive &loco = locos[addr];
+ loco.reverse = rev;
+ loco_command(addr, loco.speed, rev, loco.funcs|0x100);
+ signal_loco_speed.emit(addr, loco.speed, rev);
+}
+
+void Intellibox::set_loco_function(unsigned addr, unsigned func, bool state)
+{
+ Locomotive &loco = locos[addr];
+ if(state)
+ loco.funcs |= 1<<func;
+ else
+ loco.funcs &= ~(1<<func);
+ loco_command(addr, loco.speed, loco.reverse, loco.funcs);
+ signal_loco_function.emit(addr, func, state);
+}
+
+void Intellibox::add_turnout(unsigned addr)
+{
+ if(!turnouts.count(addr))
+ {
+ turnouts[addr];
+
+ unsigned char data[2];
+ data[0] = addr&0xFF;
+ data[1] = (addr>>8)&0xFF;
+ command(CMD_TURNOUT_STATUS, addr, data, 2);
+ }
+}
+
+void Intellibox::set_turnout(unsigned addr, bool state)
+{
+ Turnout &turnout = turnouts[addr];
+ if(state==turnout.state || state==turnout.pending)
+ return;
+
+ turnout.pending = state;
+ turnout.active = true;
+
+ turnout_command(addr, state, true);
+}
+
+bool Intellibox::get_turnout(unsigned addr) const
+{
+ map<unsigned, Turnout>::const_iterator i = turnouts.find(addr);
+ if(i!=turnouts.end())
+ return i->second.state;
+ return false;
+}
+
+void Intellibox::add_sensor(unsigned addr)
+{
+ if(!sensors.count(addr))
+ {
+ sensors[addr];
+ update_sensors = true;
+ }
+}
+
+bool Intellibox::get_sensor(unsigned addr) const
+{
+ map<unsigned, Sensor>::const_iterator i = sensors.find(addr);
+ if(i!=sensors.end())
+ return i->second.state;
+ return false;
+}
+
+void Intellibox::tick()
+{
+ const Time::TimeStamp t = Time::now();
+
+ if(t>next_event_query)
+ {
+ next_event_query = t+200*Time::msec;
+ command(CMD_EVENT);
+ }
+
+ for(map<unsigned, Turnout>::iterator i=turnouts.begin(); i!=turnouts.end(); ++i)
+ if(i->second.active && i->second.off_timeout && t>i->second.off_timeout)
+ {
+ i->second.active = false;
+ i->second.off_timeout = Time::TimeStamp();
+ turnout_command(i->first, i->second.state, false);
+ }
+
+ for(map<unsigned, Sensor>::iterator i=sensors.begin(); i!=sensors.end(); ++i)
+ if(i->second.off_timeout && t>i->second.off_timeout)
+ {
+ i->second.state = false;
+ i->second.off_timeout = Time::TimeStamp();
+ signal_sensor.emit(i->first, false);
+ }
+
+ if(update_sensors)
+ {
+ unsigned max_addr = (--sensors.end())->first;
+ unsigned char data[2];
+ data[0] = 0;
+ data[1] = (max_addr+7)/8;
+ command(CMD_SENSOR_PARAM_SET, data, 2);
+ command(CMD_SENSOR_REPORT);
+ update_sensors = false;
+ }
+
+ if(!queue.empty() && command_sent)
+ {
+ pollfd pfd = { serial_fd, POLLIN, 0 };
+ if(poll(&pfd, 1, 0)>0)
+ {
+ process_reply(t);
+ queue.erase(queue.begin());
+ command_sent = false;
+ }
+ else
+ return;
+ }
+
+ if(!queue.empty())
+ {
+ const CommandSlot &slot = queue.front();
+ write(serial_fd, slot.data, slot.length);
+ command_sent = true;
+ }
+}
+
+void Intellibox::flush()
+{
+ for(list<CommandSlot>::iterator i=queue.begin(); i!=queue.end(); ++i)
+ write(serial_fd, i->data, i->length);
+}
+
+void Intellibox::command(Command cmd)
+{
+ command(cmd, 0, 0);
+}
+
+void Intellibox::command(Command cmd, const unsigned char *data, unsigned len)
+{
+ command(cmd, 0, data, len);
+}
+
+void Intellibox::command(Command cmd, unsigned addr, const unsigned char *data, unsigned len)
+{
+ CommandSlot slot;
+ slot.cmd = cmd;
+ slot.addr = addr;
+ slot.data[0] = cmd;
+ copy(data, data+len, slot.data+1);
+ slot.length = 1+len;
+ queue.push_back(slot);
+}
+
+void Intellibox::loco_command(unsigned addr, unsigned speed, bool rev, unsigned funcs)
+{
+ unsigned char data[4];
+ data[0] = addr&0xFF;
+ data[1] = (addr>>8)&0xFF;
+
+ if(speed==0)
+ data[2] = 0;
+ else if(speed==1)
+ data[2] = 2;
+ else
+ data[2] = (speed*19-18)/2;
+
+ data[3] = (rev ? 0 : 0x20) | ((funcs&1) ? 0x10 : 0);
+
+ if(!(funcs&0x100))
+ data[3] |= 0x80 | ((funcs>>1)&0xF);
+
+ command(CMD_LOK, addr, data, 4);
+}
+
+void Intellibox::turnout_command(unsigned addr, bool state, bool active)
+{
+ unsigned char data[2];
+ data[0] = addr&0xFF;
+ data[1] = ((addr>>8)&0x7) | (active ? 0x40 : 0) | (state ? 0x80 : 0);
+ command(CMD_TURNOUT, addr, data, 2);
+}
+
+void Intellibox::process_reply(const Time::TimeStamp &t)
+{
+ Command cmd = queue.front().cmd;
+
+ if(cmd==CMD_STATUS)
+ {
+ unsigned char status;
+ read_all(&status, 1);
+ power = status&0x08;
+ signal_power.emit(power);
+ }
+ else if(cmd==CMD_EVENT)
+ {
+ for(unsigned i=0;; ++i)
+ {
+ unsigned char byte;
+ read_all(&byte, 1);
+
+ if(i==0)
+ {
+ if(byte&0x01)
+ command(CMD_EVENT_LOK);
+ if(byte&0x20)
+ command(CMD_EVENT_TURNOUT);
+ if(byte&0x04)
+ command(CMD_EVENT_SENSOR);
+ }
+ else if(i==1)
+ {
+ if(byte&0x40)
+ command(CMD_STATUS);
+ }
+
+ if(!(byte&0x80))
+ break;
+ }
+ }
+ else if(cmd==CMD_EVENT_LOK)
+ {
+ while(1)
+ {
+ unsigned char data[5];
+ read_all(data, 1);
+ if(data[0]==0x80)
+ break;
+ read_all(data+1, 4);
+ }
+ }
+ else if(cmd==CMD_EVENT_TURNOUT)
+ {
+ unsigned char count;
+ read_all(&count, 1);
+ for(unsigned i=0; i<count; ++i)
+ {
+ unsigned char data[2];
+ read_all(data, 2);
+
+ unsigned addr = data[0]+((data[1]&7)<<8);
+ signal_turnout.emit(addr, (data[1]&0x80)!=0);
+ }
+ }
+ else if(cmd==CMD_EVENT_SENSOR)
+ {
+ while(1)
+ {
+ unsigned char mod;
+ read_all(&mod, 1);
+ if(!mod)
+ break;
+
+ unsigned char data[2];
+ read_all(data, 2);
+ for(unsigned i=0; i<16; ++i)
+ {
+ unsigned addr = mod*16+i-15;
+ bool state = (data[i/8]>>(7-i%8))&1;
+
+ Sensor &sensor = sensors[addr];
+ if(state)
+ {
+ sensor.off_timeout = Time::TimeStamp();
+ if(!sensor.state)
+ {
+ sensor.state = state;
+ signal_sensor(addr, state);
+ }
+ }
+ else if(sensor.state)
+ sensor.off_timeout = t+700*Time::msec;
+ }
+ }
+ }
+ else if(cmd==CMD_TURNOUT)
+ {
+ unsigned char err;
+ read_all(&err, 1);
+
+ if(err==ERR_NO_ERROR)
+ {
+ unsigned addr = queue.front().addr;
+ Turnout &turnout = turnouts[addr];
+ turnout.state = turnout.pending;
+ if(turnout.active)
+ {
+ signal_turnout.emit(addr, turnout.state);
+ turnout.off_timeout = t+500*Time::msec;
+ }
+ }
+ else if(err==ERR_NO_I2C_SPACE)
+ queue.push_back(queue.front());
+ }
+ else if(cmd==CMD_TURNOUT_STATUS)
+ {
+ unsigned char err;
+ read_all(&err, 1);
+
+ if(err==ERR_NO_ERROR)
+ {
+ unsigned char data;
+ read_all(&data, 1);
+
+ unsigned addr = queue.front().addr;
+ bool state = data&0x04;
+
+ Turnout &turnout = turnouts[addr];
+ if(state!=turnout.state)
+ {
+ turnout.state = state;
+ signal_turnout.emit(addr, turnout.state);
+ }
+ }
+ }
+ else if(cmd==CMD_LOK_STATUS)
+ {
+ unsigned char err;
+ read_all(&err, 1);
+
+ if(err==ERR_NO_ERROR)
+ {
+ unsigned char data[3];
+ read_all(data, 3);
+
+ unsigned addr = queue.front().addr;
+ Locomotive &loco = locos[addr];
+
+ unsigned speed = (data[0]<=1 ? 0 : data[0]*2/19+1);
+ bool reverse = !(data[1]&0x20);
+ if(speed!=loco.speed || reverse!=loco.reverse)
+ {
+ loco.speed = speed;
+ loco.reverse = reverse;
+ signal_loco_speed.emit(addr, loco.speed, loco.reverse);
+ }
+
+ unsigned funcs = (data[1]&0xF)<<1;
+ if(data[1]&0x10)
+ funcs |= 1;
+ if(funcs!=loco.funcs)
+ {
+ unsigned changed = loco.funcs^funcs;
+ loco.funcs = funcs;
+ for(unsigned i=0; i<5; ++i)
+ if(changed&(1<<i))
+ signal_loco_function.emit(addr, i, loco.funcs&(1<<i));
+ }
+ }
+ }
+ else
+ {
+ unsigned expected_bytes = 0;
+ if(cmd==CMD_FUNC_STATUS)
+ expected_bytes = 1;
+ if(cmd==CMD_TURNOUT_GROUP_STATUS)
+ expected_bytes = 2;
+ if(cmd==CMD_LOK_CONFIG)
+ expected_bytes = 4;
+
+ unsigned char err;
+ read_all(&err, 1);
+
+ if(err==ERR_NO_ERROR)
+ {
+ unsigned char data[8];
+ read_all(data, expected_bytes);
+ }
+ }
+}
+
+unsigned Intellibox::read_all(unsigned char *buf, unsigned len)
+{
+ unsigned pos = 0;
+ while(pos<len)
+ pos += read(serial_fd, buf+pos, len-pos);
+
+ return pos;
+}
+
+
+Intellibox::Locomotive::Locomotive():
+ speed(0),
+ reverse(false),
+ funcs(0)
+{ }
+
+
+Intellibox::Turnout::Turnout():
+ state(false),
+ active(false)
+{ }
+
+
+Intellibox::Sensor::Sensor():
+ state(false)
+{ }
+
+} // 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_INTELLIBOX_H_
+#define LIBMARKLIN_INTELLIBOX_H_
+
+#include <map>
+#include <msp/time/timestamp.h>
+#include "driver.h"
+
+namespace Marklin {
+
+class Intellibox: public Driver
+{
+private:
+ enum Command
+ {
+ CMD_LOK=0x80,
+ CMD_LOK_STATUS=0x84,
+ CMD_LOK_CONFIG=0x85,
+ CMD_FUNC=0x88,
+ CMD_FUNC_STATUS=0x8C,
+ CMD_TURNOUT=0x90,
+ CMD_TURNOUT_FREE=0x93,
+ CMD_TURNOUT_STATUS=0x94,
+ CMD_TURNOUT_GROUP_STATUS=0x95,
+ CMD_SENSOR_STATUS=0x98,
+ CMD_SENSOR_REPORT=0x99,
+ CMD_SENSOR_PARAM_SET=0x9D,
+ CMD_STATUS=0xA2,
+ CMD_POWER_OFF=0xA6,
+ CMD_POWER_ON=0xA7,
+ CMD_NOP=0xC4,
+ CMD_EVENT=0xC8,
+ CMD_EVENT_LOK=0xC9,
+ CMD_EVENT_TURNOUT=0xCA,
+ CMD_EVENT_SENSOR=0xCB
+ };
+
+ enum Error
+ {
+ ERR_NO_ERROR=0,
+ ERR_SYS_ERROR,
+ ERR_BAD_PARAM,
+ ERR_POWER_OFF=0x6,
+ ERR_NO_LOK_SPACE=0x8, // No space in lok command buffer
+ ERR_NO_TURNOUT_SPACE, // No space in turnout command buffer
+ ERR_NO_DATA, // "no Lok status available (Lok is not in a slot)"
+ ERR_NO_SLOT, // "there is no slot available"
+ ERR_BAD_LOK_ADDR,
+ ERR_LOK_BUSY,
+ ERR_BAD_TURNOUT_ADDR,
+ ERR_BAD_SO_VALUE,
+ ERR_NO_I2C_SPACE,
+ ERR_LOW_TURNOUT_SPACE=0x40,
+ ERR_LOK_HALTED,
+ ERR_LOK_POWER_OFF,
+ };
+
+ struct Locomotive
+ {
+ unsigned speed;
+ bool reverse;
+ unsigned funcs;
+
+ Locomotive();
+ };
+
+ struct Turnout
+ {
+ bool state;
+ bool active;
+ bool pending;
+ Msp::Time::TimeStamp off_timeout;
+
+ Turnout();
+ };
+
+ struct Sensor
+ {
+ bool state;
+ Msp::Time::TimeStamp off_timeout;
+
+ Sensor();
+ };
+
+ struct CommandSlot
+ {
+ Command cmd;
+ unsigned addr;
+ unsigned char data[8];
+ unsigned length;
+ };
+
+ int serial_fd;
+ bool power;
+ std::map<unsigned, Locomotive> locos;
+ std::map<unsigned, Turnout> turnouts;
+ std::map<unsigned, Sensor> sensors;
+ bool update_sensors;
+ std::list<CommandSlot> queue;
+ bool command_sent;
+ Msp::Time::TimeStamp next_event_query;
+
+public:
+ Intellibox(const std::string &);
+
+ virtual void set_power(bool);
+ virtual bool get_power() const { return power; }
+
+ virtual void add_loco(unsigned);
+ virtual void set_loco_speed(unsigned, unsigned);
+ virtual void set_loco_reverse(unsigned, bool);
+ virtual void set_loco_function(unsigned, unsigned, bool);
+
+ virtual void add_turnout(unsigned);
+ virtual void set_turnout(unsigned, bool);
+ virtual bool get_turnout(unsigned) const;
+
+ virtual void add_sensor(unsigned);
+ virtual bool get_sensor(unsigned) const;
+
+ virtual void tick();
+ virtual void flush();
+
+private:
+ void command(Command);
+ void command(Command, const unsigned char *, unsigned);
+ void command(Command, unsigned, const unsigned char *, unsigned);
+ void loco_command(unsigned, unsigned, bool, unsigned);
+ void turnout_command(unsigned, bool, bool);
+ void process_reply(const Msp::Time::TimeStamp &);
+ unsigned read_all(unsigned char *, unsigned);
+};
+
+} // namespace Marklin
+
+#endif
/* $Id$
This file is part of the MSP Märklin suite
-Copyright © 2006-2009 Mikkosoft Productions, Mikko Rasa
+Copyright © 2006-2010 Mikkosoft Productions, Mikko Rasa
Distributed under the GPL
*/
+#include <algorithm>
#include <msp/core/refptr.h>
#include <msp/datafile/parser.h>
#include <msp/datafile/writer.h>
+#include <msp/time/utils.h>
+#include "block.h"
#include "catalogue.h"
+#include "driver.h"
#include "layout.h"
+#include "locotype.h"
+#include "route.h"
+#include "track.h"
#include "tracktype.h"
+#include "train.h"
using namespace std;
using namespace Msp;
namespace Marklin {
-Layout::Layout(Catalogue &c):
- catalogue(c)
+Layout::Layout(Catalogue &c, Driver *d):
+ catalogue(c),
+ driver(d)
{ }
Layout::~Layout()
{
- for(set<Track *>::iterator i=tracks.begin(); i!=tracks.end(); ++i)
- delete *i;
- for(map<string, Route *>::iterator i=routes.begin(); i!=routes.end(); ++i)
- delete i->second;
+ delete driver;
+ while(!trains.empty())
+ delete trains.begin()->second;
+ while(!routes.empty())
+ delete routes.begin()->second;
+ while(!tracks.empty())
+ delete *tracks.begin();
+ while(!blocks.empty())
+ delete *blocks.begin();
+}
+
+Driver &Layout::get_driver() const
+{
+ if(!driver)
+ throw InvalidState("No driver");
+ return *driver;
}
void Layout::add_track(Track &t)
{
if(tracks.insert(&t).second)
+ {
+ create_blocks();
signal_track_added.emit(t);
+ }
}
void Layout::remove_track(Track &t)
{
if(tracks.erase(&t))
+ {
+ create_blocks(t);
signal_track_removed.emit(t);
+ }
+}
+
+void Layout::add_block(Block &b)
+{
+ blocks.insert(&b);
+}
+
+Block &Layout::get_block(unsigned id) const
+{
+ for(set<Block *>::const_iterator i=blocks.begin(); i!=blocks.end(); ++i)
+ if((*i)->get_id()==id)
+ return **i;
+
+ throw KeyError("Unknown block", lexical_cast(id));
+}
+
+Block &Layout::get_block_by_track(const Track &t) const
+{
+ for(set<Block *>::const_iterator i=blocks.begin(); i!=blocks.end(); ++i)
+ if((*i)->get_tracks().count(const_cast<Track *>(&t)))
+ return **i;
+
+ throw InvalidParameterValue("No block found for track");
+}
+
+void Layout::create_blocks()
+{
+ set<Track *> used_tracks;
+ for(set<Block *>::const_iterator i=blocks.begin(); i!=blocks.end(); ++i)
+ {
+ const set<Track *> &btracks = (*i)->get_tracks();
+ used_tracks.insert(btracks.begin(), btracks.end());
+ }
+
+ for(set<Track *>::const_iterator i=tracks.begin(); i!=tracks.end(); ++i)
+ if(used_tracks.count(*i)==0)
+ {
+ Block *block = new Block(*this, **i);
+ used_tracks.insert(block->get_tracks().begin(), block->get_tracks().end());
+ }
+
+ for(set<Block *>::iterator i=blocks.begin(); i!=blocks.end(); ++i)
+ for(set<Block *>::iterator j=i; j!=blocks.end(); ++j)
+ if(j!=i)
+ (*i)->check_link(**j);
+}
+
+void Layout::create_blocks(const Track &track)
+{
+ const vector<Track *> &links = track.get_links();
+ for(set<Block *>::iterator i=blocks.begin(); i!=blocks.end();)
+ {
+ bool del = (*i)->get_tracks().count(const_cast<Track *>(&track));
+ for(vector<Track *>::const_iterator j=links.begin(); (!del && j!=links.end()); ++j)
+ del = (*i)->get_tracks().count(*j);
+
+ if(del)
+ delete *i++;
+ else
+ ++i;
+ }
+
+ create_blocks();
+}
+
+void Layout::remove_block(Block &b)
+{
+ blocks.erase(&b);
}
void Layout::add_route(Route &r)
{
if(routes.count(r.get_name()))
- throw KeyError("Duplicate route name");
+ throw KeyError("Duplicate route name", r.get_name());
+
routes[r.get_name()] = &r;
signal_route_added.emit(r);
}
signal_route_removed.emit(r);
}
+void Layout::add_train(Train &t)
+{
+ if(trains.count(t.get_address()))
+ throw KeyError("Duplicate train address", lexical_cast(t.get_address()));
+
+ trains[t.get_address()] = &t;
+ signal_train_added.emit(t);
+}
+
+Train &Layout::get_train(unsigned addr) const
+{
+ map<unsigned, Train *>::const_iterator i = trains.find(addr);
+ if(i==trains.end())
+ throw KeyError("Unknown train", lexical_cast(addr));
+ return *i->second;
+}
+
+void Layout::remove_train(Train &t)
+{
+ if(trains.erase(t.get_address()))
+ signal_train_removed.emit(t);
+}
+
+void Layout::tick()
+{
+ if(driver)
+ driver->tick();
+
+ Time::TimeStamp t = Time::now();
+ Time::TimeDelta dt;
+ if(last_tick)
+ dt = t-last_tick;
+ last_tick = t;
+
+ for(map<unsigned, Train *>::iterator i=trains.begin(); i!=trains.end(); ++i)
+ i->second->tick(t, dt);
+}
+
void Layout::save(const string &fn)
{
IO::BufferedFile out(fn, IO::M_WRITE);
}
}
+void Layout::save_trains(const string &fn)
+{
+ IO::BufferedFile out(fn, IO::M_WRITE);
+ DataFile::Writer writer(out);
+
+ for(map<unsigned, Train *>::const_iterator i=trains.begin(); i!=trains.end(); ++i)
+ {
+ DataFile::Statement st("train");
+ st.append(i->second->get_locomotive_type().get_article_number());
+ st.append(i->second->get_address());
+ i->second->save(st.sub);
+ writer.write(st);
+ }
+}
+
void Layout::check_links()
{
for(set<Track *>::iterator i=tracks.begin(); i!=tracks.end(); ++i)
break;
ep = next->get_endpoint_by_link(*track);
- if(next->get_type().get_n_paths()>1)
+ if(next->get_type().is_turnout())
{
// Select correct path across the turnout, or break if we hit an unknown turnout
map<unsigned, int>::const_iterator j = turnouts.find(next->get_turnout_id());
Layout::Loader::Loader(Layout &l):
- DataFile::BasicLoader<Layout>(l)
+ DataFile::BasicLoader<Layout>(l),
+ new_tracks(false)
{
add("base", &Layout::base);
add("route", &Loader::route);
add("track", &Loader::track);
+ add("train", &Loader::train);
}
void Layout::Loader::finish()
{
- obj.check_links();
+ if(new_tracks)
+ obj.check_links();
obj.check_routes();
for(set<Track *>::iterator i=obj.tracks.begin(); i!=obj.tracks.end(); ++i)
void Layout::Loader::route(const string &n)
{
- RefPtr<Route> rte = new Route(obj, n);
+ Route *rte = new Route(obj, n);
load_sub(*rte);
- obj.add_route(*rte.release());
}
void Layout::Loader::track(unsigned art_nr)
{
- const TrackType &type = obj.catalogue.get_track(art_nr);
-
- RefPtr<Track> trk = new Track(type);
+ Track *trk = new Track(obj, obj.catalogue.get_track(art_nr));
load_sub(*trk);
- obj.add_track(*trk.release());
+ new_tracks = true;
+}
+
+void Layout::Loader::train(unsigned art_nr, unsigned addr)
+{
+ Train *trn = new Train(obj, obj.catalogue.get_locomotive(art_nr), addr);
+ load_sub(*trn);
}
} // namespace Marklin
/* $Id$
This file is part of the MSP Märklin suite
-Copyright © 2006-2009 Mikkosoft Productions, Mikko Rasa
+Copyright © 2006-2010 Mikkosoft Productions, Mikko Rasa
Distributed under the GPL
*/
#ifndef LIBMARKLIN_LAYOUT_H_
#define LIBMARKLIN_LAYOUT_H_
+#include <set>
#include <sigc++/sigc++.h>
#include <msp/datafile/loader.h>
-#include "route.h"
-#include "track.h"
+#include <msp/time/timestamp.h>
namespace Marklin {
+class Block;
class Catalogue;
+class Driver;
+class Route;
+class Track;
+class Train;
class Layout
{
public:
class Loader: public Msp::DataFile::BasicLoader<Layout>
{
+ private:
+ bool new_tracks;
+
public:
Loader(Layout &);
private:
virtual void finish();
void route(const std::string &);
void track(unsigned);
+ void train(unsigned, unsigned);
};
public:
sigc::signal<void, Track &> signal_track_removed;
sigc::signal<void, Route &> signal_route_added;
sigc::signal<void, Route &> signal_route_removed;
+ sigc::signal<void, Train &> signal_train_added;
+ sigc::signal<void, Train &> signal_train_removed;
+ sigc::signal<void, Block &, Train *> signal_block_reserved;
private:
Catalogue &catalogue;
+ Driver *driver;
std::string base;
std::set<Track *> tracks;
std::map<std::string, Route *> routes;
+ std::set<Block *> blocks;
+ std::map<unsigned, Train *> trains;
+ Msp::Time::TimeStamp last_tick;
public:
- Layout(Catalogue &);
+ Layout(Catalogue &, Driver * = 0);
~Layout();
Catalogue &get_catalogue() const { return catalogue; }
+ bool has_driver() const { return driver; }
+ Driver &get_driver() const;
const std::string &get_base() const { return base; }
- const std::set<Track *> &get_tracks() const { return tracks; }
+
void add_track(Track &);
+ const std::set<Track *> &get_tracks() const { return tracks; }
void remove_track(Track &);
+
+ void add_block(Block &);
+ Block &get_block(unsigned) const;
+ Block &get_block_by_track(const Track &) const;
+ const std::set<Block *> &get_blocks() const { return blocks; }
+ void create_blocks();
+ void create_blocks(const Track &);
+ void remove_block(Block &);
+
void add_route(Route &);
const std::map<std::string, Route *> &get_routes() const { return routes; }
Route &get_route(const std::string &) const;
void remove_route(Route &);
+
+ void add_train(Train &);
+ Train &get_train(unsigned) const;
+ const std::map<unsigned, Train *> &get_trains() const { return trains; }
+ void remove_train(Train &);
+
+ void tick();
+
void save(const std::string &);
+ void save_trains(const std::string &);
private:
void check_links();
void check_routes();
+++ /dev/null
-/* $Id$
-
-This file is part of the MSP Märklin suite
-Copyright © 2006-2009 Mikkosoft Productions, Mikko Rasa
-Distributed under the GPL
-*/
-
-#include <msp/time/timer.h>
-#include <msp/time/units.h>
-#include "command.h"
-#include "constants.h"
-#include "control.h"
-#include "locomotive.h"
-#include "locotype.h"
-#include "reply.h"
-
-using namespace std;
-using namespace Msp;
-
-namespace Marklin {
-
-Locomotive::Locomotive(const LocoType &t, Control &c, unsigned a):
- type(t),
- control(c),
- addr(a),
- speed(0),
- reverse(false),
- funcs(0)
-{
- control.add_locomotive(*this);
-
- refresh_status();
-}
-
-void Locomotive::set_speed(unsigned spd)
-{
- spd = min(spd, 14U);
- if(spd==speed)
- return;
- signal_speed_changing.emit(spd);
-
- speed = spd;
- send_command(false);
-
- signal_speed_changed.emit(speed);
-}
-
-void Locomotive::set_reverse(bool rev)
-{
- if(rev==reverse)
- return;
-
- if(speed)
- {
- control.set_timer((500+speed*150)*Time::msec).signal_timeout.connect(sigc::mem_fun(this, &Locomotive::reverse_timeout));
- set_speed(0);
- }
- else
- {
- reverse = rev;
- send_command(false);
- signal_reverse_changed.emit(reverse);
- }
-}
-
-void Locomotive::set_function(unsigned func, bool state)
-{
- if(state)
- funcs |= 1<<func;
- else
- funcs &= ~(1<<func);
-
- send_command(true);
-
- signal_function_changed.emit(func, state);
-}
-
-void Locomotive::refresh_status()
-{
- unsigned char data[2];
- data[0] = addr&0xFF;
- data[1] = (addr>>8)&0xFF;
- control.command(CMD_LOK_STATUS, data, 2).signal_done.connect(sigc::mem_fun(this, &Locomotive::status_reply));
-}
-
-void Locomotive::send_command(bool setf)
-{
- unsigned char data[4];
- data[0] = addr&0xFF;
- data[1] = (addr>>8)&0xFF;
-
- if(speed==0)
- data[2] = 0;
- else if(speed==1)
- data[2] = 2;
- else
- data[2] = (speed*19-18)/2;
-
- data[3] = (reverse ? 0 : 0x20) | ((funcs&1) ? 0x10 : 0);
-
- if(setf)
- {
- data[3] |= 0x80;
- for(unsigned i=0; i<4; ++i)
- if((funcs>>i)&2)
- data[3] |= (1<<i);
- }
- control.command(CMD_LOK, data, 4);
-
- if(setf && type.get_max_function()>4)
- {
- if(!++data[0])
- ++data[1];
- data[2] = 0;
- data[3] = 0xA0;
- for(unsigned i=0; i<4; ++i)
- if((funcs>>i)&32)
- data[3] |= (1<<i);
- control.command(CMD_LOK, data, 4);
- }
-}
-
-void Locomotive::status_reply(const Reply &reply)
-{
- if(reply.get_error()==ERR_NO_ERROR)
- {
- const unsigned char *data = reply.get_data();
-
- if(data[0]<=1)
- speed = 0;
- else
- speed = data[0]*2/19+1;
-
- reverse = (data[1]&0x20) ? false : true;
- funcs = (data[1]&0xF)<<1;
- if(data[1]&0x10)
- funcs |= 1;
-
- for(unsigned i=0; i<5; ++i)
- signal_function_changed.emit(i, (funcs>>i)&1);
- }
-}
-
-bool Locomotive::reverse_timeout()
-{
- reverse = !reverse;
- send_command(true);
- signal_reverse_changed.emit(reverse);
- return false;
-}
-
-} // namespace Marklin
+++ /dev/null
-/* $Id$
-
-This file is part of the MSP Märklin suite
-Copyright © 2006-2009 Mikkosoft Productions, Mikko Rasa
-Distributed under the GPL
-*/
-
-#ifndef LIBMARKLIN_LOCOMOTIVE_H_
-#define LIBMARKLIN_LOCOMOTIVE_H_
-
-#include <list>
-#include <string>
-#include <sigc++/signal.h>
-#include "constants.h"
-
-namespace Marklin {
-
-class Control;
-class LocoType;
-class Reply;
-
-class Locomotive
-{
-public:
- sigc::signal<void, unsigned> signal_speed_changing;
- sigc::signal<void, unsigned> signal_speed_changed;
- sigc::signal<void, bool> signal_reverse_changed;
- sigc::signal<void, unsigned, bool> signal_function_changed;
-
-private:
- const LocoType &type;
- Control &control;
- unsigned addr;
- unsigned speed;
- bool reverse;
- unsigned funcs;
-
-public:
- Locomotive(const LocoType &, Control &, unsigned);
-
- const LocoType &get_type() const { return type; }
- unsigned get_address() const { return addr; }
- void set_speed(unsigned);
- void set_reverse(bool);
- void set_function(unsigned, bool);
- unsigned get_speed() const { return speed; }
- bool get_reverse() const { return reverse; }
- bool get_function(unsigned f) const { return (funcs>>f)&1; }
- unsigned get_functions() const { return funcs; }
- void refresh_status();
-private:
- void send_command(bool);
- void status_reply(const Reply &);
- bool reverse_timeout();
-};
-
-} // namespace Marklin
-
-#endif
+++ /dev/null
-/* $Id$
-
-This file is part of the MSP Märklin suite
-Copyright © 2006-2009 Mikkosoft Productions, Mikko Rasa
-Distributed under the GPL
-*/
-
-#include <cstring>
-#include <unistd.h>
-#include <msp/strings/formatter.h>
-#include "constants.h"
-#include "reply.h"
-
-using namespace std;
-using namespace Msp;
-
-namespace {
-
-unsigned read_all(int fd, char *buf, unsigned size)
-{
- unsigned pos = 0;
- while(pos<size)
- pos += read(fd, buf+pos, size-pos);
-
- return pos;
-}
-
-}
-
-namespace Marklin {
-
-Reply::Reply():
- err(ERR_NO_ERROR),
- len(0)
-{
- memset(data, 0, 128);
-}
-
-Reply Reply::read(int fd, Cmd cmd)
-{
- Reply result;
-
- char *data = reinterpret_cast<char *>(result.data);
-
- if(cmd==CMD_EVENT)
- {
- for(unsigned i=0; i<3; ++i)
- {
- result.len += read_all(fd, data+i, 1);
- if(!(result.data[i]&0x80))
- break;
- }
- }
- else if(cmd==CMD_EVENT_LOK)
- {
- for(unsigned i=0;; i+=5)
- {
- result.len += read_all(fd, data+i, 1);
-
- if(result.data[i]&0x80)
- break;
-
- result.len += read_all(fd, data+i+1, 4);
- }
- }
- else if(cmd==CMD_EVENT_TURNOUT)
- {
- result.len += read_all(fd, data, 1);
- result.len += read_all(fd, data+1, result.data[0]*2);
- }
- else if(cmd==CMD_EVENT_SENSOR)
- {
- for(unsigned i=0;; i+=3)
- {
- result.len += read_all(fd, data+i, 1);
-
- if(result.data[i]==0)
- break;
-
- result.len += read_all(fd, data+i+1, 2);
- }
- }
- else
- {
- bool expect_errcode = (cmd!=CMD_STATUS);
-
- unsigned expected_bytes = 0;
- if(cmd==CMD_STATUS || cmd==CMD_FUNC_STATUS || cmd==CMD_TURNOUT_STATUS)
- expected_bytes = 1;
- if(cmd==CMD_SENSOR_STATUS || cmd==CMD_TURNOUT_GROUP_STATUS)
- expected_bytes = 2;
- if(cmd==CMD_LOK_STATUS)
- expected_bytes = 3;
- if(cmd==CMD_LOK_CONFIG)
- expected_bytes = 4;
-
- if(expect_errcode)
- {
- char c;
- read_all(fd, &c, 1);
- result.err = static_cast<Error>(c);
- }
-
- if(result.err==ERR_NO_ERROR)
- result.len += read_all(fd, data, expected_bytes);
- }
-
- return result;
-}
-
-Reply Reply::simulate(Cmd cmd)
-{
- Reply result;
-
- if(cmd==CMD_STATUS)
- {
- result.data[0] = 0x80;
- result.len = 1;
- }
- if(cmd==CMD_EVENT)
- result.len = 1;
- else if(cmd==CMD_TURNOUT)
- ;
- else if(cmd==CMD_TURNOUT_STATUS)
- {
- result.data[0] = 0x04;
- result.len = 1;
- }
- else if(cmd==CMD_LOK)
- ;
- else if(cmd==CMD_LOK_STATUS)
- {
- result.data[1] = 0x20;
- result.len = 3;
- }
- else if(cmd==CMD_SENSOR_PARAM_SET)
- ;
- else if(cmd==CMD_SENSOR_REPORT)
- ;
- else if(cmd==CMD_POWER_ON)
- ;
- else if(cmd==CMD_POWER_OFF)
- ;
- else
- result.err = ERR_SYS_ERROR;
-
- return result;
-}
-
-ostream &operator<<(ostream &out, const Reply &reply)
-{
- out<<reply.err;
- for(unsigned i=0; i<reply.len; ++i)
- out<<format(" %02X", static_cast<int>(reply.data[i]));
-
- return out;
-}
-
-} // namespace Marklin
+++ /dev/null
-/* $Id$
-
-This file is part of the MSP Märklin suite
-Copyright © 2006-2009 Mikkosoft Productions, Mikko Rasa
-Distributed under the GPL
-*/
-
-#ifndef LIBMARKLIN_REPLY_H_
-#define LIBMARKLIN_REPLY_H_
-
-#include <ostream>
-
-namespace Marklin {
-
-class Reply
-{
-private:
- Error err;
- unsigned char data[128];
- unsigned len;
-
- Reply();
-public:
- Error get_error() const { return err; }
- const unsigned char *get_data() const { return data; }
-
- static Reply read(int, Cmd);
- static Reply simulate(Cmd);
-
- friend std::ostream &operator<<(std::ostream &, const Reply &);
-};
-
-
-
-} // namespace Marklin
-
-#endif
/* $Id$
This file is part of the MSP Märklin suite
-Copyright © 2007-2009 Mikkosoft Productions, Mikko Rasa
+Copyright © 2007-2010 Mikkosoft Productions, Mikko Rasa
Distributed under the GPL
*/
namespace Marklin {
-Route::Route(Layout &layout, const string &n):
+Route::Route(Layout &l, const string &n):
+ layout(l),
name(n)
{
+ layout.add_route(*this);
layout.signal_track_removed.connect(sigc::mem_fun(this, &Route::track_removed));
}
+Route::~Route()
+{
+ layout.remove_route(*this);
+}
+
int Route::get_turnout(unsigned id) const
{
map<unsigned, int>::const_iterator i = turnouts.find(id);
/* $Id$
This file is part of the MSP Märklin suite
-Copyright © 2007-2009 Mikkosoft Productions, Mikko Rasa
+Copyright © 2007-2010 Mikkosoft Productions, Mikko Rasa
Distributed under the GPL
*/
#include <map>
#include <set>
#include <string>
+#include <sigc++/trackable.h>
#include <msp/datafile/loader.h>
namespace Marklin {
class Layout;
class Track;
-class Turnout;
-class Route
+class Route: public sigc::trackable
{
public:
class Loader: public Msp::DataFile::BasicLoader<Route>
};
private:
+ Layout &layout;
std::string name;
std::set<const Track *> tracks;
std::map<unsigned, int> turnouts;
public:
Route(Layout &, const std::string &);
+ ~Route();
const std::string &get_name() const { return name; }
int get_turnout(unsigned) const;
+++ /dev/null
-/* $Id$
-
-This file is part of the MSP Märklin suite
-Copyright © 2007-2008 Mikkosoft Productions, Mikko Rasa
-Distributed under the GPL
-*/
-
-#include <msp/time/utils.h>
-#include <msp/time/units.h>
-#include "control.h"
-#include "sensor.h"
-
-using namespace Msp;
-
-namespace Marklin {
-
-Sensor::Sensor(Control &c, unsigned a):
- control(c),
- addr(a),
- state(false)
-{
- control.add_sensor(*this);
- control.signal_sensor_event.connect(sigc::mem_fun(this, &Sensor::sensor_event));
-}
-
-void Sensor::sensor_event(unsigned a, bool s)
-{
- if(a==addr)
- {
- if(s)
- {
- off_timeout = Time::TimeStamp();
- if(s!=state)
- {
- state = s;
- signal_state_changed.emit(state);
- }
- }
- else
- off_timeout = Time::now()+0.5*Time::sec;
- }
-}
-
-void Sensor::tick()
-{
- if(off_timeout)
- {
- const Time::TimeStamp t = Time::now();
- if(t>off_timeout)
- {
- off_timeout = Time::TimeStamp();
- if(state)
- {
- state = false;
- signal_state_changed.emit(state);
- }
- }
- }
-}
-
-} // namespace Marklin
+++ /dev/null
-/* $Id$
-
-This file is part of the MSP Märklin suite
-Copyright © 2007-2008 Mikkosoft Productions, Mikko Rasa
-Distributed under the GPL
-*/
-
-#ifndef LIBMARKLIN_SENSOR_H_
-#define LIBMARKLIN_SENSOR_H_
-
-#include <list>
-#include <map>
-#include <sigc++/signal.h>
-#include <msp/time/timestamp.h>
-
-namespace Marklin {
-
-class Control;
-
-class Sensor
-{
-public:
- sigc::signal<void, bool> signal_state_changed;
-
-private:
- Control &control;
- unsigned addr;
- bool state;
- Msp::Time::TimeStamp off_timeout;
-
-public:
- Sensor(Control &, unsigned);
-
- unsigned get_address() const { return addr; }
- bool get_state() const { return state; }
- void tick();
-private:
- void sensor_event(unsigned, bool);
-};
-
-} // namespace Marklin
-
-#endif
/* $Id$
This file is part of the MSP Märklin suite
-Copyright © 2006-2009 Mikkosoft Productions, Mikko Rasa
+Copyright © 2006-2010 Mikkosoft Productions, Mikko Rasa
Distributed under the GPL
*/
#include <cmath>
+#include "driver.h"
+#include "layout.h"
#include "track.h"
#include "tracktype.h"
namespace Marklin {
-Track::Track(const TrackType &t):
+Track::Track(Layout &l, const TrackType &t):
+ layout(l),
type(t),
rot(0),
slope(0),
flex(false),
turnout_id(0),
sensor_id(0),
- links(t.get_endpoints().size())
-{ }
+ links(t.get_endpoints().size()),
+ active_path(0)
+{
+ layout.add_track(*this);
+
+ if(layout.has_driver())
+ layout.get_driver().signal_turnout.connect(sigc::mem_fun(this, &Track::turnout_event));
+}
Track::~Track()
{
break_links();
+ layout.remove_track(*this);
}
void Track::set_position(const Point &p)
void Track::set_turnout_id(unsigned i)
{
+ if(!type.is_turnout())
+ throw InvalidState("Not a turnout");
+
turnout_id = i;
+ layout.create_blocks(*this);
+ if(layout.has_driver() && turnout_id)
+ layout.get_driver().add_turnout(turnout_id);
}
void Track::set_sensor_id(unsigned i)
{
+ if(type.is_turnout())
+ throw InvalidState("Can't set sensor on a turnout");
+
sensor_id = i;
+ layout.create_blocks(*this);
+ if(layout.has_driver() && sensor_id)
+ layout.get_driver().add_sensor(sensor_id);
+}
+
+void Track::set_active_path(unsigned p)
+{
+ if(!turnout_id)
+ throw InvalidState("Not a turnout");
+ if(!(type.get_paths()&(1<<p)))
+ throw InvalidParameterValue("Invalid path");
+
+ layout.get_driver().set_turnout(turnout_id, p&1);
+ if(type.get_n_paths()>2)
+ layout.get_driver().set_turnout(turnout_id+1, p&2);
}
int Track::get_endpoint_by_link(const Track &other) const
break_link(*links[i]);
links[i] = &other;
other.links[j] = this;
+ layout.create_blocks(*this);
}
return true;
{
*i = 0;
trk.break_link(*this);
+ // XXX Creates the blocks twice
+ layout.create_blocks(*this);
return;
}
}
}
}
-Track *Track::copy() const
-{
- Track *trk = new Track(type);
- trk->set_position(pos);
- trk->set_rotation(rot);
- trk->set_slope(slope);
- trk->set_flex(flex);
-
- return trk;
-}
-
void Track::save(list<DataFile::Statement> &st) const
{
st.push_back((DataFile::Statement("position"), pos.x, pos.y, pos.z));
st.push_back((DataFile::Statement("flex"), true));
}
+void Track::turnout_event(unsigned addr, bool state)
+{
+ if(!turnout_id)
+ return;
+
+ if(addr==turnout_id)
+ active_path = (active_path&2) | (state ? 1 : 0);
+ else if(type.is_double_address() && addr==turnout_id+1)
+ active_path = (active_path&1) | (state ? 2 : 0);
+}
+
Track::Loader::Loader(Track &t):
DataFile::BasicLoader<Track>(t)
add("position", &Loader::position);
add("rotation", &Track::rot);
add("slope", &Track::slope);
- add("turnout_id", &Track::turnout_id);
- add("sensor_id", &Track::sensor_id);
+ add("turnout_id", &Loader::turnout_id);
+ add("sensor_id", &Loader::sensor_id);
add("flex", &Track::flex);
}
obj.pos = Point(x, y, z);
}
+void Track::Loader::sensor_id(unsigned id)
+{
+ obj.set_sensor_id(id);
+}
+
+void Track::Loader::turnout_id(unsigned id)
+{
+ obj.set_turnout_id(id);
+}
+
} // namespace Marklin
/* $Id$
This file is part of the MSP Märklin suite
-Copyright © 2006-2009 Mikkosoft Productions, Mikko Rasa
+Copyright © 2006-2010 Mikkosoft Productions, Mikko Rasa
Distributed under the GPL
*/
#include <list>
#include <set>
+#include <sigc++/trackable.h>
#include <msp/datafile/loader.h>
#include "geometry.h"
namespace Marklin {
+class Layout;
class TrackType;
-class Track
+class Track: public sigc::trackable
{
public:
class Loader: public Msp::DataFile::BasicLoader<Track>
Loader(Track &);
private:
void position(float, float, float);
+ void sensor_id(unsigned);
+ void turnout_id(unsigned);
};
private:
+ Layout &layout;
const TrackType &type;
Point pos;
float rot;
unsigned turnout_id;
unsigned sensor_id;
std::vector<Track *> links;
+ unsigned active_path;
- // Direct copying not allowed due to links. See the copy() function.
Track(const Track &);
Track &operator=(const Track &);
public:
- Track(const TrackType &);
+ Track(Layout &, const TrackType &);
~Track();
const TrackType &get_type() const { return type; }
void set_sensor_id(unsigned);
unsigned get_turnout_id() const { return turnout_id; }
unsigned get_sensor_id() const { return sensor_id; }
+ void set_active_path(unsigned);
+ unsigned get_active_path() const { return active_path; }
int get_endpoint_by_link(const Track &) const;
Point get_endpoint_position(unsigned) const;
unsigned traverse(unsigned, unsigned) const;
Point get_point(unsigned, unsigned, float) const;
- /**
- Creates a copy of the track. The new track will be almost identical, but
- won't have any links to other tracks, nor a turnout or sensor id.
- */
- Track *copy() const;
-
void save(std::list<Msp::DataFile::Statement> &) const;
+private:
+ void turnout_event(unsigned, bool);
};
} // namespace Marklin
/* $Id$
This file is part of the MSP Märklin suite
-Copyright © 2006-2009 Mikkosoft Productions, Mikko Rasa
+Copyright © 2006-2010 Mikkosoft Productions, Mikko Rasa
Distributed under the GPL
*/
return len;
}
-unsigned TrackType::get_n_paths() const
+unsigned TrackType::get_paths() const
{
- unsigned n = 1;
+ unsigned mask = 0;
for(vector<TrackPart>::const_iterator i=parts.begin(); i!=parts.end(); ++i)
- if(i->path>=n)
- n = i->path+1;
+ mask |= 1<<i->path;
+ return mask;
+}
+
+unsigned TrackType::get_n_paths() const
+{
+ unsigned n = 0;
+ for(unsigned mask = get_paths(); mask; ++n)
+ mask &= mask-1;
return n;
}
+bool TrackType::is_turnout() const
+{
+ return endpoints.size()>2;
+}
+
+bool TrackType::is_double_address() const
+{
+ return get_n_paths()>2;
+}
+
void TrackType::collect_endpoints()
{
endpoints.clear();
/* $Id$
This file is part of the MSP Märklin suite
-Copyright © 2006-2009 Mikkosoft Productions, Mikko Rasa
+Copyright © 2006-2010 Mikkosoft Productions, Mikko Rasa
Distributed under the GPL
*/
const std::string &get_description() const { return description; }
float get_total_length() const;
float get_path_length(int) const;
+ unsigned get_paths() const;
unsigned get_n_paths() const;
+ bool is_turnout() const;
+ bool is_double_address() const;
const std::vector<TrackPart> &get_parts() const { return parts; }
const std::vector<Endpoint> &get_endpoints() const { return endpoints; }
+++ /dev/null
-/* $Id$
-
-This file is part of the MSP Märklin suite
-Copyright © 2006-2009 Mikkosoft Productions, Mikko Rasa
-Distributed under the GPL
-*/
-
-#include <algorithm>
-#include <msp/datafile/writer.h>
-#include <msp/time/utils.h>
-#include "catalogue.h"
-#include "control.h"
-#include "layout.h"
-#include "locotype.h"
-#include "tracktype.h"
-#include "trafficmanager.h"
-#include "turnout.h"
-
-using namespace std;
-using namespace Msp;
-
-namespace Marklin {
-
-TrafficManager::TrafficManager(Control &c, Layout &l):
- control(c),
- layout(l)
-{
- const set<Track *> &tracks = layout.get_tracks();
-
- set<Track *> used_tracks;
- for(set<Track *>::const_iterator i=tracks.begin(); i!=tracks.end(); ++i)
- {
- if(unsigned tid=(*i)->get_turnout_id())
- new Turnout(control, tid, (*i)->get_type().get_n_paths()>=3);
- if(unsigned sid=(*i)->get_sensor_id())
- if(!control.get_sensors().count(sid))
- new Sensor(control, sid);
-
- if(used_tracks.count(*i)==0)
- {
- Block *block = new Block(*this, **i);
- blocks.push_back(block);
- used_tracks.insert(block->get_tracks().begin(), block->get_tracks().end());
- }
- }
-
- for(list<Block *>::iterator i=blocks.begin(); i!=blocks.end(); ++i)
- for(list<Block *>::iterator j=i; j!=blocks.end(); ++j)
- if(j!=i)
- (*i)->check_link(**j);
-}
-
-TrafficManager::~TrafficManager()
-{
- for(list<Block *>::iterator i=blocks.begin(); i!=blocks.end(); ++i)
- delete *i;
- for(list<Train *>::iterator i=trains.begin(); i!=trains.end(); ++i)
- delete *i;
-}
-
-Block &TrafficManager::get_block(unsigned id) const
-{
- for(list<Block *>::const_iterator i=blocks.begin(); i!=blocks.end(); ++i)
- if((*i)->get_id()==id)
- return **i;
-
- throw KeyError("Unknown block", lexical_cast(id));
-}
-
-Block &TrafficManager::get_block_by_track(const Track &t) const
-{
- for(list<Block *>::const_iterator i=blocks.begin(); i!=blocks.end(); ++i)
- {
- const set<Track *> &tracks = (*i)->get_tracks();
- if(tracks.count(const_cast<Track *>(&t)))
- return **i;
- }
-
- throw InvalidParameterValue("Unknown track");
-}
-
-Train &TrafficManager::get_train_by_locomotive(const Locomotive &loco) const
-{
- for(list<Train *>::const_iterator i=trains.begin(); i!=trains.end(); ++i)
- if(&(*i)->get_locomotive()==&loco)
- return **i;
-
- throw InvalidParameterValue("Unknown locomotive");
-}
-
-void TrafficManager::add_train(Train *t)
-{
- if(find(trains.begin(), trains.end(), t)==trains.end())
- {
- trains.push_back(t);
- signal_train_added.emit(*t);
- }
-}
-
-void TrafficManager::tick()
-{
- Time::TimeStamp t = Time::now();
- Time::TimeDelta dt;
- if(last_tick)
- dt = t-last_tick;
- last_tick = t;
-
- for(list<Train *>::iterator i=trains.begin(); i!=trains.end(); ++i)
- (*i)->tick(t, dt);
-}
-
-void TrafficManager::save(const string &fn) const
-{
- IO::BufferedFile out(fn, IO::M_WRITE);
- DataFile::Writer writer(out);
- for(list<Train *>::const_iterator i=trains.begin(); i!=trains.end(); ++i)
- {
- const Locomotive &loco = (*i)->get_locomotive();
- DataFile::Statement st("train");
- st.append(loco.get_type().get_article_number());
- st.append(loco.get_address());
- (*i)->save(st.sub);
- writer.write(st);
- }
-}
-
-void TrafficManager::turnout_path_changed(unsigned, Turnout *)
-{
-}
-
-
-TrafficManager::Loader::Loader(TrafficManager &tm):
- DataFile::BasicLoader<TrafficManager>(tm)
-{
- add("train", &Loader::train);
-}
-
-void TrafficManager::Loader::train(unsigned art_nr, unsigned addr)
-{
- Locomotive *loco = new Locomotive(obj.layout.get_catalogue().get_locomotive(art_nr), obj.control, addr);
- Train *trn = new Train(obj, *loco);
- load_sub(*trn);
-}
-
-} // namespace Marklin
+++ /dev/null
-/* $Id$
-
-This file is part of the MSP Märklin suite
-Copyright © 2006-2009 Mikkosoft Productions, Mikko Rasa
-Distributed under the GPL
-*/
-
-#ifndef LIBMARKLIN_TRAFFICMANAGER_H_
-#define LIBMARKLIN_TRAFFICMANAGER_H_
-
-#include "block.h"
-#include "train.h"
-
-namespace Marklin {
-
-class Control;
-class Layout;
-class Turnout;
-
-class TrafficManager
-{
-public:
- class Loader: public Msp::DataFile::BasicLoader<TrafficManager>
- {
- public:
- Loader(TrafficManager &);
- private:
- void train(unsigned, unsigned);
- };
-
- sigc::signal<void, Train &> signal_train_added;
- sigc::signal<void, const Block &, const Train *> signal_block_reserved;
-
-private:
- Control &control;
- Layout &layout;
- std::list<Block *> blocks;
- std::list<Train *> trains;
- Msp::Time::TimeStamp last_tick;
-
-public:
- TrafficManager(Control &, Layout &);
- ~TrafficManager();
-
- Control &get_control() const { return control; }
- Layout &get_layout() const { return layout; }
- const std::list<Block *> &get_blocks() const { return blocks; }
- Block &get_block(unsigned) const;
- Block &get_block_by_track(const Track &) const;
- const std::list<Train *> &get_trains() const { return trains; }
- Train &get_train_by_locomotive(const Locomotive &) const;
- void add_train(Train *);
- void tick();
- void save(const std::string &) const;
-private:
- void turnout_path_changed(unsigned, Turnout *);
-};
-
-} // namespace Marklin
-
-#endif
/* $Id$
This file is part of the MSP Märklin suite
-Copyright © 2006-2009 Mikkosoft Productions, Mikko Rasa
+Copyright © 2006-2010 Mikkosoft Productions, Mikko Rasa
Distributed under the GPL
*/
#include <msp/strings/formatter.h>
#include <msp/time/units.h>
#include <msp/time/utils.h>
-#include "control.h"
-#include "except.h"
+#include "driver.h"
#include "layout.h"
+#include "locotype.h"
#include "route.h"
#include "tracktype.h"
-#include "trafficmanager.h"
#include "train.h"
using namespace std;
namespace Marklin {
-Train::Train(TrafficManager &tm, Locomotive &l):
- trfc_mgr(tm),
- loco(l),
+Train::Train(Layout &l, const LocoType &t, unsigned a):
+ layout(l),
+ loco_type(t),
+ address(a),
pending_block(0),
target_speed(0),
+ current_speed(0),
+ reverse(false),
route(0),
status("Unplaced"),
travel_dist(0),
real_speed(15),
cur_track(0)
{
- trfc_mgr.add_train(this);
+ layout.add_train(*this);
- loco.signal_reverse_changed.connect(sigc::mem_fun(this, &Train::locomotive_reverse_changed));
+ layout.get_driver().add_loco(address);
+ 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));
- const map<unsigned, Sensor *> &sensors = trfc_mgr.get_control().get_sensors();
- for(map<unsigned, Sensor *>::const_iterator i=sensors.begin(); i!=sensors.end(); ++i)
- i->second->signal_state_changed.connect(sigc::bind(sigc::mem_fun(this, &Train::sensor_event), i->second));
-
- const map<unsigned, Turnout *> &turnouts = trfc_mgr.get_control().get_turnouts();
- for(map<unsigned, Turnout *>::const_iterator i=turnouts.begin(); i!=turnouts.end(); ++i)
- {
- i->second->signal_path_changing.connect(sigc::bind(sigc::mem_fun(this, &Train::turnout_path_changing), i->second));
- i->second->signal_path_changed.connect(sigc::bind(sigc::mem_fun(this, &Train::turnout_path_changed), i->second));
- }
+ layout.signal_block_reserved.connect(sigc::mem_fun(this, &Train::block_reserved));
+ layout.get_driver().signal_sensor.connect(sigc::mem_fun(this, &Train::sensor_event));
+ layout.get_driver().signal_turnout.connect(sigc::mem_fun(this, &Train::turnout_event));
+}
- trfc_mgr.signal_block_reserved.connect(sigc::mem_fun(this, &Train::block_reserved));
+Train::~Train()
+{
+ layout.remove_train(*this);
}
void Train::set_name(const string &n)
if(!target_speed)
{
pending_block = 0;
- trfc_mgr.get_control().set_timer(3*Time::sec).signal_timeout.connect(
- sigc::bind_return(sigc::bind(
- sigc::mem_fun(this, static_cast<void (Train::*)(list<BlockRef> &)>(&Train::release_blocks)),
- sigc::ref(rsv_blocks)), false));
+ stop_timeout = Time::now()+(800+current_speed*150)*Time::msec;
}
else
reserve_more();
void Train::set_reverse(bool rev)
{
- loco.set_reverse(rev);
+ if(rev==reverse)
+ return;
+
+ if(target_speed)
+ {
+ set_speed(0);
+ return;
+ }
+ else if(stop_timeout)
+ return;
+
+ layout.get_driver().set_loco_reverse(address, rev);
+
+ release_blocks(rsv_blocks);
+ reverse_blocks(cur_blocks);
+
+ if(cur_track)
+ {
+ unsigned path = cur_track->get_active_path();
+ cur_track_ep = cur_track->traverse(cur_track_ep, path);
+ offset = cur_track->get_type().get_path_length(path)-offset;
+ }
+}
+
+void Train::set_function(unsigned func, bool state)
+{
+ if(!loco_type.get_functions().count(func))
+ throw InvalidParameterValue("Invalid function");
+ if(func<5)
+ layout.get_driver().set_loco_function(address, func, state);
+ else
+ layout.get_driver().set_loco_function(address+1, func-4, state);
+}
+
+bool Train::get_function(unsigned func) const
+{
+ return (functions>>func)&1;
}
void Train::set_route(const Route *r)
return -1;
}
-void Train::tick(const Time::TimeStamp &, const Time::TimeDelta &dt)
+void Train::tick(const Time::TimeStamp &t, const Time::TimeDelta &dt)
{
+ if(stop_timeout && t>=stop_timeout)
+ {
+ release_blocks(rsv_blocks);
+ stop_timeout = Time::TimeStamp();
+ }
+
if(cur_track)
{
- unsigned path = 0;
- if(cur_track->get_turnout_id())
- path = trfc_mgr.get_control().get_turnout(cur_track->get_turnout_id()).get_path();
+ unsigned path = cur_track->get_active_path();
- offset += get_real_speed(loco.get_speed())*(dt/Time::sec);
+ offset += get_real_speed(current_speed)*(dt/Time::sec);
float path_len = cur_track->get_type().get_path_length(path);
if(offset>path_len)
{
if(!cur_blocks.empty())
{
list<BlockRef> blocks = cur_blocks;
- if(loco.get_reverse())
+ if(reverse)
reverse_blocks(blocks);
Block *prev = blocks.front().block->get_endpoints()[blocks.front().entry].link;
}
}
-void Train::locomotive_reverse_changed(bool)
+void Train::loco_speed_event(unsigned addr, unsigned speed, bool rev)
{
- release_blocks(rsv_blocks);
- reverse_blocks(cur_blocks);
- reserve_more();
-
- if(cur_track)
+ if(addr==address)
{
- unsigned path = 0;
- if(unsigned turnout = cur_track->get_turnout_id())
- path = trfc_mgr.get_control().get_turnout(turnout).get_path();
- cur_track_ep = cur_track->traverse(cur_track_ep, path);
- offset = cur_track->get_type().get_path_length(path)-offset;
+ current_speed = speed;
+ reverse = rev;
+
+ signal_speed_changed.emit(current_speed);
+ signal_reverse_changed.emit(reverse);
}
}
-void Train::sensor_event(bool state, Sensor *sensor)
+void Train::loco_func_event(unsigned addr, unsigned func, bool state)
{
- unsigned addr = sensor->get_address();
+ if(addr==address || (addr==address+1 && loco_type.get_max_function()>4))
+ {
+ if(addr==address+1)
+ func += 4;
+ if(state)
+ functions |= 1<<func;
+ else
+ functions &= ~(1<<func);
+ signal_function_changed.emit(func, state);
+ }
+}
+
+void Train::sensor_event(unsigned addr, bool state)
+{
if(state)
{
// Find the first sensor block from our reserved blocks that isn't this sensor
if(pure_speed)
{
- RealSpeed &rs = real_speed[loco.get_speed()];
+ RealSpeed &rs = real_speed[current_speed];
rs.add(travel_dist/travel_time_secs, travel_time_secs);
}
for(list<BlockRef>::iterator i=cur_blocks.begin(); i!=cur_blocks.end(); ++i)
if(i->block->get_sensor_id())
{
- if(trfc_mgr.get_control().get_sensor(i->block->get_sensor_id()).get_state())
+ if(layout.get_driver().get_sensor(i->block->get_sensor_id()))
break;
else
end = i;
}
}
-void Train::turnout_path_changing(unsigned, Turnout *turnout)
-{
- unsigned tid = turnout->get_address();
- for(list<BlockRef>::const_iterator i=cur_blocks.begin(); i!=cur_blocks.end(); ++i)
- if(i->block->get_turnout_id()==tid)
- throw TurnoutBusy(this);
-
- unsigned nsens = 0;
- for(list<BlockRef>::const_iterator i=rsv_blocks.begin(); i!=rsv_blocks.end(); ++i)
- {
- if(i->block->get_turnout_id()==tid)
- {
- if(nsens<1)
- throw TurnoutBusy(this);
- break;
- }
- else if(i->block->get_sensor_id())
- ++nsens;
- }
-}
-
-void Train::turnout_path_changed(unsigned, Turnout *turnout)
+void Train::turnout_event(unsigned addr, bool)
{
- unsigned tid = turnout->get_address();
- for(list<BlockRef>::iterator i=rsv_blocks.begin(); i!=rsv_blocks.end(); ++i)
- if(i->block->get_turnout_id()==tid)
- {
- release_blocks(rsv_blocks, i, rsv_blocks.end());
- reserve_more();
- return;
- }
-
- if(pending_block && tid==pending_block->get_turnout_id())
+ if(pending_block && addr==pending_block->get_turnout_id())
reserve_more();
}
good = last;
good_sens = nsens;
- Turnout &turnout = trfc_mgr.get_control().get_turnout(link->get_turnout_id());
-
// Figure out what path we'd like to take on the turnout
int path = -1;
if(route)
path = route->get_turnout(link->get_turnout_id());
if(path<0)
- path = turnout.get_path();
+ path = ep.track->get_active_path();
if(!((track_ep.paths>>path)&1))
{
for(unsigned i=0; track_ep.paths>>i; ++i)
path = i;
}
- if(path!=turnout.get_path())
+ if(path!=static_cast<int>(ep.track->get_active_path()))
{
// The turnout is set to wrong path - switch and wait for it
link->reserve(0);
pending_block = link;
- turnout.set_path(path);
+ ep.track->set_active_path(path);
break;
}
}
void Train::update_speed()
{
+ Driver &driver = layout.get_driver();
+
+ unsigned speed;
if(!target_speed)
{
- loco.set_speed(0);
+ speed = 0;
set_status("Stopped");
}
else
unsigned slow_speed = find_speed(0.1); // 31.3 km/h
if(nsens==0)
{
- loco.set_speed(0);
+ speed = 0;
pure_speed = false;
set_status("Blocked");
}
else if(nsens==1 && target_speed>slow_speed)
{
- loco.set_speed(slow_speed);
+ speed = slow_speed;
pure_speed = false;
set_status("Slow");
}
else
{
- loco.set_speed(target_speed);
+ 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
void Train::Loader::block(unsigned id)
{
- Block &blk = obj.trfc_mgr.get_block(id);
+ Block &blk = obj.layout.get_block(id);
int entry = -1;
if(prev_block)
entry = blk.get_endpoint_by_link(*prev_block);
void Train::Loader::block_hint(unsigned id)
{
- prev_block = &obj.trfc_mgr.get_block(id);
+ prev_block = &obj.layout.get_block(id);
}
void Train::Loader::name(const string &n)
void Train::Loader::route(const string &n)
{
- obj.set_route(&obj.trfc_mgr.get_layout().get_route(n));
+ obj.set_route(&obj.layout.get_route(n));
}
} // namespace Marklin
/* $Id$
This file is part of the MSP Märklin suite
-Copyright © 2006-2009 Mikkosoft Productions, Mikko Rasa
+Copyright © 2006-2010 Mikkosoft Productions, Mikko Rasa
Distributed under the GPL
*/
namespace Marklin {
-class Locomotive;
+class LocoType;
class Route;
-class Sensor;
-class TrafficManager;
-class Turnout;
class Train: public sigc::trackable
{
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, unsigned, bool> signal_function_changed;
sigc::signal<void, const Route *> signal_route_changed;
sigc::signal<void, const std::string &> signal_status_changed;
void add(float, float);
};
- TrafficManager &trfc_mgr;
+ Layout &layout;
+ const LocoType &loco_type;
+ unsigned address;
std::string name;
- Locomotive &loco;
std::list<BlockRef> cur_blocks;
std::list<BlockRef> rsv_blocks;
Block *pending_block;
unsigned target_speed;
+ unsigned current_speed;
+ bool reverse;
+ Msp::Time::TimeStamp stop_timeout;
+ unsigned functions;
const Route *route;
std::string status;
Point pos;
public:
- Train(TrafficManager &, Locomotive &);
+ Train(Layout &, const LocoType &, unsigned);
+ ~Train();
+ 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; }
+
void set_speed(unsigned);
void set_reverse(bool);
- void set_route(const Route *);
- const std::string &get_name() const { return name; }
- Locomotive &get_locomotive() const { return loco; }
+ 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; }
+ bool get_function(unsigned) const;
+ unsigned get_functions() const { return functions; }
+
+ void set_route(const Route *);
const Route *get_route() const { return route; }
- const std::string &get_status() const { return status; }
- const Point &get_position() const { return pos; }
void place(Block &, unsigned);
bool is_placed() const { return !cur_blocks.empty(); }
bool free_block(Block &);
int get_entry_to_block(Block &) const;
+
+ const std::string &get_status() const { return status; }
+ const Point &get_position() const { return pos; }
+
void tick(const Msp::Time::TimeStamp &, const Msp::Time::TimeDelta &);
+
void save(std::list<Msp::DataFile::Statement> &) const;
private:
- void locomotive_reverse_changed(bool);
- void sensor_event(bool, Sensor *);
- void turnout_path_changing(unsigned, Turnout *);
- void turnout_path_changed(unsigned, Turnout *);
+ void loco_speed_event(unsigned, unsigned, bool);
+ void loco_func_event(unsigned, unsigned, bool);
+ void sensor_event(unsigned, bool);
+ void turnout_event(unsigned, bool);
void block_reserved(const Block &, const Train *);
unsigned reserve_more();
void update_speed();
+++ /dev/null
-/* $Id$
-
-This file is part of the MSP Märklin suite
-Copyright © 2006-2009 Mikkosoft Productions, Mikko Rasa
-Distributed under the GPL
-*/
-
-#include <msp/time/timer.h>
-#include <msp/time/units.h>
-#include "command.h"
-#include "control.h"
-#include "reply.h"
-#include "turnout.h"
-
-using namespace std;
-using namespace Msp;
-
-namespace Marklin {
-
-Turnout::Turnout(Control &c, unsigned a, bool d):
- control(c),
- addr(a),
- path(0),
- pending_path(0),
- pending_cmds(0),
- dual(d),
- on(false)
-{
- control.add_turnout(*this);
-
- control.signal_turnout_event.connect(sigc::mem_fun(this, &Turnout::turnout_event));
-
- unsigned char data[2];
- data[0] = addr&0xFF;
- data[1] = (addr>>8)&0xFF;
- control.command(CMD_TURNOUT_STATUS, data, 2).signal_done.connect(sigc::bind(sigc::mem_fun(this, &Turnout::status_reply), 1));
- if(dual)
- {
- data[0] = (addr+1)&0xFF;
- data[1] = ((addr+1)>>8)&0xFF;
- control.command(CMD_TURNOUT_STATUS, data, 2).signal_done.connect(sigc::bind(sigc::mem_fun(this, &Turnout::status_reply), 2));
- }
-}
-
-void Turnout::set_path(unsigned char p)
-{
- if(path==p || pending_cmds)
- return;
-
- signal_path_changing.emit(p);
-
- pending_path = p;
- on = true;
- command(3);
-}
-
-void Turnout::command(unsigned char mask)
-{
- unsigned char data[2];
- if(mask&1)
- {
- data[0] = addr&0xFF;
- data[1] = ((addr>>8)&0x7) | (on ? 0x40 : 0) | (pending_path&1 ? 0 : 0x80);
- control.command(CMD_TURNOUT, data, 2).signal_done.connect(sigc::bind(sigc::mem_fun(this, &Turnout::command_reply), 1));
- pending_cmds |= 1;
- }
- if(dual && (mask&2))
- {
- data[0] = (addr+1)&0xFF;
- data[1] = (((addr+1)>>8)&0x7) | (on ? 0x40 : 0) | (pending_path&2 ? 0 : 0x80);
- control.command(CMD_TURNOUT, data, 2).signal_done.connect(sigc::bind(sigc::mem_fun(this, &Turnout::command_reply), 2));
- pending_cmds |= 2;
- }
-}
-
-void Turnout::command_reply(const Reply &reply, unsigned char bit)
-{
- pending_cmds &= ~bit;
- if(reply.get_error()==ERR_NO_ERROR)
- {
- if(on && !pending_cmds)
- {
- path = pending_path;
- on = false;
- control.set_timer(500*Time::msec).signal_timeout.connect(
- sigc::bind_return(sigc::bind(sigc::mem_fun(this, &Turnout::command), 3), false));
- signal_path_changed.emit(path);
- }
- }
- else if(reply.get_error()==ERR_NO_I2C_SPACE)
- {
- control.set_timer(100*Time::msec).signal_timeout.connect(
- sigc::bind_return(sigc::bind(sigc::mem_fun(this, &Turnout::command), bit), false));
- }
-}
-
-void Turnout::status_reply(const Reply &reply, unsigned char bit)
-{
- if(reply.get_error()==ERR_NO_ERROR)
- {
- bool v = !(reply.get_data()[0]&0x04);
- path = (path&~bit)|(v?bit:0);
- signal_path_changed.emit(path);
- }
-}
-
-void Turnout::turnout_event(unsigned a, bool p)
-{
- if(a==addr && p!=(path&1))
- {
- path = (path&2)|(p?1:0);
- signal_path_changed.emit(path);
- }
- else if(dual && a==addr+1 && p!=((path>>1)&1))
- {
- path = (path&1)|(p?2:0);
- signal_path_changed.emit(path);
- }
-}
-
-} // namespace Marklin
+++ /dev/null
-/* $Id$
-
-This file is part of the MSP Märklin suite
-Copyright © 2006-2009 Mikkosoft Productions, Mikko Rasa
-Distributed under the GPL
-*/
-
-#ifndef LIBMARKLIN_TURNOUT_H_
-#define LIBMARKLIN_TURNOUT_H_
-
-#include <list>
-#include <map>
-#include <string>
-#include <sigc++/sigc++.h>
-#include "constants.h"
-
-namespace Marklin {
-
-class Control;
-class Reply;
-
-class Turnout
-{
-public:
- sigc::signal<void, unsigned> signal_path_changing;
- sigc::signal<void, unsigned> signal_path_changed;
-
-private:
- Control &control;
- unsigned addr;
- unsigned char path;
- unsigned char pending_path;
- unsigned char pending_cmds;
- bool dual;
- bool on;
-
-public:
- Turnout(Control &, unsigned, bool =false);
-
- void set_path(unsigned char);
- unsigned get_address() const { return addr; }
- unsigned char get_path() const { return path; }
-private:
- void command(unsigned char);
- void command_reply(const Reply &, unsigned char);
- void status_reply(const Reply &, unsigned char);
- void turnout_event(unsigned, bool);
-};
-
-} // namespace Marklin
-
-#endif
/* $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 <msp/net/inet.h>
-#include "libmarklin/control.h"
-#include "libmarklin/layout.h"
-#include "libmarklin/locomotive.h"
#include "libmarklin/locotype.h"
#include "libmarklin/route.h"
+#include "libmarklin/train.h"
#include "server.h"
using namespace std;
namespace Marklin {
-Server::Server(TrafficManager &tm):
- trfc_mgr(tm),
+Server::Server(Layout &l):
+ layout(l),
listen_sock(Net::INET),
event_disp(0)
{
- const list<Train *> &trains = trfc_mgr.get_trains();
- for(list<Train *>::const_iterator i=trains.begin(); i!=trains.end(); ++i)
- train_added(**i);
+ layout.signal_train_added.connect(sigc::mem_fun(this, &Server::train_added));
+
+ const map<unsigned, Train *> &trains = layout.get_trains();
+ for(map<unsigned, Train *>::const_iterator i=trains.begin(); i!=trains.end(); ++i)
+ train_added(*i->second);
listen_sock.listen(Net::InetAddr(0, 8315), 4);
listen_sock.signal_data_available.connect(sigc::mem_fun(this, &Server::incoming_connection));
void Server::train_added(Train &train)
{
- Locomotive &loco = train.get_locomotive();
train.signal_target_speed_changed.connect(sigc::bind<0>(sigc::mem_fun(this, &Server::train_speed_changed), sigc::ref(train)));
- loco.signal_reverse_changed.connect(sigc::bind<0>(sigc::mem_fun(this, &Server::train_reverse_changed), sigc::ref(train)));
- loco.signal_function_changed.connect(sigc::bind<0>(sigc::mem_fun(this, &Server::train_function_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_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)));
TrainInfoPacket pkt;
- pkt.address = loco.get_address();
- pkt.loco_type = loco.get_type().get_article_number();
+ pkt.address = train.get_address();
+ pkt.loco_type = train.get_locomotive_type().get_article_number();
pkt.name = train.get_name();
send(pkt);
}
void Server::train_speed_changed(const Train &train, unsigned speed)
{
TrainSpeedPacket pkt;
- pkt.address = train.get_locomotive().get_address();
+ pkt.address = train.get_address();
pkt.speed = speed;
- pkt.reverse = train.get_locomotive().get_reverse();
+ pkt.reverse = train.get_reverse();
send(pkt);
}
void Server::train_reverse_changed(const Train &train, bool reverse)
{
TrainSpeedPacket pkt;
- pkt.address = train.get_locomotive().get_address();
+ pkt.address = train.get_address();
pkt.speed = train.get_target_speed();
pkt.reverse = reverse;
send(pkt);
void Server::train_function_changed(const Train &train, unsigned, bool)
{
TrainFunctionPacket pkt;
- pkt.address = train.get_locomotive().get_address();
- pkt.functions = train.get_locomotive().get_functions();
+ pkt.address = train.get_address();
+ pkt.functions = train.get_functions();
send(pkt);
}
void Server::train_route_changed(const Train &train, const Route *route)
{
TrainRoutePacket pkt;
- pkt.address = train.get_locomotive().get_address();
+ pkt.address = train.get_address();
if(route)
pkt.route = route->get_name();
send(pkt);
void Server::train_status_changed(const Train &train, const string &status)
{
TrainStatusPacket pkt;
- pkt.address = train.get_locomotive().get_address();
+ pkt.address = train.get_address();
pkt.status = status;
send(pkt);
}
void Server::Connection::handshake_done()
{
- const map<string, Route *> &routes = server.trfc_mgr.get_layout().get_routes();
+ const map<string, Route *> &routes = server.layout.get_routes();
for(map<string, Route *>::const_iterator i=routes.begin(); i!=routes.end(); ++i)
{
RouteInfoPacket pkt;
comm.send(pkt);
}
- const list<Train *> &trains = server.trfc_mgr.get_trains();
- for(list<Train *>::const_iterator i=trains.begin(); i!=trains.end(); ++i)
+ const map<unsigned, Train *> &trains = server.layout.get_trains();
+ for(map<unsigned, Train *>::const_iterator i=trains.begin(); i!=trains.end(); ++i)
{
- Locomotive &loco = (*i)->get_locomotive();
+ const Train &train = *i->second;
{
TrainInfoPacket pkt;
- pkt.address = loco.get_address();
- pkt.loco_type = loco.get_type().get_article_number();
- pkt.name = (*i)->get_name();
+ pkt.address = train.get_address();
+ pkt.loco_type = train.get_locomotive_type().get_article_number();
+ pkt.name = train.get_name();
comm.send(pkt);
}
{
TrainSpeedPacket pkt;
- pkt.address = loco.get_address();
- pkt.speed = (*i)->get_target_speed();
- pkt.reverse = loco.get_reverse();
+ pkt.address = train.get_address();
+ pkt.speed = train.get_target_speed();
+ pkt.reverse = train.get_reverse();
comm.send(pkt);
}
{
TrainFunctionPacket pkt;
- pkt.address = loco.get_address();
- pkt.functions = loco.get_functions();
+ pkt.address = train.get_address();
+ pkt.functions = train.get_functions();
comm.send(pkt);
}
{
TrainStatusPacket pkt;
- pkt.address = loco.get_address();
- pkt.status = (*i)->get_status();
+ pkt.address = train.get_address();
+ pkt.status = train.get_status();
comm.send(pkt);
}
- if((*i)->get_route())
+ if(train.get_route())
{
TrainRoutePacket pkt;
- pkt.address = loco.get_address();
- pkt.route = (*i)->get_route()->get_name();
+ pkt.address = train.get_address();
+ pkt.route = train.get_route()->get_name();
comm.send(pkt);
}
}
{
try
{
- Locomotive &loco = server.trfc_mgr.get_control().get_locomotive(pkt.address);
- Train &train = server.trfc_mgr.get_train_by_locomotive(loco);
- if(pkt.reverse!=loco.get_reverse())
+ 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);
{
try
{
- Locomotive &loco = server.trfc_mgr.get_control().get_locomotive(pkt.address);
+ Train &train = server.layout.get_train(pkt.address);
for(unsigned i=0; i<9; ++i)
- if(((pkt.functions^loco.get_functions())>>i)&1)
- loco.set_function(i, (pkt.functions>>i)&1);
+ if(((pkt.functions^train.get_functions())>>i)&1)
+ train.set_function(i, (pkt.functions>>i)&1);
}
catch(const Exception &e)
{
{
try
{
- Locomotive &loco = server.trfc_mgr.get_control().get_locomotive(pkt.address);
- Train &train = server.trfc_mgr.get_train_by_locomotive(loco);
+ Train &train = server.layout.get_train(pkt.address);
if(pkt.route.empty())
train.set_route(0);
else
{
- Route &route = server.trfc_mgr.get_layout().get_route(pkt.route);
+ Route &route = server.layout.get_route(pkt.route);
train.set_route(&route);
}
}
/* $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 <msp/net/communicator.h>
#include <msp/net/streamsocket.h>
#include <msp/net/streamlistensocket.h>
-#include "libmarklin/trafficmanager.h"
+#include "libmarklin/layout.h"
#include "packets.h"
#include "protocol.h"
};
Protocol proto;
- TrafficManager &trfc_mgr;
+ Layout &layout;
Msp::Net::StreamListenSocket listen_sock;
Msp::IO::EventDispatcher *event_disp;
std::vector<Connection *> connections;
public:
- Server(TrafficManager &);
+ Server(Layout &);
void use_event_dispatcher(Msp::IO::EventDispatcher &);
private:
void incoming_connection();
part
{
length 77.5;
- path 0;
+ path 1;
};
part
{
start 77.5 0 0;
length 30;
radius 360;
- path 0;
+ path 1;
};
part
{
length 30;
radius 360;
- path 1;
+ path 0;
};
};
part
{
length 77.5;
- path 0;
+ path 1;
};
part
{
start 77.5 0 0;
length 30;
radius -360;
- path 0;
+ path 1;
};
part
{
length 30;
radius -360;
- path 1;
+ path 0;
};
};
part
{
length 188.3;
- path 0;
+ path 1;
};
part
{
length 24.3;
radius 437.5;
- path 1;
+ path 0;
};
};
part
{
length 188.3;
- path 0;
+ path 1;
};
part
{
length 24.3;
radius -437.5;
- path 1;
+ path 0;
};
};
part
{
length 188.3;
- path 0;
+ path 3;
};
part
{
length 24.3;
radius 437.5;
- path 1;
+ path 2;
};
part
{
length 24.3;
radius -437.5;
- path 2;
+ path 1;
};
};
part
{
length 188.3;
- path 0;
+ path 3;
};
part
{
length 24.3;
radius -437.5;
- path 1;
+ path 2;
};
part
{
start 8.34 38.74 -24.3;
length 188.3;
- path 2;
+ path 1;
};
part
{
start 8.34 38.74 -24.3;
length 24.3;
radius 437.5;
- path 3;
+ path 0;
};
};
part
{
length 236.1;
- path 0;
+ path 1;
};
part
{
length 12.1;
radius 1114.6;
- path 1;
+ path 0;
};
};
part
{
length 236.1;
- path 0;
+ path 1;
};
part
{
length 12.1;
radius -1114.6;
- path 1;
+ path 0;
};
};