1 #include <msp/core/maputils.h>
2 #include <msp/io/print.h>
3 #include <msp/time/utils.h>
4 #include "arducontrol.h"
12 ArduControl::ArduControl(const string &dev):
16 next_refresh(refresh_cycle.end()),
24 ArduControl::~ArduControl()
29 void ArduControl::set_power(bool p)
37 QueuedCommand cmd(POWER);
38 cmd.tag.serial = power.serial;
39 cmd.command[0] = (p ? POWER_ON : POWER_OFF);
44 void ArduControl::halt(bool)
48 const char *ArduControl::enumerate_protocols(unsigned i) const
56 ArduControl::Protocol ArduControl::map_protocol(const string &proto_name)
61 throw invalid_argument("ArduControl::map_protocol");
64 unsigned ArduControl::get_protocol_speed_steps(const string &proto_name) const
66 Protocol proto = map_protocol(proto_name);
73 unsigned ArduControl::add_loco(unsigned addr, const string &proto_name, const VehicleType &)
76 throw invalid_argument("ArduControl::add_loco");
78 Protocol proto = map_protocol(proto_name);
83 throw invalid_argument("ArduControl::add_loco");
86 Locomotive loco(proto, addr);
87 insert_unique(locomotives, loco.id, loco);
92 void ArduControl::remove_loco(unsigned id)
94 Locomotive &loco = get_item(locomotives, id);
95 remove_loco_from_refresh(loco);
96 locomotives.erase(id);
99 void ArduControl::set_loco_speed(unsigned id, unsigned speed)
101 Locomotive &loco = get_item(locomotives, id);
102 if(loco.speed.set(speed))
104 QueuedCommand cmd(loco, Locomotive::SPEED);
107 add_loco_to_refresh(loco);
111 void ArduControl::set_loco_reverse(unsigned id, bool rev)
113 Locomotive &loco = get_item(locomotives, id);
114 if(loco.reverse.set(rev))
116 QueuedCommand cmd(loco, Locomotive::REVERSE);
119 add_loco_to_refresh(loco);
123 void ArduControl::set_loco_function(unsigned id, unsigned func, bool state)
125 Locomotive &loco = get_item(locomotives, id);
126 unsigned mask = 1<<func;
127 if(loco.funcs.set((loco.funcs&~mask)|(mask*state)))
131 QueuedCommand cmd(loco, Locomotive::FUNCTIONS, func);
135 add_loco_to_refresh(loco);
139 void ArduControl::add_loco_to_refresh(Locomotive &loco)
141 MutexLock lock(mutex);
142 refresh_cycle.push_back(&loco);
143 if(refresh_cycle.size()>15)
145 LocomotivePtrList::iterator oldest = refresh_cycle.begin();
146 for(LocomotivePtrList::iterator i=refresh_cycle.begin(); ++i!=refresh_cycle.end(); )
147 if((*i)->last_change_age>(*oldest)->last_change_age)
149 if(oldest==next_refresh)
150 advance_next_refresh();
151 refresh_cycle.erase(oldest);
153 if(next_refresh==refresh_cycle.end())
154 next_refresh = refresh_cycle.begin();
157 void ArduControl::remove_loco_from_refresh(Locomotive &loco)
159 MutexLock lock(mutex);
160 for(LocomotivePtrList::iterator i=refresh_cycle.begin(); i!=refresh_cycle.end(); ++i)
165 if(refresh_cycle.size()>1)
166 advance_next_refresh();
168 next_refresh = refresh_cycle.end();
170 refresh_cycle.erase(i);
175 ArduControl::Locomotive *ArduControl::get_loco_to_refresh()
177 MutexLock lock(mutex);
178 if(refresh_cycle.empty())
181 Locomotive *loco = *next_refresh;
182 advance_next_refresh();
186 void ArduControl::advance_next_refresh()
189 if(next_refresh==refresh_cycle.end())
191 next_refresh = refresh_cycle.begin();
196 unsigned ArduControl::add_turnout(unsigned addr, const TrackType &type)
198 if(!addr || !type.is_turnout())
199 throw invalid_argument("ArduControl::add_turnout");
201 return add_accessory(Accessory::TURNOUT, addr, type.get_state_bits());
204 void ArduControl::remove_turnout(unsigned addr)
206 remove_accessory(Accessory::TURNOUT, addr);
209 void ArduControl::set_turnout(unsigned addr, unsigned state)
211 set_accessory(Accessory::TURNOUT, addr, state);
214 unsigned ArduControl::get_turnout(unsigned addr) const
216 return get_accessory(Accessory::TURNOUT, addr);
219 unsigned ArduControl::add_signal(unsigned addr, const SignalType &)
221 return add_accessory(Accessory::SIGNAL, addr, 1);
224 void ArduControl::remove_signal(unsigned addr)
226 remove_accessory(Accessory::SIGNAL, addr);
229 void ArduControl::set_signal(unsigned addr, unsigned state)
231 set_accessory(Accessory::SIGNAL, addr, state);
234 unsigned ArduControl::get_signal(unsigned addr) const
236 return get_accessory(Accessory::SIGNAL, addr);
239 unsigned ArduControl::add_accessory(Accessory::Kind kind, unsigned addr, unsigned bits)
241 AccessoryMap::iterator i = accessories.lower_bound(addr);
242 AccessoryMap::iterator j = accessories.upper_bound(addr+bits-1);
244 throw key_error(addr);
245 if(i!=accessories.begin())
248 if(i->first+i->second.bits>addr)
249 throw key_error(addr);
252 insert_unique(accessories, addr, Accessory(kind, addr, bits));
256 void ArduControl::remove_accessory(Accessory::Kind kind, unsigned addr)
258 Accessory &acc = get_item(accessories, addr);
260 throw key_error(addr);
261 accessories.erase(addr);
264 void ArduControl::set_accessory(Accessory::Kind kind, unsigned addr, unsigned state)
266 Accessory &acc = get_item(accessories, addr);
268 throw key_error(addr);
270 if(state!=acc.target)
273 accessory_queue.push_back(&acc);
277 unsigned ArduControl::get_accessory(Accessory::Kind kind, unsigned addr) const
279 const Accessory &acc = get_item(accessories, addr);
281 throw key_error(addr);
285 unsigned ArduControl::add_sensor(unsigned addr)
288 throw invalid_argument("ArduControl::add_sensor");
290 insert_unique(sensors, addr, Sensor(addr));
291 unsigned octet_index = (addr-1)/8;
292 if(octet_index>=n_s88_octets)
293 n_s88_octets = octet_index+1;
298 void ArduControl::remove_sensor(unsigned addr)
300 remove_existing(sensors, addr);
301 // TODO update n_s88_octets
304 bool ArduControl::get_sensor(unsigned addr) const
306 return get_item(sensors, addr).state;
309 void ArduControl::tick()
311 while(Tag tag = pop_completed_tag())
313 if(tag.type==Tag::GENERAL)
315 if(tag.command==POWER)
317 if(power.commit(tag.serial))
318 signal_power.emit(power.current);
321 else if(tag.type==Tag::LOCOMOTIVE)
323 LocomotiveMap::iterator i = locomotives.find(tag.id);
324 if(i==locomotives.end())
327 Locomotive &loco = i->second;
328 if(tag.command==Locomotive::SPEED)
330 if(loco.speed.commit(tag.serial))
331 signal_loco_speed.emit(loco.id, loco.speed, loco.reverse);
333 else if(tag.command==Locomotive::REVERSE)
335 if(loco.reverse.commit(tag.serial))
336 signal_loco_speed.emit(loco.id, loco.speed, loco.reverse);
338 else if(tag.command==Locomotive::FUNCTIONS)
340 unsigned old = loco.funcs;
341 if(loco.funcs.commit(tag.serial))
343 unsigned changed = old^loco.funcs;
344 for(unsigned j=0; changed>>j; ++j)
346 signal_loco_function.emit(loco.id, j, (loco.funcs>>j)&1);
350 else if(tag.type==Tag::ACCESSORY)
352 AccessoryMap::iterator i = accessories.find(tag.id);
353 if(i==accessories.end())
356 Accessory &acc = i->second;
357 if(tag.command==Accessory::ACTIVATE)
359 off_timeout = Time::now()+acc.active_time;
361 else if(tag.command==Accessory::DEACTIVATE)
363 if(acc.state.commit(tag.serial))
365 if(acc.state==acc.target)
367 if(acc.kind==Accessory::TURNOUT)
368 signal_turnout.emit(acc.address, acc.state);
369 else if(acc.kind==Accessory::SIGNAL)
370 signal_signal.emit(acc.address, acc.state);
372 if(&acc==active_accessory)
373 active_accessory = 0;
377 else if(tag.type==Tag::SENSOR)
379 SensorMap::iterator i = sensors.find(tag.id);
383 Sensor &sensor = i->second;
384 if(tag.command==Sensor::STATE)
386 if(sensor.state.commit(tag.serial))
387 signal_sensor.emit(sensor.address, sensor.state);
392 while(!active_accessory && !accessory_queue.empty())
394 Accessory &acc = *accessory_queue.front();
396 if(acc.state!=acc.target)
398 active_accessory = &acc;
400 unsigned changes = acc.state^acc.target;
401 unsigned lowest_bit = changes&~(changes-1);
403 for(i=0; (lowest_bit>>i)>1; ++i) ;
404 acc.state.set(acc.state^lowest_bit);
405 QueuedCommand cmd(acc, Accessory::ACTIVATE, i);
409 accessory_queue.pop_front();
412 if(active_accessory && off_timeout)
414 Time::TimeStamp t = Time::now();
417 off_timeout = Time::TimeStamp();
418 QueuedCommand cmd(*active_accessory, Accessory::DEACTIVATE);
424 void ArduControl::flush()
428 void ArduControl::push_command(const QueuedCommand &cmd)
430 MutexLock lock(mutex);
431 command_queue.push_back(cmd);
434 bool ArduControl::pop_command(QueuedCommand &cmd)
436 MutexLock lock(mutex);
437 if(command_queue.empty())
439 cmd = command_queue.front();
440 command_queue.pop_front();
444 void ArduControl::push_completed_tag(const Tag &tag)
446 MutexLock lock(mutex);
447 completed_commands.push_back(tag);
450 ArduControl::Tag ArduControl::pop_completed_tag()
452 MutexLock lock(mutex);
453 if(completed_commands.empty())
455 Tag tag = completed_commands.front();
456 completed_commands.pop_front();
461 ArduControl::Tag::Tag():
469 ArduControl::Locomotive::Locomotive(Protocol p, unsigned a):
479 unsigned ArduControl::Locomotive::create_speed_dir_command(char *buffer) const
483 buffer[0] = MOTOROLA_SPEED_DIRECTION;
485 buffer[2] = funcs.pending&1;
486 buffer[3] = speed.pending+reverse.pending*0x80;
493 unsigned ArduControl::Locomotive::create_speed_func_command(unsigned f, char *buffer) const
498 throw invalid_argument("Locomotive::create_speed_func_command");
500 buffer[0] = MOTOROLA_SPEED_FUNCTION;
502 buffer[2] = (f<<4)|(((funcs.pending>>f)&1)<<1)|(funcs.pending&1);
503 buffer[3] = speed.pending;
511 ArduControl::Accessory::Accessory(Kind k, unsigned a, unsigned b):
516 active_time(500*Time::msec)
519 unsigned ArduControl::Accessory::create_state_command(unsigned b, bool c, char *buffer) const
522 throw invalid_argument("Accessory::create_state_command");
524 unsigned a = (address+b+3)*2;
525 if(!((state.pending>>b)&1))
527 buffer[0] = MOTOROLA_SOLENOID;
529 buffer[2] = ((a&7)<<4)|c;
534 ArduControl::Sensor::Sensor(unsigned a):
540 ArduControl::ControlThread::ControlThread(ArduControl &c):
547 void ArduControl::ControlThread::exit()
553 void ArduControl::ControlThread::main()
557 unsigned repeat_count = 0;
559 Locomotive *loco = 0;
561 unsigned s88_octets_remaining = 0;
570 if(control.pop_command(qcmd))
572 length = qcmd.length;
573 copy(qcmd.command, qcmd.command+length, command);
574 if(qcmd.tag.type==Tag::LOCOMOTIVE)
578 else if(loco && phase==0)
580 length = loco->create_speed_func_command(control.refresh_counter%4+1, command);
584 else if(!s88_octets_remaining && control.n_s88_octets)
586 command[0] = S88_READ;
587 command[1] = control.n_s88_octets;
589 s88_octets_remaining = control.n_s88_octets;
591 else if((loco = control.get_loco_to_refresh()))
593 length = loco->create_speed_dir_command(command);
599 // Send an idle packet for the MM protocol
600 command[0] = MOTOROLA_SPEED;
611 for(unsigned i=0; i<length; ++i)
612 cmd_hex += format(" %02X", static_cast<unsigned char>(command[i]));
613 IO::print("< %02X%s\n", length^0xFF, cmd_hex);
616 control.serial.put(length^0xFF);
617 control.serial.write(command, length);
620 bool got_reply = false;
621 bool got_data = false;
622 while(!got_reply || got_data)
625 got_data = IO::poll(control.serial, IO::P_INPUT, Time::zero);
627 got_data = IO::poll(control.serial, IO::P_INPUT);
631 unsigned rlength = control.serial.get()^0xFF;
634 IO::print("Invalid length %02X\n", rlength);
641 pos += control.serial.read(reply+pos, rlength-pos);
646 for(unsigned i=0; i<rlength; ++i)
647 reply_hex += format(" %02X", static_cast<unsigned char>(reply[i]));
648 IO::print("> %02X%s\n", rlength^0xFF, reply_hex);
651 unsigned char type = reply[0];
652 if((type&0xE0)==0x80)
656 IO::print("Error %02X\n", type);
657 else if(tag && !repeat_count)
658 control.push_completed_tag(tag);
660 else if(type==S88_DATA && rlength>2)
662 unsigned offset = static_cast<unsigned char>(reply[1]);
663 unsigned count = rlength-2;
665 SensorMap::iterator begin = control.sensors.lower_bound(offset*8+1);
666 SensorMap::iterator end = control.sensors.upper_bound((offset+count)*8);
667 for(SensorMap::iterator i=begin; i!=end; ++i)
669 unsigned bit_index = i->first-1-offset*8;
670 bool state = (reply[2+bit_index/8]>>(7-bit_index%8))&1;
671 i->second.state.set(state);
674 stag.type = Tag::SENSOR;
675 stag.command = Sensor::STATE;
676 stag.serial = i->second.state.serial;
678 control.push_completed_tag(stag);
681 if(count>s88_octets_remaining)
682 s88_octets_remaining = 0;
684 s88_octets_remaining -= count;
692 ArduControl::QueuedCommand::QueuedCommand():
696 ArduControl::QueuedCommand::QueuedCommand(GeneralCommand cmd):
699 tag.type = Tag::GENERAL;
703 ArduControl::QueuedCommand::QueuedCommand(Locomotive &loco, Locomotive::Command cmd, unsigned index)
705 tag.type = Tag::LOCOMOTIVE;
708 if(cmd==Locomotive::SPEED)
710 tag.serial = loco.speed.serial;
711 length = loco.create_speed_dir_command(command);
713 else if(cmd==Locomotive::REVERSE)
715 tag.serial = loco.reverse.serial;
716 length = loco.create_speed_dir_command(command);
718 else if(cmd==Locomotive::FUNCTIONS)
720 tag.serial = loco.funcs.serial;
721 length = loco.create_speed_func_command(index, command);
724 throw invalid_argument("QueuedCommand");
727 ArduControl::QueuedCommand::QueuedCommand(Accessory &acc, Accessory::Command cmd, unsigned index)
729 tag.type = Tag::ACCESSORY;
731 tag.id = acc.address;
732 if(cmd==Accessory::ACTIVATE || cmd==Accessory::DEACTIVATE)
734 tag.serial = acc.state.serial;
735 length = acc.create_state_command(index, (cmd==Accessory::ACTIVATE), command);
738 throw invalid_argument("QueuedCommand");