1 #include <msp/core/maputils.h>
2 #include <msp/io/print.h>
3 #include <msp/time/utils.h>
4 #include "arducontrol.h"
12 ArduControl::ProtocolInfo ArduControl::protocol_info[2] =
15 { 0x3FFF, 126, 15 } // MFX
18 ArduControl::ArduControl(const string &dev):
28 cmd.command[0] = READ_POWER_STATE;
32 cmd.command[0] = MFX_SET_STATION_ID;
41 ArduControl::~ArduControl()
46 void ArduControl::set_power(bool p)
50 PendingCommand cmd(POWER);
51 cmd.tag.serial = power.serial;
52 cmd.command[0] = (p ? POWER_ON : POWER_OFF);
58 void ArduControl::halt(bool)
62 const char *ArduControl::enumerate_protocols(unsigned i) const
72 ArduControl::Protocol ArduControl::map_protocol(const string &proto_name)
76 else if(proto_name=="MFX")
79 throw invalid_argument("ArduControl::map_protocol");
82 unsigned ArduControl::get_protocol_speed_steps(const string &proto_name) const
84 return protocol_info[map_protocol(proto_name)].max_speed;
87 const Driver::DetectedLocomotive *ArduControl::enumerate_detected_locos(unsigned i) const
89 if(i>=mfx_info.size())
95 unsigned ArduControl::add_loco(unsigned addr, const string &proto_name, const VehicleType &)
98 throw invalid_argument("ArduControl::add_loco");
100 Protocol proto = map_protocol(proto_name);
101 if(addr>protocol_info[proto].max_address)
102 throw invalid_argument("ArduControl::add_loco");
104 Locomotive loco(proto, addr);
105 insert_unique(locomotives, loco.id, loco);
110 void ArduControl::remove_loco(unsigned id)
112 Locomotive &loco = get_item(locomotives, id);
113 refresh.remove_loco(loco);
114 locomotives.erase(id);
117 void ArduControl::set_loco_speed(unsigned id, unsigned speed)
119 Locomotive &loco = get_item(locomotives, id);
120 if(speed>protocol_info[loco.proto].max_speed)
121 throw invalid_argument("ArduControl::set_loco_speed");
123 if(loco.speed.set(speed))
125 PendingCommand cmd(loco, Locomotive::SPEED);
128 refresh.add_loco(loco);
132 void ArduControl::set_loco_reverse(unsigned id, bool rev)
134 Locomotive &loco = get_item(locomotives, id);
135 if(loco.reverse.set(rev))
137 PendingCommand cmd(loco, Locomotive::REVERSE);
140 refresh.add_loco(loco);
144 void ArduControl::set_loco_function(unsigned id, unsigned func, bool state)
146 Locomotive &loco = get_item(locomotives, id);
147 if(func>protocol_info[loco.proto].max_func)
148 throw invalid_argument("ArduControl::set_loco_function");
150 unsigned mask = 1<<func;
151 if(loco.funcs.set((loco.funcs&~mask)|(mask*state)))
153 if(func>0 || loco.proto!=MM)
155 PendingCommand cmd(loco, Locomotive::FUNCTIONS, func);
159 refresh.add_loco(loco);
163 unsigned ArduControl::add_turnout(unsigned addr, const TrackType &type)
165 if(!addr || !type.is_turnout())
166 throw invalid_argument("ArduControl::add_turnout");
168 return add_accessory(Accessory::TURNOUT, addr, type.get_state_bits());
171 void ArduControl::remove_turnout(unsigned addr)
173 remove_accessory(Accessory::TURNOUT, addr);
176 void ArduControl::set_turnout(unsigned addr, unsigned state)
178 set_accessory(Accessory::TURNOUT, addr, state);
181 unsigned ArduControl::get_turnout(unsigned addr) const
183 return get_accessory(Accessory::TURNOUT, addr);
186 unsigned ArduControl::add_signal(unsigned addr, const SignalType &)
188 return add_accessory(Accessory::SIGNAL, addr, 1);
191 void ArduControl::remove_signal(unsigned addr)
193 remove_accessory(Accessory::SIGNAL, addr);
196 void ArduControl::set_signal(unsigned addr, unsigned state)
198 set_accessory(Accessory::SIGNAL, addr, state);
201 unsigned ArduControl::get_signal(unsigned addr) const
203 return get_accessory(Accessory::SIGNAL, addr);
206 unsigned ArduControl::add_accessory(Accessory::Kind kind, unsigned addr, unsigned bits)
208 AccessoryMap::iterator i = accessories.lower_bound(addr);
209 AccessoryMap::iterator j = accessories.upper_bound(addr+bits-1);
211 throw key_error(addr);
212 if(i!=accessories.begin())
215 if(i->first+i->second.bits>addr)
216 throw key_error(addr);
219 insert_unique(accessories, addr, Accessory(kind, addr, bits));
223 void ArduControl::remove_accessory(Accessory::Kind kind, unsigned addr)
225 Accessory &acc = get_item(accessories, addr);
227 throw key_error(addr);
228 accessories.erase(addr);
231 void ArduControl::set_accessory(Accessory::Kind kind, unsigned addr, unsigned state)
233 Accessory &acc = get_item(accessories, addr);
235 throw key_error(addr);
237 if(state!=acc.target)
240 accessory_queue.push_back(&acc);
244 unsigned ArduControl::get_accessory(Accessory::Kind kind, unsigned addr) const
246 const Accessory &acc = get_item(accessories, addr);
248 throw key_error(addr);
252 unsigned ArduControl::add_sensor(unsigned addr)
255 throw invalid_argument("ArduControl::add_sensor");
257 insert_unique(sensors, addr, Sensor(addr));
258 s88.grow_n_octets((addr+7)/8);
263 void ArduControl::remove_sensor(unsigned addr)
265 remove_existing(sensors, addr);
266 // TODO update s88.n_octets
269 bool ArduControl::get_sensor(unsigned addr) const
271 return get_item(sensors, addr).state;
274 void ArduControl::tick()
276 while(Tag tag = pop_completed_tag())
278 if(tag.type==Tag::GENERAL)
280 if(tag.command==POWER)
282 if(power.commit(tag.serial))
283 signal_power.emit(power.current);
285 else if(tag.command==NEW_LOCO)
288 if(mfx_search.pop_info(info))
290 MfxInfoArray::iterator i;
291 for(i=mfx_info.begin(); (i!=mfx_info.end() && i->id!=info.id); ++i) ;
292 if(i==mfx_info.end())
294 mfx_info.push_back(info);
295 i = --mfx_info.end();
299 signal_locomotive_detected.emit(*i);
303 else if(tag.type==Tag::LOCOMOTIVE)
305 LocomotiveMap::iterator i = locomotives.find(tag.id);
306 if(i==locomotives.end())
309 Locomotive &loco = i->second;
310 if(tag.command==Locomotive::SPEED)
312 if(loco.speed.commit(tag.serial))
313 signal_loco_speed.emit(loco.id, loco.speed, loco.reverse);
315 else if(tag.command==Locomotive::REVERSE)
317 if(loco.reverse.commit(tag.serial))
318 signal_loco_speed.emit(loco.id, loco.speed, loco.reverse);
320 else if(tag.command==Locomotive::FUNCTIONS)
322 unsigned old = loco.funcs;
323 if(loco.funcs.commit(tag.serial))
325 unsigned changed = old^loco.funcs;
326 for(unsigned j=0; changed>>j; ++j)
328 signal_loco_function.emit(loco.id, j, (loco.funcs>>j)&1);
332 else if(tag.type==Tag::ACCESSORY)
334 AccessoryMap::iterator i = accessories.find(tag.id);
335 if(i==accessories.end())
338 Accessory &acc = i->second;
339 if(tag.command==Accessory::ACTIVATE)
341 off_timeout = Time::now()+acc.active_time;
343 else if(tag.command==Accessory::DEACTIVATE)
345 if(acc.state.commit(tag.serial))
347 if(acc.state==acc.target)
349 if(acc.kind==Accessory::TURNOUT)
350 signal_turnout.emit(acc.address, acc.state);
351 else if(acc.kind==Accessory::SIGNAL)
352 signal_signal.emit(acc.address, acc.state);
354 if(&acc==active_accessory)
355 active_accessory = 0;
359 else if(tag.type==Tag::SENSOR)
361 SensorMap::iterator i = sensors.find(tag.id);
365 Sensor &sensor = i->second;
366 if(tag.command==Sensor::STATE)
368 if(sensor.state.commit(tag.serial))
369 signal_sensor.emit(sensor.address, sensor.state);
374 while(!active_accessory && !accessory_queue.empty())
376 Accessory &acc = *accessory_queue.front();
378 if(acc.state!=acc.target)
380 active_accessory = &acc;
382 unsigned changes = acc.state^acc.target;
383 unsigned lowest_bit = changes&~(changes-1);
385 for(i=0; (lowest_bit>>i)>1; ++i) ;
386 acc.state.set(acc.state^lowest_bit);
387 PendingCommand cmd(acc, Accessory::ACTIVATE, i);
391 accessory_queue.pop_front();
394 if(active_accessory && off_timeout)
396 Time::TimeStamp t = Time::now();
399 off_timeout = Time::TimeStamp();
400 PendingCommand cmd(*active_accessory, Accessory::DEACTIVATE);
406 void ArduControl::flush()
410 void ArduControl::push_command(const PendingCommand &cmd)
412 MutexLock lock(mutex);
413 command_queue.push_back(cmd);
416 bool ArduControl::pop_command(PendingCommand &cmd)
418 MutexLock lock(mutex);
419 if(command_queue.empty())
421 cmd = command_queue.front();
422 command_queue.pop_front();
426 void ArduControl::push_completed_tag(const Tag &tag)
428 MutexLock lock(mutex);
429 completed_commands.push_back(tag);
432 ArduControl::Tag ArduControl::pop_completed_tag()
434 MutexLock lock(mutex);
435 if(completed_commands.empty())
437 Tag tag = completed_commands.front();
438 completed_commands.pop_front();
443 ArduControl::Tag::Tag():
451 ArduControl::Locomotive::Locomotive(Protocol p, unsigned a):
461 unsigned ArduControl::Locomotive::create_speed_dir_command(char *buffer) const
465 buffer[0] = MOTOROLA_SPEED_DIRECTION;
467 buffer[2] = funcs.pending&1;
468 buffer[3] = speed.pending+reverse.pending*0x80;
473 buffer[0] = MFX_SPEED;
474 buffer[1] = address>>8;
476 buffer[3] = speed.pending+reverse.pending*0x80;
483 unsigned ArduControl::Locomotive::create_speed_func_command(unsigned f, char *buffer) const
488 throw invalid_argument("Locomotive::create_speed_func_command");
490 buffer[0] = MOTOROLA_SPEED_FUNCTION;
492 buffer[2] = (f<<4)|(((funcs.pending>>f)&1)<<1)|(funcs.pending&1);
493 buffer[3] = speed.pending;
498 bool f16 = (funcs.pending>0xFF);
499 buffer[0] = (f16 ? MFX_SPEED_FUNCS16 : MFX_SPEED_FUNCS8);
500 buffer[1] = address>>8;
502 buffer[3] = speed.pending+reverse.pending*0x80;
505 buffer[4] = funcs.pending>>8;
506 buffer[5] = funcs.pending;
511 buffer[4] = funcs.pending;
520 ArduControl::Accessory::Accessory(Kind k, unsigned a, unsigned b):
525 active_time(500*Time::msec)
528 unsigned ArduControl::Accessory::create_state_command(unsigned b, bool c, char *buffer) const
531 throw invalid_argument("Accessory::create_state_command");
533 unsigned a = (address+b+3)*2;
534 if(!((state.pending>>b)&1))
536 buffer[0] = MOTOROLA_SOLENOID;
538 buffer[2] = ((a&7)<<4)|c;
543 ArduControl::Sensor::Sensor(unsigned a):
549 ArduControl::PendingCommand::PendingCommand():
554 ArduControl::PendingCommand::PendingCommand(GeneralCommand cmd):
558 tag.type = Tag::GENERAL;
562 ArduControl::PendingCommand::PendingCommand(Locomotive &loco, Locomotive::Command cmd, unsigned index):
565 tag.type = Tag::LOCOMOTIVE;
568 if(cmd==Locomotive::SPEED)
570 tag.serial = loco.speed.serial;
571 length = loco.create_speed_dir_command(command);
573 else if(cmd==Locomotive::REVERSE)
575 tag.serial = loco.reverse.serial;
576 length = loco.create_speed_dir_command(command);
578 else if(cmd==Locomotive::FUNCTIONS)
580 tag.serial = loco.funcs.serial;
581 length = loco.create_speed_func_command(index, command);
584 throw invalid_argument("PendingCommand");
587 ArduControl::PendingCommand::PendingCommand(Accessory &acc, Accessory::Command cmd, unsigned index):
590 tag.type = Tag::ACCESSORY;
592 tag.id = acc.address;
593 if(cmd==Accessory::ACTIVATE || cmd==Accessory::DEACTIVATE)
595 tag.serial = acc.state.serial;
596 length = acc.create_state_command(index, (cmd==Accessory::ACTIVATE), command);
599 throw invalid_argument("PendingCommand");
603 ArduControl::RefreshTask::RefreshTask():
610 bool ArduControl::RefreshTask::get_work(PendingCommand &cmd)
612 if(loco && loco->proto==MM && phase==0)
614 cmd.length = loco->create_speed_func_command(round%4+1, cmd.command);
615 cmd.repeat_count = 2;
620 loco = get_next_loco();
627 cmd.length = loco->create_speed_dir_command(cmd.command);
628 cmd.repeat_count = 2;
630 else if(loco->proto==MFX)
631 cmd.length = loco->create_speed_func_command(0, cmd.command);
638 void ArduControl::RefreshTask::add_loco(Locomotive &l)
640 MutexLock lock(mutex);
644 LocomotivePtrList::iterator oldest = cycle.begin();
645 for(LocomotivePtrList::iterator i=cycle.begin(); ++i!=cycle.end(); )
646 if((*i)->last_change_age>(*oldest)->last_change_age)
652 if(next==cycle.end())
653 next = cycle.begin();
656 void ArduControl::RefreshTask::remove_loco(Locomotive &l)
658 MutexLock lock(mutex);
659 for(LocomotivePtrList::iterator i=cycle.begin(); i!=cycle.end(); ++i)
674 ArduControl::Locomotive *ArduControl::RefreshTask::get_next_loco()
676 MutexLock lock(mutex);
680 Locomotive *l = *next;
685 void ArduControl::RefreshTask::advance()
688 if(next==cycle.end())
696 ArduControl::S88Task::S88Task(ArduControl &c):
702 bool ArduControl::S88Task::get_work(PendingCommand &cmd)
704 if(octets_remaining || !n_octets)
707 octets_remaining = n_octets;
708 cmd.command[0] = S88_READ;
709 cmd.command[1] = octets_remaining;
715 void ArduControl::S88Task::process_reply(const char *reply, unsigned length)
717 unsigned char type = reply[0];
718 if(type==S88_DATA && length>2)
720 unsigned offset = static_cast<unsigned char>(reply[1]);
721 unsigned count = length-2;
723 SensorMap::iterator begin = control.sensors.lower_bound(offset*8+1);
724 SensorMap::iterator end = control.sensors.upper_bound((offset+count)*8);
725 for(SensorMap::iterator i=begin; i!=end; ++i)
727 unsigned bit_index = i->first-1-offset*8;
728 bool state = (reply[2+bit_index/8]>>(7-bit_index%8))&1;
729 i->second.state.set(state);
732 tag.type = Tag::SENSOR;
733 tag.command = Sensor::STATE;
734 tag.serial = i->second.state.serial;
736 control.push_completed_tag(tag);
739 if(count>octets_remaining)
740 octets_remaining = 0;
742 octets_remaining -= count;
746 void ArduControl::S88Task::set_n_octets(unsigned n)
751 void ArduControl::S88Task::grow_n_octets(unsigned n)
758 ArduControl::MfxAnnounceTask::MfxAnnounceTask():
762 bool ArduControl::MfxAnnounceTask::get_work(PendingCommand &cmd)
764 Time::TimeStamp t = Time::now();
768 cmd.command[0] = MFX_ANNOUNCE;
769 cmd.command[1] = serial>>8;
770 cmd.command[2] = serial;
772 next = t+400*Time::msec;
777 void ArduControl::MfxAnnounceTask::set_serial(unsigned s)
783 ArduControl::MfxSearchTask::MfxSearchTask(ArduControl &c):
791 bool ArduControl::MfxSearchTask::get_work(PendingCommand &cmd)
796 IO::print("Assigning MFX address %d to decoder %08X\n", next_address, bits);
799 info.protocol = "MFX";
800 info.address = next_address;
801 info.name = format("%08X", bits);
805 cmd.command[0] = MFX_ASSIGN_ADDRESS;
806 cmd.command[1] = next_address>>8;
807 cmd.command[2] = next_address;
808 for(unsigned i=0; i<4; ++i)
809 cmd.command[3+i] = bits>>(24-i*8);
812 cmd.tag.type = Tag::GENERAL;
813 cmd.tag.command = NEW_LOCO;
823 Time::TimeStamp t = Time::now();
827 cmd.command[0] = MFX_SEARCH;
828 for(unsigned i=0; i<4; ++i)
829 cmd.command[1+i] = bits>>(24-i*8);
830 cmd.command[5] = size;
833 next = t+200*Time::msec;
836 IO::print("Search %08X/%d\n", bits, size);
841 void ArduControl::MfxSearchTask::process_reply(const char *reply, unsigned length)
843 unsigned char type = reply[0];
844 if(type==MFX_SEARCH_FEEDBACK && length==2)
851 else if(size>0 && misses<6)
854 bits ^= 1<<(32-size);
858 next = Time::now()+2*Time::sec;
866 void ArduControl::MfxSearchTask::push_info(const MfxInfo &info)
868 MutexLock lock(mutex);
869 queue.push_back(info);
872 bool ArduControl::MfxSearchTask::pop_info(MfxInfo &info)
874 MutexLock lock(mutex);
883 ArduControl::ControlThread::ControlThread(ArduControl &c):
887 tasks.push_back(&control.mfx_announce);
888 tasks.push_back(&control.mfx_search);
889 tasks.push_back(&control.s88);
890 tasks.push_back(&control.refresh);
895 void ArduControl::ControlThread::exit()
901 void ArduControl::ControlThread::main()
911 for(unsigned i=0; (success && i<cmd.repeat_count); ++i)
912 success = (do_command(cmd)==COMMAND_OK);
913 if(success && cmd.tag)
914 control.push_completed_tag(cmd.tag);
917 Time::sleep(10*Time::msec);
921 void ArduControl::ControlThread::init_baud_rate()
923 static unsigned rates[] = { 57600, 9600, 19200, 38400, 0 };
925 control.serial.set_data_bits(8);
926 control.serial.set_parity(IO::Serial::NONE);
927 control.serial.set_stop_bits(1);
928 for(unsigned i=0; rates[i]; ++i)
930 control.serial.set_baud_rate(rates[i]);
931 control.serial.put('\xFF');
932 if(IO::poll(control.serial, IO::P_INPUT, 500*Time::msec))
934 int c = control.serial.get();
944 IO::print("ArduControl detected at %d bits/s\n", rate);
949 cmd.command[0] = SET_BAUD_RATE;
950 cmd.command[1] = rates[0]>>8;
951 cmd.command[2] = rates[0];
953 if(do_command(cmd)==COMMAND_OK)
955 control.serial.set_baud_rate(rates[0]);
956 if(do_command(cmd)==COMMAND_OK)
958 Time::sleep(Time::sec);
960 IO::print("Rate changed to %d bits/s\n", rates[0]);
966 bool ArduControl::ControlThread::get_work(PendingCommand &cmd)
968 if(control.pop_command(cmd))
971 for(vector<Task *>::iterator i=tasks.begin(); i!=tasks.end(); ++i)
972 if((*i)->get_work(cmd))
975 // As fallback, send an idle packet for the MM protocol
976 cmd.command[0] = MOTOROLA_SPEED;
985 unsigned ArduControl::ControlThread::do_command(const PendingCommand &cmd)
990 for(unsigned i=0; i<cmd.length; ++i)
991 cmd_hex += format(" %02X", static_cast<unsigned char>(cmd.command[i]));
992 IO::print("< %02X%s\n", cmd.length^0xFF, cmd_hex);
995 control.serial.put(cmd.length^0xFF);
996 control.serial.write(cmd.command, cmd.length);
1003 got_data = IO::poll(control.serial, IO::P_INPUT, Time::zero);
1005 got_data = IO::poll(control.serial, IO::P_INPUT);
1010 unsigned rlength = control.serial.get()^0xFF;
1013 IO::print("Invalid length %02X\n", rlength);
1020 pos += control.serial.read(reply+pos, rlength-pos);
1022 if(control.debug>=2)
1025 for(unsigned i=0; i<rlength; ++i)
1026 reply_hex += format(" %02X", static_cast<unsigned char>(reply[i]));
1027 IO::print("> %02X%s\n", rlength^0xFF, reply_hex);
1030 unsigned r = process_reply(reply, rlength);
1038 unsigned ArduControl::ControlThread::process_reply(const char *reply, unsigned rlength)
1040 unsigned char type = reply[0];
1041 if((type&0xE0)==0x80)
1043 if(type!=COMMAND_OK)
1044 IO::print("Error %02X\n", type);
1047 else if(type==POWER_STATE && rlength==2)
1049 control.power.set(reply[1]);
1052 tag.type = Tag::GENERAL;
1053 tag.command = POWER;
1054 tag.serial = control.power.serial;
1055 control.push_completed_tag(tag);
1059 for(vector<Task *>::iterator i=tasks.begin(); i!=tasks.end(); ++i)
1060 (*i)->process_reply(reply, rlength);