1 #include <msp/core/maputils.h>
2 #include <msp/datafile/writer.h>
3 #include <msp/fs/redirectedpath.h>
4 #include <msp/fs/stat.h>
5 #include <msp/io/print.h>
6 #include <msp/time/utils.h>
7 #include "arducontrol.h"
15 ArduControl::ProtocolInfo ArduControl::protocol_info[2] =
18 { 0x3FFF, 126, 15 } // MFX
21 ArduControl::ArduControl(const string &dev):
24 state_file("arducontrol.state"),
31 if(FS::exists(state_file))
32 DataFile::load(*this, state_file.str());
34 unsigned max_address = 0;
35 for(MfxInfoArray::const_iterator i=mfx_info.begin(); i!=mfx_info.end(); ++i)
36 max_address = max(max_address, i->address);
37 mfx_search.set_next_address(max_address+1);
40 cmd.command[0] = READ_POWER_STATE;
42 command_queue.push(cmd);
44 cmd.command[0] = MFX_SET_STATION_ID;
50 command_queue.push(cmd);
53 ArduControl::~ArduControl()
58 void ArduControl::set_power(bool p)
62 PendingCommand cmd(POWER);
63 cmd.tag.serial = power.serial;
64 cmd.command[0] = (p ? POWER_ON : POWER_OFF);
66 command_queue.push(cmd);
70 void ArduControl::halt(bool)
74 const char *ArduControl::enumerate_protocols(unsigned i) const
84 ArduControl::Protocol ArduControl::map_protocol(const string &proto_name)
88 else if(proto_name=="MFX")
91 throw invalid_argument("ArduControl::map_protocol");
94 unsigned ArduControl::get_protocol_speed_steps(const string &proto_name) const
96 return protocol_info[map_protocol(proto_name)].max_speed;
99 const Driver::DetectedLocomotive *ArduControl::enumerate_detected_locos(unsigned i) const
101 if(i>=mfx_info.size())
107 unsigned ArduControl::add_loco(unsigned addr, const string &proto_name, const VehicleType &)
110 throw invalid_argument("ArduControl::add_loco");
112 Protocol proto = map_protocol(proto_name);
113 if(addr>protocol_info[proto].max_address)
114 throw invalid_argument("ArduControl::add_loco");
116 Locomotive loco(proto, addr);
117 insert_unique(locomotives, loco.id, loco);
122 ArduControl::MfxInfoArray::iterator ArduControl::add_mfx_info(const MfxInfo &info)
124 MfxInfoArray::iterator i;
125 for(i=mfx_info.begin(); (i!=mfx_info.end() && i->id!=info.id); ++i) ;
126 if(i==mfx_info.end())
128 mfx_info.push_back(info);
129 i = --mfx_info.end();
136 void ArduControl::remove_loco(unsigned id)
138 Locomotive &loco = get_item(locomotives, id);
139 refresh.remove_loco(loco);
140 locomotives.erase(id);
143 void ArduControl::set_loco_speed(unsigned id, unsigned speed)
145 Locomotive &loco = get_item(locomotives, id);
146 if(speed>protocol_info[loco.proto].max_speed)
147 throw invalid_argument("ArduControl::set_loco_speed");
149 if(loco.speed.set(speed))
151 PendingCommand cmd(loco, Locomotive::SPEED);
152 command_queue.push(cmd);
154 refresh.add_loco(loco);
158 void ArduControl::set_loco_reverse(unsigned id, bool rev)
160 Locomotive &loco = get_item(locomotives, id);
161 if(loco.reverse.set(rev))
163 PendingCommand cmd(loco, Locomotive::REVERSE);
164 command_queue.push(cmd);
166 refresh.add_loco(loco);
170 void ArduControl::set_loco_function(unsigned id, unsigned func, bool state)
172 Locomotive &loco = get_item(locomotives, id);
173 if(func>protocol_info[loco.proto].max_func)
174 throw invalid_argument("ArduControl::set_loco_function");
176 unsigned mask = 1<<func;
177 if(loco.funcs.set((loco.funcs&~mask)|(mask*state)))
179 if(func>0 || loco.proto!=MM)
181 PendingCommand cmd(loco, Locomotive::FUNCTIONS, func);
182 command_queue.push(cmd);
185 refresh.add_loco(loco);
189 unsigned ArduControl::add_turnout(unsigned addr, const TrackType &type)
191 if(!addr || !type.is_turnout())
192 throw invalid_argument("ArduControl::add_turnout");
194 return add_accessory(Accessory::TURNOUT, addr, type.get_state_bits());
197 void ArduControl::remove_turnout(unsigned addr)
199 remove_accessory(Accessory::TURNOUT, addr);
202 void ArduControl::set_turnout(unsigned addr, unsigned state)
204 set_accessory(Accessory::TURNOUT, addr, state);
207 unsigned ArduControl::get_turnout(unsigned addr) const
209 return get_accessory(Accessory::TURNOUT, addr);
212 unsigned ArduControl::add_signal(unsigned addr, const SignalType &)
214 return add_accessory(Accessory::SIGNAL, addr, 1);
217 void ArduControl::remove_signal(unsigned addr)
219 remove_accessory(Accessory::SIGNAL, addr);
222 void ArduControl::set_signal(unsigned addr, unsigned state)
224 set_accessory(Accessory::SIGNAL, addr, state);
227 unsigned ArduControl::get_signal(unsigned addr) const
229 return get_accessory(Accessory::SIGNAL, addr);
232 unsigned ArduControl::add_accessory(Accessory::Kind kind, unsigned addr, unsigned bits)
234 AccessoryMap::iterator i = accessories.lower_bound(addr);
235 AccessoryMap::iterator j = accessories.upper_bound(addr+bits-1);
237 throw key_error(addr);
238 if(i!=accessories.begin())
241 if(i->first+i->second.bits>addr)
242 throw key_error(addr);
245 insert_unique(accessories, addr, Accessory(kind, addr, bits));
249 void ArduControl::remove_accessory(Accessory::Kind kind, unsigned addr)
251 Accessory &acc = get_item(accessories, addr);
253 throw key_error(addr);
254 accessories.erase(addr);
257 void ArduControl::set_accessory(Accessory::Kind kind, unsigned addr, unsigned state)
259 Accessory &acc = get_item(accessories, addr);
261 throw key_error(addr);
263 if(state!=acc.target)
266 accessory_queue.push_back(&acc);
270 unsigned ArduControl::get_accessory(Accessory::Kind kind, unsigned addr) const
272 const Accessory &acc = get_item(accessories, addr);
274 throw key_error(addr);
278 unsigned ArduControl::add_sensor(unsigned addr)
281 throw invalid_argument("ArduControl::add_sensor");
283 insert_unique(sensors, addr, Sensor(addr));
284 s88.grow_n_octets((addr+7)/8);
289 void ArduControl::remove_sensor(unsigned addr)
291 remove_existing(sensors, addr);
292 // TODO update s88.n_octets
295 bool ArduControl::get_sensor(unsigned addr) const
297 return get_item(sensors, addr).state;
300 void ArduControl::tick()
303 while(completed_commands.pop(tag))
305 if(tag.type==Tag::GENERAL)
307 if(tag.command==POWER)
309 if(power.commit(tag.serial))
310 signal_power.emit(power.current);
312 else if(tag.command==NEW_LOCO)
315 if(mfx_search.pop_info(info))
317 MfxInfoArray::iterator i = add_mfx_info(info);
319 signal_locomotive_detected.emit(*i);
323 else if(tag.type==Tag::LOCOMOTIVE)
325 LocomotiveMap::iterator i = locomotives.find(tag.id);
326 if(i==locomotives.end())
329 Locomotive &loco = i->second;
330 if(tag.command==Locomotive::SPEED)
332 if(loco.speed.commit(tag.serial))
333 signal_loco_speed.emit(loco.id, loco.speed, loco.reverse);
335 else if(tag.command==Locomotive::REVERSE)
337 if(loco.reverse.commit(tag.serial))
338 signal_loco_speed.emit(loco.id, loco.speed, loco.reverse);
340 else if(tag.command==Locomotive::FUNCTIONS)
342 unsigned old = loco.funcs;
343 if(loco.funcs.commit(tag.serial))
345 unsigned changed = old^loco.funcs;
346 for(unsigned j=0; changed>>j; ++j)
348 signal_loco_function.emit(loco.id, j, (loco.funcs>>j)&1);
352 else if(tag.type==Tag::ACCESSORY)
354 AccessoryMap::iterator i = accessories.find(tag.id);
355 if(i==accessories.end())
358 Accessory &acc = i->second;
359 if(tag.command==Accessory::ACTIVATE)
361 off_timeout = Time::now()+acc.active_time;
363 else if(tag.command==Accessory::DEACTIVATE)
365 if(acc.state.commit(tag.serial))
367 if(acc.state==acc.target)
369 if(acc.kind==Accessory::TURNOUT)
370 signal_turnout.emit(acc.address, acc.state);
371 else if(acc.kind==Accessory::SIGNAL)
372 signal_signal.emit(acc.address, acc.state);
374 if(&acc==active_accessory)
375 active_accessory = 0;
379 else if(tag.type==Tag::SENSOR)
381 SensorMap::iterator i = sensors.find(tag.id);
385 Sensor &sensor = i->second;
386 if(tag.command==Sensor::STATE)
388 if(sensor.state.commit(tag.serial))
389 signal_sensor.emit(sensor.address, sensor.state);
394 while(!active_accessory && !accessory_queue.empty())
396 Accessory &acc = *accessory_queue.front();
398 if(acc.state!=acc.target)
400 active_accessory = &acc;
402 unsigned changes = acc.state^acc.target;
403 unsigned lowest_bit = changes&~(changes-1);
405 for(i=0; (lowest_bit>>i)>1; ++i) ;
406 acc.state.set(acc.state^lowest_bit);
407 PendingCommand cmd(acc, Accessory::ACTIVATE, i);
408 command_queue.push(cmd);
411 accessory_queue.pop_front();
414 if(active_accessory && off_timeout)
416 Time::TimeStamp t = Time::now();
419 off_timeout = Time::TimeStamp();
420 PendingCommand cmd(*active_accessory, Accessory::DEACTIVATE);
421 command_queue.push(cmd);
426 void ArduControl::flush()
430 void ArduControl::save_state() const
432 FS::RedirectedPath tmp_file(state_file);
433 IO::BufferedFile out(tmp_file.str(), IO::M_WRITE);
434 DataFile::Writer writer(out);
436 writer.write((DataFile::Statement("mfx_announce_serial"), mfx_announce.get_serial()));
437 for(MfxInfoArray::const_iterator i=mfx_info.begin(); i!=mfx_info.end(); ++i)
439 DataFile::Statement st("mfx_locomotive");
441 st.sub.push_back((DataFile::Statement("address"), i->address));
442 st.sub.push_back((DataFile::Statement("name"), i->name));
448 ArduControl::Tag::Tag():
456 ArduControl::Locomotive::Locomotive(Protocol p, unsigned a):
466 unsigned ArduControl::Locomotive::create_speed_dir_command(char *buffer) const
470 buffer[0] = MOTOROLA_SPEED_DIRECTION;
472 buffer[2] = funcs.pending&1;
473 buffer[3] = speed.pending+reverse.pending*0x80;
478 buffer[0] = MFX_SPEED;
479 buffer[1] = address>>8;
481 buffer[3] = speed.pending+reverse.pending*0x80;
488 unsigned ArduControl::Locomotive::create_speed_func_command(unsigned f, char *buffer) const
493 throw invalid_argument("Locomotive::create_speed_func_command");
495 buffer[0] = MOTOROLA_SPEED_FUNCTION;
497 buffer[2] = (f<<4)|(((funcs.pending>>f)&1)<<1)|(funcs.pending&1);
498 buffer[3] = speed.pending;
503 bool f16 = (funcs.pending>0xFF);
504 buffer[0] = (f16 ? MFX_SPEED_FUNCS16 : MFX_SPEED_FUNCS8);
505 buffer[1] = address>>8;
507 buffer[3] = speed.pending+reverse.pending*0x80;
510 buffer[4] = funcs.pending>>8;
511 buffer[5] = funcs.pending;
516 buffer[4] = funcs.pending;
525 ArduControl::Accessory::Accessory(Kind k, unsigned a, unsigned b):
530 active_time(500*Time::msec)
533 unsigned ArduControl::Accessory::create_state_command(unsigned b, bool c, char *buffer) const
536 throw invalid_argument("Accessory::create_state_command");
538 unsigned a = (address+b+3)*2;
539 if(!((state.pending>>b)&1))
541 buffer[0] = MOTOROLA_SOLENOID;
543 buffer[2] = ((a&7)<<4)|c;
548 ArduControl::Sensor::Sensor(unsigned a):
554 ArduControl::PendingCommand::PendingCommand():
559 ArduControl::PendingCommand::PendingCommand(GeneralCommand cmd):
563 tag.type = Tag::GENERAL;
567 ArduControl::PendingCommand::PendingCommand(Locomotive &loco, Locomotive::Command cmd, unsigned index):
570 tag.type = Tag::LOCOMOTIVE;
573 if(cmd==Locomotive::SPEED)
575 tag.serial = loco.speed.serial;
576 length = loco.create_speed_dir_command(command);
578 else if(cmd==Locomotive::REVERSE)
580 tag.serial = loco.reverse.serial;
581 length = loco.create_speed_dir_command(command);
583 else if(cmd==Locomotive::FUNCTIONS)
585 tag.serial = loco.funcs.serial;
586 length = loco.create_speed_func_command(index, command);
589 throw invalid_argument("PendingCommand");
592 ArduControl::PendingCommand::PendingCommand(Accessory &acc, Accessory::Command cmd, unsigned index):
595 tag.type = Tag::ACCESSORY;
597 tag.id = acc.address;
598 if(cmd==Accessory::ACTIVATE || cmd==Accessory::DEACTIVATE)
600 tag.serial = acc.state.serial;
601 length = acc.create_state_command(index, (cmd==Accessory::ACTIVATE), command);
604 throw invalid_argument("PendingCommand");
609 void ArduControl::Queue<T>::push(const T &item)
611 MutexLock lock(mutex);
612 items.push_back(item);
616 bool ArduControl::Queue<T>::pop(T &item)
618 MutexLock lock(mutex);
622 item = items.front();
628 ArduControl::RefreshTask::RefreshTask():
635 bool ArduControl::RefreshTask::get_work(PendingCommand &cmd)
637 if(loco && loco->proto==MM && phase==0)
639 cmd.length = loco->create_speed_func_command(round%4+1, cmd.command);
640 cmd.repeat_count = 2;
645 loco = get_next_loco();
652 cmd.length = loco->create_speed_dir_command(cmd.command);
653 cmd.repeat_count = 2;
655 else if(loco->proto==MFX)
656 cmd.length = loco->create_speed_func_command(0, cmd.command);
663 void ArduControl::RefreshTask::add_loco(Locomotive &l)
665 MutexLock lock(mutex);
669 LocomotivePtrList::iterator oldest = cycle.begin();
670 for(LocomotivePtrList::iterator i=cycle.begin(); ++i!=cycle.end(); )
671 if((*i)->last_change_age>(*oldest)->last_change_age)
677 if(next==cycle.end())
678 next = cycle.begin();
681 void ArduControl::RefreshTask::remove_loco(Locomotive &l)
683 MutexLock lock(mutex);
684 for(LocomotivePtrList::iterator i=cycle.begin(); i!=cycle.end(); ++i)
699 ArduControl::Locomotive *ArduControl::RefreshTask::get_next_loco()
701 MutexLock lock(mutex);
705 Locomotive *l = *next;
710 void ArduControl::RefreshTask::advance()
713 if(next==cycle.end())
721 ArduControl::S88Task::S88Task(ArduControl &c):
727 bool ArduControl::S88Task::get_work(PendingCommand &cmd)
729 if(octets_remaining || !n_octets)
732 octets_remaining = n_octets;
733 cmd.command[0] = S88_READ;
734 cmd.command[1] = octets_remaining;
740 void ArduControl::S88Task::process_reply(const char *reply, unsigned length)
742 unsigned char type = reply[0];
743 if(type==S88_DATA && length>2)
745 unsigned offset = static_cast<unsigned char>(reply[1]);
746 unsigned count = length-2;
748 SensorMap::iterator begin = control.sensors.lower_bound(offset*8+1);
749 SensorMap::iterator end = control.sensors.upper_bound((offset+count)*8);
750 for(SensorMap::iterator i=begin; i!=end; ++i)
752 unsigned bit_index = i->first-1-offset*8;
753 bool state = (reply[2+bit_index/8]>>(7-bit_index%8))&1;
754 i->second.state.set(state);
757 tag.type = Tag::SENSOR;
758 tag.command = Sensor::STATE;
759 tag.serial = i->second.state.serial;
761 control.completed_commands.push(tag);
764 if(count>octets_remaining)
765 octets_remaining = 0;
767 octets_remaining -= count;
771 void ArduControl::S88Task::set_n_octets(unsigned n)
776 void ArduControl::S88Task::grow_n_octets(unsigned n)
783 ArduControl::MfxAnnounceTask::MfxAnnounceTask():
787 bool ArduControl::MfxAnnounceTask::get_work(PendingCommand &cmd)
789 Time::TimeStamp t = Time::now();
793 cmd.command[0] = MFX_ANNOUNCE;
794 cmd.command[1] = serial>>8;
795 cmd.command[2] = serial;
797 next = t+400*Time::msec;
802 void ArduControl::MfxAnnounceTask::set_serial(unsigned s)
808 ArduControl::MfxSearchTask::MfxSearchTask(ArduControl &c):
816 bool ArduControl::MfxSearchTask::get_work(PendingCommand &cmd)
821 IO::print("Assigning MFX address %d to decoder %08X\n", next_address, bits);
824 info.protocol = "MFX";
825 info.address = next_address;
826 info.name = format("%08X", bits);
830 cmd.command[0] = MFX_ASSIGN_ADDRESS;
831 cmd.command[1] = next_address>>8;
832 cmd.command[2] = next_address;
833 for(unsigned i=0; i<4; ++i)
834 cmd.command[3+i] = bits>>(24-i*8);
837 cmd.tag.type = Tag::GENERAL;
838 cmd.tag.command = NEW_LOCO;
848 Time::TimeStamp t = Time::now();
852 cmd.command[0] = MFX_SEARCH;
853 for(unsigned i=0; i<4; ++i)
854 cmd.command[1+i] = bits>>(24-i*8);
855 cmd.command[5] = size;
858 next = t+200*Time::msec;
861 IO::print("Search %08X/%d\n", bits, size);
866 void ArduControl::MfxSearchTask::process_reply(const char *reply, unsigned length)
868 unsigned char type = reply[0];
869 if(type==MFX_SEARCH_FEEDBACK && length==2)
876 else if(size>0 && misses<6)
879 bits ^= 1<<(32-size);
883 next = Time::now()+2*Time::sec;
891 void ArduControl::MfxSearchTask::set_next_address(unsigned a)
896 bool ArduControl::MfxSearchTask::pop_info(MfxInfo &info)
898 return queue.pop(info);
902 ArduControl::ControlThread::ControlThread(ArduControl &c):
906 tasks.push_back(&control.mfx_announce);
907 tasks.push_back(&control.mfx_search);
908 tasks.push_back(&control.s88);
909 tasks.push_back(&control.refresh);
914 void ArduControl::ControlThread::exit()
920 void ArduControl::ControlThread::main()
930 for(unsigned i=0; (success && i<cmd.repeat_count); ++i)
931 success = (do_command(cmd)==COMMAND_OK);
932 if(success && cmd.tag)
933 control.completed_commands.push(cmd.tag);
936 Time::sleep(10*Time::msec);
940 void ArduControl::ControlThread::init_baud_rate()
942 static unsigned rates[] = { 57600, 9600, 19200, 38400, 0 };
944 control.serial.set_data_bits(8);
945 control.serial.set_parity(IO::Serial::NONE);
946 control.serial.set_stop_bits(1);
947 for(unsigned i=0; rates[i]; ++i)
949 control.serial.set_baud_rate(rates[i]);
950 control.serial.put('\xFF');
951 if(IO::poll(control.serial, IO::P_INPUT, 500*Time::msec))
953 int c = control.serial.get();
969 IO::print("ArduControl detected at %d bits/s\n", rate);
974 cmd.command[0] = SET_BAUD_RATE;
975 cmd.command[1] = rates[0]>>8;
976 cmd.command[2] = rates[0];
978 if(do_command(cmd)==COMMAND_OK)
980 control.serial.set_baud_rate(rates[0]);
981 Time::sleep(Time::sec);
982 if(do_command(cmd)==COMMAND_OK)
985 IO::print("Rate changed to %d bits/s\n", rates[0]);
991 bool ArduControl::ControlThread::get_work(PendingCommand &cmd)
993 if(control.command_queue.pop(cmd))
996 for(vector<Task *>::iterator i=tasks.begin(); i!=tasks.end(); ++i)
997 if((*i)->get_work(cmd))
1000 // As fallback, send an idle packet for the MM protocol
1001 cmd.command[0] = MOTOROLA_SPEED;
1002 cmd.command[1] = 80;
1010 unsigned ArduControl::ControlThread::do_command(const PendingCommand &cmd)
1012 if(control.debug>=2)
1015 for(unsigned i=0; i<cmd.length; ++i)
1016 cmd_hex += format(" %02X", static_cast<unsigned char>(cmd.command[i]));
1017 IO::print("< %02X%s\n", cmd.length^0xFF, cmd_hex);
1020 control.serial.put(cmd.length^0xFF);
1021 control.serial.write(cmd.command, cmd.length);
1023 unsigned result = 0;
1028 got_data = IO::poll(control.serial, IO::P_INPUT, Time::zero);
1030 got_data = IO::poll(control.serial, IO::P_INPUT);
1035 unsigned rlength = control.serial.get()^0xFF;
1038 IO::print("Invalid length %02X\n", rlength);
1045 pos += control.serial.read(reply+pos, rlength-pos);
1047 if(control.debug>=2)
1050 for(unsigned i=0; i<rlength; ++i)
1051 reply_hex += format(" %02X", static_cast<unsigned char>(reply[i]));
1052 IO::print("> %02X%s\n", rlength^0xFF, reply_hex);
1055 unsigned r = process_reply(reply, rlength);
1063 unsigned ArduControl::ControlThread::process_reply(const char *reply, unsigned rlength)
1065 unsigned char type = reply[0];
1066 if((type&0xE0)==0x80)
1068 if(type!=COMMAND_OK)
1069 IO::print("Error %02X\n", type);
1072 else if(type==POWER_STATE && rlength==2)
1074 control.power.set(reply[1]);
1077 tag.type = Tag::GENERAL;
1078 tag.command = POWER;
1079 tag.serial = control.power.serial;
1080 control.completed_commands.push(tag);
1084 for(vector<Task *>::iterator i=tasks.begin(); i!=tasks.end(); ++i)
1085 (*i)->process_reply(reply, rlength);
1092 ArduControl::Loader::Loader(ArduControl &c):
1093 DataFile::ObjectLoader<ArduControl>(c)
1095 add("mfx_announce_serial", &Loader::mfx_announce_serial);
1096 add("mfx_locomotive", &Loader::mfx_locomotive);
1099 void ArduControl::Loader::mfx_announce_serial(unsigned s)
1101 obj.mfx_announce.set_serial(s);
1104 void ArduControl::Loader::mfx_locomotive(unsigned id)
1108 info.protocol = "MFX";
1110 obj.add_mfx_info(info);
1114 ArduControl::MfxInfo::Loader::Loader(MfxInfo &i):
1115 DataFile::ObjectLoader<MfxInfo>(i)
1117 add("address", static_cast<unsigned MfxInfo::*>(&MfxInfo::address));
1118 add("name", static_cast<string MfxInfo::*>(&MfxInfo::name));