3 This file is part of the MSP Märklin suite
4 Copyright © 2010 Mikkosoft Productions, Mikko Rasa
5 Distributed under the GPL
11 #include <msp/io/print.h>
12 #include <msp/time/units.h>
13 #include <msp/time/utils.h>
14 #include "intellibox.h"
21 Intellibox::Intellibox(const string &dev):
24 update_sensors(false),
27 serial_fd = ::open(dev.c_str(), O_RDWR);
29 throw Exception("Couldn't open serial port\n");
31 static unsigned baud[]=
41 tcgetattr(serial_fd, &attr);
43 attr.c_cflag |= CSTOPB;
47 for(unsigned i=0; baud[i]; i+=2)
49 cfsetospeed(&attr, baud[i+1]);
50 tcsetattr(serial_fd, TCSADRAIN, &attr);
52 write(serial_fd, "\xC4", 1);
54 pollfd pfd = { serial_fd, POLLIN, 0 };
55 if(poll(&pfd, 1, 500)>0)
57 IO::print("IB detected at %d bits/s\n", baud[i]);
59 p50 = (read(serial_fd, buf, 2)==2);
66 throw Exception("IB not detected");
69 write(serial_fd, "xZzA1\r", 6);
74 void Intellibox::set_power(bool p)
78 command(CMD_POWER_ON);
80 command(CMD_POWER_OFF);
81 signal_power.emit(power);
84 void Intellibox::halt(bool h)
89 for(map<unsigned, Locomotive>::iterator i=locos.begin(); i!=locos.end(); ++i)
91 set_loco_speed(i->first, 0);
94 signal_halt.emit(halted);
97 void Intellibox::add_loco(unsigned addr)
99 if(!locos.count(addr))
103 unsigned char data[2];
105 data[1] = (addr>>8)&0xFF;
106 command(CMD_LOK_STATUS, addr, data, 2);
110 void Intellibox::set_loco_speed(unsigned addr, unsigned speed)
112 Locomotive &loco = locos[addr];
113 if(speed==loco.speed)
119 loco_command(addr, speed, loco.reverse, loco.funcs|0x100);
122 void Intellibox::set_loco_reverse(unsigned addr, bool rev)
124 Locomotive &loco = locos[addr];
125 if(rev==loco.reverse)
129 loco_command(addr, loco.speed, rev, loco.funcs|0x100);
132 void Intellibox::set_loco_function(unsigned addr, unsigned func, bool state)
134 Locomotive &loco = locos[addr];
136 loco.funcs |= 1<<func;
138 loco.funcs &= ~(1<<func);
139 loco_command(addr, loco.speed, loco.reverse, loco.funcs);
140 signal_loco_function.emit(addr, func, state);
143 void Intellibox::add_turnout(unsigned addr)
145 if(!turnouts.count(addr))
149 unsigned char data[2];
151 data[1] = (addr>>8)&0xFF;
152 command(CMD_TURNOUT_STATUS, addr, data, 2);
156 void Intellibox::set_turnout(unsigned addr, bool state)
158 Turnout &turnout = turnouts[addr];
159 if(state==turnout.state || state==turnout.pending)
162 turnout.pending = state;
163 turnout.active = true;
164 turnout.off_timeout = Time::TimeStamp();
166 turnout_command(addr, state, true);
169 bool Intellibox::get_turnout(unsigned addr) const
171 map<unsigned, Turnout>::const_iterator i = turnouts.find(addr);
172 if(i!=turnouts.end())
173 return i->second.state;
177 void Intellibox::add_sensor(unsigned addr)
179 if(!sensors.count(addr))
182 update_sensors = true;
186 bool Intellibox::get_sensor(unsigned addr) const
188 map<unsigned, Sensor>::const_iterator i = sensors.find(addr);
190 return i->second.state;
194 void Intellibox::tick()
196 const Time::TimeStamp t = Time::now();
198 if(t>next_event_query)
200 next_event_query = t+200*Time::msec;
204 for(map<unsigned, Turnout>::iterator i=turnouts.begin(); i!=turnouts.end(); ++i)
205 if(i->second.active && i->second.off_timeout && t>i->second.off_timeout)
207 i->second.active = false;
208 i->second.off_timeout = Time::TimeStamp();
209 turnout_command(i->first, i->second.state, false);
212 for(map<unsigned, Sensor>::iterator i=sensors.begin(); i!=sensors.end(); ++i)
213 if(i->second.off_timeout && t>i->second.off_timeout)
215 i->second.state = false;
216 i->second.off_timeout = Time::TimeStamp();
217 signal_sensor.emit(i->first, false);
222 unsigned max_addr = (--sensors.end())->first;
223 unsigned char data[2];
225 data[1] = (max_addr+7)/8;
226 command(CMD_SENSOR_PARAM_SET, data, 2);
227 command(CMD_SENSOR_REPORT);
228 update_sensors = false;
231 if(!queue.empty() && command_sent)
233 pollfd pfd = { serial_fd, POLLIN, 0 };
234 if(poll(&pfd, 1, 0)>0)
237 queue.erase(queue.begin());
238 command_sent = false;
246 const CommandSlot &slot = queue.front();
247 write(serial_fd, slot.data, slot.length);
252 void Intellibox::flush()
254 Time::TimeStamp t = Time::now();
255 for(list<CommandSlot>::iterator i=queue.begin(); i!=queue.end(); ++i)
257 write(serial_fd, i->data, i->length);
258 pollfd pfd = { serial_fd, POLLIN, 0 };
260 while(poll(&pfd, 1, (first ? -1 : 0))>0)
263 read(serial_fd, data, 16);
269 command_sent = false;
272 void Intellibox::command(Command cmd)
277 void Intellibox::command(Command cmd, const unsigned char *data, unsigned len)
279 command(cmd, 0, data, len);
282 void Intellibox::command(Command cmd, unsigned addr, const unsigned char *data, unsigned len)
288 copy(data, data+len, slot.data+1);
290 queue.push_back(slot);
293 void Intellibox::loco_command(unsigned addr, unsigned speed, bool rev, unsigned funcs)
295 unsigned char data[4];
297 data[1] = (addr>>8)&0xFF;
304 data[2] = (speed*19-18)/2;
306 data[3] = (rev ? 0 : 0x20) | ((funcs&1) ? 0x10 : 0);
309 data[3] |= 0x80 | ((funcs>>1)&0xF);
311 command(CMD_LOK, addr, data, 4);
314 void Intellibox::turnout_command(unsigned addr, bool state, bool active)
316 unsigned char data[2];
318 data[1] = ((addr>>8)&0x7) | (active ? 0x40 : 0) | (state ? 0x80 : 0);
319 command(CMD_TURNOUT, addr, data, 2);
322 void Intellibox::process_reply(const Time::TimeStamp &t)
324 Command cmd = queue.front().cmd;
328 unsigned char status;
329 read_all(&status, 1);
331 signal_power.emit(power);
333 else if(cmd==CMD_EVENT)
335 for(unsigned i=0;; ++i)
343 command(CMD_EVENT_LOK);
345 command(CMD_EVENT_TURNOUT);
347 command(CMD_EVENT_SENSOR);
359 else if(cmd==CMD_EVENT_LOK)
363 unsigned char data[5];
370 else if(cmd==CMD_EVENT_TURNOUT)
374 for(unsigned i=0; i<count; ++i)
376 unsigned char data[2];
379 unsigned addr = data[0]+((data[1]&7)<<8);
380 Turnout &turnout = turnouts[addr];
381 turnout.state = (data[1]&0x80)!=0;
382 turnout.pending = turnout.state;
383 signal_turnout.emit(addr, turnout.state);
386 else if(cmd==CMD_EVENT_SENSOR)
395 unsigned char data[2];
397 for(unsigned i=0; i<16; ++i)
399 unsigned addr = mod*16+i-15;
400 bool state = (data[i/8]>>(7-i%8))&1;
402 Sensor &sensor = sensors[addr];
405 sensor.off_timeout = Time::TimeStamp();
408 sensor.state = state;
409 signal_sensor(addr, state);
412 else if(sensor.state)
413 sensor.off_timeout = t+700*Time::msec;
417 else if(cmd==CMD_LOK)
422 if(err==ERR_NO_ERROR)
424 unsigned addr = queue.front().addr;
425 Locomotive &loco = locos[addr];
426 signal_loco_speed.emit(addr, loco.speed, loco.reverse);
429 else if(cmd==CMD_TURNOUT)
434 unsigned addr = queue.front().addr;
435 Turnout &turnout = turnouts[addr];
437 if(err==ERR_NO_ERROR)
439 turnout.state = turnout.pending;
442 signal_turnout.emit(addr, turnout.state);
443 turnout.off_timeout = t+500*Time::msec;
446 else if(err==ERR_NO_I2C_SPACE)
447 queue.push_back(queue.front());
449 turnout.pending = turnout.state;
451 else if(cmd==CMD_TURNOUT_STATUS)
456 if(err==ERR_NO_ERROR)
461 unsigned addr = queue.front().addr;
462 bool state = data&0x04;
464 Turnout &turnout = turnouts[addr];
465 if(state!=turnout.state)
467 turnout.state = state;
468 turnout.pending = state;
469 signal_turnout.emit(addr, turnout.state);
473 else if(cmd==CMD_LOK_STATUS)
478 if(err==ERR_NO_ERROR)
480 unsigned char data[3];
483 unsigned addr = queue.front().addr;
484 Locomotive &loco = locos[addr];
486 unsigned speed = (data[0]<=1 ? 0 : data[0]*2/19+1);
487 bool reverse = !(data[1]&0x20);
488 if(speed!=loco.speed || reverse!=loco.reverse)
491 loco.reverse = reverse;
492 signal_loco_speed.emit(addr, loco.speed, loco.reverse);
495 unsigned funcs = (data[1]&0xF)<<1;
498 if(funcs!=loco.funcs)
500 unsigned changed = loco.funcs^funcs;
502 for(unsigned i=0; i<5; ++i)
504 signal_loco_function.emit(addr, i, loco.funcs&(1<<i));
510 unsigned expected_bytes = 0;
511 if(cmd==CMD_FUNC_STATUS)
513 if(cmd==CMD_TURNOUT_GROUP_STATUS)
515 if(cmd==CMD_LOK_CONFIG)
521 if(err==ERR_NO_ERROR)
523 unsigned char data[8];
524 read_all(data, expected_bytes);
529 unsigned Intellibox::read_all(unsigned char *buf, unsigned len)
533 pos += read(serial_fd, buf+pos, len-pos);
539 Intellibox::Locomotive::Locomotive():
546 Intellibox::Turnout::Turnout():
553 Intellibox::Sensor::Sensor():
557 } // namespace Marklin