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"),
32 if(FS::exists(state_file))
33 DataFile::load(*this, state_file.str());
35 unsigned max_address = 0;
36 for(MfxInfoArray::const_iterator i=mfx_info.begin(); i!=mfx_info.end(); ++i)
37 max_address = max(max_address, i->address);
38 mfx_search.set_next_address(max_address+1);
41 cmd.command[0] = READ_POWER_STATE;
43 command_queue.push(cmd);
45 cmd.command[0] = MFX_SET_STATION_ID;
51 command_queue.push(cmd);
54 ArduControl::~ArduControl()
59 void ArduControl::set_power(bool p)
63 PendingCommand cmd(POWER);
64 cmd.tag.serial = power.serial;
65 cmd.command[0] = (p ? POWER_ON : POWER_OFF);
67 command_queue.push(cmd);
71 void ArduControl::halt(bool h)
79 for(LocomotiveMap::const_iterator i=locomotives.begin(); i!=locomotives.end(); ++i)
80 set_loco_speed(i->first, 0);
83 signal_halt.emit(halted);
86 const char *ArduControl::enumerate_protocols(unsigned i) const
96 ArduControl::Protocol ArduControl::map_protocol(const string &proto_name)
100 else if(proto_name=="MFX")
103 throw invalid_argument("ArduControl::map_protocol");
106 unsigned ArduControl::get_protocol_speed_steps(const string &proto_name) const
108 return protocol_info[map_protocol(proto_name)].max_speed;
111 const Driver::DetectedLocomotive *ArduControl::enumerate_detected_locos(unsigned i) const
113 if(i>=mfx_info.size())
119 unsigned ArduControl::add_loco(unsigned addr, const string &proto_name, const VehicleType &)
122 throw invalid_argument("ArduControl::add_loco");
124 Protocol proto = map_protocol(proto_name);
125 if(addr>protocol_info[proto].max_address)
126 throw invalid_argument("ArduControl::add_loco");
128 Locomotive loco(proto, addr);
129 insert_unique(locomotives, loco.id, loco);
134 ArduControl::MfxInfoArray::iterator ArduControl::add_mfx_info(const MfxInfo &info)
136 MfxInfoArray::iterator i;
137 for(i=mfx_info.begin(); (i!=mfx_info.end() && i->id!=info.id); ++i) ;
138 if(i==mfx_info.end())
140 mfx_info.push_back(info);
141 i = --mfx_info.end();
148 void ArduControl::remove_loco(unsigned id)
150 Locomotive &loco = get_item(locomotives, id);
151 refresh.remove_loco(loco);
152 locomotives.erase(id);
155 void ArduControl::set_loco_speed(unsigned id, unsigned speed)
157 Locomotive &loco = get_item(locomotives, id);
158 if(speed>protocol_info[loco.proto].max_speed)
159 throw invalid_argument("ArduControl::set_loco_speed");
164 if(loco.speed.set(speed))
166 PendingCommand cmd(loco, Locomotive::SPEED);
167 command_queue.push(cmd);
169 refresh.add_loco(loco);
173 void ArduControl::set_loco_reverse(unsigned id, bool rev)
175 Locomotive &loco = get_item(locomotives, id);
176 if(loco.reverse.set(rev))
178 PendingCommand cmd(loco, Locomotive::REVERSE);
179 command_queue.push(cmd);
181 refresh.add_loco(loco);
185 void ArduControl::set_loco_function(unsigned id, unsigned func, bool state)
187 Locomotive &loco = get_item(locomotives, id);
188 if(func>protocol_info[loco.proto].max_func)
189 throw invalid_argument("ArduControl::set_loco_function");
191 unsigned mask = 1<<func;
192 if(loco.funcs.set((loco.funcs&~mask)|(mask*state)))
194 if(func>0 || loco.proto!=MM)
196 PendingCommand cmd(loco, Locomotive::FUNCTIONS, func);
197 command_queue.push(cmd);
200 refresh.add_loco(loco);
204 unsigned ArduControl::add_turnout(unsigned addr, const TrackType &type)
206 if(!addr || !type.is_turnout())
207 throw invalid_argument("ArduControl::add_turnout");
209 return add_accessory(Accessory::TURNOUT, addr, type.get_state_bits());
212 void ArduControl::remove_turnout(unsigned addr)
214 remove_accessory(Accessory::TURNOUT, addr);
217 void ArduControl::set_turnout(unsigned addr, unsigned state)
219 set_accessory(Accessory::TURNOUT, addr, state);
222 unsigned ArduControl::get_turnout(unsigned addr) const
224 return get_accessory(Accessory::TURNOUT, addr);
227 unsigned ArduControl::add_signal(unsigned addr, const SignalType &)
229 return add_accessory(Accessory::SIGNAL, addr, 1);
232 void ArduControl::remove_signal(unsigned addr)
234 remove_accessory(Accessory::SIGNAL, addr);
237 void ArduControl::set_signal(unsigned addr, unsigned state)
239 set_accessory(Accessory::SIGNAL, addr, state);
242 unsigned ArduControl::get_signal(unsigned addr) const
244 return get_accessory(Accessory::SIGNAL, addr);
247 unsigned ArduControl::add_accessory(Accessory::Kind kind, unsigned addr, unsigned bits)
249 AccessoryMap::iterator i = accessories.lower_bound(addr);
250 AccessoryMap::iterator j = accessories.upper_bound(addr+bits-1);
252 throw key_error(addr);
253 if(i!=accessories.begin())
256 if(i->first+i->second.bits>addr)
257 throw key_error(addr);
260 insert_unique(accessories, addr, Accessory(kind, addr, bits));
264 void ArduControl::remove_accessory(Accessory::Kind kind, unsigned addr)
266 Accessory &acc = get_item(accessories, addr);
268 throw key_error(addr);
269 accessories.erase(addr);
272 void ArduControl::set_accessory(Accessory::Kind kind, unsigned addr, unsigned state)
274 Accessory &acc = get_item(accessories, addr);
276 throw key_error(addr);
278 if(state!=acc.target)
281 accessory_queue.push_back(&acc);
285 unsigned ArduControl::get_accessory(Accessory::Kind kind, unsigned addr) const
287 const Accessory &acc = get_item(accessories, addr);
289 throw key_error(addr);
293 unsigned ArduControl::add_sensor(unsigned addr)
296 throw invalid_argument("ArduControl::add_sensor");
298 insert_unique(sensors, addr, Sensor(addr));
299 s88.grow_n_octets((addr+7)/8);
304 void ArduControl::remove_sensor(unsigned addr)
306 remove_existing(sensors, addr);
307 // TODO update s88.n_octets
310 bool ArduControl::get_sensor(unsigned addr) const
312 return get_item(sensors, addr).state;
315 void ArduControl::tick()
318 while(completed_commands.pop(tag))
320 if(tag.type==Tag::GENERAL)
322 if(tag.command==POWER)
324 if(power.commit(tag.serial))
325 signal_power.emit(power.current);
327 else if(tag.command==NEW_LOCO)
330 if(mfx_search.pop_info(info))
332 MfxInfoArray::iterator i = add_mfx_info(info);
334 signal_locomotive_detected.emit(*i);
338 else if(tag.type==Tag::LOCOMOTIVE)
340 LocomotiveMap::iterator i = locomotives.find(tag.id);
341 if(i==locomotives.end())
344 Locomotive &loco = i->second;
345 if(tag.command==Locomotive::SPEED)
347 if(loco.speed.commit(tag.serial))
348 signal_loco_speed.emit(loco.id, loco.speed, loco.reverse);
350 else if(tag.command==Locomotive::REVERSE)
352 if(loco.reverse.commit(tag.serial))
353 signal_loco_speed.emit(loco.id, loco.speed, loco.reverse);
355 else if(tag.command==Locomotive::FUNCTIONS)
357 unsigned old = loco.funcs;
358 if(loco.funcs.commit(tag.serial))
360 unsigned changed = old^loco.funcs;
361 for(unsigned j=0; changed>>j; ++j)
363 signal_loco_function.emit(loco.id, j, (loco.funcs>>j)&1);
367 else if(tag.type==Tag::ACCESSORY)
369 AccessoryMap::iterator i = accessories.find(tag.id);
370 if(i==accessories.end())
373 Accessory &acc = i->second;
374 if(tag.command==Accessory::ACTIVATE)
375 off_timeout = Time::now()+acc.active_time;
376 else if(tag.command==Accessory::DEACTIVATE)
378 if(acc.state.commit(tag.serial))
380 if(acc.state==acc.target)
382 if(acc.kind==Accessory::TURNOUT)
383 signal_turnout.emit(acc.address, acc.state);
384 else if(acc.kind==Accessory::SIGNAL)
385 signal_signal.emit(acc.address, acc.state);
387 if(&acc==active_accessory)
388 active_accessory = 0;
392 else if(tag.type==Tag::SENSOR)
394 SensorMap::iterator i = sensors.find(tag.id);
398 Sensor &sensor = i->second;
399 if(tag.command==Sensor::STATE)
401 if(sensor.state.commit(tag.serial))
402 signal_sensor.emit(sensor.address, sensor.state);
407 while(power && !active_accessory && !accessory_queue.empty())
409 Accessory &acc = *accessory_queue.front();
411 if(acc.state!=acc.target)
413 active_accessory = &acc;
415 unsigned changes = acc.state^acc.target;
416 unsigned lowest_bit = changes&~(changes-1);
418 for(i=0; (lowest_bit>>i)>1; ++i) ;
420 acc.state.set(acc.state^lowest_bit);
421 PendingCommand cmd(acc, Accessory::ACTIVATE, i);
422 command_queue.push(cmd);
424 monitor.reset_peak();
427 accessory_queue.pop_front();
430 if(active_accessory && off_timeout)
432 Time::TimeStamp t = Time::now();
435 Accessory &acc = *active_accessory;
437 if(acc.kind==Accessory::TURNOUT && monitor.get_peak()<0.5f)
439 unsigned bit = 1<<active_index;
440 if(acc.uncertain&bit)
441 acc.uncertain &= ~bit;
444 signal_turnout_failed.emit(acc.address);
445 acc.state.rollback();
450 off_timeout = Time::TimeStamp();
451 PendingCommand cmd(acc, Accessory::DEACTIVATE, active_index);
452 command_queue.push(cmd);
457 void ArduControl::flush()
459 while(!command_queue.empty() || !accessory_queue.empty())
463 void ArduControl::save_state() const
465 FS::RedirectedPath tmp_file(state_file);
466 IO::BufferedFile out(tmp_file.str(), IO::M_WRITE);
467 DataFile::Writer writer(out);
469 writer.write((DataFile::Statement("mfx_announce_serial"), mfx_announce.get_serial()));
470 for(MfxInfoArray::const_iterator i=mfx_info.begin(); i!=mfx_info.end(); ++i)
472 DataFile::Statement st("mfx_locomotive");
474 st.sub.push_back((DataFile::Statement("address"), i->address));
475 st.sub.push_back((DataFile::Statement("name"), i->name));
481 ArduControl::Tag::Tag():
489 ArduControl::Locomotive::Locomotive(Protocol p, unsigned a):
499 unsigned ArduControl::Locomotive::create_speed_dir_command(char *buffer) const
503 buffer[0] = MOTOROLA_SPEED_DIRECTION;
505 buffer[2] = funcs.pending&1;
506 buffer[3] = speed.pending+reverse.pending*0x80;
511 buffer[0] = MFX_SPEED;
512 buffer[1] = address>>8;
514 buffer[3] = speed.pending+reverse.pending*0x80;
521 unsigned ArduControl::Locomotive::create_speed_func_command(unsigned f, char *buffer) const
526 throw invalid_argument("Locomotive::create_speed_func_command");
528 buffer[0] = MOTOROLA_SPEED_FUNCTION;
530 buffer[2] = (f<<4)|(((funcs.pending>>f)&1)<<1)|(funcs.pending&1);
531 buffer[3] = speed.pending;
536 bool f16 = (funcs.pending>0xFF);
537 buffer[0] = (f16 ? MFX_SPEED_FUNCS16 : MFX_SPEED_FUNCS8);
538 buffer[1] = address>>8;
540 buffer[3] = speed.pending+reverse.pending*0x80;
543 buffer[4] = funcs.pending>>8;
544 buffer[5] = funcs.pending;
549 buffer[4] = funcs.pending;
558 ArduControl::Accessory::Accessory(Kind k, unsigned a, unsigned b):
563 uncertain((1<<bits)-1),
565 active_time(500*Time::msec)
568 unsigned ArduControl::Accessory::create_state_command(unsigned b, bool c, char *buffer) const
571 throw invalid_argument("Accessory::create_state_command");
573 unsigned a = (address+b+3)*2;
574 if(!((state.pending>>b)&1))
576 buffer[0] = MOTOROLA_SOLENOID;
578 buffer[2] = ((a&7)<<4)|c;
583 ArduControl::Sensor::Sensor(unsigned a):
589 ArduControl::PendingCommand::PendingCommand():
594 ArduControl::PendingCommand::PendingCommand(GeneralCommand cmd):
598 tag.type = Tag::GENERAL;
602 ArduControl::PendingCommand::PendingCommand(Locomotive &loco, Locomotive::Command cmd, unsigned index):
605 tag.type = Tag::LOCOMOTIVE;
608 if(cmd==Locomotive::SPEED)
610 tag.serial = loco.speed.serial;
611 length = loco.create_speed_dir_command(command);
613 else if(cmd==Locomotive::REVERSE)
615 tag.serial = loco.reverse.serial;
616 length = loco.create_speed_dir_command(command);
618 else if(cmd==Locomotive::FUNCTIONS)
620 tag.serial = loco.funcs.serial;
621 length = loco.create_speed_func_command(index, command);
624 throw invalid_argument("PendingCommand");
627 ArduControl::PendingCommand::PendingCommand(Accessory &acc, Accessory::Command cmd, unsigned index):
630 tag.type = Tag::ACCESSORY;
632 tag.id = acc.address;
633 if(cmd==Accessory::ACTIVATE || cmd==Accessory::DEACTIVATE)
635 tag.serial = acc.state.serial;
636 length = acc.create_state_command(index, (cmd==Accessory::ACTIVATE), command);
639 throw invalid_argument("PendingCommand");
644 void ArduControl::Queue<T>::push(const T &item)
646 MutexLock lock(mutex);
647 items.push_back(item);
651 bool ArduControl::Queue<T>::pop(T &item)
653 MutexLock lock(mutex);
657 item = items.front();
663 bool ArduControl::Queue<T>::empty() const
665 return items.empty();
669 ArduControl::RefreshTask::RefreshTask():
676 bool ArduControl::RefreshTask::get_work(PendingCommand &cmd)
678 if(loco && loco->proto==MM && phase==0)
680 cmd.length = loco->create_speed_func_command(round%4+1, cmd.command);
681 cmd.repeat_count = 2;
686 loco = get_next_loco();
693 cmd.length = loco->create_speed_dir_command(cmd.command);
694 cmd.repeat_count = 2;
696 else if(loco->proto==MFX)
697 cmd.length = loco->create_speed_func_command(0, cmd.command);
704 void ArduControl::RefreshTask::add_loco(Locomotive &l)
706 MutexLock lock(mutex);
710 LocomotivePtrList::iterator oldest = cycle.begin();
711 for(LocomotivePtrList::iterator i=cycle.begin(); ++i!=cycle.end(); )
712 if((*i)->last_change_age>(*oldest)->last_change_age)
718 if(next==cycle.end())
719 next = cycle.begin();
722 void ArduControl::RefreshTask::remove_loco(Locomotive &l)
724 MutexLock lock(mutex);
725 for(LocomotivePtrList::iterator i=cycle.begin(); i!=cycle.end(); ++i)
740 ArduControl::Locomotive *ArduControl::RefreshTask::get_next_loco()
742 MutexLock lock(mutex);
746 Locomotive *l = *next;
751 void ArduControl::RefreshTask::advance()
754 if(next==cycle.end())
762 ArduControl::S88Task::S88Task(ArduControl &c):
769 bool ArduControl::S88Task::get_work(PendingCommand &cmd)
776 if(octets_remaining || !n_octets)
779 octets_remaining = n_octets;
780 cmd.command[0] = S88_READ;
781 cmd.command[1] = octets_remaining;
789 void ArduControl::S88Task::process_reply(const char *reply, unsigned length)
791 unsigned char type = reply[0];
792 if(type==S88_DATA && length>2)
794 unsigned offset = static_cast<unsigned char>(reply[1]);
795 unsigned count = length-2;
797 SensorMap::iterator begin = control.sensors.lower_bound(offset*8+1);
798 SensorMap::iterator end = control.sensors.upper_bound((offset+count)*8);
799 for(SensorMap::iterator i=begin; i!=end; ++i)
801 unsigned bit_index = i->first-1-offset*8;
802 bool state = (reply[2+bit_index/8]>>(7-bit_index%8))&1;
803 i->second.state.set(state);
806 tag.type = Tag::SENSOR;
807 tag.command = Sensor::STATE;
808 tag.serial = i->second.state.serial;
810 control.completed_commands.push(tag);
813 if(count>octets_remaining)
814 octets_remaining = 0;
816 octets_remaining -= count;
820 void ArduControl::S88Task::set_n_octets(unsigned n)
825 void ArduControl::S88Task::grow_n_octets(unsigned n)
832 ArduControl::MfxAnnounceTask::MfxAnnounceTask():
836 bool ArduControl::MfxAnnounceTask::get_work(PendingCommand &cmd)
838 Time::TimeStamp t = Time::now();
842 cmd.command[0] = MFX_ANNOUNCE;
843 cmd.command[1] = serial>>8;
844 cmd.command[2] = serial;
846 next = t+400*Time::msec;
851 void ArduControl::MfxAnnounceTask::set_serial(unsigned s)
857 ArduControl::MfxSearchTask::MfxSearchTask(ArduControl &c):
865 bool ArduControl::MfxSearchTask::get_work(PendingCommand &cmd)
870 IO::print("Assigning MFX address %d to decoder %08X\n", next_address, bits);
873 info.protocol = "MFX";
874 info.address = next_address;
875 info.name = format("%08X", bits);
879 cmd.command[0] = MFX_ASSIGN_ADDRESS;
880 cmd.command[1] = next_address>>8;
881 cmd.command[2] = next_address;
882 for(unsigned i=0; i<4; ++i)
883 cmd.command[3+i] = bits>>(24-i*8);
886 cmd.tag.type = Tag::GENERAL;
887 cmd.tag.command = NEW_LOCO;
897 Time::TimeStamp t = Time::now();
901 cmd.command[0] = MFX_SEARCH;
902 for(unsigned i=0; i<4; ++i)
903 cmd.command[1+i] = bits>>(24-i*8);
904 cmd.command[5] = size;
907 next = t+200*Time::msec;
910 IO::print("Search %08X/%d\n", bits, size);
915 void ArduControl::MfxSearchTask::process_reply(const char *reply, unsigned length)
917 unsigned char type = reply[0];
918 if(type==MFX_SEARCH_FEEDBACK && length==2)
925 else if(size>0 && misses<6)
928 bits ^= 1<<(32-size);
932 next = Time::now()+2*Time::sec;
940 void ArduControl::MfxSearchTask::set_next_address(unsigned a)
945 bool ArduControl::MfxSearchTask::pop_info(MfxInfo &info)
947 return queue.pop(info);
951 ArduControl::MonitorTask::MonitorTask():
959 bool ArduControl::MonitorTask::get_work(PendingCommand &cmd)
961 Time::TimeStamp t = Time::now();
966 cmd.command[0] = READ_INPUT_VOLTAGE;
968 cmd.command[0] = READ_TRACK_CURRENT;
971 next_poll = t+200*Time::msec;
972 next_type = (next_type+1)%5;
977 void ArduControl::MonitorTask::process_reply(const char *reply, unsigned length)
979 unsigned char type = reply[0];
980 if(type==INPUT_VOLTAGE && length==3)
981 voltage = ((static_cast<unsigned char>(reply[1])<<8) | static_cast<unsigned char>(reply[2]))/1000.0f;
982 else if(type==TRACK_CURRENT && length==5)
984 current = ((static_cast<unsigned char>(reply[1])<<8) | static_cast<unsigned char>(reply[2]))/1000.0f;
985 float peak = ((static_cast<unsigned char>(reply[3])<<8) | static_cast<unsigned char>(reply[4]))/1000.0f;
986 peak_level = max(peak_level, peak);
987 base_level = min(base_level, current);
991 void ArduControl::MonitorTask::reset_peak()
993 base_level = current;
994 peak_level = current;
998 ArduControl::ControlThread::ControlThread(ArduControl &c):
1002 tasks.push_back(&control.monitor);
1003 tasks.push_back(&control.mfx_announce);
1004 tasks.push_back(&control.mfx_search);
1005 tasks.push_back(&control.s88);
1006 tasks.push_back(&control.refresh);
1011 void ArduControl::ControlThread::exit()
1017 void ArduControl::ControlThread::main()
1026 bool success = true;
1027 for(unsigned i=0; (success && i<cmd.repeat_count); ++i)
1028 success = (do_command(cmd)==COMMAND_OK);
1029 if(success && cmd.tag)
1030 control.completed_commands.push(cmd.tag);
1033 Time::sleep(10*Time::msec);
1037 void ArduControl::ControlThread::init_baud_rate()
1039 static unsigned rates[] = { 57600, 9600, 19200, 38400, 0 };
1041 control.serial.set_data_bits(8);
1042 control.serial.set_parity(IO::Serial::NONE);
1043 control.serial.set_stop_bits(1);
1044 for(unsigned i=0; rates[i]; ++i)
1046 control.serial.set_baud_rate(rates[i]);
1047 control.serial.put('\xFF');
1048 if(IO::poll(control.serial, IO::P_INPUT, 500*Time::msec))
1050 int c = control.serial.get();
1061 if(control.debug>=1)
1062 IO::print("ArduControl detection failed\n");
1067 if(control.debug>=1)
1068 IO::print("ArduControl detected at %d bits/s\n", rate);
1073 cmd.command[0] = SET_BAUD_RATE;
1074 cmd.command[1] = rates[0]>>8;
1075 cmd.command[2] = rates[0];
1077 if(do_command(cmd)==COMMAND_OK)
1079 control.serial.set_baud_rate(rates[0]);
1080 Time::sleep(Time::sec);
1081 if(do_command(cmd)==COMMAND_OK)
1083 if(control.debug>=1)
1084 IO::print("Rate changed to %d bits/s\n", rates[0]);
1090 bool ArduControl::ControlThread::get_work(PendingCommand &cmd)
1092 if(control.command_queue.pop(cmd))
1095 for(vector<Task *>::iterator i=tasks.begin(); i!=tasks.end(); ++i)
1096 if((*i)->get_work(cmd))
1099 // As fallback, send an idle packet for the MM protocol
1100 cmd.command[0] = MOTOROLA_SPEED;
1101 cmd.command[1] = 80;
1109 unsigned ArduControl::ControlThread::do_command(const PendingCommand &cmd)
1111 if(control.debug>=2)
1114 for(unsigned i=0; i<cmd.length; ++i)
1115 cmd_hex += format(" %02X", static_cast<unsigned char>(cmd.command[i]));
1116 IO::print("< %02X%s\n", cmd.length^0xFF, cmd_hex);
1119 control.serial.put(cmd.length^0xFF);
1120 control.serial.write(cmd.command, cmd.length);
1122 unsigned result = 0;
1127 got_data = IO::poll(control.serial, IO::P_INPUT, Time::zero);
1129 got_data = IO::poll(control.serial, IO::P_INPUT);
1134 unsigned rlength = control.serial.get()^0xFF;
1137 IO::print("Invalid length %02X\n", rlength);
1144 pos += control.serial.read(reply+pos, rlength-pos);
1146 if(control.debug>=2)
1149 for(unsigned i=0; i<rlength; ++i)
1150 reply_hex += format(" %02X", static_cast<unsigned char>(reply[i]));
1151 IO::print("> %02X%s\n", rlength^0xFF, reply_hex);
1154 unsigned r = process_reply(reply, rlength);
1162 unsigned ArduControl::ControlThread::process_reply(const char *reply, unsigned rlength)
1164 unsigned char type = reply[0];
1165 if((type&0xE0)==0x80)
1167 if(type!=COMMAND_OK)
1168 IO::print("Error %02X\n", type);
1171 else if(type==POWER_STATE && rlength==2)
1173 control.power.set(reply[1]);
1176 tag.type = Tag::GENERAL;
1177 tag.command = POWER;
1178 tag.serial = control.power.serial;
1179 control.completed_commands.push(tag);
1183 for(vector<Task *>::iterator i=tasks.begin(); i!=tasks.end(); ++i)
1184 (*i)->process_reply(reply, rlength);
1191 ArduControl::Loader::Loader(ArduControl &c):
1192 DataFile::ObjectLoader<ArduControl>(c)
1194 add("mfx_announce_serial", &Loader::mfx_announce_serial);
1195 add("mfx_locomotive", &Loader::mfx_locomotive);
1198 void ArduControl::Loader::mfx_announce_serial(unsigned s)
1200 obj.mfx_announce.set_serial(s);
1203 void ArduControl::Loader::mfx_locomotive(unsigned id)
1207 info.protocol = "MFX";
1209 obj.add_mfx_info(info);
1213 ArduControl::MfxInfo::Loader::Loader(MfxInfo &i):
1214 DataFile::ObjectLoader<MfxInfo>(i)
1216 add("address", static_cast<unsigned MfxInfo::*>(&MfxInfo::address));
1217 add("name", static_cast<string MfxInfo::*>(&MfxInfo::name));