]> 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 70372f4f10dae20b930f091affe7cc80416249a4..1dc2e7049c7cc2e94f3c34597872b35de5f29dd7 100644 (file)
@@ -94,11 +94,32 @@ void Intellibox::halt(bool h)
        signal_halt.emit(halted);
 }
 
-void Intellibox::add_loco(unsigned addr)
+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;
@@ -111,12 +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)
@@ -161,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);
 }
@@ -200,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)
                {
@@ -268,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);
@@ -376,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)
@@ -412,20 +491,24 @@ 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];
@@ -442,12 +525,15 @@ 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)
                {
@@ -461,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)
                {
@@ -499,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
        {
@@ -510,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);
        }
 }
 
@@ -530,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),
@@ -540,7 +694,8 @@ Intellibox::Locomotive::Locomotive():
 
 Intellibox::Turnout::Turnout():
        state(false),
-       active(false)
+       active(false),
+       pending(false)
 { }