]> git.tdb.fi Git - r2c2.git/blobdiff - source/libmarklin/intellibox.cpp
Add an SVG exporter to Designer
[r2c2.git] / source / libmarklin / intellibox.cpp
index 35880610145de2d426c4c860bbf4ba9a56e43652..416d02184e9564836361da811ef247a4777a0f3c 100644 (file)
@@ -20,6 +20,7 @@ namespace Marklin {
 
 Intellibox::Intellibox(const string &dev):
        power(false),
+       halted(false),
        update_sensors(false),
        command_sent(false)
 {
@@ -80,6 +81,19 @@ void Intellibox::set_power(bool p)
        signal_power.emit(power);
 }
 
+void Intellibox::halt(bool h)
+{
+       halted = h;
+       if(halted)
+       {
+               for(map<unsigned, Locomotive>::iterator i=locos.begin(); i!=locos.end(); ++i)
+                       if(i->second.speed)
+                               set_loco_speed(i->first, 0);
+       }
+
+       signal_halt.emit(halted);
+}
+
 void Intellibox::add_loco(unsigned addr)
 {
        if(!locos.count(addr))
@@ -96,17 +110,23 @@ void Intellibox::add_loco(unsigned addr)
 void Intellibox::set_loco_speed(unsigned addr, unsigned speed)
 {
        Locomotive &loco = locos[addr];
+       if(speed==loco.speed)
+               return;
+       if(speed && halted)
+               return;
+
        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];
+       if(rev==loco.reverse)
+               return;
+
        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)
@@ -141,6 +161,7 @@ void Intellibox::set_turnout(unsigned addr, bool state)
 
        turnout.pending = state;
        turnout.active = true;
+       turnout.off_timeout = Time::TimeStamp();
 
        turnout_command(addr, state, true);
 }
@@ -230,8 +251,22 @@ void Intellibox::tick()
 
 void Intellibox::flush()
 {
+       Time::TimeStamp t = Time::now();
        for(list<CommandSlot>::iterator i=queue.begin(); i!=queue.end(); ++i)
+       {
                write(serial_fd, i->data, i->length);
+               pollfd pfd = { serial_fd, POLLIN, 0 };
+               bool first = true;
+               while(poll(&pfd, 1, (first ? -1 : 0))>0)
+               {
+                       char data[16];
+                       read(serial_fd, data, 16);
+                       first = false;
+               }
+       }
+
+       queue.clear();
+       command_sent = false;
 }
 
 void Intellibox::command(Command cmd)
@@ -342,7 +377,10 @@ void Intellibox::process_reply(const Time::TimeStamp &t)
                        read_all(data, 2);
 
                        unsigned addr = data[0]+((data[1]&7)<<8);
-                       signal_turnout.emit(addr, (data[1]&0x80)!=0);
+                       Turnout &turnout = turnouts[addr];
+                       turnout.state = (data[1]&0x80)!=0;
+                       turnout.pending = turnout.state;
+                       signal_turnout.emit(addr, turnout.state);
                }
        }
        else if(cmd==CMD_EVENT_SENSOR)
@@ -376,15 +414,30 @@ void Intellibox::process_reply(const Time::TimeStamp &t)
                        }
                }
        }
-       else if(cmd==CMD_TURNOUT)
+       else if(cmd==CMD_LOK)
        {
-               unsigned char err;
-               read_all(&err, 1);
+               Error err;
+               read_status(&err);
 
                if(err==ERR_NO_ERROR)
                {
                        unsigned addr = queue.front().addr;
-                       Turnout &turnout = turnouts[addr];
+                       Locomotive &loco = locos[addr];
+                       signal_loco_speed.emit(addr, loco.speed, loco.reverse);
+               }
+               else
+                       error(cmd, err);
+       }
+       else if(cmd==CMD_TURNOUT)
+       {
+               Error err;
+               read_status(&err);
+
+               unsigned addr = queue.front().addr;
+               Turnout &turnout = turnouts[addr];
+
+               if(err==ERR_NO_ERROR)
+               {
                        turnout.state = turnout.pending;
                        if(turnout.active)
                        {
@@ -394,11 +447,16 @@ void Intellibox::process_reply(const Time::TimeStamp &t)
                }
                else if(err==ERR_NO_I2C_SPACE)
                        queue.push_back(queue.front());
+               else
+               {
+                       turnout.pending = turnout.state;
+                       error(cmd, err);
+               }
        }
        else if(cmd==CMD_TURNOUT_STATUS)
        {
-               unsigned char err;
-               read_all(&err, 1);
+               Error err;
+               read_status(&err);
 
                if(err==ERR_NO_ERROR)
                {
@@ -412,14 +470,17 @@ void Intellibox::process_reply(const Time::TimeStamp &t)
                        if(state!=turnout.state)
                        {
                                turnout.state = state;
+                               turnout.pending = state;
                                signal_turnout.emit(addr, turnout.state);
                        }
                }
+               else
+                       error(cmd, err);
        }
        else if(cmd==CMD_LOK_STATUS)
        {
-               unsigned char err;
-               read_all(&err, 1);
+               Error err;
+               read_status(&err);
 
                if(err==ERR_NO_ERROR)
                {
@@ -450,6 +511,8 @@ void Intellibox::process_reply(const Time::TimeStamp &t)
                                                signal_loco_function.emit(addr, i, loco.funcs&(1<<i));
                        }
                }
+               else
+                       error(cmd, err);
        }
        else
        {
@@ -461,14 +524,16 @@ void Intellibox::process_reply(const Time::TimeStamp &t)
                if(cmd==CMD_LOK_CONFIG)
                        expected_bytes = 4;
 
-               unsigned char err;
-               read_all(&err, 1);
+               Error err;
+               read_status(&err);
 
                if(err==ERR_NO_ERROR)
                {
                        unsigned char data[8];
                        read_all(data, expected_bytes);
                }
+               else
+                       error(cmd, err);
        }
 }
 
@@ -481,6 +546,67 @@ unsigned Intellibox::read_all(unsigned char *buf, unsigned len)
        return pos;
 }
 
+unsigned Intellibox::read_status(Error *err)
+{
+       unsigned char c;
+       unsigned ret = read_all(&c, 1);
+       *err = static_cast<Error>(c);
+       return ret;
+}
+
+void Intellibox::error(Command cmd, Error err)
+{
+       const char *cmd_str = 0;
+       switch(cmd)
+       {
+       case CMD_LOK: cmd_str = "CMD_LOK"; break;
+       case CMD_LOK_STATUS: cmd_str = "CMD_LOK_STATUS"; break;
+       case CMD_LOK_CONFIG: cmd_str = "CMD_LOK_CONFIG"; break;
+       case CMD_FUNC: cmd_str = "CMD_FUNC"; break;
+       case CMD_FUNC_STATUS: cmd_str = "CMD_FUNC_STATUS"; break;
+       case CMD_TURNOUT: cmd_str = "CMD_TURNOUT"; break;
+       case CMD_TURNOUT_FREE: cmd_str = "CMD_TURNOUT_FREE"; break;
+       case CMD_TURNOUT_STATUS: cmd_str = "CMD_TURNOUT_STATUS"; break;
+       case CMD_TURNOUT_GROUP_STATUS: cmd_str = "CMD_TURNOUT_GROUP_STATUS"; break;
+       case CMD_SENSOR_STATUS: cmd_str = "CMD_SENSOR_STATUS"; break;
+       case CMD_SENSOR_REPORT: cmd_str = "CMD_SENSOR_REPORT"; break;
+       case CMD_SENSOR_PARAM_SET: cmd_str = "CMD_SENSOR_PARAM_SET"; break;
+       case CMD_STATUS: cmd_str = "CMD_STATUS"; break;
+       case CMD_POWER_OFF: cmd_str = "CMD_POWER_OFF"; break;
+       case CMD_POWER_ON: cmd_str = "CMD_POWER_ON"; break;
+       case CMD_NOP: cmd_str = "CMD_NOP"; break;
+       case CMD_EVENT: cmd_str = "CMD_EVENT"; break;
+       case CMD_EVENT_LOK: cmd_str = "CMD_EVENT_LOK"; break;
+       case CMD_EVENT_TURNOUT: cmd_str = "CMD_EVENT_TURNOUT"; break;
+       case CMD_EVENT_SENSOR: cmd_str = "CMD_EVENT_SENSOR"; break;
+       default: cmd_str = "(unknown command)";
+       }
+
+       const char *err_str = 0;
+       switch(err)
+       {
+       case ERR_NO_ERROR: err_str = "ERR_NO_ERROR"; break;
+       case ERR_SYS_ERROR: err_str = "ERR_SYS_ERROR"; break;
+       case ERR_BAD_PARAM: err_str = "ERR_BAD_PARAM"; break;
+       case ERR_POWER_OFF: err_str = "ERR_POWER_OFF"; break;
+       case ERR_NO_LOK_SPACE: err_str = "ERR_NO_LOK_SPACE"; break;
+       case ERR_NO_TURNOUT_SPACE: err_str = "ERR_NO_TURNOUT_SPACE"; break;
+       case ERR_NO_DATA: err_str = "ERR_NO_DATA"; break;
+       case ERR_NO_SLOT: err_str = "ERR_NO_SLOT"; break;
+       case ERR_BAD_LOK_ADDR: err_str = "ERR_BAD_LOK_ADDR"; break;
+       case ERR_LOK_BUSY: err_str = "ERR_LOK_BUSY"; break;
+       case ERR_BAD_TURNOUT_ADDR: err_str = "ERR_BAD_TURNOUT_ADDR"; break;
+       case ERR_BAD_SO_VALUE: err_str = "ERR_BAD_SO_VALUE"; break;
+       case ERR_NO_I2C_SPACE: err_str = "ERR_NO_I2C_SPACE"; break;
+       case ERR_LOW_TURNOUT_SPACE: err_str = "ERR_LOW_TURNOUT_SPACE"; break;
+       case ERR_LOK_HALTED: err_str = "ERR_LOK_HALTED"; break;
+       case ERR_LOK_POWER_OFF: err_str = "ERR_LOK_POWER_OFF"; break;
+       default: cmd_str = "(unknown error)";
+       }
+
+       IO::print("Error: %s: %s\n", cmd_str, err_str);
+}
+
 
 Intellibox::Locomotive::Locomotive():
        speed(0),
@@ -491,7 +617,8 @@ Intellibox::Locomotive::Locomotive():
 
 Intellibox::Turnout::Turnout():
        state(false),
-       active(false)
+       active(false),
+       pending(false)
 { }