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 Options &opts):
22 serial(opts.get<string>(string(), "ttyUSB0")),
23 debug(opts.get<unsigned>("debug")),
24 state_file("arducontrol.state"),
28 command_timeout(200*Time::msec),
33 if(FS::exists(state_file))
34 DataFile::load(*this, state_file.str());
36 unsigned max_address = 0;
37 for(MfxInfoArray::const_iterator i=mfx_info.begin(); i!=mfx_info.end(); ++i)
38 max_address = max(max_address, i->address);
39 mfx_search.set_next_address(max_address+1);
42 cmd.command[0] = READ_POWER_STATE;
44 command_queue.push(cmd);
46 cmd.command[0] = MFX_SET_STATION_ID;
52 command_queue.push(cmd);
55 ArduControl::~ArduControl()
60 void ArduControl::set_power(bool p)
64 PendingCommand cmd(POWER);
65 cmd.tag.serial = power.serial;
66 cmd.command[0] = (p ? POWER_ON : POWER_OFF);
68 command_queue.push(cmd);
72 void ArduControl::halt(bool h)
80 for(LocomotiveMap::const_iterator i=locomotives.begin(); i!=locomotives.end(); ++i)
81 set_loco_speed(i->first, 0);
84 signal_halt.emit(halted);
87 const char *ArduControl::enumerate_protocols(unsigned i) const
97 ArduControl::Protocol ArduControl::map_protocol(const string &proto_name)
101 else if(proto_name=="MFX")
104 throw invalid_argument("ArduControl::map_protocol");
107 unsigned ArduControl::get_protocol_speed_steps(const string &proto_name) const
109 return protocol_info[map_protocol(proto_name)].max_speed;
112 const Driver::DetectedLocomotive *ArduControl::enumerate_detected_locos(unsigned i) const
114 if(i>=mfx_info.size())
120 unsigned ArduControl::add_loco(unsigned addr, const string &proto_name, const VehicleType &)
123 throw invalid_argument("ArduControl::add_loco");
125 Protocol proto = map_protocol(proto_name);
126 if(addr>protocol_info[proto].max_address)
127 throw invalid_argument("ArduControl::add_loco");
129 Locomotive loco(proto, addr);
130 insert_unique(locomotives, loco.id, loco);
135 ArduControl::MfxInfoArray::iterator ArduControl::add_mfx_info(const MfxInfo &info)
137 MfxInfoArray::iterator i;
138 for(i=mfx_info.begin(); (i!=mfx_info.end() && i->id!=info.id); ++i) ;
139 if(i==mfx_info.end())
141 mfx_info.push_back(info);
142 i = --mfx_info.end();
149 void ArduControl::remove_loco(unsigned id)
151 Locomotive &loco = get_item(locomotives, id);
152 refresh.remove_loco(loco);
153 locomotives.erase(id);
156 void ArduControl::set_loco_speed(unsigned id, unsigned speed)
158 Locomotive &loco = get_item(locomotives, id);
159 if(speed>protocol_info[loco.proto].max_speed)
160 throw invalid_argument("ArduControl::set_loco_speed");
165 if(loco.speed.set(speed))
167 PendingCommand cmd(loco, Locomotive::SPEED);
168 command_queue.push(cmd);
170 refresh.add_loco(loco);
174 void ArduControl::set_loco_reverse(unsigned id, bool rev)
176 Locomotive &loco = get_item(locomotives, id);
177 if(loco.reverse.set(rev))
179 PendingCommand cmd(loco, Locomotive::REVERSE);
180 command_queue.push(cmd);
182 refresh.add_loco(loco);
186 void ArduControl::set_loco_function(unsigned id, unsigned func, bool state)
188 Locomotive &loco = get_item(locomotives, id);
189 if(func>protocol_info[loco.proto].max_func)
190 throw invalid_argument("ArduControl::set_loco_function");
192 unsigned mask = 1<<func;
193 if(loco.funcs.set((loco.funcs&~mask)|(mask*state)))
195 if(func>0 || loco.proto!=MM)
197 PendingCommand cmd(loco, Locomotive::FUNCTIONS, func);
198 command_queue.push(cmd);
201 refresh.add_loco(loco);
205 unsigned ArduControl::add_turnout(unsigned addr, const TrackType &type)
207 if(!addr || !type.is_turnout())
208 throw invalid_argument("ArduControl::add_turnout");
210 return add_accessory(Accessory::TURNOUT, addr, type.get_state_bits(), type.get_paths());
213 void ArduControl::remove_turnout(unsigned addr)
215 remove_accessory(Accessory::TURNOUT, addr);
218 void ArduControl::set_turnout(unsigned addr, unsigned state)
220 set_accessory(Accessory::TURNOUT, addr, state);
223 unsigned ArduControl::get_turnout(unsigned addr) const
225 return get_accessory(Accessory::TURNOUT, addr);
228 unsigned ArduControl::add_signal(unsigned addr, const SignalType &)
230 return add_accessory(Accessory::SIGNAL, addr, 1, 3);
233 void ArduControl::remove_signal(unsigned addr)
235 remove_accessory(Accessory::SIGNAL, addr);
238 void ArduControl::set_signal(unsigned addr, unsigned state)
240 set_accessory(Accessory::SIGNAL, addr, state);
243 unsigned ArduControl::get_signal(unsigned addr) const
245 return get_accessory(Accessory::SIGNAL, addr);
248 unsigned ArduControl::add_accessory(Accessory::Kind kind, unsigned addr, unsigned bits, unsigned states)
250 AccessoryMap::iterator i = accessories.lower_bound(addr);
251 AccessoryMap::iterator j = accessories.upper_bound(addr+bits-1);
253 throw key_error(addr);
254 if(i!=accessories.begin())
257 if(i->first+i->second.bits>addr)
258 throw key_error(addr);
261 insert_unique(accessories, addr, Accessory(kind, addr, bits, states));
265 void ArduControl::remove_accessory(Accessory::Kind kind, unsigned addr)
267 Accessory &acc = get_item(accessories, addr);
269 throw key_error(addr);
270 accessories.erase(addr);
273 void ArduControl::set_accessory(Accessory::Kind kind, unsigned addr, unsigned state)
275 Accessory &acc = get_item(accessories, addr);
277 throw key_error(addr);
279 if(state!=acc.target || acc.uncertain)
282 accessory_queue.push_back(&acc);
286 unsigned ArduControl::get_accessory(Accessory::Kind kind, unsigned addr) const
288 const Accessory &acc = get_item(accessories, addr);
290 throw key_error(addr);
294 void ArduControl::activate_accessory_by_mask(Accessory &acc, unsigned mask)
296 unsigned bit = mask&~(mask-1);
297 for(active_index=0; (bit>>active_index)>1; ++active_index) ;
298 acc.state.set((acc.state&~bit)|(acc.target&bit));
299 PendingCommand cmd(acc, Accessory::ACTIVATE, active_index);
300 command_queue.push(cmd);
301 active_accessory = &acc;
303 monitor.reset_peak();
306 unsigned ArduControl::add_sensor(unsigned addr)
309 throw invalid_argument("ArduControl::add_sensor");
311 insert_unique(sensors, addr, Sensor(addr));
312 s88.grow_n_octets((addr+7)/8);
317 void ArduControl::remove_sensor(unsigned addr)
319 remove_existing(sensors, addr);
320 // TODO update s88.n_octets
323 bool ArduControl::get_sensor(unsigned addr) const
325 return get_item(sensors, addr).state;
328 void ArduControl::tick()
331 while(completed_commands.pop(tag))
333 if(tag.type==Tag::GENERAL)
335 if(tag.command==POWER)
337 if(power.commit(tag.serial))
338 signal_power.emit(power.current);
340 else if(tag.command==NEW_LOCO)
343 if(mfx_search.pop_info(info))
345 MfxInfoArray::iterator i = add_mfx_info(info);
347 signal_locomotive_detected.emit(*i);
351 else if(tag.type==Tag::LOCOMOTIVE)
353 LocomotiveMap::iterator i = locomotives.find(tag.id);
354 if(i==locomotives.end())
357 Locomotive &loco = i->second;
358 if(tag.command==Locomotive::SPEED)
360 if(loco.speed.commit(tag.serial))
361 signal_loco_speed.emit(loco.id, loco.speed, loco.reverse);
363 else if(tag.command==Locomotive::REVERSE)
365 if(loco.reverse.commit(tag.serial))
366 signal_loco_speed.emit(loco.id, loco.speed, loco.reverse);
368 else if(tag.command==Locomotive::FUNCTIONS)
370 unsigned old = loco.funcs;
371 if(loco.funcs.commit(tag.serial))
373 unsigned changed = old^loco.funcs;
374 for(unsigned j=0; changed>>j; ++j)
376 signal_loco_function.emit(loco.id, j, (loco.funcs>>j)&1);
380 else if(tag.type==Tag::ACCESSORY)
382 AccessoryMap::iterator i = accessories.find(tag.id);
383 if(i==accessories.end())
386 Accessory &acc = i->second;
387 if(tag.command==Accessory::ACTIVATE)
388 off_timeout = Time::now()+acc.active_time;
389 else if(tag.command==Accessory::DEACTIVATE)
391 if(acc.state.commit(tag.serial))
393 if(&acc==active_accessory)
394 active_accessory = 0;
398 else if(tag.type==Tag::SENSOR)
400 SensorMap::iterator i = sensors.find(tag.id);
404 Sensor &sensor = i->second;
405 if(tag.command==Sensor::STATE)
407 if(sensor.state.commit(tag.serial))
408 signal_sensor.emit(sensor.address, sensor.state);
413 while(power && !active_accessory && !accessory_queue.empty())
415 Accessory &acc = *accessory_queue.front();
419 unsigned zeroes = acc.uncertain&~acc.target;
421 activate_accessory_by_mask(acc, zeroes);
423 activate_accessory_by_mask(acc, acc.uncertain);
425 else if(acc.state!=acc.target)
427 unsigned changes = acc.state^acc.target;
428 if(!(changes&((1<<acc.bits)-1)))
430 // All remaining changes are in non-physical bits
431 acc.state.set(acc.state^changes);
432 acc.state.commit(acc.state.serial);
436 unsigned toggle_bit = 0;
437 for(unsigned bit=1; (!toggle_bit && bit<=changes); bit<<=1)
438 if((changes&bit) && (acc.valid_states&(1<<(acc.state^bit))))
441 activate_accessory_by_mask(acc, toggle_bit);
446 accessory_queue.pop_front();
448 if(acc.state==acc.target)
450 if(acc.kind==Accessory::TURNOUT)
451 signal_turnout.emit(acc.address, acc.state);
452 else if(acc.kind==Accessory::SIGNAL)
453 signal_signal.emit(acc.address, acc.state);
458 if(active_accessory && off_timeout)
460 Time::TimeStamp t = Time::now();
463 Accessory &acc = *active_accessory;
465 unsigned bit = 1<<active_index;
467 // Assume success if we were uncertain of the physical setting
468 if(acc.uncertain&bit)
469 acc.uncertain &= ~bit;
470 else if(acc.kind==Accessory::TURNOUT && monitor.get_peak()<0.5f)
472 signal_turnout_failed.emit(acc.address);
473 acc.state.rollback();
474 if(acc.valid_states&(1<<(acc.target^bit)))
477 acc.target = acc.state;
480 off_timeout = Time::TimeStamp();
481 PendingCommand cmd(acc, Accessory::DEACTIVATE, active_index);
482 command_queue.push(cmd);
487 void ArduControl::flush()
489 while(!command_queue.empty() || (power && !accessory_queue.empty()))
493 void ArduControl::save_state() const
495 FS::RedirectedPath tmp_file(state_file);
496 IO::BufferedFile out(tmp_file.str(), IO::M_WRITE);
497 DataFile::Writer writer(out);
499 writer.write((DataFile::Statement("mfx_announce_serial"), mfx_announce.get_serial()));
500 for(MfxInfoArray::const_iterator i=mfx_info.begin(); i!=mfx_info.end(); ++i)
502 DataFile::Statement st("mfx_locomotive");
504 st.sub.push_back((DataFile::Statement("address"), i->address));
505 st.sub.push_back((DataFile::Statement("name"), i->name));
511 ArduControl::Tag::Tag():
519 ArduControl::Locomotive::Locomotive(Protocol p, unsigned a):
529 unsigned ArduControl::Locomotive::create_speed_dir_command(char *buffer) const
533 buffer[0] = MOTOROLA_SPEED_DIRECTION;
535 buffer[2] = funcs.pending&1;
536 buffer[3] = speed.pending+reverse.pending*0x80;
541 buffer[0] = MFX_SPEED;
542 buffer[1] = address>>8;
544 buffer[3] = speed.pending+reverse.pending*0x80;
551 unsigned ArduControl::Locomotive::create_speed_func_command(unsigned f, char *buffer) const
556 throw invalid_argument("Locomotive::create_speed_func_command");
558 buffer[0] = MOTOROLA_SPEED_FUNCTION;
560 buffer[2] = (f<<4)|(((funcs.pending>>f)&1)<<1)|(funcs.pending&1);
561 buffer[3] = speed.pending;
566 bool f16 = (funcs.pending>0xFF);
567 buffer[0] = (f16 ? MFX_SPEED_FUNCS16 : MFX_SPEED_FUNCS8);
568 buffer[1] = address>>8;
570 buffer[3] = speed.pending+reverse.pending*0x80;
573 buffer[4] = funcs.pending>>8;
574 buffer[5] = funcs.pending;
579 buffer[4] = funcs.pending;
588 ArduControl::Accessory::Accessory(Kind k, unsigned a, unsigned b, unsigned s):
594 uncertain((1<<bits)-1),
596 active_time(500*Time::msec)
599 unsigned ArduControl::Accessory::create_state_command(unsigned b, bool c, char *buffer) const
602 throw invalid_argument("Accessory::create_state_command");
604 unsigned a = (address+b+3)*2;
605 if(!((state.pending>>b)&1))
607 buffer[0] = MOTOROLA_SOLENOID;
609 buffer[2] = ((a&7)<<4)|c;
614 ArduControl::Sensor::Sensor(unsigned a):
620 ArduControl::PendingCommand::PendingCommand():
625 ArduControl::PendingCommand::PendingCommand(GeneralCommand cmd):
629 tag.type = Tag::GENERAL;
633 ArduControl::PendingCommand::PendingCommand(Locomotive &loco, Locomotive::Command cmd, unsigned index):
636 tag.type = Tag::LOCOMOTIVE;
639 if(cmd==Locomotive::SPEED)
641 tag.serial = loco.speed.serial;
642 length = loco.create_speed_dir_command(command);
644 else if(cmd==Locomotive::REVERSE)
646 tag.serial = loco.reverse.serial;
647 length = loco.create_speed_dir_command(command);
649 else if(cmd==Locomotive::FUNCTIONS)
651 tag.serial = loco.funcs.serial;
652 length = loco.create_speed_func_command(index, command);
655 throw invalid_argument("PendingCommand");
658 ArduControl::PendingCommand::PendingCommand(Accessory &acc, Accessory::Command cmd, unsigned index):
661 tag.type = Tag::ACCESSORY;
663 tag.id = acc.address;
664 if(cmd==Accessory::ACTIVATE || cmd==Accessory::DEACTIVATE)
666 tag.serial = acc.state.serial;
667 length = acc.create_state_command(index, (cmd==Accessory::ACTIVATE), command);
670 throw invalid_argument("PendingCommand");
675 void ArduControl::Queue<T>::push(const T &item)
677 MutexLock lock(mutex);
678 items.push_back(item);
682 bool ArduControl::Queue<T>::pop(T &item)
684 MutexLock lock(mutex);
688 item = items.front();
694 bool ArduControl::Queue<T>::empty() const
696 return items.empty();
700 ArduControl::RefreshTask::RefreshTask():
707 bool ArduControl::RefreshTask::get_work(PendingCommand &cmd)
709 if(loco && loco->proto==MM && phase==0)
711 cmd.length = loco->create_speed_func_command(round%4+1, cmd.command);
712 cmd.repeat_count = 2;
717 loco = get_next_loco();
724 cmd.length = loco->create_speed_dir_command(cmd.command);
725 cmd.repeat_count = 2;
727 else if(loco->proto==MFX)
728 cmd.length = loco->create_speed_func_command(0, cmd.command);
735 void ArduControl::RefreshTask::add_loco(Locomotive &l)
737 MutexLock lock(mutex);
741 LocomotivePtrList::iterator oldest = cycle.begin();
742 for(LocomotivePtrList::iterator i=cycle.begin(); ++i!=cycle.end(); )
743 if((*i)->last_change_age>(*oldest)->last_change_age)
749 if(next==cycle.end())
750 next = cycle.begin();
753 void ArduControl::RefreshTask::remove_loco(Locomotive &l)
755 MutexLock lock(mutex);
756 for(LocomotivePtrList::iterator i=cycle.begin(); i!=cycle.end(); ++i)
771 ArduControl::Locomotive *ArduControl::RefreshTask::get_next_loco()
773 MutexLock lock(mutex);
777 Locomotive *l = *next;
782 void ArduControl::RefreshTask::advance()
785 if(next==cycle.end())
793 ArduControl::S88Task::S88Task(ArduControl &c):
800 bool ArduControl::S88Task::get_work(PendingCommand &cmd)
807 if(octets_remaining || !n_octets)
810 octets_remaining = n_octets;
811 cmd.command[0] = S88_READ;
812 cmd.command[1] = octets_remaining;
820 void ArduControl::S88Task::process_reply(const char *reply, unsigned length)
822 unsigned char type = reply[0];
823 if(type==S88_DATA && length>2)
825 unsigned offset = static_cast<unsigned char>(reply[1]);
826 unsigned count = length-2;
828 SensorMap::iterator begin = control.sensors.lower_bound(offset*8+1);
829 SensorMap::iterator end = control.sensors.upper_bound((offset+count)*8);
830 for(SensorMap::iterator i=begin; i!=end; ++i)
832 unsigned bit_index = i->first-1-offset*8;
833 bool state = (reply[2+bit_index/8]>>(7-bit_index%8))&1;
834 i->second.state.set(state);
837 tag.type = Tag::SENSOR;
838 tag.command = Sensor::STATE;
839 tag.serial = i->second.state.serial;
841 control.completed_commands.push(tag);
844 if(count>octets_remaining)
845 octets_remaining = 0;
847 octets_remaining -= count;
851 void ArduControl::S88Task::set_n_octets(unsigned n)
856 void ArduControl::S88Task::grow_n_octets(unsigned n)
863 ArduControl::MfxAnnounceTask::MfxAnnounceTask():
867 bool ArduControl::MfxAnnounceTask::get_work(PendingCommand &cmd)
869 Time::TimeStamp t = Time::now();
873 cmd.command[0] = MFX_ANNOUNCE;
874 cmd.command[1] = serial>>8;
875 cmd.command[2] = serial;
877 next = t+400*Time::msec;
882 void ArduControl::MfxAnnounceTask::set_serial(unsigned s)
888 ArduControl::MfxSearchTask::MfxSearchTask(ArduControl &c):
896 bool ArduControl::MfxSearchTask::get_work(PendingCommand &cmd)
901 IO::print("Assigning MFX address %d to decoder %08X\n", next_address, bits);
904 info.protocol = "MFX";
905 info.address = next_address;
906 info.name = format("%08X", bits);
910 cmd.command[0] = MFX_ASSIGN_ADDRESS;
911 cmd.command[1] = next_address>>8;
912 cmd.command[2] = next_address;
913 for(unsigned i=0; i<4; ++i)
914 cmd.command[3+i] = bits>>(24-i*8);
917 cmd.tag.type = Tag::GENERAL;
918 cmd.tag.command = NEW_LOCO;
928 Time::TimeStamp t = Time::now();
932 cmd.command[0] = MFX_SEARCH;
933 for(unsigned i=0; i<4; ++i)
934 cmd.command[1+i] = bits>>(24-i*8);
935 cmd.command[5] = size;
938 next = t+200*Time::msec;
941 IO::print("Search %08X/%d\n", bits, size);
946 void ArduControl::MfxSearchTask::process_reply(const char *reply, unsigned length)
948 unsigned char type = reply[0];
949 if(type==MFX_SEARCH_FEEDBACK && length==2)
956 else if(size>0 && misses<6)
959 bits ^= 1<<(32-size);
963 next = Time::now()+2*Time::sec;
971 void ArduControl::MfxSearchTask::set_next_address(unsigned a)
976 bool ArduControl::MfxSearchTask::pop_info(MfxInfo &info)
978 return queue.pop(info);
982 ArduControl::MonitorTask::MonitorTask():
990 bool ArduControl::MonitorTask::get_work(PendingCommand &cmd)
992 Time::TimeStamp t = Time::now();
997 cmd.command[0] = READ_INPUT_VOLTAGE;
999 cmd.command[0] = READ_TRACK_CURRENT;
1002 next_poll = t+200*Time::msec;
1003 next_type = (next_type+1)%5;
1008 void ArduControl::MonitorTask::process_reply(const char *reply, unsigned length)
1010 unsigned char type = reply[0];
1011 if(type==INPUT_VOLTAGE && length==3)
1012 voltage = ((static_cast<unsigned char>(reply[1])<<8) | static_cast<unsigned char>(reply[2]))/1000.0f;
1013 else if(type==TRACK_CURRENT && length==5)
1015 current = ((static_cast<unsigned char>(reply[1])<<8) | static_cast<unsigned char>(reply[2]))/1000.0f;
1016 float peak = ((static_cast<unsigned char>(reply[3])<<8) | static_cast<unsigned char>(reply[4]))/1000.0f;
1017 peak_level = max(peak_level, peak);
1018 base_level = min(base_level, current);
1022 void ArduControl::MonitorTask::reset_peak()
1024 base_level = current;
1025 peak_level = current;
1029 ArduControl::ControlThread::ControlThread(ArduControl &c):
1033 tasks.push_back(&control.monitor);
1034 tasks.push_back(&control.mfx_announce);
1035 tasks.push_back(&control.mfx_search);
1036 tasks.push_back(&control.s88);
1037 tasks.push_back(&control.refresh);
1042 void ArduControl::ControlThread::exit()
1048 void ArduControl::ControlThread::main()
1057 bool success = true;
1058 bool resync = false;
1059 for(unsigned i=0; (success && i<cmd.repeat_count); ++i)
1061 unsigned result = do_command(cmd, control.command_timeout);
1062 success = (result==COMMAND_OK);
1063 resync = (result==0);
1066 if(success && cmd.tag)
1067 control.completed_commands.push(cmd.tag);
1071 if(control.debug>=1)
1072 IO::print("Synchronization with ArduControl lost, attempting to recover\n");
1073 for(unsigned i=0; (resync && i<16); ++i)
1075 control.serial.put('\xFF');
1076 while(IO::poll(control.serial, IO::P_INPUT, control.command_timeout))
1077 resync = (control.serial.get()!=0xFF);
1081 if(control.debug>=1)
1082 IO::print("Resynchronization failed, giving up\n");
1087 if(control.debug>=1)
1088 IO::print("Resynchronization successful\n");
1090 control.command_queue.push(cmd);
1095 Time::sleep(10*Time::msec);
1099 void ArduControl::ControlThread::init_baud_rate()
1101 static unsigned rates[] = { 57600, 9600, 19200, 38400, 0 };
1103 control.serial.set_data_bits(8);
1104 control.serial.set_parity(IO::Serial::NONE);
1105 control.serial.set_stop_bits(1);
1106 for(unsigned i=0; rates[i]; ++i)
1108 control.serial.set_baud_rate(rates[i]);
1109 control.serial.put('\xFF');
1110 if(IO::poll(control.serial, IO::P_INPUT, 500*Time::msec))
1112 int c = control.serial.get();
1123 if(control.debug>=1)
1124 IO::print("ArduControl detection failed\n");
1129 if(control.debug>=1)
1130 IO::print("ArduControl detected at %d bits/s\n", rate);
1135 cmd.command[0] = SET_BAUD_RATE;
1136 cmd.command[1] = rates[0]>>8;
1137 cmd.command[2] = rates[0];
1139 if(do_command(cmd, Time::sec)==COMMAND_OK)
1141 control.serial.set_baud_rate(rates[0]);
1142 Time::sleep(Time::sec);
1143 if(do_command(cmd, Time::sec)==COMMAND_OK)
1145 if(control.debug>=1)
1146 IO::print("Rate changed to %d bits/s\n", rates[0]);
1152 bool ArduControl::ControlThread::get_work(PendingCommand &cmd)
1154 if(control.command_queue.pop(cmd))
1157 for(vector<Task *>::iterator i=tasks.begin(); i!=tasks.end(); ++i)
1158 if((*i)->get_work(cmd))
1161 // As fallback, send an idle packet for the MM protocol
1162 cmd.command[0] = MOTOROLA_SPEED;
1163 cmd.command[1] = 80;
1171 unsigned ArduControl::ControlThread::do_command(const PendingCommand &cmd, const Time::TimeDelta &timeout)
1173 if(control.debug>=2)
1176 for(unsigned i=0; i<cmd.length; ++i)
1177 cmd_hex += format(" %02X", static_cast<unsigned char>(cmd.command[i]));
1178 IO::print("< %02X%s\n", cmd.length^0xFF, cmd_hex);
1181 control.serial.put(cmd.length^0xFF);
1182 control.serial.write(cmd.command, cmd.length);
1184 unsigned result = 0;
1189 got_data = IO::poll(control.serial, IO::P_INPUT, Time::zero);
1191 got_data = IO::poll(control.serial, IO::P_INPUT, timeout);
1196 unsigned rlength = control.serial.get()^0xFF;
1199 IO::print("Invalid length %02X\n", rlength);
1207 if(!IO::poll(control.serial, IO::P_INPUT, timeout))
1209 pos += control.serial.read(reply+pos, rlength-pos);
1212 if(control.debug>=2)
1215 for(unsigned i=0; i<rlength; ++i)
1216 reply_hex += format(" %02X", static_cast<unsigned char>(reply[i]));
1217 IO::print("> %02X%s\n", rlength^0xFF, reply_hex);
1220 unsigned r = process_reply(reply, rlength);
1228 unsigned ArduControl::ControlThread::process_reply(const char *reply, unsigned rlength)
1230 unsigned char type = reply[0];
1231 if((type&0xE0)==0x80)
1233 if(type!=COMMAND_OK)
1234 IO::print("Error %02X\n", type);
1237 else if(type==POWER_STATE && rlength==2)
1238 set_power(reply[1]);
1239 else if(type==OVERCURRENT)
1242 IO::print("Overcurrent detected!\n");
1246 for(vector<Task *>::iterator i=tasks.begin(); i!=tasks.end(); ++i)
1247 (*i)->process_reply(reply, rlength);
1253 void ArduControl::ControlThread::set_power(bool p)
1255 control.power.set(p);
1258 tag.type = Tag::GENERAL;
1259 tag.command = POWER;
1260 tag.serial = control.power.serial;
1261 control.completed_commands.push(tag);
1265 ArduControl::Loader::Loader(ArduControl &c):
1266 DataFile::ObjectLoader<ArduControl>(c)
1268 add("mfx_announce_serial", &Loader::mfx_announce_serial);
1269 add("mfx_locomotive", &Loader::mfx_locomotive);
1272 void ArduControl::Loader::mfx_announce_serial(unsigned s)
1274 obj.mfx_announce.set_serial(s);
1277 void ArduControl::Loader::mfx_locomotive(unsigned id)
1281 info.protocol = "MFX";
1283 obj.add_mfx_info(info);
1287 ArduControl::MfxInfo::Loader::Loader(MfxInfo &i):
1288 DataFile::ObjectLoader<MfxInfo>(i)
1290 add("address", static_cast<unsigned MfxInfo::*>(&MfxInfo::address));
1291 add("name", static_cast<string MfxInfo::*>(&MfxInfo::name));