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;
{
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)
turnout.pending = state;
turnout.active = true;
+ turnout.off_timeout = Time::TimeStamp();
turnout_command(addr, state, true);
}
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)
{
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);
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)
}
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];
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)
{
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)
{
signal_loco_function.emit(addr, i, loco.funcs&(1<<i));
}
}
+ else
+ error(cmd, err);
}
else
{
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);
}
}
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),
Intellibox::Turnout::Turnout():
state(false),
- active(false)
+ active(false),
+ pending(false)
{ }