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 Driver::TelemetryInfo ArduControl::telemetry_info[6] =
23 { "voltage", "Voltage", "V", 1 },
24 { "current", "Current", "A", 2 },
25 { "cmd-rate", "Cmd rate", "/ s", 0 },
26 { "cmd-queue-depth", "Cmd queue", "", 0 },
27 { "acc-queue-depth", "Acc queue", "", 0 },
28 { "s88-latency", "S88 latency", "ms", 0 }
31 ArduControl::ArduControl(const Options &opts):
32 serial(opts.get<string>(string(), "ttyUSB0")),
33 debug(opts.get<unsigned>("debug")),
34 state_file("arducontrol.state"),
38 command_timeout(200*Time::msec),
43 if(FS::exists(state_file))
44 DataFile::load(*this, state_file.str());
46 unsigned max_address = 0;
47 for(MfxInfoArray::const_iterator i=mfx_info.begin(); i!=mfx_info.end(); ++i)
48 max_address = max(max_address, i->address);
49 mfx_search.set_next_address(max_address+1);
52 cmd.command[0] = READ_POWER_STATE;
54 command_queue.push(cmd);
56 cmd.command[0] = MFX_SET_STATION_ID;
62 command_queue.push(cmd);
65 ArduControl::~ArduControl()
70 void ArduControl::set_power(bool p)
74 PendingCommand cmd(POWER);
75 cmd.tag.serial = power.serial;
76 cmd.command[0] = (p ? POWER_ON : POWER_OFF);
78 command_queue.push(cmd);
82 void ArduControl::halt(bool h)
90 for(LocomotiveMap::const_iterator i=locomotives.begin(); i!=locomotives.end(); ++i)
91 set_loco_speed(i->first, 0);
94 signal_halt.emit(halted);
97 const char *ArduControl::enumerate_protocols(unsigned i) const
107 ArduControl::Protocol ArduControl::map_protocol(const string &proto_name)
111 else if(proto_name=="MFX")
114 throw invalid_argument("ArduControl::map_protocol");
117 unsigned ArduControl::get_protocol_speed_steps(const string &proto_name) const
119 return protocol_info[map_protocol(proto_name)].max_speed;
122 const Driver::DetectedLocomotive *ArduControl::enumerate_detected_locos(unsigned i) const
124 if(i>=mfx_info.size())
130 unsigned ArduControl::add_loco(unsigned addr, const string &proto_name, const VehicleType &)
133 throw invalid_argument("ArduControl::add_loco");
135 Protocol proto = map_protocol(proto_name);
136 if(addr>protocol_info[proto].max_address)
137 throw invalid_argument("ArduControl::add_loco");
139 Locomotive loco(proto, addr);
140 insert_unique(locomotives, loco.id, loco);
145 ArduControl::MfxInfoArray::iterator ArduControl::add_mfx_info(const MfxInfo &info)
147 MfxInfoArray::iterator i;
148 for(i=mfx_info.begin(); (i!=mfx_info.end() && i->id!=info.id); ++i) ;
149 if(i==mfx_info.end())
151 mfx_info.push_back(info);
152 i = --mfx_info.end();
159 ArduControl::MfxInfo *ArduControl::find_mfx_info(unsigned id)
161 for(MfxInfoArray::iterator i=mfx_info.begin(); i!=mfx_info.end(); ++i)
167 void ArduControl::remove_loco(unsigned id)
169 Locomotive &loco = get_item(locomotives, id);
170 refresh.remove_loco(loco);
171 locomotives.erase(id);
174 void ArduControl::set_loco_speed(unsigned id, unsigned speed)
176 Locomotive &loco = get_item(locomotives, id);
177 if(speed>protocol_info[loco.proto].max_speed)
178 throw invalid_argument("ArduControl::set_loco_speed");
183 if(loco.speed.set(speed))
185 PendingCommand cmd(loco, Locomotive::SPEED);
186 command_queue.push(cmd);
188 refresh.add_loco(loco);
192 void ArduControl::set_loco_reverse(unsigned id, bool rev)
194 Locomotive &loco = get_item(locomotives, id);
195 if(loco.reverse.set(rev))
197 PendingCommand cmd(loco, Locomotive::REVERSE);
198 command_queue.push(cmd);
200 refresh.add_loco(loco);
204 void ArduControl::set_loco_function(unsigned id, unsigned func, bool state)
206 Locomotive &loco = get_item(locomotives, id);
207 if(func>protocol_info[loco.proto].max_func)
208 throw invalid_argument("ArduControl::set_loco_function");
210 unsigned mask = 1<<func;
211 if(loco.funcs.set((loco.funcs.pending&~mask)|(mask*state)))
213 if(func>0 || loco.proto!=MM)
215 PendingCommand cmd(loco, Locomotive::FUNCTIONS, func);
216 command_queue.push(cmd);
219 refresh.add_loco(loco);
223 unsigned ArduControl::add_turnout(unsigned addr, const TrackType &type)
225 if(!addr || !type.is_turnout())
226 throw invalid_argument("ArduControl::add_turnout");
228 return add_accessory(Accessory::TURNOUT, addr, type.get_state_bits(), type.get_paths());
231 void ArduControl::remove_turnout(unsigned addr)
233 remove_accessory(Accessory::TURNOUT, addr);
236 void ArduControl::set_turnout(unsigned addr, unsigned state)
238 set_accessory(Accessory::TURNOUT, addr, state);
241 unsigned ArduControl::get_turnout(unsigned addr) const
243 return get_accessory(Accessory::TURNOUT, addr);
246 unsigned ArduControl::add_signal(unsigned addr, const SignalType &)
248 return add_accessory(Accessory::SIGNAL, addr, 1, 3);
251 void ArduControl::remove_signal(unsigned addr)
253 remove_accessory(Accessory::SIGNAL, addr);
256 void ArduControl::set_signal(unsigned addr, unsigned state)
258 set_accessory(Accessory::SIGNAL, addr, state);
261 unsigned ArduControl::get_signal(unsigned addr) const
263 return get_accessory(Accessory::SIGNAL, addr);
266 unsigned ArduControl::add_accessory(Accessory::Kind kind, unsigned addr, unsigned bits, unsigned states)
268 AccessoryMap::iterator i = accessories.lower_bound(addr);
269 AccessoryMap::iterator j = accessories.upper_bound(addr+bits-1);
271 throw key_error(addr);
272 if(i!=accessories.begin())
275 if(i->first+i->second.bits>addr)
276 throw key_error(addr);
279 insert_unique(accessories, addr, Accessory(kind, addr, bits, states));
283 void ArduControl::remove_accessory(Accessory::Kind kind, unsigned addr)
285 Accessory &acc = get_item(accessories, addr);
287 throw key_error(addr);
288 accessories.erase(addr);
291 void ArduControl::set_accessory(Accessory::Kind kind, unsigned addr, unsigned state)
293 Accessory &acc = get_item(accessories, addr);
295 throw key_error(addr);
297 if(state!=acc.target || acc.uncertain)
300 accessory_queue.push_back(&acc);
304 unsigned ArduControl::get_accessory(Accessory::Kind kind, unsigned addr) const
306 const Accessory &acc = get_item(accessories, addr);
308 throw key_error(addr);
312 void ArduControl::activate_accessory_by_mask(Accessory &acc, unsigned mask)
314 unsigned bit = mask&~(mask-1);
315 for(active_index=0; (bit>>active_index)>1; ++active_index) ;
316 acc.state.set((acc.state&~bit)|(acc.target&bit));
318 IO::print("Setting accessory %d bit %d, state=%d\n", acc.address, active_index, acc.state.pending);
319 PendingCommand cmd(acc, Accessory::ACTIVATE, active_index);
320 command_queue.push(cmd);
321 active_accessory = &acc;
323 monitor.reset_peak();
326 unsigned ArduControl::add_sensor(unsigned addr)
329 throw invalid_argument("ArduControl::add_sensor");
331 insert_unique(sensors, addr, Sensor(addr));
332 s88.grow_n_octets((addr+7)/8);
337 void ArduControl::remove_sensor(unsigned addr)
339 remove_existing(sensors, addr);
340 // TODO update s88.n_octets
343 bool ArduControl::get_sensor(unsigned addr) const
345 return get_item(sensors, addr).state;
348 const Driver::TelemetryInfo *ArduControl::enumerate_telemetry(unsigned i) const
351 return telemetry_info+i;
356 float ArduControl::get_telemetry_value(const string &name) const
358 if(name==telemetry_info[0].name)
359 return monitor.get_voltage();
360 else if(name==telemetry_info[1].name)
361 return monitor.get_current();
362 else if(name==telemetry_info[2].name)
363 return thread.get_command_rate();
364 else if(name==telemetry_info[3].name)
365 return command_queue.size();
366 else if(name==telemetry_info[4].name)
367 return accessory_queue.size();
368 else if(name==telemetry_info[5].name)
369 return s88.get_latency()/Time::msec;
371 throw key_error(name);
374 void ArduControl::tick()
377 while(completed_commands.pop(tag))
379 if(tag.type==Tag::GENERAL)
381 if(tag.command==POWER)
383 if(power.commit(tag.serial))
384 signal_power.emit(power.current);
386 else if(tag.command==NEW_LOCO)
389 if(mfx_search.pop_info(info))
391 MfxInfoArray::iterator i = add_mfx_info(info);
393 signal_locomotive_detected.emit(*i);
397 else if(tag.type==Tag::LOCOMOTIVE)
399 LocomotiveMap::iterator i = locomotives.find(tag.id);
400 if(i==locomotives.end())
403 Locomotive &loco = i->second;
404 if(tag.command==Locomotive::SPEED)
406 if(loco.speed.commit(tag.serial))
407 signal_loco_speed.emit(loco.id, loco.speed, loco.reverse);
409 else if(tag.command==Locomotive::REVERSE)
411 if(loco.reverse.commit(tag.serial))
412 signal_loco_speed.emit(loco.id, loco.speed, loco.reverse);
414 else if(tag.command==Locomotive::FUNCTIONS)
416 unsigned old = loco.funcs;
417 if(loco.funcs.commit(tag.serial))
419 unsigned changed = old^loco.funcs;
420 for(unsigned j=0; changed>>j; ++j)
422 signal_loco_function.emit(loco.id, j, (loco.funcs>>j)&1);
426 else if(tag.type==Tag::ACCESSORY)
428 AccessoryMap::iterator i = accessories.find(tag.id);
429 if(i==accessories.end())
432 Accessory &acc = i->second;
433 if(tag.command==Accessory::ACTIVATE)
434 off_timeout = Time::now()+acc.active_time;
435 else if(tag.command==Accessory::DEACTIVATE)
437 if(acc.state.commit(tag.serial))
439 if(&acc==active_accessory)
440 active_accessory = 0;
444 else if(tag.type==Tag::SENSOR)
446 SensorMap::iterator i = sensors.find(tag.id);
450 Sensor &sensor = i->second;
451 if(tag.command==Sensor::STATE)
453 if(sensor.state.commit(tag.serial))
454 signal_sensor.emit(sensor.address, sensor.state);
459 while(power && !active_accessory && !accessory_queue.empty())
461 Accessory &acc = *accessory_queue.front();
465 unsigned zeroes = acc.uncertain&~acc.target;
467 activate_accessory_by_mask(acc, zeroes);
469 activate_accessory_by_mask(acc, acc.uncertain);
471 else if(acc.state!=acc.target)
473 unsigned changes = acc.state^acc.target;
474 if(!(changes&((1<<acc.bits)-1)))
476 // All remaining changes are in non-physical bits
477 acc.state.set(acc.state^changes);
478 acc.state.commit(acc.state.serial);
482 unsigned toggle_bit = 0;
483 for(unsigned bit=1; (!toggle_bit && bit<=changes); bit<<=1)
484 if((changes&bit) && (acc.valid_states&(1<<(acc.state^bit))))
487 activate_accessory_by_mask(acc, toggle_bit);
492 accessory_queue.pop_front();
494 if(acc.state==acc.target)
496 if(acc.kind==Accessory::TURNOUT)
497 signal_turnout.emit(acc.address, acc.state);
498 else if(acc.kind==Accessory::SIGNAL)
499 signal_signal.emit(acc.address, acc.state);
504 if(active_accessory && off_timeout)
506 bool got_peak = monitor.get_peak()>0.0f;
507 bool success = monitor.get_peak()>0.42f;
508 bool complete = (success && monitor.get_current()<monitor.get_peak()-0.2f);
509 Time::TimeStamp t = Time::now();
510 if((t>off_timeout && got_peak) || complete)
512 Accessory &acc = *active_accessory;
514 unsigned bit = 1<<active_index;
516 // Assume success if we were uncertain of the physical setting
517 if(acc.uncertain&bit)
518 acc.uncertain &= ~bit;
519 else if(acc.kind==Accessory::TURNOUT && !success)
522 IO::print("Peak current only %.2f A\n", monitor.get_peak());
523 signal_turnout_failed.emit(acc.address);
524 acc.state.rollback();
525 if(acc.valid_states&(1<<(acc.target^bit)))
528 acc.target = acc.state;
531 off_timeout = Time::TimeStamp();
532 PendingCommand cmd(acc, Accessory::DEACTIVATE, active_index);
533 command_queue.push(cmd);
538 void ArduControl::flush()
540 while(!command_queue.empty() || (power && !accessory_queue.empty()))
544 void ArduControl::save_state() const
546 FS::RedirectedPath tmp_file(state_file);
547 IO::BufferedFile out(tmp_file.str(), IO::M_WRITE);
548 DataFile::Writer writer(out);
550 writer.write((DataFile::Statement("mfx_announce_serial"), mfx_announce.get_serial()));
551 for(MfxInfoArray::const_iterator i=mfx_info.begin(); i!=mfx_info.end(); ++i)
553 DataFile::Statement st("mfx_locomotive");
555 st.sub.push_back((DataFile::Statement("address"), i->address));
556 st.sub.push_back((DataFile::Statement("name"), i->name));
562 ArduControl::Tag::Tag():
570 ArduControl::Locomotive::Locomotive(Protocol p, unsigned a):
580 unsigned ArduControl::Locomotive::create_speed_dir_command(char *buffer) const
584 buffer[0] = MOTOROLA_SPEED_DIRECTION;
586 buffer[2] = funcs.pending&1;
587 buffer[3] = speed.pending+reverse.pending*0x80;
592 buffer[0] = MFX_SPEED;
593 buffer[1] = address>>8;
595 buffer[3] = speed.pending+reverse.pending*0x80;
602 unsigned ArduControl::Locomotive::create_speed_func_command(unsigned f, char *buffer) const
607 throw invalid_argument("Locomotive::create_speed_func_command");
609 buffer[0] = MOTOROLA_SPEED_FUNCTION;
611 buffer[2] = (f<<4)|(((funcs.pending>>f)&1)<<1)|(funcs.pending&1);
612 buffer[3] = speed.pending;
617 bool f16 = (funcs.pending>0xFF);
618 buffer[0] = (f16 ? MFX_SPEED_FUNCS16 : MFX_SPEED_FUNCS8);
619 buffer[1] = address>>8;
621 buffer[3] = speed.pending+reverse.pending*0x80;
624 buffer[4] = funcs.pending>>8;
625 buffer[5] = funcs.pending;
630 buffer[4] = funcs.pending;
639 ArduControl::Accessory::Accessory(Kind k, unsigned a, unsigned b, unsigned s):
645 uncertain((1<<bits)-1),
647 active_time((bits*700)*Time::msec)
650 unsigned ArduControl::Accessory::create_state_command(unsigned b, bool c, char *buffer) const
653 throw invalid_argument("Accessory::create_state_command");
655 unsigned a = (address+b+3)*2;
656 if(!((state.pending>>b)&1))
658 buffer[0] = MOTOROLA_SOLENOID;
660 buffer[2] = ((a&7)<<4)|c;
665 ArduControl::Sensor::Sensor(unsigned a):
671 ArduControl::PendingCommand::PendingCommand():
676 ArduControl::PendingCommand::PendingCommand(GeneralCommand cmd):
680 tag.type = Tag::GENERAL;
684 ArduControl::PendingCommand::PendingCommand(Locomotive &loco, Locomotive::Command cmd, unsigned index):
687 tag.type = Tag::LOCOMOTIVE;
690 if(cmd==Locomotive::SPEED)
692 tag.serial = loco.speed.serial;
693 length = loco.create_speed_dir_command(command);
695 else if(cmd==Locomotive::REVERSE)
697 tag.serial = loco.reverse.serial;
698 length = loco.create_speed_dir_command(command);
700 else if(cmd==Locomotive::FUNCTIONS)
702 tag.serial = loco.funcs.serial;
703 length = loco.create_speed_func_command(index, command);
706 throw invalid_argument("PendingCommand");
709 ArduControl::PendingCommand::PendingCommand(Accessory &acc, Accessory::Command cmd, unsigned index):
712 tag.type = Tag::ACCESSORY;
714 tag.id = acc.address;
715 if(cmd==Accessory::ACTIVATE || cmd==Accessory::DEACTIVATE)
717 tag.serial = acc.state.serial;
718 length = acc.create_state_command(index, (cmd==Accessory::ACTIVATE), command);
721 throw invalid_argument("PendingCommand");
726 void ArduControl::Queue<T>::push(const T &item)
728 MutexLock lock(mutex);
729 items.push_back(item);
733 bool ArduControl::Queue<T>::pop(T &item)
735 MutexLock lock(mutex);
739 item = items.front();
745 unsigned ArduControl::Queue<T>::size() const
751 bool ArduControl::Queue<T>::empty() const
753 return items.empty();
757 bool ArduControl::CommandQueueTask::get_work(PendingCommand &cmd)
759 return queue.pop(cmd);
762 void ArduControl::CommandQueueTask::push(const PendingCommand &cmd)
768 ArduControl::Task::Task(const string &n, unsigned p):
773 void ArduControl::Task::sleep(const Time::TimeDelta &dt)
775 sleep_timeout = Time::now()+dt;
779 ArduControl::CommandQueueTask::CommandQueueTask():
784 ArduControl::RefreshTask::RefreshTask():
792 bool ArduControl::RefreshTask::get_work(PendingCommand &cmd)
794 if(loco && loco->proto==MM && phase==0)
796 cmd.length = loco->create_speed_func_command(round%4+1, cmd.command);
797 cmd.repeat_count = 2;
802 loco = get_next_loco();
809 cmd.length = loco->create_speed_dir_command(cmd.command);
810 cmd.repeat_count = 2;
812 else if(loco->proto==MFX)
813 cmd.length = loco->create_speed_func_command(0, cmd.command);
820 void ArduControl::RefreshTask::add_loco(Locomotive &l)
822 MutexLock lock(mutex);
826 LocomotivePtrList::iterator oldest = cycle.begin();
827 for(LocomotivePtrList::iterator i=cycle.begin(); ++i!=cycle.end(); )
828 if((*i)->last_change_age>(*oldest)->last_change_age)
834 if(next==cycle.end())
835 next = cycle.begin();
838 void ArduControl::RefreshTask::remove_loco(Locomotive &l)
840 MutexLock lock(mutex);
841 for(LocomotivePtrList::iterator i=cycle.begin(); i!=cycle.end(); ++i)
856 ArduControl::Locomotive *ArduControl::RefreshTask::get_next_loco()
858 MutexLock lock(mutex);
862 Locomotive *l = *next;
867 void ArduControl::RefreshTask::advance()
870 if(next==cycle.end())
872 next = cycle.begin();
878 ArduControl::S88Task::S88Task(ArduControl &c):
885 bool ArduControl::S88Task::get_work(PendingCommand &cmd)
887 if(octets_remaining || !n_octets)
890 Time::TimeStamp t = Time::now();
892 latency = t-last_poll;
895 octets_remaining = n_octets;
896 cmd.command[0] = S88_READ;
897 cmd.command[1] = octets_remaining;
900 sleep(100*Time::msec);
905 void ArduControl::S88Task::process_reply(const char *reply, unsigned length)
907 unsigned char type = reply[0];
908 if(type==S88_DATA && length>2)
910 unsigned offset = static_cast<unsigned char>(reply[1]);
911 unsigned count = length-2;
913 SensorMap::iterator begin = control.sensors.lower_bound(offset*8+1);
914 SensorMap::iterator end = control.sensors.upper_bound((offset+count)*8);
915 for(SensorMap::iterator i=begin; i!=end; ++i)
917 unsigned bit_index = i->first-1-offset*8;
918 bool state = (reply[2+bit_index/8]>>(7-bit_index%8))&1;
919 i->second.state.set(state);
922 tag.type = Tag::SENSOR;
923 tag.command = Sensor::STATE;
924 tag.serial = i->second.state.serial;
926 control.completed_commands.push(tag);
929 if(count>octets_remaining)
930 octets_remaining = 0;
932 octets_remaining -= count;
936 void ArduControl::S88Task::set_n_octets(unsigned n)
941 void ArduControl::S88Task::grow_n_octets(unsigned n)
948 ArduControl::MfxAnnounceTask::MfxAnnounceTask():
949 Task("MfxAnnounce", 1),
953 bool ArduControl::MfxAnnounceTask::get_work(PendingCommand &cmd)
955 cmd.command[0] = MFX_ANNOUNCE;
956 cmd.command[1] = serial>>8;
957 cmd.command[2] = serial;
960 sleep(400*Time::msec);
965 void ArduControl::MfxAnnounceTask::set_serial(unsigned s)
971 ArduControl::MfxSearchTask::MfxSearchTask(ArduControl &c):
972 Task("MfxSearch", 1),
985 bool ArduControl::MfxSearchTask::get_work(PendingCommand &cmd)
989 cmd.command[0] = MFX_READ;
990 cmd.command[1] = pending_info->address>>8;
991 cmd.command[2] = pending_info->address;
992 unsigned index = read_array*0x40+read_offset;
993 cmd.command[3] = index>>8;
994 cmd.command[4] = index;
995 unsigned length = (read_length>=4 ? 4 : read_length>=2 ? 2 : 1);
996 cmd.command[5] = length;
999 sleep(100*Time::msec);
1003 else if(pending_info)
1005 queue.push(*pending_info);
1007 tag.type = Tag::GENERAL;
1008 tag.command = NEW_LOCO;
1009 tag.id = pending_info->id;
1010 control.completed_commands.push(tag);
1012 if(control.debug>=1)
1013 IO::print("Completed processing locomotive %s at address %d\n", pending_info->name, pending_info->address);
1015 delete pending_info;
1021 unsigned address = 0;
1022 if(MfxInfo *existing = control.find_mfx_info(bits))
1023 address = existing->address;
1025 address = next_address++;
1027 if(control.debug>=1)
1028 IO::print("Assigning MFX address %d to decoder %08X\n", address, bits);
1030 pending_info = new MfxInfo;
1031 pending_info->protocol = "MFX";
1032 pending_info->address = address;
1033 pending_info->name = format("%08X", bits);
1034 pending_info->id = bits;
1036 cmd.command[0] = MFX_ASSIGN_ADDRESS;
1037 cmd.command[1] = address>>8;
1038 cmd.command[2] = address;
1039 for(unsigned i=0; i<4; ++i)
1040 cmd.command[3+i] = bits>>(24-i*8);
1054 cmd.command[0] = MFX_SEARCH;
1055 for(unsigned i=0; i<4; ++i)
1056 cmd.command[1+i] = bits>>(24-i*8);
1057 cmd.command[5] = size;
1060 sleep(100*Time::msec);
1062 if(control.debug>=1)
1063 IO::print("Search %08X/%d\n", bits, size);
1068 void ArduControl::MfxSearchTask::process_reply(const char *reply, unsigned length)
1070 unsigned char type = reply[0];
1071 if(type==MFX_SEARCH_FEEDBACK && length==2)
1078 else if(size>0 && misses<6)
1081 bits ^= 1<<(32-size);
1091 else if(type==MFX_READ_FEEDBACK && length>=3)
1097 for(unsigned i=2; i<length; ++i)
1098 read_data[read_offset+i-2] = reply[i];
1099 read_offset += length-2;
1100 read_length -= length-2;
1105 block_size = static_cast<unsigned char>(read_data[4])*static_cast<unsigned char>(read_data[5]);
1107 bool array_handled = false;
1108 if(read_data[0]==0x18)
1110 for(unsigned i=1; i<read_offset; ++i)
1113 pending_info->name = string(read_data+1, i-1);
1114 array_handled = true;
1122 array_handled = true;
1124 if(array_handled && control.debug>=1)
1126 IO::print("MFX CA %03X:", read_array);
1127 for(unsigned i=0; i<read_offset; ++i)
1128 IO::print(" %02X", static_cast<unsigned char>(read_data[i]));
1132 if(array_handled && read_array<block_size)
1145 if(control.debug>=1)
1146 IO::print("Failed to read MFX configuration from %d\n", pending_info->address);
1153 void ArduControl::MfxSearchTask::set_next_address(unsigned a)
1158 bool ArduControl::MfxSearchTask::pop_info(MfxInfo &info)
1160 return queue.pop(info);
1164 ArduControl::MonitorTask::MonitorTask():
1173 bool ArduControl::MonitorTask::get_work(PendingCommand &cmd)
1176 cmd.command[0] = READ_INPUT_VOLTAGE;
1178 cmd.command[0] = READ_TRACK_CURRENT;
1181 sleep(200*Time::msec);
1182 next_type = (next_type+1)%5;
1187 void ArduControl::MonitorTask::process_reply(const char *reply, unsigned length)
1189 unsigned char type = reply[0];
1190 if(type==INPUT_VOLTAGE && length==3)
1191 voltage = ((static_cast<unsigned char>(reply[1])<<8) | static_cast<unsigned char>(reply[2]))/1000.0f;
1192 else if(type==TRACK_CURRENT && length==5)
1194 current = ((static_cast<unsigned char>(reply[1])<<8) | static_cast<unsigned char>(reply[2]))/1000.0f;
1195 float peak = ((static_cast<unsigned char>(reply[3])<<8) | static_cast<unsigned char>(reply[4]))/1000.0f;
1196 peak_level = max(peak_level, peak);
1197 base_level = min(base_level, current);
1201 void ArduControl::MonitorTask::reset_peak()
1203 base_level = current;
1204 peak_level = current;
1208 ArduControl::ControlThread::ControlThread(ArduControl &c):
1214 tasks.push_back(&control.command_queue);
1215 tasks.push_back(&control.monitor);
1216 tasks.push_back(&control.mfx_announce);
1217 tasks.push_back(&control.mfx_search);
1218 tasks.push_back(&control.s88);
1219 tasks.push_back(&control.refresh);
1224 void ArduControl::ControlThread::exit()
1230 void ArduControl::ControlThread::main()
1233 cmd_rate_start = Time::now();
1240 bool success = true;
1241 bool resync = false;
1242 for(unsigned i=0; (success && i<cmd.repeat_count); ++i)
1244 unsigned result = do_command(cmd, control.command_timeout);
1245 success = (result==COMMAND_OK);
1246 resync = (result==0);
1249 if(success && cmd.tag)
1250 control.completed_commands.push(cmd.tag);
1254 if(control.debug>=1)
1255 IO::print("Synchronization with ArduControl lost, attempting to recover\n");
1256 for(unsigned i=0; (resync && i<16); ++i)
1258 control.serial.put('\xFF');
1259 while(IO::poll(control.serial, IO::P_INPUT, control.command_timeout))
1260 resync = (control.serial.get()!=0xFF);
1264 if(control.debug>=1)
1265 IO::print("Resynchronization failed, giving up\n");
1270 if(control.debug>=1)
1271 IO::print("Resynchronization successful\n");
1273 control.command_queue.push(cmd);
1277 if(cmd_count>=cmd_rate)
1279 Time::TimeStamp t = Time::now();
1280 cmd_rate = cmd_count/((t-cmd_rate_start)/Time::sec);
1286 Time::sleep(10*Time::msec);
1290 void ArduControl::ControlThread::init_baud_rate()
1292 static unsigned rates[] = { 57600, 9600, 19200, 38400, 0 };
1294 control.serial.set_data_bits(8);
1295 control.serial.set_parity(IO::Serial::NONE);
1296 control.serial.set_stop_bits(1);
1297 for(unsigned i=0; rates[i]; ++i)
1299 control.serial.set_baud_rate(rates[i]);
1300 control.serial.put('\xFF');
1301 if(IO::poll(control.serial, IO::P_INPUT, 500*Time::msec))
1303 int c = control.serial.get();
1314 if(control.debug>=1)
1315 IO::print("ArduControl detection failed\n");
1320 if(control.debug>=1)
1321 IO::print("ArduControl detected at %d bits/s\n", rate);
1326 cmd.command[0] = SET_BAUD_RATE;
1327 cmd.command[1] = rates[0]>>8;
1328 cmd.command[2] = rates[0];
1330 if(do_command(cmd, Time::sec)==COMMAND_OK)
1332 control.serial.set_baud_rate(rates[0]);
1333 Time::sleep(Time::sec);
1334 if(do_command(cmd, Time::sec)==COMMAND_OK)
1336 if(control.debug>=1)
1337 IO::print("Rate changed to %d bits/s\n", rates[0]);
1343 bool ArduControl::ControlThread::get_work(PendingCommand &cmd)
1345 Time::TimeStamp t = Time::now();
1348 for(; (count<tasks.size() && tasks[count]->get_sleep_timeout()<=t); ++count) ;
1350 for(; count>0; --count)
1353 for(unsigned j=1; j<count; ++j)
1354 if(tasks[j]->get_priority()<tasks[i]->get_priority())
1357 Task *task = tasks[i];
1358 bool result = task->get_work(cmd);
1360 Time::TimeStamp st = max(task->get_sleep_timeout(), t);
1361 for(; (i+1<tasks.size() && tasks[i+1]->get_sleep_timeout()<=st); ++i)
1362 tasks[i] = tasks[i+1];
1367 if(control.debug>=2)
1368 IO::print("Scheduled task %s\n", task->get_name());
1373 // As fallback, send an idle packet for the MM protocol
1374 cmd.command[0] = MOTOROLA_SPEED;
1375 cmd.command[1] = 80;
1383 unsigned ArduControl::ControlThread::do_command(const PendingCommand &cmd, const Time::TimeDelta &timeout)
1385 if(control.debug>=2)
1388 for(unsigned i=0; i<cmd.length; ++i)
1389 cmd_hex += format(" %02X", static_cast<unsigned char>(cmd.command[i]));
1390 IO::print("< %02X%s\n", cmd.length^0xFF, cmd_hex);
1393 control.serial.put(cmd.length^0xFF);
1394 control.serial.write(cmd.command, cmd.length);
1396 unsigned result = 0;
1401 got_data = IO::poll(control.serial, IO::P_INPUT, Time::zero);
1403 got_data = IO::poll(control.serial, IO::P_INPUT, timeout);
1408 unsigned rlength = control.serial.get()^0xFF;
1411 IO::print("Invalid length %02X\n", rlength);
1419 if(!IO::poll(control.serial, IO::P_INPUT, timeout))
1421 pos += control.serial.read(reply+pos, rlength-pos);
1424 if(control.debug>=2)
1427 for(unsigned i=0; i<rlength; ++i)
1428 reply_hex += format(" %02X", static_cast<unsigned char>(reply[i]));
1429 IO::print("> %02X%s\n", rlength^0xFF, reply_hex);
1432 unsigned r = process_reply(reply, rlength);
1442 unsigned ArduControl::ControlThread::process_reply(const char *reply, unsigned rlength)
1444 unsigned char type = reply[0];
1445 if((type&0xE0)==0x80)
1447 if(type!=COMMAND_OK)
1448 IO::print("Error %02X\n", type);
1451 else if(type==POWER_STATE && rlength==2)
1452 set_power(reply[1]);
1453 else if(type==OVERCURRENT)
1456 IO::print("Overcurrent detected!\n");
1460 for(vector<Task *>::iterator i=tasks.begin(); i!=tasks.end(); ++i)
1461 (*i)->process_reply(reply, rlength);
1467 void ArduControl::ControlThread::set_power(bool p)
1469 control.power.set(p);
1472 tag.type = Tag::GENERAL;
1473 tag.command = POWER;
1474 tag.serial = control.power.serial;
1475 control.completed_commands.push(tag);
1479 ArduControl::Loader::Loader(ArduControl &c):
1480 DataFile::ObjectLoader<ArduControl>(c)
1482 add("mfx_announce_serial", &Loader::mfx_announce_serial);
1483 add("mfx_locomotive", &Loader::mfx_locomotive);
1486 void ArduControl::Loader::mfx_announce_serial(unsigned s)
1488 obj.mfx_announce.set_serial(s);
1491 void ArduControl::Loader::mfx_locomotive(unsigned id)
1495 info.protocol = "MFX";
1497 obj.add_mfx_info(info);
1501 ArduControl::MfxInfo::Loader::Loader(MfxInfo &i):
1502 DataFile::ObjectLoader<MfxInfo>(i)
1504 add("address", static_cast<unsigned MfxInfo::*>(&MfxInfo::address));
1505 add("name", static_cast<string MfxInfo::*>(&MfxInfo::name));