]> git.tdb.fi Git - r2c2.git/blobdiff - source/libr2c2/centralstation.cpp
Add telemetry framework for drivers
[r2c2.git] / source / libr2c2 / centralstation.cpp
index 69bb8a9cf48c78745bf882f764cf0c7270087152..5e82f2e5f117c23d9137bb80ff41b0b4359d2ecb 100644 (file)
@@ -1,11 +1,5 @@
-/* $Id$
-
-This file is part of R²C²
-Copyright © 2010  Mikkosoft Productions, Mikko Rasa
-Distributed under the GPL
-*/
-
 #include <algorithm>
+#include <msp/core/maputils.h>
 #include <msp/core/refptr.h>
 #include <msp/io/print.h>
 #include <msp/net/resolve.h>
@@ -21,16 +15,16 @@ using namespace Msp;
 
 namespace R2C2 {
 
-CentralStation::CentralStation(const string &host):
+CentralStation::CentralStation(const Options &opts):
        socket(Net::INET),
        pending_commands(0),
        power(false),
        halted(false),
        locos_synced(false),
-       turnouts_synced(false),
+       accessories_synced(false),
        sensors_synced(false)
 {
-       RefPtr<Net::SockAddr> addr = Net::resolve(host+":15471");
+       RefPtr<Net::SockAddr> addr = Net::resolve(opts.get<string>(string())+":15471");
        socket.connect(*addr);
 
        IO::print("Connected to central station at %s\n", addr->str());
@@ -47,7 +41,7 @@ CentralStation::~CentralStation()
        command("release(1, view)", true);
        for(LocoMap::iterator i=locos.begin(); (i!=locos.end() && !(i->first&0x10000)); ++i)
                command(format("release(%d, view, control)", i->first));
-       for(TurnoutMap::iterator i=turnouts.begin(); (i!=turnouts.end() && !(i->first&0x10000)); ++i)
+       for(AccessoryMap::iterator i=accessories.begin(); (i!=accessories.end() && !(i->first&0x10000)); ++i)
                command(format("release(%d, view, control)", i->first));
        while(IO::poll(socket, IO::P_INPUT, 100*Time::msec))
                while(receive()) ;
@@ -90,12 +84,12 @@ unsigned CentralStation::get_protocol_speed_steps(const string &name) const
        {
        case MM: return 14;
        case MM_27: return 27;
-       case MFX: return 127;
+       case MFX: return 126;
        default: return 0;
        }
 }
 
-void CentralStation::add_loco(unsigned addr, const string &proto_name, const VehicleType &type)
+unsigned CentralStation::add_loco(unsigned addr, const string &proto_name, const VehicleType &type)
 {
        Protocol proto = map_protocol(proto_name);
 
@@ -107,11 +101,24 @@ void CentralStation::add_loco(unsigned addr, const string &proto_name, const Veh
                loco.protocol = proto;
                loco.address = addr;
 
+               const VehicleType::FunctionMap &type_funcs = type.get_functions();
+               for(VehicleType::FunctionMap::const_iterator i=type_funcs.begin(); i!=type_funcs.end(); ++i)
+                       loco.func_mask |= 1<<i->first;
+
                if(locos_synced && proto!=MFX)
                        command("create(10)");
        }
        else
                command(format("request(%d, view, control, force)", id));
+
+       return addr;
+}
+
+void CentralStation::remove_loco(unsigned addr)
+{
+       unsigned id = map_address(locos, loco_addr, addr);
+       if(id)
+               command(format("release(%d, view, control)", id));
 }
 
 void CentralStation::set_loco_speed(unsigned addr, unsigned speed)
@@ -121,7 +128,12 @@ void CentralStation::set_loco_speed(unsigned addr, unsigned speed)
 
        unsigned id = map_address(locos, loco_addr, addr);
        if(id)
+       {
+               Locomotive &loco = locos[id];
+               if(loco.protocol==MFX && speed)
+                       ++speed;
                command(format("set(%d, speedstep[%d])", id, speed));
+       }
 }
 
 void CentralStation::set_loco_reverse(unsigned addr, bool rev)
@@ -138,8 +150,9 @@ void CentralStation::set_loco_function(unsigned addr, unsigned func, bool state)
                command(format("set(%d, func[%d, %d])", id, func, state));
 }
 
-void CentralStation::add_turnout(unsigned addr, const TrackType &type)
+unsigned CentralStation::add_turnout(unsigned addr, const TrackType &type)
 {
+       unsigned straight = type.get_paths();
        bool left = false;
        bool right = false;
        bool cross = false;
@@ -147,84 +160,151 @@ void CentralStation::add_turnout(unsigned addr, const TrackType &type)
        const vector<TrackPart> &parts = type.get_parts();
        for(vector<TrackPart>::const_iterator i=parts.begin(); i!=parts.end(); ++i)
        {
-               TrackPoint start = i->get_point(0);
-               TrackPoint end = i->get_point(i->get_length());
-               if(end.dir>start.dir+0.01)
-                       left = true;
-               else if(end.dir<start.dir-0.01)
-                       right = true;
-               else if(start.dir<-0.01 || start.dir>0.01)
+               OrientedPoint start = i->get_point(0);
+               OrientedPoint end = i->get_point(i->get_length());
+               if(abs(end.rotation-start.rotation).radians()<0.01)
+               {
+                       (end.rotation>start.rotation ? left : right) = true;
+                       straight &= ~(1<<i->get_path());
+               }
+               else if(abs(start.rotation).radians()>0.01)
                        cross = true;
        }
 
-       unsigned symbol;
+       MagnetAccessory::Symbol symbol = MagnetAccessory::TURNOUT_LEFT;
        if(cross)
-               symbol = Turnout::DOUBLESLIP;
+               symbol = MagnetAccessory::TURNOUT_DOUBLESLIP;
        else if(left && right)
-               symbol = Turnout::THREEWAY;
+               symbol = MagnetAccessory::TURNOUT_THREEWAY;
        else if(left)
-               symbol = Turnout::LEFT;
+               symbol = (straight ? MagnetAccessory::TURNOUT_LEFT : MagnetAccessory::TURNOUT_CURVED_LEFT);
        else if(right)
-               symbol = Turnout::RIGHT;
+               symbol = (straight ? MagnetAccessory::TURNOUT_RIGHT : MagnetAccessory::TURNOUT_CURVED_RIGHT);
+
+       MagnetAccessory &turnout = add_accessory(addr, MagnetAccessory::TURNOUT, symbol);
+       turnout.bits = type.get_state_bits();
+
+       return addr;
+}
+
+void CentralStation::remove_turnout(unsigned addr)
+{
+       remove_accessory(addr);
+}
+
+void CentralStation::set_turnout(unsigned addr, unsigned state)
+{
+       set_accessory_state(addr, MagnetAccessory::TURNOUT, state);
+}
 
-       unsigned id = map_address(turnouts, turnout_addr, addr);
+unsigned CentralStation::get_turnout(unsigned addr) const
+{
+       return get_accessory_state(addr, MagnetAccessory::TURNOUT);
+}
+
+unsigned CentralStation::add_signal(unsigned addr, const SignalType &)
+{
+       add_accessory(addr, MagnetAccessory::SIGNAL, MagnetAccessory::SEMAPHORE_HOME);
+       return addr;
+}
+
+void CentralStation::remove_signal(unsigned addr)
+{
+       remove_accessory(addr);
+}
+
+void CentralStation::set_signal(unsigned addr, unsigned state)
+{
+       set_accessory_state(addr, MagnetAccessory::SIGNAL, state);
+}
+
+unsigned CentralStation::get_signal(unsigned addr) const
+{
+       return get_accessory_state(addr, MagnetAccessory::SIGNAL);
+}
+
+CentralStation::MagnetAccessory &CentralStation::add_accessory(unsigned addr, MagnetAccessory::Type type, MagnetAccessory::Symbol symbol)
+{
+       unsigned id = map_address(accessories, accessory_addr, addr);
        if(!id)
        {
                id = addr|0x10000;
 
-               Turnout &turnout = turnouts[id];
-               turnout.address = addr;
-               turnout.bits = type.get_state_bits();
-               turnout.symbol = symbol;
+               MagnetAccessory &accessory = accessories[id];
+               accessory.address = addr;
+               accessory.type = type;
+               accessory.symbol = symbol;
 
-               turnout_addr[addr] = id;
+               accessory_addr[addr] = id;
 
-               if(turnouts_synced)
+               if(accessories_synced)
                        command("create(11, append)");
+
+               return accessory;
        }
        else
        {
-               Turnout &turnout = turnouts[id];
+               MagnetAccessory &accessory = accessories[id];
                command(format("request(%d, view, control)", id));
-               if(turnout.symbol!=symbol)
+               if(accessory.symbol!=symbol)
                        command(format("set(%d, symbol[%d])", symbol));
+
+               return accessory;
        }
 }
 
-void CentralStation::set_turnout(unsigned addr, unsigned state)
+void CentralStation::remove_accessory(unsigned addr)
+{
+       unsigned id = map_address(accessories, accessory_addr, addr);
+       if(id)
+               command(format("release(%d, view, control)", id));
+}
+
+void CentralStation::set_accessory_state(unsigned addr, MagnetAccessory::Type type, unsigned state)
 {
-       unsigned id = map_address(turnouts, turnout_addr, addr);
+       unsigned id = map_address(accessories, accessory_addr, addr);
        if(id)
        {
-               Turnout &turnout = turnouts[id];
-               unsigned mask = (1<<turnout.bits)-1;
+               MagnetAccessory &accessory = accessories[id];
+               if(accessory.type!=type)
+                       throw logic_error("accessory type conflict");
 
-               if(((state^turnout.state)&mask)==0 || !turnout.synced)
+               unsigned mask = (1<<accessory.bits)-1;
+
+               if(((state^accessory.state)&mask)==0 || !accessory.synced)
                {
-                       turnout.state = state;
-                       signal_turnout.emit(addr, turnout.state);
+                       accessory.state = state;
+                       accessory_state_changed(accessory);
                        return;
                }
 
-               turnout.state = (turnout.state&mask) | (state&~mask);
+               accessory.state = (accessory.state&mask) | (state&~mask);
 
                command(format("set(%d, state[%d])", id, state&mask));
        }
 }
 
-unsigned CentralStation::get_turnout(unsigned addr) const
+unsigned CentralStation::get_accessory_state(unsigned addr, MagnetAccessory::Type type) const
 {
-       unsigned id = map_address(turnouts, turnout_addr, addr);
+       unsigned id = map_address(accessories, accessory_addr, addr);
        if(id)
        {
-               TurnoutMap::const_iterator i = turnouts.find(id);
-               if(i!=turnouts.end())
+               AccessoryMap::const_iterator i = accessories.find(id);
+               if(i!=accessories.end() && i->second.type==type)
                        return i->second.state;
        }
        return 0;
 }
 
-void CentralStation::add_sensor(unsigned addr)
+void CentralStation::accessory_state_changed(const MagnetAccessory &accessory) const
+{
+       if(accessory.type==MagnetAccessory::TURNOUT)
+               signal_turnout.emit(accessory.address, accessory.state);
+       else if(accessory.type==MagnetAccessory::SIGNAL)
+               signal_signal.emit(accessory.address, accessory.state);
+}
+
+unsigned CentralStation::add_sensor(unsigned addr)
 {
        sensors.insert(SensorMap::value_type(addr, Sensor()));
 
@@ -233,6 +313,12 @@ void CentralStation::add_sensor(unsigned addr)
                if(addr>s88.size()*16)
                        command("create(26, add[0])");
        }
+
+       return addr;
+}
+
+void CentralStation::remove_sensor(unsigned)
+{
 }
 
 bool CentralStation::get_sensor(unsigned addr) const
@@ -243,17 +329,13 @@ bool CentralStation::get_sensor(unsigned addr) const
        return false;
 }
 
-void CentralStation::tick()
+float CentralStation::get_telemetry_value(const string &name) const
 {
-       Time::TimeStamp t = Time::now();
-       for(SensorMap::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, i->second.state);
-               }
+       throw key_error(name);
+}
 
+void CentralStation::tick()
+{
        while(Message msg = receive())
        {
                if(msg.footer.code)
@@ -324,8 +406,8 @@ void CentralStation::process_reply(const Message &msg)
        {
                for(Message::ObjectMap::const_iterator i=msg.content.begin(); i!=msg.content.end(); ++i)
                {
-                       if(turnouts.count(i->first))
-                               turnouts[i->first].synced = true;
+                       if(accessories.count(i->first))
+                               accessories[i->first].synced = true;
 
                        process_object(i->first, i->second);
                }
@@ -347,7 +429,12 @@ void CentralStation::process_reply(const Message &msg)
                                        if(j!=locos.end())
                                        {
                                                command(format("request(%d, view, control, force)", i->first));
-                                               command(format("get(%d, dir, func[0])", i->first));
+                                               string cmd = format("get(%d, dir", i->first);
+                                               for(unsigned l=0; j->second.func_mask>>l; ++l)
+                                                       if((j->second.func_mask>>l)&1)
+                                                               cmd += format(", func[%d]", l);
+                                               cmd += ')';
+                                               command(cmd);
 
                                                locos.insert(LocoMap::value_type(i->first, j->second));
                                                locos.erase(j);
@@ -372,8 +459,8 @@ void CentralStation::process_reply(const Message &msg)
        {
                for(Message::ObjectMap::const_iterator i=msg.content.begin(); i!=msg.content.end(); ++i)
                {
-                       TurnoutMap::iterator j = turnouts.find(i->first);
-                       if(j==turnouts.end())
+                       AccessoryMap::iterator j = accessories.find(i->first);
+                       if(j==accessories.end())
                        {
                                bool found = false;
                                Message::AttribMap::const_iterator k = i->second.find("addr");
@@ -381,30 +468,30 @@ void CentralStation::process_reply(const Message &msg)
                                {
                                        unsigned addr = lexical_cast<unsigned>(k->second);
 
-                                       j = turnouts.find(addr|0x10000);
-                                       if(j!=turnouts.end())
+                                       j = accessories.find(addr|0x10000);
+                                       if(j!=accessories.end())
                                        {
                                                command(format("request(%d, view, control)", i->first));
                                                command(format("set(%d, symbol[%d])", i->first, j->second.symbol));
                                                command(format("get(%d, state)", i->first));
 
-                                               turnouts.insert(TurnoutMap::value_type(i->first, j->second));
-                                               turnouts.erase(j);
+                                               accessories.insert(AccessoryMap::value_type(i->first, j->second));
+                                               accessories.erase(j);
 
                                                found = true;
                                        }
                                }
 
                                if(!found)
-                                       turnouts.insert(TurnoutMap::value_type(i->first, Turnout()));
+                                       accessories.insert(AccessoryMap::value_type(i->first, MagnetAccessory()));
                        }
 
                        process_object(i->first, i->second);
                }
 
-               turnouts_synced = true;
+               accessories_synced = true;
 
-               for(TurnoutMap::const_iterator i=turnouts.lower_bound(0x10000); i!=turnouts.end(); ++i)
+               for(AccessoryMap::const_iterator i=accessories.lower_bound(0x10000); i!=accessories.end(); ++i)
                        command("create(11, append)");
        }
        else if(msg.header.value=="queryObjects(26)")
@@ -414,6 +501,7 @@ void CentralStation::process_reply(const Message &msg)
                {
                        s88.push_back(i->first);
                        command(format("request(%d, view)", i->first));
+                       command(format("get(%d, state)", i->first));
                }
 
                sensors_synced = true;
@@ -460,17 +548,18 @@ void CentralStation::process_reply(const Message &msg)
                        if(j!=i->second.end())
                        {
                                unsigned id = lexical_cast<unsigned>(j->second);
-                               TurnoutMap::iterator k = turnouts.lower_bound(0x10000);
-                               if(k!=turnouts.end())
+                               AccessoryMap::iterator k = accessories.lower_bound(0x10000);
+                               if(k!=accessories.end())
                                {
                                        command(format("request(%d, view, control)", id));
-                                       command(format("set(%d, addr[%d], symbol[%d], name1[\"Switch\"], name2[\"%d\"], name3[\"\"])",
-                                               id, k->second.address, k->second.symbol, k->second.address));
+                                       const char *label = (k->second.type==MagnetAccessory::SIGNAL ? "Signal" : "Switch");
+                                       command(format("set(%d, addr[%d], symbol[%d], name1[\"%s\"], name2[\"%d\"], name3[\"\"])",
+                                               id, k->second.address, k->second.symbol, label, k->second.address));
                                        command(format("set(%d, state[%d])", id, k->second.state&((1<<k->second.bits)-1)));
 
                                        k->second.synced = true;
-                                       turnouts.insert(TurnoutMap::value_type(id, k->second));
-                                       turnouts.erase(k);
+                                       accessories.insert(AccessoryMap::value_type(id, k->second));
+                                       accessories.erase(k);
                                }
                        }
                }
@@ -523,6 +612,8 @@ void CentralStation::process_object(unsigned id, const Message::AttribMap &attri
                        else if(i->first=="speedstep")
                        {
                                loco.speed = lexical_cast<unsigned>(i->second);
+                               if(loco.protocol==MFX && loco.speed)
+                                       --loco.speed;
                                speed_changed = true;
                        }
                        else if(i->first=="dir")
@@ -553,29 +644,29 @@ void CentralStation::process_object(unsigned id, const Message::AttribMap &attri
                        if(funcs_changed&(1<<i))
                                signal_loco_function.emit(loco.address, i, loco.funcs&(1<<i));
        }
-       else if(turnouts.count(id))
+       else if(accessories.count(id))
        {
-               Turnout &turnout = turnouts[id];
+               MagnetAccessory &accessory = accessories[id];
                bool state_changed = false;
                for(Message::AttribMap::const_iterator i=attribs.begin(); i!=attribs.end(); ++i)
                {
                        if(i->first=="addr")
                        {
-                               turnout_addr.erase(turnout.address);
-                               turnout.address = lexical_cast<unsigned>(i->second);
-                               turnout_addr[turnout.address] = id;
+                               accessory_addr.erase(accessory.address);
+                               accessory.address = lexical_cast<unsigned>(i->second);
+                               accessory_addr[accessory.address] = id;
                        }
                        else if(i->first=="state")
                        {
                                unsigned state = lexical_cast<unsigned>(i->second);
-                               unsigned mask = (1<<turnout.bits)-1;
-                               turnout.state = (turnout.state&~mask) | (state&mask);
+                               unsigned mask = (1<<accessory.bits)-1;
+                               accessory.state = (accessory.state&~mask) | (state&mask);
                                state_changed = true;
                        }
                }
 
                if(state_changed)
-                       signal_turnout.emit(turnout.address, turnout.state);
+                       accessory_state_changed(accessory);
        }
        else if(find(s88.begin(), s88.end(), id)!=s88.end())
        {
@@ -592,17 +683,11 @@ void CentralStation::process_object(unsigned id, const Message::AttribMap &attri
                                        unsigned addr = base*16+j+1;
                                        Sensor &sensor = sensors[addr];
                                        bool s = state&(1<<j);
-                                       if(s)
+                                       if(s!=sensor.state)
                                        {
-                                               sensor.off_timeout = Time::TimeStamp();
-                                               if(!sensor.state)
-                                               {
-                                                       sensor.state = true;
-                                                       signal_sensor.emit(addr, sensor.state);
-                                               }
+                                               sensor.state = s;
+                                               signal_sensor.emit(addr, sensor.state);
                                        }
-                                       else if(sensor.state)
-                                               sensor.off_timeout = Time::now()+700*Time::msec;
                                }
                        }
                }
@@ -618,7 +703,7 @@ CentralStation::Protocol CentralStation::map_protocol(const string &name) const
        else if(name=="MFX")
                return MFX;
        else
-               throw InvalidParameterValue("Unknown protocol");
+               throw invalid_argument("CentralStation::map_protocol");
 }
 
 template<typename T>
@@ -714,10 +799,12 @@ CentralStation::Message CentralStation::parse_message(string::iterator &iter, co
                        if(open_bracket!=string::npos)
                        {
                                string::size_type close_bracket = attr.rfind(']');
-                               attribs[attr.substr(0, open_bracket)] = attr.substr(open_bracket+1, close_bracket-open_bracket-1);
+                               string attr_name = attr.substr(0, open_bracket);
+                               string attr_value = attr.substr(open_bracket+1, close_bracket-open_bracket-1);
+                               attribs.insert(Message::AttribMap::value_type(attr_name, attr_value));
                        }
                        else
-                               attribs[attr];
+                               attribs.insert(Message::AttribMap::value_type(attr, string()));
                }
        }
 
@@ -749,16 +836,18 @@ CentralStation::Locomotive::Locomotive():
        address(0),
        speed(0),
        reverse(false),
+       func_mask(0),
        funcs(0),
        control(false)
 { }
 
 
-CentralStation::Turnout::Turnout():
+CentralStation::MagnetAccessory::MagnetAccessory():
        address(0),
-       symbol(0),
+       type(TURNOUT),
+       symbol(TURNOUT_LEFT),
        state(0),
-       bits(0),
+       bits(1),
        synced(false)
 { }