]> git.tdb.fi Git - r2c2.git/blobdiff - source/libmarklin/intellibox.cpp
Make LCD output selectable at runtime through an extra I/O pin
[r2c2.git] / source / libmarklin / intellibox.cpp
index ace23e8f6166762926782fbd2a9dbcb01e290656..1dc2e7049c7cc2e94f3c34597872b35de5f29dd7 100644 (file)
@@ -20,6 +20,7 @@ namespace Marklin {
 
 Intellibox::Intellibox(const string &dev):
        power(false),
+       halted(false),
        update_sensors(false),
        command_sent(false)
 {
@@ -80,11 +81,45 @@ void Intellibox::set_power(bool p)
        signal_power.emit(power);
 }
 
-void Intellibox::add_loco(unsigned addr)
+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);
+}
+
+const char *Intellibox::enumerate_protocols(unsigned i) const
+{
+       if(i==MM)
+               return "MM";
+       else if(i==MM_27)
+               return "MM-27";
+       return 0;
+}
+
+unsigned Intellibox::get_protocol_speed_steps(const string &proto_name) const
+{
+       Protocol proto = map_protocol(proto_name);
+       if(proto==MM)
+               return 14;
+       else if(proto==MM_27)
+               return 27;
+       return 0;
+}
+
+void Intellibox::add_loco(unsigned addr, const string &proto_name)
+{
+       Protocol proto = map_protocol(proto_name);
+
        if(!locos.count(addr))
        {
-               locos[addr];
+               locos[addr].protocol = proto;
 
                unsigned char data[2];
                data[0] = addr&0xFF;
@@ -97,10 +132,47 @@ void Intellibox::set_loco_speed(unsigned addr, unsigned speed)
 {
        Locomotive &loco = locos[addr];
        if(speed==loco.speed)
+       {
+               if(loco.pending_half_step)
+               {
+                       loco.pending_half_step = 0;
+                       loco.half_step_delay = Time::TimeStamp();
+                       signal_loco_speed.emit(addr, speed, loco.reverse);
+               }
                return;
+       }
+       if(speed && halted)
+               return;
+
+       if(loco.protocol==MM_27)
+       {
+               if(speed>27)
+                       speed = 27;
 
+               if(speed>loco.speed && !(speed&1))
+               {
+                       loco.pending_half_step = -1;
+                       speed |= 1;
+               }
+               else if(speed<loco.speed && (speed&1))
+               {
+                       loco.pending_half_step = 1;
+                       speed &= ~1;
+               }
+               else
+                       loco.pending_half_step = 0;
+               loco.half_step_delay = Time::TimeStamp();
+
+               loco_command(addr, (speed+1)/2, loco.reverse, loco.funcs|0x100);
+       }
+       else
+       {
+               if(speed>14)
+                       speed = 14;
+
+               loco_command(addr, speed, loco.reverse, loco.funcs|0x100);
+       }
        loco.speed = speed;
-       loco_command(addr, speed, loco.reverse, loco.funcs|0x100);
 }
 
 void Intellibox::set_loco_reverse(unsigned addr, bool rev)
@@ -145,6 +217,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);
 }
@@ -184,6 +257,15 @@ void Intellibox::tick()
                command(CMD_EVENT);
        }
 
+       for(map<unsigned, Locomotive>::iterator i=locos.begin(); i!=locos.end(); ++i)
+               if(i->second.protocol==MM_27 && i->second.pending_half_step && i->second.half_step_delay && t>i->second.half_step_delay)
+               {
+                       i->second.speed += i->second.pending_half_step;
+                       i->second.pending_half_step = 0;
+                       i->second.half_step_delay = Time::TimeStamp();
+                       loco_command(i->first, (i->second.speed+1)/2, i->second.reverse, i->second.funcs|0x100);
+               }
+
        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)
                {
@@ -252,6 +334,16 @@ void Intellibox::flush()
        command_sent = false;
 }
 
+Intellibox::Protocol Intellibox::map_protocol(const string &name) const
+{
+       if(name=="MM")
+               return MM;
+       else if(name=="MM-27")
+               return MM_27;
+       else
+               throw InvalidParameterValue("Unknown protocol");
+}
+
 void Intellibox::command(Command cmd)
 {
        command(cmd, 0, 0);
@@ -360,7 +452,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)
@@ -396,25 +491,30 @@ void Intellibox::process_reply(const Time::TimeStamp &t)
        }
        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;
                        Locomotive &loco = locos[addr];
-                       signal_loco_speed.emit(addr, loco.speed, loco.reverse);
+                       signal_loco_speed.emit(addr, loco.speed+loco.pending_half_step, loco.reverse);
+                       if(loco.pending_half_step)
+                               loco.half_step_delay = Time::now()+500*Time::msec;
                }
+               else
+                       error(cmd, err);
        }
        else if(cmd==CMD_TURNOUT)
        {
-               unsigned char err;
-               read_all(&err, 1);
+               Error err;
+               read_status(&err);
+
+               unsigned addr = queue.front().addr;
+               Turnout &turnout = turnouts[addr];
 
                if(err==ERR_NO_ERROR)
                {
-                       unsigned addr = queue.front().addr;
-                       Turnout &turnout = turnouts[addr];
                        turnout.state = turnout.pending;
                        if(turnout.active)
                        {
@@ -424,11 +524,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)
                {
@@ -442,14 +547,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)
                {
@@ -480,6 +588,8 @@ void Intellibox::process_reply(const Time::TimeStamp &t)
                                                signal_loco_function.emit(addr, i, loco.funcs&(1<<i));
                        }
                }
+               else
+                       error(cmd, err);
        }
        else
        {
@@ -491,14 +601,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);
        }
 }
 
@@ -511,6 +623,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),
@@ -521,7 +694,8 @@ Intellibox::Locomotive::Locomotive():
 
 Intellibox::Turnout::Turnout():
        state(false),
-       active(false)
+       active(false),
+       pending(false)
 { }