4 #include <msp/core/maputils.h>
5 #include <msp/io/print.h>
6 #include <msp/time/units.h>
7 #include <msp/time/utils.h>
8 #include "intellibox.h"
10 #include "vehicletype.h"
17 Intellibox::Intellibox(const Options &opts):
18 serial(opts.get<string>(string(), "ttyUSB0")),
21 update_sensors(false),
24 static unsigned baud[]= { 2400, 4800, 9600, 19200, 0 };
26 serial.set_stop_bits(2);
30 for(unsigned i=0; baud[i]; ++i)
32 serial.set_baud_rate(baud[i]);
35 if(IO::poll(serial, IO::P_INPUT, 500*Time::msec))
37 IO::print("IB detected at %d bits/s\n", baud[i]);
39 p50 = (serial.read(buf, 2)==2);
46 throw runtime_error("IB not detected");
49 serial.write("xZzA1\r", 6);
54 void Intellibox::set_power(bool p)
58 command(CMD_POWER_ON);
60 command(CMD_POWER_OFF);
61 signal_power.emit(power);
64 void Intellibox::halt(bool h)
69 for(map<unsigned, Locomotive>::iterator i=locos.begin(); i!=locos.end(); ++i)
71 set_loco_speed(i->first, 0);
74 signal_halt.emit(halted);
77 const char *Intellibox::enumerate_protocols(unsigned i) const
87 unsigned Intellibox::get_protocol_speed_steps(const string &proto_name) const
89 Protocol proto = map_protocol(proto_name);
97 unsigned Intellibox::add_loco(unsigned addr, const string &proto_name, const VehicleType &type)
99 Protocol proto = map_protocol(proto_name);
101 if(!locos.count(addr))
103 Locomotive &loco = locos[addr];
104 loco.protocol = proto;
105 if(type.get_max_function()>4)
107 loco.ext_func = true;
108 locos[addr+1].protocol = NONE;
111 unsigned char data[2];
113 data[1] = (addr>>8)&0xFF;
114 command(CMD_LOK_STATUS, addr, data, 2);
120 void Intellibox::remove_loco(unsigned addr)
125 void Intellibox::set_loco_speed(unsigned addr, unsigned speed)
127 Locomotive &loco = locos[addr];
128 if(loco.protocol==NONE)
131 if(speed==loco.speed)
133 if(loco.pending_half_step)
135 loco.pending_half_step = 0;
136 loco.half_step_delay = Time::TimeStamp();
137 signal_loco_speed.emit(addr, speed, loco.reverse);
144 if(loco.protocol==MM_27)
149 if(speed>loco.speed && !(speed&1))
151 loco.pending_half_step = -1;
154 else if(speed<loco.speed && (speed&1))
156 loco.pending_half_step = 1;
160 loco.pending_half_step = 0;
161 loco.half_step_delay = Time::TimeStamp();
163 loco_command(addr, (speed+1)/2, loco.reverse, loco.funcs, false);
165 else if(loco.protocol==MM)
170 loco_command(addr, speed, loco.reverse, loco.funcs, false);
175 void Intellibox::set_loco_reverse(unsigned addr, bool rev)
177 Locomotive &loco = locos[addr];
178 if(loco.protocol==NONE || rev==loco.reverse)
183 loco_command(addr, 0, rev, loco.funcs, false);
186 void Intellibox::set_loco_function(unsigned addr, unsigned func, bool state)
188 Locomotive &loco = locos[addr];
189 if(loco.protocol==NONE)
193 loco.funcs |= 1<<func;
195 loco.funcs &= ~(1<<func);
197 loco_command(addr, loco.speed, loco.reverse, loco.funcs, true);
198 else if(loco.ext_func && func<=8)
199 loco_command(addr+1, 0, false, (loco.funcs>>4)&0x1E, true);
200 signal_loco_function.emit(addr, func, state);
203 unsigned Intellibox::add_turnout(unsigned addr, const TrackType &type)
205 return add_turnout(addr, type.get_state_bits(), false);
208 void Intellibox::remove_turnout(unsigned addr)
210 turnouts.erase(addr);
213 unsigned Intellibox::add_turnout(unsigned addr, unsigned bits, bool signal)
215 if(!turnouts.count(addr))
217 Turnout &turnout = turnouts[addr];
219 turnout.signal = signal;
221 unsigned char data[2];
223 data[1] = (addr>>8)&0xFF;
224 command(CMD_TURNOUT_STATUS, addr, data, 2);
225 for(unsigned i=1; i<turnout.bits; ++i)
227 turnouts[addr+i].bits = 0;
232 command(CMD_TURNOUT_STATUS, addr+i, data, 2);
239 void Intellibox::turnout_state_changed(unsigned addr, const Turnout &turnout) const
242 signal_signal.emit(addr, turnout.state);
244 signal_turnout.emit(addr, turnout.state);
247 void Intellibox::set_turnout(unsigned addr, unsigned state)
249 Turnout &turnout = turnouts[addr];
250 unsigned mask = (1<<turnout.bits)-1;
251 if(((state^turnout.state)&mask)==0 || ((state^turnout.pending)&mask)==0 || !turnout.synced)
253 turnout.state = state;
254 turnout.pending = state;
255 turnout_state_changed(addr, turnout);
259 turnout.state = (turnout.state&mask) | (state&~mask);
260 turnout.pending = state;
261 turnout.active = true;
262 turnout.off_timeout = Time::TimeStamp();
264 for(unsigned i=0; i<turnout.bits; ++i)
265 if((state^turnout.state)&(1<<i))
266 turnout_command(addr+i, !(state&(1<<i)), true);
269 unsigned Intellibox::get_turnout(unsigned addr) const
271 map<unsigned, Turnout>::const_iterator i = turnouts.find(addr);
272 if(i!=turnouts.end())
273 return i->second.state;
277 unsigned Intellibox::add_signal(unsigned addr, const SignalType &)
279 return add_turnout(addr, 1, true);
282 void Intellibox::remove_signal(unsigned addr)
284 remove_turnout(addr);
287 void Intellibox::set_signal(unsigned addr, unsigned state)
289 set_turnout(addr, state);
292 unsigned Intellibox::get_signal(unsigned addr) const
294 return get_turnout(addr);
297 unsigned Intellibox::add_sensor(unsigned addr)
299 if(!sensors.count(addr))
302 update_sensors = true;
308 void Intellibox::remove_sensor(unsigned addr)
311 update_sensors = true;
314 bool Intellibox::get_sensor(unsigned addr) const
316 map<unsigned, Sensor>::const_iterator i = sensors.find(addr);
318 return i->second.state;
322 float Intellibox::get_telemetry_value(const string &name) const
324 throw key_error(name);
327 void Intellibox::tick()
329 const Time::TimeStamp t = Time::now();
331 if(t>next_event_query)
333 next_event_query = t+200*Time::msec;
337 for(map<unsigned, Locomotive>::iterator i=locos.begin(); i!=locos.end(); ++i)
338 if(i->second.protocol==MM_27 && i->second.pending_half_step && i->second.half_step_delay && t>i->second.half_step_delay)
340 i->second.speed += i->second.pending_half_step;
341 i->second.pending_half_step = 0;
342 i->second.half_step_delay = Time::TimeStamp();
343 loco_command(i->first, (i->second.speed+1)/2, i->second.reverse, i->second.funcs, false);
346 for(map<unsigned, Turnout>::iterator i=turnouts.begin(); i!=turnouts.end(); ++i)
347 if(i->second.active && i->second.off_timeout && t>i->second.off_timeout)
349 i->second.active = false;
350 i->second.off_timeout = Time::TimeStamp();
351 for(unsigned j=0; j<i->second.bits; ++j)
352 turnout_command(i->first+j, !(i->second.state&(1<<j)), false);
355 for(map<unsigned, Sensor>::iterator i=sensors.begin(); i!=sensors.end(); ++i)
356 if(i->second.off_timeout && t>i->second.off_timeout)
358 i->second.state = false;
359 i->second.off_timeout = Time::TimeStamp();
360 signal_sensor.emit(i->first, false);
365 unsigned max_addr = (--sensors.end())->first;
366 unsigned char data[2];
368 data[1] = (max_addr+7)/8;
369 command(CMD_SENSOR_PARAM_SET, data, 2);
370 command(CMD_SENSOR_REPORT);
371 update_sensors = false;
374 if(!queue.empty() && command_sent)
376 if(IO::poll(serial, IO::P_INPUT, Time::zero))
379 queue.erase(queue.begin());
380 command_sent = false;
388 const CommandSlot &slot = queue.front();
389 serial.write(reinterpret_cast<const char *>(slot.data), slot.length);
394 void Intellibox::flush()
396 for(list<CommandSlot>::iterator i=queue.begin(); i!=queue.end(); ++i)
398 serial.write(reinterpret_cast<const char *>(i->data), i->length);
400 while(first ? IO::poll(serial, IO::P_INPUT) : IO::poll(serial, IO::P_INPUT, Time::zero))
403 serial.read(data, 16);
409 command_sent = false;
412 Intellibox::Protocol Intellibox::map_protocol(const string &name) const
416 else if(name=="MM-27")
419 throw invalid_argument("Intellibox::map_protocol");
422 void Intellibox::command(Command cmd)
427 void Intellibox::command(Command cmd, const unsigned char *data, unsigned len)
429 command(cmd, 0, data, len);
432 void Intellibox::command(Command cmd, unsigned addr, const unsigned char *data, unsigned len)
438 copy(data, data+len, slot.data+1);
440 queue.push_back(slot);
443 void Intellibox::loco_command(unsigned addr, unsigned speed, bool rev, unsigned funcs, bool setf)
445 unsigned char data[4];
447 data[1] = (addr>>8)&0xFF;
454 data[2] = (speed*19-18)/2;
456 data[3] = (rev ? 0 : 0x20) | ((funcs&1) ? 0x10 : 0);
459 data[3] |= 0x80 | ((funcs>>1)&0xF);
461 command(CMD_LOK, addr, data, 4);
464 void Intellibox::turnout_command(unsigned addr, bool state, bool active)
466 unsigned char data[2];
468 data[1] = ((addr>>8)&0x7) | (active ? 0x40 : 0) | (state ? 0x80 : 0);
469 command(CMD_TURNOUT, addr, data, 2);
472 void Intellibox::process_reply(const Time::TimeStamp &t)
474 Command cmd = queue.front().cmd;
478 unsigned char status;
479 read_all(&status, 1);
481 signal_power.emit(power);
483 else if(cmd==CMD_EVENT)
485 for(unsigned i=0;; ++i)
493 command(CMD_EVENT_LOK);
495 command(CMD_EVENT_TURNOUT);
497 command(CMD_EVENT_SENSOR);
509 else if(cmd==CMD_EVENT_LOK)
513 unsigned char data[5];
520 else if(cmd==CMD_EVENT_TURNOUT)
524 for(unsigned i=0; i<count; ++i)
526 unsigned char data[2];
529 unsigned addr = data[0]+((data[1]&7)<<8);
531 for(; !turnouts[addr].bits; --addr, mask<<=1) ;
532 Turnout &turnout = turnouts[addr];
534 unsigned bit = !(data[1]&0x80);
535 turnout.state = (turnout.state&~mask) | (bit*mask);
536 turnout.pending = turnout.state;
537 turnout_state_changed(addr,turnout);
540 else if(cmd==CMD_EVENT_SENSOR)
549 unsigned char data[2];
551 for(unsigned i=0; i<16; ++i)
553 unsigned addr = mod*16+i-15;
554 bool state = (data[i/8]>>(7-i%8))&1;
556 Sensor &sensor = sensors[addr];
559 sensor.off_timeout = Time::TimeStamp();
562 sensor.state = state;
563 signal_sensor(addr, state);
566 else if(sensor.state)
567 sensor.off_timeout = t+700*Time::msec;
571 else if(cmd==CMD_LOK)
576 if(err==ERR_NO_ERROR)
578 unsigned addr = queue.front().addr;
579 Locomotive &loco = locos[addr];
582 signal_loco_speed.emit(addr, loco.speed+loco.pending_half_step, loco.reverse);
583 if(loco.pending_half_step)
584 loco.half_step_delay = Time::now()+500*Time::msec;
590 else if(cmd==CMD_TURNOUT)
595 unsigned addr = queue.front().addr;
597 for(; !turnouts[addr].bits; --addr, mask<<=1) ;
598 Turnout &turnout = turnouts[addr];
600 if(err==ERR_NO_ERROR)
602 turnout.state = (turnout.state&~mask) | (turnout.pending&mask);
605 if(turnout.state==turnout.pending)
606 turnout_state_changed(addr, turnout);
607 turnout.off_timeout = t+500*Time::msec;
610 else if(err==ERR_NO_I2C_SPACE)
611 queue.push_back(queue.front());
614 turnout.pending = (turnout.pending&~mask) | (turnout.state&mask);
618 else if(cmd==CMD_TURNOUT_STATUS)
623 if(err==ERR_NO_ERROR)
628 unsigned addr = queue.front().addr;
630 for(; !turnouts[addr].bits; --addr, mask<<=1) ;
631 Turnout &turnout = turnouts[addr];
633 bool bit = !(data&0x04);
634 if(bit!=((turnout.state&mask)!=0))
636 turnout.state = (turnout.state&~mask) | (bit*mask);
637 turnout.pending = turnout.state;
638 turnout_state_changed(addr, turnout);
641 turnout.synced = true;
646 else if(cmd==CMD_LOK_STATUS)
651 if(err==ERR_NO_ERROR)
653 unsigned char data[3];
656 unsigned addr = queue.front().addr;
657 Locomotive &loco = locos[addr];
659 unsigned speed = (data[0]<=1 ? 0 : data[0]*2/19+1);
660 bool reverse = !(data[1]&0x20);
661 bool speed_changed = (speed!=loco.speed || reverse!=loco.reverse);
664 loco.reverse = reverse;
666 unsigned funcs = (data[1]&0xF)<<1;
669 unsigned funcs_changed = loco.funcs^funcs;
673 signal_loco_speed.emit(addr, loco.speed, loco.reverse);
674 for(unsigned i=0; i<5; ++i)
675 if(funcs_changed&(1<<i))
676 signal_loco_function.emit(addr, i, loco.funcs&(1<<i));
683 unsigned expected_bytes = 0;
684 if(cmd==CMD_FUNC_STATUS)
686 if(cmd==CMD_TURNOUT_GROUP_STATUS)
688 if(cmd==CMD_LOK_CONFIG)
694 if(err==ERR_NO_ERROR)
696 unsigned char data[8];
697 read_all(data, expected_bytes);
704 unsigned Intellibox::read_all(unsigned char *buf, unsigned len)
708 pos += serial.read(reinterpret_cast<char *>(buf+pos), len-pos);
713 unsigned Intellibox::read_status(Error *err)
716 unsigned ret = read_all(&c, 1);
717 *err = static_cast<Error>(c);
721 void Intellibox::error(Command cmd, Error err)
723 const char *cmd_str = 0;
726 case CMD_LOK: cmd_str = "CMD_LOK"; break;
727 case CMD_LOK_STATUS: cmd_str = "CMD_LOK_STATUS"; break;
728 case CMD_LOK_CONFIG: cmd_str = "CMD_LOK_CONFIG"; break;
729 case CMD_FUNC: cmd_str = "CMD_FUNC"; break;
730 case CMD_FUNC_STATUS: cmd_str = "CMD_FUNC_STATUS"; break;
731 case CMD_TURNOUT: cmd_str = "CMD_TURNOUT"; break;
732 case CMD_TURNOUT_FREE: cmd_str = "CMD_TURNOUT_FREE"; break;
733 case CMD_TURNOUT_STATUS: cmd_str = "CMD_TURNOUT_STATUS"; break;
734 case CMD_TURNOUT_GROUP_STATUS: cmd_str = "CMD_TURNOUT_GROUP_STATUS"; break;
735 case CMD_SENSOR_STATUS: cmd_str = "CMD_SENSOR_STATUS"; break;
736 case CMD_SENSOR_REPORT: cmd_str = "CMD_SENSOR_REPORT"; break;
737 case CMD_SENSOR_PARAM_SET: cmd_str = "CMD_SENSOR_PARAM_SET"; break;
738 case CMD_STATUS: cmd_str = "CMD_STATUS"; break;
739 case CMD_POWER_OFF: cmd_str = "CMD_POWER_OFF"; break;
740 case CMD_POWER_ON: cmd_str = "CMD_POWER_ON"; break;
741 case CMD_NOP: cmd_str = "CMD_NOP"; break;
742 case CMD_EVENT: cmd_str = "CMD_EVENT"; break;
743 case CMD_EVENT_LOK: cmd_str = "CMD_EVENT_LOK"; break;
744 case CMD_EVENT_TURNOUT: cmd_str = "CMD_EVENT_TURNOUT"; break;
745 case CMD_EVENT_SENSOR: cmd_str = "CMD_EVENT_SENSOR"; break;
746 default: cmd_str = "(unknown command)";
749 const char *err_str = 0;
752 case ERR_NO_ERROR: err_str = "ERR_NO_ERROR"; break;
753 case ERR_SYS_ERROR: err_str = "ERR_SYS_ERROR"; break;
754 case ERR_BAD_PARAM: err_str = "ERR_BAD_PARAM"; break;
755 case ERR_POWER_OFF: err_str = "ERR_POWER_OFF"; break;
756 case ERR_NO_LOK_SPACE: err_str = "ERR_NO_LOK_SPACE"; break;
757 case ERR_NO_TURNOUT_SPACE: err_str = "ERR_NO_TURNOUT_SPACE"; break;
758 case ERR_NO_DATA: err_str = "ERR_NO_DATA"; break;
759 case ERR_NO_SLOT: err_str = "ERR_NO_SLOT"; break;
760 case ERR_BAD_LOK_ADDR: err_str = "ERR_BAD_LOK_ADDR"; break;
761 case ERR_LOK_BUSY: err_str = "ERR_LOK_BUSY"; break;
762 case ERR_BAD_TURNOUT_ADDR: err_str = "ERR_BAD_TURNOUT_ADDR"; break;
763 case ERR_BAD_SO_VALUE: err_str = "ERR_BAD_SO_VALUE"; break;
764 case ERR_NO_I2C_SPACE: err_str = "ERR_NO_I2C_SPACE"; break;
765 case ERR_LOW_TURNOUT_SPACE: err_str = "ERR_LOW_TURNOUT_SPACE"; break;
766 case ERR_LOK_HALTED: err_str = "ERR_LOK_HALTED"; break;
767 case ERR_LOK_POWER_OFF: err_str = "ERR_LOK_POWER_OFF"; break;
768 default: cmd_str = "(unknown error)";
771 IO::print("Error: %s: %s\n", cmd_str, err_str);
775 Intellibox::Locomotive::Locomotive():
785 Intellibox::Turnout::Turnout():
795 Intellibox::Sensor::Sensor():