X-Git-Url: http://git.tdb.fi/?a=blobdiff_plain;ds=inline;f=source%2Flibmarklin%2Fintellibox.cpp;h=1dc2e7049c7cc2e94f3c34597872b35de5f29dd7;hb=9b05c573a38639827697fe393d55b7c76f5bde45;hp=35880610145de2d426c4c860bbf4ba9a56e43652;hpb=651698847d5293cfb15b6fb23a394701388c0151;p=r2c2.git diff --git a/source/libmarklin/intellibox.cpp b/source/libmarklin/intellibox.cpp index 3588061..1dc2e70 100644 --- a/source/libmarklin/intellibox.cpp +++ b/source/libmarklin/intellibox.cpp @@ -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::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; @@ -96,17 +131,58 @@ void Intellibox::add_loco(unsigned addr) 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(speed14) + speed = 14; + + loco_command(addr, speed, loco.reverse, loco.funcs|0x100); + } 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 +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); } @@ -180,6 +257,15 @@ void Intellibox::tick() command(CMD_EVENT); } + for(map::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::iterator i=turnouts.begin(); i!=turnouts.end(); ++i) if(i->second.active && i->second.off_timeout && t>i->second.off_timeout) { @@ -230,8 +316,32 @@ void Intellibox::tick() void Intellibox::flush() { + Time::TimeStamp t = Time::now(); for(list::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; +} + +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) @@ -342,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) @@ -376,15 +489,32 @@ 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.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) + { + 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 +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) { @@ -412,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) { @@ -450,6 +588,8 @@ void Intellibox::process_reply(const Time::TimeStamp &t) signal_loco_function.emit(addr, i, loco.funcs&(1<(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 +694,8 @@ Intellibox::Locomotive::Locomotive(): Intellibox::Turnout::Turnout(): state(false), - active(false) + active(false), + pending(false) { }