]> git.tdb.fi Git - r2c2.git/commitdiff
Add networking library and a remote control program
authorMikko Rasa <tdb@tdb.fi>
Thu, 10 Dec 2009 20:05:34 +0000 (20:05 +0000)
committerMikko Rasa <tdb@tdb.fi>
Thu, 10 Dec 2009 20:05:34 +0000 (20:05 +0000)
24 files changed:
Build
source/engineer/engineer.cpp
source/engineer/engineer.h
source/engineer/trainpanel.cpp
source/engineer/trainpanel.h
source/libmarklin/locomotive.cpp
source/libmarklin/locomotive.h
source/libmarklin/trafficmanager.cpp
source/libmarklin/trafficmanager.h
source/libmarklin/train.cpp
source/libmarklin/train.h
source/network/client.cpp [new file with mode: 0644]
source/network/client.h [new file with mode: 0644]
source/network/packets.h [new file with mode: 0644]
source/network/protocol.cpp [new file with mode: 0644]
source/network/protocol.h [new file with mode: 0644]
source/network/server.cpp [new file with mode: 0644]
source/network/server.h [new file with mode: 0644]
source/network/train.cpp [new file with mode: 0644]
source/network/train.h [new file with mode: 0644]
source/remote/remote.cpp [new file with mode: 0644]
source/remote/remote.h [new file with mode: 0644]
source/remote/trainpanel.cpp [new file with mode: 0644]
source/remote/trainpanel.h [new file with mode: 0644]

diff --git a/Build b/Build
index 817d757f9c6dc2485429fc2e498cbd515885c7b6..a69a03da0906407452619ed64d8af0a0b8c60feb 100644 (file)
--- a/Build
+++ b/Build
@@ -13,6 +13,7 @@ package "märklin"
        {
                source "source/libmarklin";
                require "mspdatafile";
+               install true;
        };
 
        library "marklin3d"
@@ -23,20 +24,21 @@ package "märklin"
                {
                        incpath "source";
                        library "marklin";
-                       libpath ".";
                };
+               install true;
        };
 
-       /*program "newconsole"
+       library "marklinnet"
        {
-               source "source/newconsole";
+               source "source/network";
+               require "mspnet";
                build_info
                {
-                       library "marklincontrol";
                        incpath "source";
-                       libpath ".";
+                       library "marklin";
                };
-       };*/
+               install true;
+       };
 
        program "designer"
        {
@@ -48,7 +50,6 @@ package "märklin"
                {
                        incpath "source";
                        library "marklin3d";
-                       libpath ".";
                };
        };
 
@@ -62,7 +63,18 @@ package "märklin"
                {
                        incpath "source";
                        library "marklin3d";
-                       libpath ".";
+                       library "marklinnet";
+               };
+       };
+
+       program "remote"
+       {
+               source "source/remote";
+               require "gtkmm-2.4";
+               build_info
+               {
+                       incpath "source";
+                       library "marklinnet";
                };
        };
 };
index 14e98c6b6b7aa2074bcfdb6c5f59a7ad0b696caa..e0d45b0e00f7719cd3626a352533c6ea12227cc4 100644 (file)
@@ -20,6 +20,7 @@ Distributed under the GPL
 #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/tracktype.h"
 #include "engineer.h"
@@ -37,6 +38,7 @@ Engineer::Engineer(int argc, char **argv):
        fullscreen(false),
        layout(catalogue),
        layout_3d(layout),
+       server(0),
        train_prop(0),
        train_prop_stale(false),
        placing_train(0),
@@ -49,6 +51,7 @@ Engineer::Engineer(int argc, char **argv):
        bool debug = false;
        string device = "/dev/ttyS0";
        unsigned quality = 4;
+       bool network = false;
 
        GetOpt getopt;
        getopt.add_option('r', "resolution",  res,         GetOpt::REQUIRED_ARG);
@@ -57,6 +60,7 @@ Engineer::Engineer(int argc, char **argv):
        getopt.add_option('d', "device",      device,      GetOpt::REQUIRED_ARG);
        getopt.add_option('q', "quality",     quality,     GetOpt::REQUIRED_ARG);
        getopt.add_option('s', "simulate",    simulate,    GetOpt::NO_ARG);
+       getopt.add_option('n', "network",     network,     GetOpt::NO_ARG);
        getopt.add_option(     "no-lighting", no_lighting, GetOpt::NO_ARG);
        getopt(argc, argv);
 
@@ -91,6 +95,12 @@ Engineer::Engineer(int argc, char **argv):
        if(FS::exists("engineer.state"))
                DataFile::load(*trfc_mgr, "engineer.state");
 
+       if(network)
+       {
+               server = new Server(*trfc_mgr);
+               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));
@@ -213,6 +223,7 @@ void Engineer::tick()
 
        control.tick();
        trfc_mgr->tick();
+       event_disp.tick(Time::zero);
 
        glClear(GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT);
 
index ad4bbccec1d3e7ab6cb1971144844282145d4e38..c7c062fe2347331d41289a74f1378d862e84dee2 100644 (file)
@@ -19,6 +19,7 @@ Distributed under the GPL
 #include "libmarklin/trafficmanager.h"
 #include "libmarklin/train.h"
 #include "3d/layout.h"
+#include "network/server.h"
 
 class MainPanel;
 class TrainPanel;
@@ -41,6 +42,8 @@ private:
        Marklin::Layout3D layout_3d;
        Marklin::Control control;
        Marklin::TrafficManager *trfc_mgr;
+       Marklin::Server *server;
+       Msp::IO::EventDispatcher event_disp;
 
        Marklin::Point cam_pos;
        float cam_rot;
index a01df557c0c1fe3f126defd63c65a9443ba8d8d4..a8c0e90536580d3c7c40a27a32300d940446fb22 100644 (file)
@@ -35,7 +35,7 @@ TrainPanel::TrainPanel(Engineer &e, const GLtk::Resources &r, Train &t):
        add(*(lbl_speed=new GLtk::Label(res, format("%2d", train.get_locomotive().get_speed()))));
        lbl_speed->set_style("digital");
        lbl_speed->set_geometry(GLtk::Geometry(10, geom.h-58, 35, 24));
-       train.get_locomotive().signal_speed_changed.connect(sigc::mem_fun(this, &TrainPanel::loco_speed_changed));
+       train.signal_target_speed_changed.connect(sigc::mem_fun(this, &TrainPanel::train_speed_changed));
 
        add(*(sld_speed=new GLtk::HSlider(res)));
        sld_speed->set_geometry(GLtk::Geometry(50, geom.h-51, geom.w-80, 10));
@@ -89,9 +89,10 @@ void TrainPanel::speed_slider_changed(double v)
        train.set_speed(static_cast<unsigned>(v));
 }
 
-void TrainPanel::loco_speed_changed(unsigned speed)
+void TrainPanel::train_speed_changed(unsigned speed)
 {
        lbl_speed->set_text(format("%2d", speed));
+       sld_speed->set_value(speed);
 }
 
 void TrainPanel::loco_function_changed(unsigned func, bool value)
index 2597d040c3c76f2e05afad4a214c7b8df2bd1f8b..d8b1ab0bae0b4f63ccdb62a4b6083deb714c2870 100644 (file)
@@ -33,7 +33,7 @@ public:
        TrainPanel(Engineer &, const Msp::GLtk::Resources &, Marklin::Train &);
 private:
        void speed_slider_changed(double);
-       void loco_speed_changed(unsigned);
+       void train_speed_changed(unsigned);
        void loco_function_changed(unsigned, bool);
        void train_status_changed(const std::string &);
        void place_clicked();
index 3088d4655ba3d100b2c64d2c704782e4e3c04e57..2df46a0d7fae67429c6ddea6be63da3e81c9f6eb 100644 (file)
@@ -34,6 +34,8 @@ Locomotive::Locomotive(const LocoType &t, Control &c, unsigned a):
 void Locomotive::set_speed(unsigned spd)
 {
        spd = min(spd, 14U);
+       if(spd==speed)
+               return;
        signal_speed_changing.emit(spd);
 
        speed = spd;
index 4c1f056eefec7ab65bf0ab0906dd461f3eba2624..4a6770ec6c9feb701165828286d49bd52b9a1801 100644 (file)
@@ -46,6 +46,7 @@ public:
        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);
index 069731e060ae3a80e741a754fa83519af0b6da59..b7301d3dd63e19674ebc0f0896c9aa1ce3ff6f5e 100644 (file)
@@ -69,10 +69,22 @@ Block &TrafficManager::get_block_by_track(const Track &t) const
        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()
index d75bf3ae50b48cecca640c87cb4ac354e5e6867a..85381e0f4454bdb2f05ef3b47d78831c9abf607b 100644 (file)
@@ -28,6 +28,7 @@ public:
                void train(unsigned, unsigned);
        };
 
+       sigc::signal<void, Train &> signal_train_added;
        sigc::signal<void, const Block &, const Train *> signal_block_reserved;
 
 private:
@@ -45,6 +46,7 @@ public:
        const std::list<Block *> &get_blocks() const { return blocks; }
        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;
index 09f587020588bc69fd91c5f51d2747a9094a7110..42d3e01e1f87b1675d9edd32fd7439533ced4af4 100644 (file)
@@ -59,6 +59,8 @@ void Train::set_name(const string &n)
 
 void Train::set_speed(unsigned speed)
 {
+       if(speed==target_speed)
+               return;
        if(!target_speed && speed)
                travel_speed = static_cast<int>(round(speed*speed_scale*87*3.6/5))*5;
 
@@ -73,6 +75,8 @@ void Train::set_speed(unsigned speed)
        else
                reserve_more();
 
+       signal_target_speed_changed.emit(target_speed);
+
        update_speed();
        pure_speed = false;
 }
index 4c19caf01a9625a9921b4a345bd4baada65a177e..9b6344c2fa104066bbb782f0dc6e87bfa3fcd832 100644 (file)
@@ -29,6 +29,7 @@ public:
        };
 
        sigc::signal<void, const std::string &> signal_name_changed;
+       sigc::signal<void, unsigned> signal_target_speed_changed;
        sigc::signal<void, const std::string &> signal_status_changed;
 
 private:
@@ -69,6 +70,7 @@ public:
        void set_reverse(bool);
        const std::string &get_name() const { return name; }
        Locomotive &get_locomotive() const { return loco; }
+       unsigned get_target_speed() const { return target_speed; }
        const std::string &get_status() const { return status; }
        const Point &get_position() const { return pos; }
        void place(Block *, unsigned);
diff --git a/source/network/client.cpp b/source/network/client.cpp
new file mode 100644 (file)
index 0000000..4eb4b25
--- /dev/null
@@ -0,0 +1,76 @@
+/* $Id$
+
+This file is part of the MSP Märklin suite
+Copyright © 2009  Mikkosoft Productions, Mikko Rasa
+Distributed under the GPL
+*/
+
+#include "client.h"
+
+using namespace std;
+using namespace Msp;
+
+namespace Marklin {
+
+Client::Client(const Catalogue &c):
+       catalogue(c),
+       socket(0),
+       comm(0),
+       event_disp(0)
+{ }
+
+Client::~Client()
+{
+       delete comm;
+       delete socket;
+       for(map<unsigned, NetTrain *>::iterator i=trains.begin(); i!=trains.end(); ++i)
+               delete i->second;
+}
+
+void Client::use_event_dispatcher(Msp::IO::EventDispatcher &ed)
+{
+       event_disp = &ed;
+       if(socket)
+               event_disp->add(*socket);
+}
+
+void Client::connect(const Net::SockAddr &addr)
+{
+       socket = new Net::StreamSocket(addr.get_family());
+       socket->connect(addr);
+       if(event_disp)
+               event_disp->add(*socket);
+       comm = new Net::Communicator(*socket, proto, *this);
+}
+
+NetTrain &Client::get_train(unsigned addr)
+{
+       map<unsigned, NetTrain *>::iterator i = trains.find(addr);
+       if(i==trains.end())
+               throw KeyError("Unknown train");
+       return *i->second;
+}
+
+void Client::receive(const TrainInfoPacket &pkt)
+{
+       NetTrain *train = new NetTrain(*this, pkt);
+       trains[pkt.address] = train;
+       signal_train_added.emit(*train);
+}
+
+void Client::receive(const TrainSpeedPacket &pkt)
+{
+       get_train(pkt.address).process_packet(pkt);
+}
+
+void Client::receive(const TrainFunctionPacket &pkt)
+{
+       get_train(pkt.address).process_packet(pkt);
+}
+
+void Client::receive(const TrainStatusPacket &pkt)
+{
+       get_train(pkt.address).process_packet(pkt);
+}
+
+} // namespace Marklin
diff --git a/source/network/client.h b/source/network/client.h
new file mode 100644 (file)
index 0000000..ce8dcb6
--- /dev/null
@@ -0,0 +1,59 @@
+/* $Id$
+
+This file is part of the MSP Märklin suite
+Copyright © 2009  Mikkosoft Productions, Mikko Rasa
+Distributed under the GPL
+*/
+
+#ifndef MARKLINNET_CLIENT_H_
+#define MARKLINNET_CLIENT_H_
+
+#include <msp/io/eventdispatcher.h>
+#include <msp/net/communicator.h>
+#include "libmarklin/catalogue.h"
+#include "packets.h"
+#include "protocol.h"
+#include "train.h"
+
+namespace Marklin {
+
+class Client: public Msp::Net::PacketReceiver<TrainInfoPacket>,
+       Msp::Net::PacketReceiver<TrainSpeedPacket>,
+       Msp::Net::PacketReceiver<TrainFunctionPacket>,
+       Msp::Net::PacketReceiver<TrainStatusPacket>
+{
+public:
+       sigc::signal<void, NetTrain &> signal_train_added;
+
+private:
+       const Catalogue &catalogue;
+       Protocol proto;
+       Msp::Net::StreamSocket *socket;
+       Msp::Net::Communicator *comm;
+       Msp::IO::EventDispatcher *event_disp;
+       std::map<unsigned, NetTrain *> trains;
+
+public:
+       Client(const Catalogue &);
+       ~Client();
+
+       void use_event_dispatcher(Msp::IO::EventDispatcher &);
+       void connect(const Msp::Net::SockAddr &);
+
+       template<typename P>
+       void send(const P &pkt)
+       { if(comm) comm->send(pkt); }
+
+       const Catalogue &get_catalogue() const { return catalogue; }
+       NetTrain &get_train(unsigned);
+
+private:
+       virtual void receive(const TrainInfoPacket &);
+       virtual void receive(const TrainSpeedPacket &);
+       virtual void receive(const TrainFunctionPacket &);
+       virtual void receive(const TrainStatusPacket &);
+};
+
+} // namespace Marklin
+
+#endif
diff --git a/source/network/packets.h b/source/network/packets.h
new file mode 100644 (file)
index 0000000..d6efafc
--- /dev/null
@@ -0,0 +1,43 @@
+/* $Id$
+
+This file is part of the MSP Märklin suite
+Copyright © 2009  Mikkosoft Productions, Mikko Rasa
+Distributed under the GPL
+*/
+
+#ifndef MARKLINNET_PACKETS_H_
+#define MARKLINNET_PACKETS_H_
+
+#include <string>
+
+namespace Marklin {
+
+struct TrainInfoPacket
+{
+       unsigned address;
+       unsigned loco_type;
+       std::string name;
+};
+
+struct TrainSpeedPacket
+{
+       unsigned address;
+       unsigned speed;
+       char reverse;
+};
+
+struct TrainFunctionPacket
+{
+       unsigned address;
+       unsigned functions;
+};
+
+struct TrainStatusPacket
+{
+       unsigned address;
+       std::string status;
+};
+
+} // namespace Marklin
+
+#endif
diff --git a/source/network/protocol.cpp b/source/network/protocol.cpp
new file mode 100644 (file)
index 0000000..5801d37
--- /dev/null
@@ -0,0 +1,25 @@
+/* $Id$
+
+This file is part of the MSP Märklin suite
+Copyright © 2009  Mikkosoft Productions, Mikko Rasa
+Distributed under the GPL
+*/
+
+#include "packets.h"
+#include "protocol.h"
+
+namespace Marklin {
+
+Protocol::Protocol()
+{
+       add<TrainInfoPacket>() (&TrainInfoPacket::address)
+               (&TrainInfoPacket::loco_type) (&TrainInfoPacket::name);
+       add<TrainSpeedPacket>() (&TrainSpeedPacket::address)
+               (&TrainSpeedPacket::speed) (&TrainSpeedPacket::reverse);
+       add<TrainFunctionPacket>() (&TrainFunctionPacket::address)
+               (&TrainFunctionPacket::functions);
+       add<TrainStatusPacket>() (&TrainStatusPacket::address)
+               (&TrainStatusPacket::status);
+}
+
+} // namespace Marklin
diff --git a/source/network/protocol.h b/source/network/protocol.h
new file mode 100644 (file)
index 0000000..176fdad
--- /dev/null
@@ -0,0 +1,23 @@
+/* $Id$
+
+This file is part of the MSP Märklin suite
+Copyright © 2009  Mikkosoft Productions, Mikko Rasa
+Distributed under the GPL
+*/
+
+#ifndef MARKLINNET_PROTOCOL_H_
+#define MARKLINNET_PROTOCOL_H_
+
+#include <msp/net/protocol.h>
+
+namespace Marklin {
+
+class Protocol: public Msp::Net::Protocol
+{
+public:
+       Protocol();
+};
+
+} // namespace Marklin
+
+#endif
diff --git a/source/network/server.cpp b/source/network/server.cpp
new file mode 100644 (file)
index 0000000..17142f8
--- /dev/null
@@ -0,0 +1,174 @@
+/* $Id$
+
+This file is part of the MSP Märklin suite
+Copyright © 2009  Mikkosoft Productions, Mikko Rasa
+Distributed under the GPL
+*/
+
+#include <msp/net/inet.h>
+#include "libmarklin/control.h"
+#include "libmarklin/locomotive.h"
+#include "libmarklin/locotype.h"
+#include "server.h"
+
+using namespace std;
+using namespace Msp;
+
+namespace Marklin {
+
+Server::Server(TrafficManager &tm):
+       trfc_mgr(tm),
+       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)
+       {
+               Locomotive &loco = (*i)->get_locomotive();
+               (*i)->signal_target_speed_changed.connect(sigc::bind<0>(sigc::mem_fun(this, &Server::train_speed_changed), sigc::ref(**i)));
+               loco.signal_function_changed.connect(sigc::bind<0>(sigc::mem_fun(this, &Server::train_function_changed), sigc::ref(**i)));
+               (*i)->signal_status_changed.connect(sigc::bind<0>(sigc::mem_fun(this, &Server::train_status_changed), sigc::ref(**i)));
+       }
+
+       listen_sock.listen(Net::InetAddr(0, 8315), 4);
+       listen_sock.signal_data_available.connect(sigc::mem_fun(this, &Server::incoming_connection));
+}
+
+void Server::use_event_dispatcher(IO::EventDispatcher &ed)
+{
+       event_disp = &ed;
+       event_disp->add(listen_sock);
+}
+
+void Server::incoming_connection()
+{
+       Net::StreamSocket *sock = listen_sock.accept();
+       if(event_disp)
+               event_disp->add(*sock);
+       connections.push_back(new Connection(*this, sock));
+}
+
+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_function_changed.connect(sigc::bind<0>(sigc::mem_fun(this, &Server::train_function_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.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.speed = speed;
+       pkt.reverse = train.get_locomotive().get_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();
+       send(pkt);
+}
+
+void Server::train_status_changed(const Train &train, const string &status)
+{
+       TrainStatusPacket pkt;
+       pkt.address = train.get_locomotive().get_address();
+       pkt.status = status;
+       send(pkt);
+}
+
+template<typename P>
+void Server::send(const P &pkt)
+{
+       for(vector<Connection *>::const_iterator i=connections.begin(); i!=connections.end(); ++i)
+               if(!(*i)->stale)
+                       (*i)->comm.send(pkt);
+}
+
+
+Server::Connection::Connection(Server &s, Net::StreamSocket *o):
+       server(s),
+       socket(o),
+       comm(*socket, server.proto, *this)
+{
+       socket->signal_end_of_file.connect(sigc::mem_fun(this, &Connection::end_of_file));
+       comm.signal_handshake_done.connect(sigc::mem_fun(this, &Connection::handshake_done));
+       comm.initiate_handshake();
+}
+
+Server::Connection::~Connection()
+{
+       delete socket;
+}
+
+void Server::Connection::handshake_done()
+{
+       const list<Train *> &trains = server.trfc_mgr.get_trains();
+       for(list<Train *>::const_iterator i=trains.begin(); i!=trains.end(); ++i)
+       {
+               Locomotive &loco = (*i)->get_locomotive();
+
+               {
+                       TrainInfoPacket pkt;
+                       pkt.address = loco.get_address();
+                       pkt.loco_type = loco.get_type().get_article_number();
+                       pkt.name = (*i)->get_name();
+                       comm.send(pkt);
+               }
+               {
+                       TrainSpeedPacket pkt;
+                       pkt.address = loco.get_address();
+                       pkt.speed = (*i)->get_target_speed();
+                       pkt.reverse = loco.get_reverse();
+                       comm.send(pkt);
+               }
+               {
+                       TrainFunctionPacket pkt;
+                       pkt.address = loco.get_address();
+                       pkt.functions = loco.get_functions();
+                       comm.send(pkt);
+               }
+               {
+                       TrainStatusPacket pkt;
+                       pkt.address = loco.get_address();
+                       pkt.status = (*i)->get_status();
+                       comm.send(pkt);
+               }
+       }
+}
+
+void Server::Connection::end_of_file()
+{
+       socket->close();
+       stale = true;
+}
+
+void Server::Connection::receive(const TrainSpeedPacket &pkt)
+{
+       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.set_reverse(pkt.reverse);
+       else
+               train.set_speed(pkt.speed);
+}
+
+void Server::Connection::receive(const TrainFunctionPacket &pkt)
+{
+       Locomotive &loco = server.trfc_mgr.get_control().get_locomotive(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);
+}
+
+} // namespace Marklin
diff --git a/source/network/server.h b/source/network/server.h
new file mode 100644 (file)
index 0000000..6ce965e
--- /dev/null
@@ -0,0 +1,64 @@
+/* $Id$
+
+This file is part of the MSP Märklin suite
+Copyright © 2009  Mikkosoft Productions, Mikko Rasa
+Distributed under the GPL
+*/
+
+#ifndef MARKLINNET_SERVER_H_
+#define MARKLINNET_SERVER_H_
+
+#include <msp/io/eventdispatcher.h>
+#include <msp/net/communicator.h>
+#include <msp/net/streamsocket.h>
+#include <msp/net/streamlistensocket.h>
+#include "libmarklin/trafficmanager.h"
+#include "packets.h"
+#include "protocol.h"
+
+namespace Marklin {
+
+class Server
+{
+private:
+       struct Connection: private Msp::Net::PacketReceiver<TrainSpeedPacket>,
+               private Msp::Net::PacketReceiver<TrainFunctionPacket>
+       {
+               Server &server;
+               Msp::Net::StreamSocket *socket;
+               Msp::Net::Communicator comm;
+               bool stale;
+
+               Connection(Server &, Msp::Net::StreamSocket *);
+               ~Connection();
+
+               void handshake_done();
+               void end_of_file();
+               virtual void receive(const TrainSpeedPacket &);
+               virtual void receive(const TrainFunctionPacket &);
+       };
+
+       Protocol proto;
+       TrafficManager &trfc_mgr;
+       Msp::Net::StreamListenSocket listen_sock;
+       Msp::IO::EventDispatcher *event_disp;
+       std::vector<Connection *> connections;
+
+public:
+       Server(TrafficManager &);
+       void use_event_dispatcher(Msp::IO::EventDispatcher &);
+private:
+       void incoming_connection();
+
+       void train_added(Train &);
+       void train_speed_changed(const Train &, unsigned);
+       void train_function_changed(const Train &, unsigned, bool);
+       void train_status_changed(const Train &, const std::string &);
+
+       template<typename P>
+       void send(const P &);
+};
+
+} // namespace Marklin
+
+#endif
diff --git a/source/network/train.cpp b/source/network/train.cpp
new file mode 100644 (file)
index 0000000..468b220
--- /dev/null
@@ -0,0 +1,90 @@
+/* $Id$
+
+This file is part of the MSP Märklin suite
+Copyright © 2009  Mikkosoft Productions, Mikko Rasa
+Distributed under the GPL
+*/
+
+#include "client.h"
+#include "train.h"
+
+using namespace std;
+
+namespace Marklin {
+
+NetTrain::NetTrain(Client &c, const TrainInfoPacket &pkt):
+       client(c),
+       loco_type(client.get_catalogue().get_locomotive(pkt.loco_type)),
+       address(pkt.address),
+       name(pkt.name),
+       speed(0),
+       reverse(0),
+       functions(0)
+{ }
+
+void NetTrain::set_speed(unsigned s)
+{
+       if(s==speed)
+               return;
+
+       TrainSpeedPacket pkt;
+       pkt.address = address;
+       pkt.speed = s;
+       pkt.reverse = reverse;
+       client.send(pkt);
+}
+
+void NetTrain::set_reverse(bool r)
+{
+       if(r==reverse)
+               return;
+
+       TrainSpeedPacket pkt;
+       pkt.address = address;
+       pkt.speed = speed;
+       pkt.reverse = r;
+       client.send(pkt);
+}
+
+void NetTrain::set_function(unsigned i, bool set)
+{
+       TrainFunctionPacket pkt;
+       pkt.address = address;
+       pkt.functions = functions;
+       if(set)
+               pkt.functions |= 1<<i;
+       else
+               pkt.functions &= ~(1<<i);
+       client.send(pkt);
+}
+
+void NetTrain::process_packet(const TrainSpeedPacket &pkt)
+{
+       if(pkt.speed!=speed)
+       {
+               speed = pkt.speed;
+               signal_speed_changed.emit(speed);
+       }
+       if(pkt.reverse!=reverse)
+       {
+               reverse = pkt.reverse;
+               signal_reverse_changed.emit(reverse);
+       }
+}
+
+void NetTrain::process_packet(const TrainFunctionPacket &pkt)
+{
+       unsigned old_funcs = functions;
+       functions = pkt.functions;
+       for(unsigned i=0; i<9; ++i)
+               if(((functions^old_funcs)>>i)&1)
+                       signal_function_changed.emit(i, (functions>>i)&1);
+}
+
+void NetTrain::process_packet(const TrainStatusPacket &pkt)
+{
+       status = pkt.status;
+       signal_status_changed.emit(status);
+}
+
+} // namespace Marklin
diff --git a/source/network/train.h b/source/network/train.h
new file mode 100644 (file)
index 0000000..57154a0
--- /dev/null
@@ -0,0 +1,58 @@
+/* $Id$
+
+This file is part of the MSP Märklin suite
+Copyright © 2009  Mikkosoft Productions, Mikko Rasa
+Distributed under the GPL
+*/
+
+#ifndef MARKLINNET_TRAIN_H_
+#define MARKLINNET_TRAIN_H_
+
+#include <sigc++/signal.h>
+#include "libmarklin/locotype.h"
+#include "packets.h"
+
+namespace Marklin {
+
+class Client;
+
+class NetTrain
+{
+public:
+       sigc::signal<void, const std::string &> signal_name_changed;
+       sigc::signal<void, unsigned> signal_speed_changed;
+       sigc::signal<void, bool> signal_reverse_changed;
+       sigc::signal<void, unsigned, bool> signal_function_changed;
+       sigc::signal<void, const std::string &> signal_status_changed;
+
+private:
+       Client &client;
+       const LocoType &loco_type;
+       unsigned address;
+       std::string name;
+       unsigned speed;
+       bool reverse;
+       unsigned functions;
+       std::string status;
+
+public:
+       NetTrain(Client &, const TrainInfoPacket &);
+
+       const LocoType &get_loco_type() const { return loco_type; }
+       unsigned get_address() const { return address; }
+       const std::string &get_name() const { return name; }
+       void set_speed(unsigned);
+       unsigned get_speed() const { return speed; }
+       void set_reverse(bool);
+       bool get_reverse() const { return reverse; }
+       void set_function(unsigned, bool);
+       bool get_function(unsigned i) const { return (functions>>i)&1; }
+
+       void process_packet(const TrainSpeedPacket &);
+       void process_packet(const TrainFunctionPacket &);
+       void process_packet(const TrainStatusPacket &);
+};
+
+} // namespace Marklin
+
+#endif
diff --git a/source/remote/remote.cpp b/source/remote/remote.cpp
new file mode 100644 (file)
index 0000000..57080c1
--- /dev/null
@@ -0,0 +1,53 @@
+/* $Id$
+
+This file is part of the MSP Märklin suite
+Copyright © 2009  Mikkosoft Productions, Mikko Rasa
+Distributed under the GPL
+*/
+
+#include <msp/net/resolve.h>
+#include <msp/time/units.h>
+#include "remote.h"
+#include "trainpanel.h"
+
+using namespace std;
+using namespace Msp;
+
+Application::RegApp<Remote> Remote::reg;
+
+Remote::Remote(int argc, char **argv):
+       client(catalogue),
+       gtk(argc, argv)
+{
+       if(argc<2)
+               throw UsageError("No address given");
+
+       DataFile::load(catalogue, "locos.dat");
+
+       client.use_event_dispatcher(event_disp);
+       client.signal_train_added.connect(sigc::mem_fun(this, &Remote::train_added));
+       string addr_str = argv[1];
+       if(addr_str.find(':')==string::npos)
+               addr_str += ":8315";
+       Net::SockAddr *addr = Net::resolve(addr_str);
+       client.connect(*addr);
+       delete addr;
+
+       window.signal_hide().connect(sigc::bind(sigc::mem_fun(this, &Remote::exit), 0));
+       window.set_border_width(5);
+       train_box = new Gtk::VBox(false, 5);
+       window.add(*manage(train_box));
+       window.show_all();
+}
+
+void Remote::tick()
+{
+       event_disp.tick(Time::zero);
+       gtk.iteration(false);
+}
+
+void Remote::train_added(Marklin::NetTrain &t)
+{
+       TrainPanel *panel = new TrainPanel(t);
+       train_box->add(*manage(panel));
+}
diff --git a/source/remote/remote.h b/source/remote/remote.h
new file mode 100644 (file)
index 0000000..4599a13
--- /dev/null
@@ -0,0 +1,37 @@
+/* $Id$
+
+This file is part of the MSP Märklin suite
+Copyright © 2009  Mikkosoft Productions, Mikko Rasa
+Distributed under the GPL
+*/
+
+#ifndef REMOTE_H_
+#define REMOTE_H_
+
+#include <gtkmm/main.h>
+#include <gtkmm/window.h>
+#include <gtkmm/box.h>
+#include <msp/core/application.h>
+#include "network/client.h"
+
+class Remote: public Msp::Application
+{
+private:
+       Msp::IO::EventDispatcher event_disp;
+       Marklin::Catalogue catalogue;
+       Marklin::Client client;
+       Gtk::Main gtk;
+       Gtk::Window window;
+       Gtk::Box *train_box;
+
+       static Msp::Application::RegApp<Remote> reg;
+
+public:
+       Remote(int argc, char **argv);
+private:
+       void tick();
+
+       void train_added(Marklin::NetTrain &);
+};
+
+#endif
diff --git a/source/remote/trainpanel.cpp b/source/remote/trainpanel.cpp
new file mode 100644 (file)
index 0000000..e3db1ba
--- /dev/null
@@ -0,0 +1,83 @@
+/* $Id$
+
+This file is part of the MSP Märklin suite
+Copyright © 2009  Mikkosoft Productions, Mikko Rasa
+Distributed under the GPL
+*/
+
+#include <gtkmm/box.h>
+#include "libmarklin/locotype.h"
+#include "trainpanel.h"
+
+using namespace std;
+
+TrainPanel::TrainPanel(Marklin::NetTrain &t):
+       train(t)
+{
+       train.signal_name_changed.connect(sigc::mem_fun(this, &TrainPanel::name_changed));
+       train.signal_speed_changed.connect(sigc::mem_fun(this, &TrainPanel::speed_changed));
+       train.signal_function_changed.connect(sigc::mem_fun(this, &TrainPanel::function_changed));
+       train.signal_status_changed.connect(sigc::mem_fun(this, &TrainPanel::status_changed));
+
+       set_label(train.get_name());
+
+       Gtk::VBox *vbox = new Gtk::VBox(false, 5);
+       add(*manage(vbox));
+       vbox->set_border_width(5);
+
+       vbox->add(*manage(scl_speed = new Gtk::HScale));
+       scl_speed->set_digits(0);
+       scl_speed->set_range(0, 14);
+       scl_speed->set_increments(1, 1);
+       scl_speed->set_size_request(280, -1);
+       scl_speed->signal_value_changed().connect(sigc::mem_fun(this, &TrainPanel::ui_speed_changed));
+
+       Gtk::HBox *func_box = new Gtk::HBox(false, 5);
+       vbox->add(*manage(func_box));
+       const std::map<unsigned, string> &funcs = train.get_loco_type().get_functions();
+       for(std::map<unsigned, string>::const_iterator i=funcs.begin(); i!=funcs.end(); ++i)
+       {
+               Gtk::CheckButton *&chk = chk_funcs[i->first];
+               chk = new Gtk::CheckButton(i->second);
+               func_box->pack_start(*manage(chk), false, true);
+               chk->signal_toggled().connect(sigc::bind(sigc::mem_fun(this, &TrainPanel::ui_function_changed), i->first));
+       }
+
+       vbox->add(*manage(lbl_status = new Gtk::Label));
+
+       show_all();
+}
+
+void TrainPanel::name_changed(const string &name)
+{
+       set_label(name);
+}
+
+void TrainPanel::status_changed(const string &status)
+{
+       lbl_status->set_text(status);
+}
+
+void TrainPanel::speed_changed(unsigned speed)
+{
+       scl_speed->set_value(speed);
+}
+
+void TrainPanel::function_changed(unsigned func, bool set)
+{
+       std::map<unsigned, Gtk::CheckButton *>::iterator i = chk_funcs.find(func);
+       if(i!=chk_funcs.end())
+               i->second->set_active(set);
+}
+
+void TrainPanel::ui_speed_changed()
+{
+       train.set_speed(static_cast<unsigned>(scl_speed->get_value()));
+}
+
+void TrainPanel::ui_function_changed(unsigned func)
+{
+       std::map<unsigned, Gtk::CheckButton *>::iterator i = chk_funcs.find(func);
+       if(i!=chk_funcs.end())
+               train.set_function(func, i->second->get_active());
+}
diff --git a/source/remote/trainpanel.h b/source/remote/trainpanel.h
new file mode 100644 (file)
index 0000000..2dcd095
--- /dev/null
@@ -0,0 +1,36 @@
+/* $Id$
+
+This file is part of the MSP Märklin suite
+Copyright © 2009  Mikkosoft Productions, Mikko Rasa
+Distributed under the GPL
+*/
+
+#ifndef TRAINPANEL_H_
+#define TRAINPANEL_H_
+
+#include <gtkmm/checkbutton.h>
+#include <gtkmm/frame.h>
+#include <gtkmm/label.h>
+#include <gtkmm/scale.h>
+#include "network/train.h"
+
+class TrainPanel: public Gtk::Frame
+{
+private:
+       Marklin::NetTrain &train;
+       Gtk::Scale *scl_speed;
+       Gtk::Label *lbl_status;
+       std::map<unsigned, Gtk::CheckButton *> chk_funcs;
+
+public:
+       TrainPanel(Marklin::NetTrain &);
+private:
+       void name_changed(const std::string &);
+       void status_changed(const std::string &);
+       void speed_changed(unsigned);
+       void function_changed(unsigned, bool);
+       void ui_speed_changed();
+       void ui_function_changed(unsigned);
+};
+
+#endif