]> git.tdb.fi Git - r2c2.git/blob - source/libr2c2/arducontrol.cpp
0edb3ddaf0c1a44e17164d23f8efcabd6ebb82f7
[r2c2.git] / source / libr2c2 / arducontrol.cpp
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"
8 #include "tracktype.h"
9
10 using namespace std;
11 using namespace Msp;
12
13 namespace R2C2 {
14
15 ArduControl::ProtocolInfo ArduControl::protocol_info[2] =
16 {
17         { 79, 14, 4 },       // MM
18         { 0x3FFF, 126, 15 }  // MFX
19 };
20
21 Driver::TelemetryInfo ArduControl::telemetry_info[4] =
22 {
23         { "voltage", "Voltage", "V", 1 },
24         { "current", "Current", "A", 2 },
25         { "cmd-queue-depth", "Cmd queue", "", 0 },
26         { "s88-latency", "S88 latency", "ms", 0 }
27 };
28
29 ArduControl::ArduControl(const Options &opts):
30         serial(opts.get<string>(string(), "ttyUSB0")),
31         debug(opts.get<unsigned>("debug")),
32         state_file("arducontrol.state"),
33         power(false),
34         halted(false),
35         active_accessory(0),
36         command_timeout(200*Time::msec),
37         s88(*this),
38         mfx_search(*this),
39         thread(*this)
40 {
41         if(FS::exists(state_file))
42                 DataFile::load(*this, state_file.str());
43
44         unsigned max_address = 0;
45         for(MfxInfoArray::const_iterator i=mfx_info.begin(); i!=mfx_info.end(); ++i)
46                 max_address = max(max_address, i->address);
47         mfx_search.set_next_address(max_address+1);
48
49         PendingCommand cmd;
50         cmd.command[0] = READ_POWER_STATE;
51         cmd.length = 1;
52         command_queue.push(cmd);
53
54         cmd.command[0] = MFX_SET_STATION_ID;
55         cmd.command[1] = 'R';
56         cmd.command[2] = '2';
57         cmd.command[3] = 'C';
58         cmd.command[4] = '2';
59         cmd.length = 5;
60         command_queue.push(cmd);
61 }
62
63 ArduControl::~ArduControl()
64 {
65         thread.exit();
66 }
67
68 void ArduControl::set_power(bool p)
69 {
70         if(power.set(p))
71         {
72                 PendingCommand cmd(POWER);
73                 cmd.tag.serial = power.serial;
74                 cmd.command[0] = (p ? POWER_ON : POWER_OFF);
75                 cmd.length = 1;
76                 command_queue.push(cmd);
77         }
78 }
79
80 void ArduControl::halt(bool h)
81 {
82         if(h==halted)
83                 return;
84
85         halted = h;
86         if(halted)
87         {
88                 for(LocomotiveMap::const_iterator i=locomotives.begin(); i!=locomotives.end(); ++i)
89                         set_loco_speed(i->first, 0);
90         }
91
92         signal_halt.emit(halted);
93 }
94
95 const char *ArduControl::enumerate_protocols(unsigned i) const
96 {
97         if(i==0)
98                 return "MM";
99         else if(i==1)
100                 return "MFX";
101         else
102                 return 0;
103 }
104
105 ArduControl::Protocol ArduControl::map_protocol(const string &proto_name)
106 {
107         if(proto_name=="MM")
108                 return MM;
109         else if(proto_name=="MFX")
110                 return MFX;
111         else
112                 throw invalid_argument("ArduControl::map_protocol");
113 }
114
115 unsigned ArduControl::get_protocol_speed_steps(const string &proto_name) const
116 {
117         return protocol_info[map_protocol(proto_name)].max_speed;
118 }
119
120 const Driver::DetectedLocomotive *ArduControl::enumerate_detected_locos(unsigned i) const
121 {
122         if(i>=mfx_info.size())
123                 return 0;
124
125         return &mfx_info[i];
126 }
127
128 unsigned ArduControl::add_loco(unsigned addr, const string &proto_name, const VehicleType &)
129 {
130         if(!addr)
131                 throw invalid_argument("ArduControl::add_loco");
132
133         Protocol proto = map_protocol(proto_name);
134         if(addr>protocol_info[proto].max_address)
135                 throw invalid_argument("ArduControl::add_loco");
136
137         Locomotive loco(proto, addr);
138         insert_unique(locomotives, loco.id, loco);
139
140         return loco.id;
141 }
142
143 ArduControl::MfxInfoArray::iterator ArduControl::add_mfx_info(const MfxInfo &info)
144 {
145         MfxInfoArray::iterator i;
146         for(i=mfx_info.begin(); (i!=mfx_info.end() && i->id!=info.id); ++i) ;
147         if(i==mfx_info.end())
148         {
149                 mfx_info.push_back(info);
150                 i = --mfx_info.end();
151         }
152         else
153                 *i = info;
154         return i;
155 }
156
157 void ArduControl::remove_loco(unsigned id)
158 {
159         Locomotive &loco = get_item(locomotives, id);
160         refresh.remove_loco(loco);
161         locomotives.erase(id);
162 }
163
164 void ArduControl::set_loco_speed(unsigned id, unsigned speed)
165 {
166         Locomotive &loco = get_item(locomotives, id);
167         if(speed>protocol_info[loco.proto].max_speed)
168                 throw invalid_argument("ArduControl::set_loco_speed");
169
170         if(speed && halted)
171                 return;
172
173         if(loco.speed.set(speed))
174         {
175                 PendingCommand cmd(loco, Locomotive::SPEED);
176                 command_queue.push(cmd);
177
178                 refresh.add_loco(loco);
179         }
180 }
181
182 void ArduControl::set_loco_reverse(unsigned id, bool rev)
183 {
184         Locomotive &loco = get_item(locomotives, id);
185         if(loco.reverse.set(rev))
186         {
187                 PendingCommand cmd(loco, Locomotive::REVERSE);
188                 command_queue.push(cmd);
189
190                 refresh.add_loco(loco);
191         }
192 }
193
194 void ArduControl::set_loco_function(unsigned id, unsigned func, bool state)
195 {
196         Locomotive &loco = get_item(locomotives, id);
197         if(func>protocol_info[loco.proto].max_func)
198                 throw invalid_argument("ArduControl::set_loco_function");
199
200         unsigned mask = 1<<func;
201         if(loco.funcs.set((loco.funcs&~mask)|(mask*state)))
202         {
203                 if(func>0 || loco.proto!=MM)
204                 {
205                         PendingCommand cmd(loco, Locomotive::FUNCTIONS, func);
206                         command_queue.push(cmd);
207                 }
208
209                 refresh.add_loco(loco);
210         }
211 }
212
213 unsigned ArduControl::add_turnout(unsigned addr, const TrackType &type)
214 {
215         if(!addr || !type.is_turnout())
216                 throw invalid_argument("ArduControl::add_turnout");
217
218         return add_accessory(Accessory::TURNOUT, addr, type.get_state_bits(), type.get_paths());
219 }
220
221 void ArduControl::remove_turnout(unsigned addr)
222 {
223         remove_accessory(Accessory::TURNOUT, addr);
224 }
225
226 void ArduControl::set_turnout(unsigned addr, unsigned state)
227 {
228         set_accessory(Accessory::TURNOUT, addr, state);
229 }
230
231 unsigned ArduControl::get_turnout(unsigned addr) const
232 {
233         return get_accessory(Accessory::TURNOUT, addr);
234 }
235
236 unsigned ArduControl::add_signal(unsigned addr, const SignalType &)
237 {
238         return add_accessory(Accessory::SIGNAL, addr, 1, 3);
239 }
240
241 void ArduControl::remove_signal(unsigned addr)
242 {
243         remove_accessory(Accessory::SIGNAL, addr);
244 }
245
246 void ArduControl::set_signal(unsigned addr, unsigned state)
247 {
248         set_accessory(Accessory::SIGNAL, addr, state);
249 }
250
251 unsigned ArduControl::get_signal(unsigned addr) const
252 {
253         return get_accessory(Accessory::SIGNAL, addr);
254 }
255
256 unsigned ArduControl::add_accessory(Accessory::Kind kind, unsigned addr, unsigned bits, unsigned states)
257 {
258         AccessoryMap::iterator i = accessories.lower_bound(addr);
259         AccessoryMap::iterator j = accessories.upper_bound(addr+bits-1);
260         if(i!=j)
261                 throw key_error(addr);
262         if(i!=accessories.begin())
263         {
264                 --i;
265                 if(i->first+i->second.bits>addr)
266                         throw key_error(addr);
267         }
268
269         insert_unique(accessories, addr, Accessory(kind, addr, bits, states));
270         return addr;
271 }
272
273 void ArduControl::remove_accessory(Accessory::Kind kind, unsigned addr)
274 {
275         Accessory &acc = get_item(accessories, addr);
276         if(acc.kind!=kind)
277                 throw key_error(addr);
278         accessories.erase(addr);
279 }
280
281 void ArduControl::set_accessory(Accessory::Kind kind, unsigned addr, unsigned state)
282 {
283         Accessory &acc = get_item(accessories, addr);
284         if(acc.kind!=kind)
285                 throw key_error(addr);
286
287         if(state!=acc.target || acc.uncertain)
288         {
289                 acc.target = state;
290                 accessory_queue.push_back(&acc);
291         }
292 }
293
294 unsigned ArduControl::get_accessory(Accessory::Kind kind, unsigned addr) const
295 {
296         const Accessory &acc = get_item(accessories, addr);
297         if(acc.kind!=kind)
298                 throw key_error(addr);
299         return acc.state;
300 }
301
302 void ArduControl::activate_accessory_by_mask(Accessory &acc, unsigned mask)
303 {
304         unsigned bit = mask&~(mask-1);
305         for(active_index=0; (bit>>active_index)>1; ++active_index) ;
306         acc.state.set((acc.state&~bit)|(acc.target&bit));
307         if(debug>=1)
308                 IO::print("Setting accessory %d bit %d, state=%d\n", acc.address, active_index, acc.state.pending);
309         PendingCommand cmd(acc, Accessory::ACTIVATE, active_index);
310         command_queue.push(cmd);
311         active_accessory = &acc;
312
313         monitor.reset_peak();
314 }
315
316 unsigned ArduControl::add_sensor(unsigned addr)
317 {
318         if(!addr)
319                 throw invalid_argument("ArduControl::add_sensor");
320
321         insert_unique(sensors, addr, Sensor(addr));
322         s88.grow_n_octets((addr+7)/8);
323
324         return addr;
325 }
326
327 void ArduControl::remove_sensor(unsigned addr)
328 {
329         remove_existing(sensors, addr);
330         // TODO update s88.n_octets
331 }
332
333 bool ArduControl::get_sensor(unsigned addr) const
334 {
335         return get_item(sensors, addr).state;
336 }
337
338 const Driver::TelemetryInfo *ArduControl::enumerate_telemetry(unsigned i) const
339 {
340         if(i<4)
341                 return telemetry_info+i;
342         else
343                 return 0;
344 }
345
346 float ArduControl::get_telemetry_value(const string &name) const
347 {
348         if(name==telemetry_info[0].name)
349                 return monitor.get_voltage();
350         else if(name==telemetry_info[1].name)
351                 return monitor.get_current();
352         else if(name==telemetry_info[2].name)
353                 return command_queue.size();
354         else if(name==telemetry_info[3].name)
355                 return s88.get_latency()/Time::msec;
356         else
357                 throw key_error(name);
358 }
359
360 void ArduControl::tick()
361 {
362         Tag tag;
363         while(completed_commands.pop(tag))
364         {
365                 if(tag.type==Tag::GENERAL)
366                 {
367                         if(tag.command==POWER)
368                         {
369                                 if(power.commit(tag.serial))
370                                         signal_power.emit(power.current);
371                         }
372                         else if(tag.command==NEW_LOCO)
373                         {
374                                 MfxInfo info;
375                                 if(mfx_search.pop_info(info))
376                                 {
377                                         MfxInfoArray::iterator i = add_mfx_info(info);
378                                         save_state();
379                                         signal_locomotive_detected.emit(*i);
380                                 }
381                         }
382                 }
383                 else if(tag.type==Tag::LOCOMOTIVE)
384                 {
385                         LocomotiveMap::iterator i = locomotives.find(tag.id);
386                         if(i==locomotives.end())
387                                 continue;
388
389                         Locomotive &loco = i->second;
390                         if(tag.command==Locomotive::SPEED)
391                         {
392                                 if(loco.speed.commit(tag.serial))
393                                         signal_loco_speed.emit(loco.id, loco.speed, loco.reverse);
394                         }
395                         else if(tag.command==Locomotive::REVERSE)
396                         {
397                                 if(loco.reverse.commit(tag.serial))
398                                         signal_loco_speed.emit(loco.id, loco.speed, loco.reverse);
399                         }
400                         else if(tag.command==Locomotive::FUNCTIONS)
401                         {
402                                 unsigned old = loco.funcs;
403                                 if(loco.funcs.commit(tag.serial))
404                                 {
405                                         unsigned changed = old^loco.funcs;
406                                         for(unsigned j=0; changed>>j; ++j)
407                                                 if((changed>>j)&1)
408                                                         signal_loco_function.emit(loco.id, j, (loco.funcs>>j)&1);
409                                 }
410                         }
411                 }
412                 else if(tag.type==Tag::ACCESSORY)
413                 {
414                         AccessoryMap::iterator i = accessories.find(tag.id);
415                         if(i==accessories.end())
416                                 continue;
417
418                         Accessory &acc = i->second;
419                         if(tag.command==Accessory::ACTIVATE)
420                                 off_timeout = Time::now()+acc.active_time;
421                         else if(tag.command==Accessory::DEACTIVATE)
422                         {
423                                 if(acc.state.commit(tag.serial))
424                                 {
425                                         if(&acc==active_accessory)
426                                                 active_accessory = 0;
427                                 }
428                         }
429                 }
430                 else if(tag.type==Tag::SENSOR)
431                 {
432                         SensorMap::iterator i = sensors.find(tag.id);
433                         if(i==sensors.end())
434                                 continue;
435
436                         Sensor &sensor = i->second;
437                         if(tag.command==Sensor::STATE)
438                         {
439                                 if(sensor.state.commit(tag.serial))
440                                         signal_sensor.emit(sensor.address, sensor.state);
441                         }
442                 }
443         }
444
445         while(power && !active_accessory && !accessory_queue.empty())
446         {
447                 Accessory &acc = *accessory_queue.front();
448
449                 if(acc.uncertain)
450                 {
451                         unsigned zeroes = acc.uncertain&~acc.target;
452                         if(zeroes)
453                                 activate_accessory_by_mask(acc, zeroes);
454                         else
455                                 activate_accessory_by_mask(acc, acc.uncertain);
456                 }
457                 else if(acc.state!=acc.target)
458                 {
459                         unsigned changes = acc.state^acc.target;
460                         if(!(changes&((1<<acc.bits)-1)))
461                         {
462                                 // All remaining changes are in non-physical bits
463                                 acc.state.set(acc.state^changes);
464                                 acc.state.commit(acc.state.serial);
465                         }
466                         else
467                         {
468                                 unsigned toggle_bit = 0;
469                                 for(unsigned bit=1; (!toggle_bit && bit<=changes); bit<<=1)
470                                         if((changes&bit) && (acc.valid_states&(1<<(acc.state^bit))))
471                                                 toggle_bit = bit;
472
473                                 activate_accessory_by_mask(acc, toggle_bit);
474                         }
475                 }
476                 else
477                 {
478                         accessory_queue.pop_front();
479
480                         if(acc.state==acc.target)
481                         {
482                                 if(acc.kind==Accessory::TURNOUT)
483                                         signal_turnout.emit(acc.address, acc.state);
484                                 else if(acc.kind==Accessory::SIGNAL)
485                                         signal_signal.emit(acc.address, acc.state);
486                         }
487                 }
488         }
489
490         if(active_accessory && off_timeout)
491         {
492                 bool success = (monitor.get_peak()>0.35f && monitor.get_current()<monitor.get_peak()-0.2f);
493                 Time::TimeStamp t = Time::now();
494                 if(t>off_timeout || success)
495                 {
496                         Accessory &acc = *active_accessory;
497
498                         unsigned bit = 1<<active_index;
499
500                         // Assume success if we were uncertain of the physical setting
501                         if(acc.uncertain&bit)
502                                 acc.uncertain &= ~bit;
503                         else if(acc.kind==Accessory::TURNOUT && !success)
504                         {
505                                 if(debug>=1)
506                                         IO::print("Peak current only %.2f A\n", monitor.get_peak());
507                                 signal_turnout_failed.emit(acc.address);
508                                 acc.state.rollback();
509                                 if(acc.valid_states&(1<<(acc.target^bit)))
510                                         acc.target ^= bit;
511                                 else
512                                         acc.target = acc.state;
513                         }
514
515                         off_timeout = Time::TimeStamp();
516                         PendingCommand cmd(acc, Accessory::DEACTIVATE, active_index);
517                         command_queue.push(cmd);
518                 }
519         }
520 }
521
522 void ArduControl::flush()
523 {
524         while(!command_queue.empty() || (power && !accessory_queue.empty()))
525                 tick();
526 }
527
528 void ArduControl::save_state() const
529 {
530         FS::RedirectedPath tmp_file(state_file);
531         IO::BufferedFile out(tmp_file.str(), IO::M_WRITE);
532         DataFile::Writer writer(out);
533
534         writer.write((DataFile::Statement("mfx_announce_serial"), mfx_announce.get_serial()));
535         for(MfxInfoArray::const_iterator i=mfx_info.begin(); i!=mfx_info.end(); ++i)
536         {
537                 DataFile::Statement st("mfx_locomotive");
538                 st.append(i->id);
539                 st.sub.push_back((DataFile::Statement("address"), i->address));
540                 st.sub.push_back((DataFile::Statement("name"), i->name));
541                 writer.write(st);
542         }
543 }
544
545
546 ArduControl::Tag::Tag():
547         type(NONE),
548         command(0),
549         serial(0),
550         id(0)
551 { }
552
553
554 ArduControl::Locomotive::Locomotive(Protocol p, unsigned a):
555         id((p<<16)|a),
556         proto(p),
557         address(a),
558         speed(0),
559         reverse(false),
560         funcs(0),
561         last_change_age(0)
562 { }
563
564 unsigned ArduControl::Locomotive::create_speed_dir_command(char *buffer) const
565 {
566         if(proto==MM)
567         {
568                 buffer[0] = MOTOROLA_SPEED_DIRECTION;
569                 buffer[1] = address;
570                 buffer[2] = funcs.pending&1;
571                 buffer[3] = speed.pending+reverse.pending*0x80;
572                 return 4;
573         }
574         else if(proto==MFX)
575         {
576                 buffer[0] = MFX_SPEED;
577                 buffer[1] = address>>8;
578                 buffer[2] = address;
579                 buffer[3] = speed.pending+reverse.pending*0x80;
580                 return 4;
581         }
582         else
583                 return 0;
584 }
585
586 unsigned ArduControl::Locomotive::create_speed_func_command(unsigned f, char *buffer) const
587 {
588         if(proto==MM)
589         {
590                 if(f<1 || f>4)
591                         throw invalid_argument("Locomotive::create_speed_func_command");
592
593                 buffer[0] = MOTOROLA_SPEED_FUNCTION;
594                 buffer[1] = address;
595                 buffer[2] = (f<<4)|(((funcs.pending>>f)&1)<<1)|(funcs.pending&1);
596                 buffer[3] = speed.pending;
597                 return 4;
598         }
599         else if(proto==MFX)
600         {
601                 bool f16 = (funcs.pending>0xFF);
602                 buffer[0] = (f16 ? MFX_SPEED_FUNCS16 : MFX_SPEED_FUNCS8);
603                 buffer[1] = address>>8;
604                 buffer[2] = address;
605                 buffer[3] = speed.pending+reverse.pending*0x80;
606                 if(f16)
607                 {
608                         buffer[4] = funcs.pending>>8;
609                         buffer[5] = funcs.pending;
610                         return 6;
611                 }
612                 else
613                 {
614                         buffer[4] = funcs.pending;
615                         return 5;
616                 }
617         }
618         else
619                 return 0;
620 }
621
622
623 ArduControl::Accessory::Accessory(Kind k, unsigned a, unsigned b, unsigned s):
624         kind(k),
625         address(a),
626         bits(b),
627         valid_states(s),
628         state(0),
629         uncertain((1<<bits)-1),
630         target(0),
631         active_time((bits*700)*Time::msec)
632 { }
633
634 unsigned ArduControl::Accessory::create_state_command(unsigned b, bool c, char *buffer) const
635 {
636         if(b>=bits)
637                 throw invalid_argument("Accessory::create_state_command");
638
639         unsigned a = (address+b+3)*2;
640         if(!((state.pending>>b)&1))
641                 ++a;
642         buffer[0] = MOTOROLA_SOLENOID;
643         buffer[1] = a>>3;
644         buffer[2] = ((a&7)<<4)|c;
645         return 3;
646 }
647
648
649 ArduControl::Sensor::Sensor(unsigned a):
650         address(a),
651         state(false)
652 { }
653
654
655 ArduControl::PendingCommand::PendingCommand():
656         length(0),
657         repeat_count(1)
658 { }
659
660 ArduControl::PendingCommand::PendingCommand(GeneralCommand cmd):
661         length(0),
662         repeat_count(1)
663 {
664         tag.type = Tag::GENERAL;
665         tag.command = cmd;
666 }
667
668 ArduControl::PendingCommand::PendingCommand(Locomotive &loco, Locomotive::Command cmd, unsigned index):
669         repeat_count(8)
670 {
671         tag.type = Tag::LOCOMOTIVE;
672         tag.command = cmd;
673         tag.id = loco.id;
674         if(cmd==Locomotive::SPEED)
675         {
676                 tag.serial = loco.speed.serial;
677                 length = loco.create_speed_dir_command(command);
678         }
679         else if(cmd==Locomotive::REVERSE)
680         {
681                 tag.serial = loco.reverse.serial;
682                 length = loco.create_speed_dir_command(command);
683         }
684         else if(cmd==Locomotive::FUNCTIONS)
685         {
686                 tag.serial = loco.funcs.serial;
687                 length = loco.create_speed_func_command(index, command);
688         }
689         else
690                 throw invalid_argument("PendingCommand");
691 }
692
693 ArduControl::PendingCommand::PendingCommand(Accessory &acc, Accessory::Command cmd, unsigned index):
694         repeat_count(1)
695 {
696         tag.type = Tag::ACCESSORY;
697         tag.command = cmd;
698         tag.id = acc.address;
699         if(cmd==Accessory::ACTIVATE || cmd==Accessory::DEACTIVATE)
700         {
701                 tag.serial = acc.state.serial;
702                 length = acc.create_state_command(index, (cmd==Accessory::ACTIVATE), command);
703         }
704         else
705                 throw invalid_argument("PendingCommand");
706 }
707
708
709 template<typename T>
710 void ArduControl::Queue<T>::push(const T &item)
711 {
712         MutexLock lock(mutex);
713         items.push_back(item);
714 }
715
716 template<typename T>
717 bool ArduControl::Queue<T>::pop(T &item)
718 {
719         MutexLock lock(mutex);
720         if(items.empty())
721                 return false;
722
723         item = items.front();
724         items.pop_front();
725         return true;
726 }
727
728 template<typename T>
729 unsigned ArduControl::Queue<T>::size() const
730 {
731         return items.size();
732 }
733
734 template<typename T>
735 bool ArduControl::Queue<T>::empty() const
736 {
737         return items.empty();
738 }
739
740
741 bool ArduControl::CommandQueueTask::get_work(PendingCommand &cmd)
742 {
743         return queue.pop(cmd);
744 }
745
746 void ArduControl::CommandQueueTask::push(const PendingCommand &cmd)
747 {
748         queue.push(cmd);
749 }
750
751
752 ArduControl::Task::Task(const string &n, unsigned p):
753         name(n),
754         priority(p)
755 { }
756
757 void ArduControl::Task::sleep(const Time::TimeDelta &dt)
758 {
759         sleep_timeout = Time::now()+dt;
760 }
761
762
763 ArduControl::CommandQueueTask::CommandQueueTask():
764         Task("CommandQueue")
765 { }
766
767
768 ArduControl::RefreshTask::RefreshTask():
769         Task("Refresh", 2),
770         next(cycle.end()),
771         round(0),
772         loco(0),
773         phase(0)
774 { }
775
776 bool ArduControl::RefreshTask::get_work(PendingCommand &cmd)
777 {
778         if(loco && loco->proto==MM && phase==0)
779         {
780                 cmd.length = loco->create_speed_func_command(round%4+1, cmd.command);
781                 cmd.repeat_count = 2;
782                 ++phase;
783                 return true;
784         }
785
786         loco = get_next_loco();
787         if(!loco)
788                 return false;
789
790         phase = 0;
791         if(loco->proto==MM)
792         {
793                 cmd.length = loco->create_speed_dir_command(cmd.command);
794                 cmd.repeat_count = 2;
795         }
796         else if(loco->proto==MFX)
797                 cmd.length = loco->create_speed_func_command(0, cmd.command);
798         else
799                 return false;
800
801         return true;
802 }
803
804 void ArduControl::RefreshTask::add_loco(Locomotive &l)
805 {
806         MutexLock lock(mutex);
807         cycle.push_back(&l);
808         if(cycle.size()>15)
809         {
810                 LocomotivePtrList::iterator oldest = cycle.begin();
811                 for(LocomotivePtrList::iterator i=cycle.begin(); ++i!=cycle.end(); )
812                         if((*i)->last_change_age>(*oldest)->last_change_age)
813                                 oldest = i;
814                 if(oldest==next)
815                         advance();
816                 cycle.erase(oldest);
817         }
818         if(next==cycle.end())
819                 next = cycle.begin();
820 }
821
822 void ArduControl::RefreshTask::remove_loco(Locomotive &l)
823 {
824         MutexLock lock(mutex);
825         for(LocomotivePtrList::iterator i=cycle.begin(); i!=cycle.end(); ++i)
826                 if(*i==&l)
827                 {
828                         if(i==next)
829                         {
830                                 if(cycle.size()>1)
831                                         advance();
832                                 else
833                                         next = cycle.end();
834                         }
835                         cycle.erase(i);
836                         return;
837                 }
838 }
839
840 ArduControl::Locomotive *ArduControl::RefreshTask::get_next_loco()
841 {
842         MutexLock lock(mutex);
843         if(cycle.empty())
844                 return 0;
845
846         Locomotive *l = *next;
847         advance();
848         return l;
849 }
850
851 void ArduControl::RefreshTask::advance()
852 {
853         ++next;
854         if(next==cycle.end())
855         {
856                 next = cycle.begin();
857                 ++round;
858         }
859 }
860
861
862 ArduControl::S88Task::S88Task(ArduControl &c):
863         Task("S88"),
864         control(c),
865         n_octets(0),
866         octets_remaining(0)
867 { }
868
869 bool ArduControl::S88Task::get_work(PendingCommand &cmd)
870 {
871         if(octets_remaining || !n_octets)
872                 return false;
873
874         Time::TimeStamp t = Time::now();
875         if(last_poll)
876                 latency = t-last_poll;
877         last_poll = t;
878
879         octets_remaining = n_octets;
880         cmd.command[0] = S88_READ;
881         cmd.command[1] = octets_remaining;
882         cmd.length = 2;
883
884         sleep(100*Time::msec);
885
886         return true;
887 }
888
889 void ArduControl::S88Task::process_reply(const char *reply, unsigned length)
890 {
891         unsigned char type = reply[0];
892         if(type==S88_DATA && length>2)
893         {
894                 unsigned offset = static_cast<unsigned char>(reply[1]);
895                 unsigned count = length-2;
896
897                 SensorMap::iterator begin = control.sensors.lower_bound(offset*8+1);
898                 SensorMap::iterator end = control.sensors.upper_bound((offset+count)*8);
899                 for(SensorMap::iterator i=begin; i!=end; ++i)
900                 {
901                         unsigned bit_index = i->first-1-offset*8;
902                         bool state = (reply[2+bit_index/8]>>(7-bit_index%8))&1;
903                         i->second.state.set(state);
904
905                         Tag tag;
906                         tag.type = Tag::SENSOR;
907                         tag.command = Sensor::STATE;
908                         tag.serial = i->second.state.serial;
909                         tag.id = i->first;
910                         control.completed_commands.push(tag);
911                 }
912
913                 if(count>octets_remaining)
914                         octets_remaining = 0;
915                 else
916                         octets_remaining -= count;
917         }
918 }
919
920 void ArduControl::S88Task::set_n_octets(unsigned n)
921 {
922         n_octets = n;
923 }
924
925 void ArduControl::S88Task::grow_n_octets(unsigned n)
926 {
927         if(n>n_octets)
928                 n_octets = n;
929 }
930
931
932 ArduControl::MfxAnnounceTask::MfxAnnounceTask():
933         Task("MfxAnnounce", 1),
934         serial(0)
935 { }
936
937 bool ArduControl::MfxAnnounceTask::get_work(PendingCommand &cmd)
938 {
939         cmd.command[0] = MFX_ANNOUNCE;
940         cmd.command[1] = serial>>8;
941         cmd.command[2] = serial;
942         cmd.length = 3;
943
944         sleep(400*Time::msec);
945
946         return true;
947 }
948
949 void ArduControl::MfxAnnounceTask::set_serial(unsigned s)
950 {
951         serial = s;
952 }
953
954
955 ArduControl::MfxSearchTask::MfxSearchTask(ArduControl &c):
956         Task("MfxSearch", 1),
957         control(c),
958         next_address(1),
959         size(0),
960         bits(0),
961         misses(0)
962 { }
963
964 bool ArduControl::MfxSearchTask::get_work(PendingCommand &cmd)
965 {
966         if(size>32)
967         {
968                 if(control.debug>=1)
969                         IO::print("Assigning MFX address %d to decoder %08X\n", next_address, bits);
970
971                 MfxInfo info;
972                 info.protocol = "MFX";
973                 info.address = next_address;
974                 info.name = format("%08X", bits);
975                 info.id = bits;
976                 queue.push(info);
977
978                 cmd.command[0] = MFX_ASSIGN_ADDRESS;
979                 cmd.command[1] = next_address>>8;
980                 cmd.command[2] = next_address;
981                 for(unsigned i=0; i<4; ++i)
982                         cmd.command[3+i] = bits>>(24-i*8);
983                 cmd.length = 7;
984
985                 cmd.tag.type = Tag::GENERAL;
986                 cmd.tag.command = NEW_LOCO;
987                 cmd.tag.id = bits;
988
989                 size = 0;
990                 bits = 0;
991                 ++next_address;
992
993                 return true;
994         }
995
996         cmd.command[0] = MFX_SEARCH;
997         for(unsigned i=0; i<4; ++i)
998                 cmd.command[1+i] = bits>>(24-i*8);
999         cmd.command[5] = size;
1000         cmd.length = 6;
1001
1002         sleep(200*Time::msec);
1003
1004         if(control.debug>=1)
1005                 IO::print("Search %08X/%d\n", bits, size);
1006
1007         return true;
1008 }
1009
1010 void ArduControl::MfxSearchTask::process_reply(const char *reply, unsigned length)
1011 {
1012         unsigned char type = reply[0];
1013         if(type==MFX_SEARCH_FEEDBACK && length==2)
1014         {
1015                 if(reply[1])
1016                 {
1017                         misses = 0;
1018                         ++size;
1019                 }
1020                 else if(size>0 && misses<6)
1021                 {
1022                         ++misses;
1023                         bits ^= 1<<(32-size);
1024                 }
1025                 else
1026                 {
1027                         sleep(2*Time::sec);
1028                         bits = 0;
1029                         size = 0;
1030                         misses = 0;
1031                 }
1032         }
1033 }
1034
1035 void ArduControl::MfxSearchTask::set_next_address(unsigned a)
1036 {
1037         next_address = a;
1038 }
1039
1040 bool ArduControl::MfxSearchTask::pop_info(MfxInfo &info)
1041 {
1042         return queue.pop(info);
1043 }
1044
1045
1046 ArduControl::MonitorTask::MonitorTask():
1047         Task("Monitor"),
1048         voltage(0),
1049         current(0),
1050         base_level(0),
1051         peak_level(0),
1052         next_type(0)
1053 { }
1054
1055 bool ArduControl::MonitorTask::get_work(PendingCommand &cmd)
1056 {
1057         if(next_type==0)
1058                 cmd.command[0] = READ_INPUT_VOLTAGE;
1059         else
1060                 cmd.command[0] = READ_TRACK_CURRENT;
1061         cmd.length = 1;
1062
1063         sleep(200*Time::msec);
1064         next_type = (next_type+1)%5;
1065
1066         return true;
1067 }
1068
1069 void ArduControl::MonitorTask::process_reply(const char *reply, unsigned length)
1070 {
1071         unsigned char type = reply[0];
1072         if(type==INPUT_VOLTAGE && length==3)
1073                 voltage = ((static_cast<unsigned char>(reply[1])<<8) | static_cast<unsigned char>(reply[2]))/1000.0f;
1074         else if(type==TRACK_CURRENT && length==5)
1075         {
1076                 current = ((static_cast<unsigned char>(reply[1])<<8) | static_cast<unsigned char>(reply[2]))/1000.0f;
1077                 float peak = ((static_cast<unsigned char>(reply[3])<<8) | static_cast<unsigned char>(reply[4]))/1000.0f;
1078                 peak_level = max(peak_level, peak);
1079                 base_level = min(base_level, current);
1080         }
1081 }
1082
1083 void ArduControl::MonitorTask::reset_peak()
1084 {
1085         base_level = current;
1086         peak_level = current;
1087 }
1088
1089
1090 ArduControl::ControlThread::ControlThread(ArduControl &c):
1091         control(c),
1092         done(false)
1093 {
1094         tasks.push_back(&control.command_queue);
1095         tasks.push_back(&control.monitor);
1096         tasks.push_back(&control.mfx_announce);
1097         tasks.push_back(&control.mfx_search);
1098         tasks.push_back(&control.s88);
1099         tasks.push_back(&control.refresh);
1100
1101         launch();
1102 }
1103
1104 void ArduControl::ControlThread::exit()
1105 {
1106         done = true;
1107         join();
1108 }
1109
1110 void ArduControl::ControlThread::main()
1111 {
1112         init_baud_rate();
1113
1114         while(!done)
1115         {
1116                 PendingCommand cmd;
1117                 if(get_work(cmd))
1118                 {
1119                         bool success = true;
1120                         bool resync = false;
1121                         for(unsigned i=0; (success && i<cmd.repeat_count); ++i)
1122                         {
1123                                 unsigned result = do_command(cmd, control.command_timeout);
1124                                 success = (result==COMMAND_OK);
1125                                 resync = (result==0);
1126                         }
1127
1128                         if(success && cmd.tag)
1129                                 control.completed_commands.push(cmd.tag);
1130
1131                         if(resync)
1132                         {
1133                                 if(control.debug>=1)
1134                                         IO::print("Synchronization with ArduControl lost, attempting to recover\n");
1135                                 for(unsigned i=0; (resync && i<16); ++i)
1136                                 {
1137                                         control.serial.put('\xFF');
1138                                         while(IO::poll(control.serial, IO::P_INPUT, control.command_timeout))
1139                                                 resync = (control.serial.get()!=0xFF);
1140                                 }
1141                                 if(resync)
1142                                 {
1143                                         if(control.debug>=1)
1144                                                 IO::print("Resynchronization failed, giving up\n");
1145                                         done = true;
1146                                 }
1147                                 else
1148                                 {
1149                                         if(control.debug>=1)
1150                                                 IO::print("Resynchronization successful\n");
1151                                         if(cmd.tag)
1152                                                 control.command_queue.push(cmd);
1153                                 }
1154                         }
1155                 }
1156                 else
1157                         Time::sleep(10*Time::msec);
1158         }
1159 }
1160
1161 void ArduControl::ControlThread::init_baud_rate()
1162 {
1163         static unsigned rates[] = { 57600, 9600, 19200, 38400, 0 };
1164         unsigned rate = 0;
1165         control.serial.set_data_bits(8);
1166         control.serial.set_parity(IO::Serial::NONE);
1167         control.serial.set_stop_bits(1);
1168         for(unsigned i=0; rates[i]; ++i)
1169         {
1170                 control.serial.set_baud_rate(rates[i]);
1171                 control.serial.put('\xFF');
1172                 if(IO::poll(control.serial, IO::P_INPUT, 500*Time::msec))
1173                 {
1174                         int c = control.serial.get();
1175                         if(c==0xFF)
1176                         {
1177                                 rate = rates[i];
1178                                 break;
1179                         }
1180                 }
1181         }
1182
1183         if(!rate)
1184         {
1185                 if(control.debug>=1)
1186                         IO::print("ArduControl detection failed\n");
1187                 done = true;
1188                 return;
1189         }
1190
1191         if(control.debug>=1)
1192                 IO::print("ArduControl detected at %d bits/s\n", rate);
1193
1194         if(rate!=rates[0])
1195         {
1196                 PendingCommand cmd;
1197                 cmd.command[0] = SET_BAUD_RATE;
1198                 cmd.command[1] = rates[0]>>8;
1199                 cmd.command[2] = rates[0];
1200                 cmd.length = 3;
1201                 if(do_command(cmd, Time::sec)==COMMAND_OK)
1202                 {
1203                         control.serial.set_baud_rate(rates[0]);
1204                         Time::sleep(Time::sec);
1205                         if(do_command(cmd, Time::sec)==COMMAND_OK)
1206                         {
1207                                 if(control.debug>=1)
1208                                         IO::print("Rate changed to %d bits/s\n", rates[0]);
1209                         }
1210                 }
1211         }
1212 }
1213
1214 bool ArduControl::ControlThread::get_work(PendingCommand &cmd)
1215 {
1216         Time::TimeStamp t = Time::now();
1217
1218         unsigned count = 0;
1219         for(; (count<tasks.size() && tasks[count]->get_sleep_timeout()<=t); ++count) ;
1220
1221         for(; count>0; --count)
1222         {
1223                 unsigned i = 0;
1224                 for(unsigned j=1; j<count; ++j)
1225                         if(tasks[j]->get_priority()<tasks[i]->get_priority())
1226                                 i = j;
1227
1228                 Task *task = tasks[i];
1229                 bool result = task->get_work(cmd);
1230
1231                 Time::TimeStamp st = max(task->get_sleep_timeout(), t);
1232                 for(; (i+1<tasks.size() && tasks[i+1]->get_sleep_timeout()<=st); ++i)
1233                         tasks[i] = tasks[i+1];
1234                 tasks[i] = task;
1235
1236                 if(result)
1237                 {
1238                         if(control.debug>=2)
1239                                 IO::print("Scheduled task %s\n", task->get_name());
1240                         return true;
1241                 }
1242         }
1243
1244         // As fallback, send an idle packet for the MM protocol
1245         cmd.command[0] = MOTOROLA_SPEED;
1246         cmd.command[1] = 80;
1247         cmd.command[2] = 0;
1248         cmd.command[3] = 0;
1249         cmd.length = 4;
1250
1251         return true;
1252 }
1253
1254 unsigned ArduControl::ControlThread::do_command(const PendingCommand &cmd, const Time::TimeDelta &timeout)
1255 {
1256         if(control.debug>=2)
1257         {
1258                 string cmd_hex;
1259                 for(unsigned i=0; i<cmd.length; ++i)
1260                         cmd_hex += format(" %02X", static_cast<unsigned char>(cmd.command[i]));
1261                 IO::print("< %02X%s\n", cmd.length^0xFF, cmd_hex);
1262         }
1263
1264         control.serial.put(cmd.length^0xFF);
1265         control.serial.write(cmd.command, cmd.length);
1266
1267         unsigned result = 0;
1268         while(1)
1269         {
1270                 bool got_data;
1271                 if(result)
1272                         got_data = IO::poll(control.serial, IO::P_INPUT, Time::zero);
1273                 else
1274                         got_data = IO::poll(control.serial, IO::P_INPUT, timeout);
1275
1276                 if(!got_data)
1277                         break;
1278
1279                 unsigned rlength = control.serial.get()^0xFF;
1280                 if(rlength>15)
1281                 {
1282                         IO::print("Invalid length %02X\n", rlength);
1283                         continue;
1284                 }
1285
1286                 char reply[15];
1287                 unsigned pos = 0;
1288                 while(pos<rlength)
1289                 {
1290                         if(!IO::poll(control.serial, IO::P_INPUT, timeout))
1291                                 return 0;
1292                         pos += control.serial.read(reply+pos, rlength-pos);
1293                 }
1294
1295                 if(control.debug>=2)
1296                 {
1297                         string reply_hex;
1298                         for(unsigned i=0; i<rlength; ++i)
1299                                 reply_hex += format(" %02X", static_cast<unsigned char>(reply[i]));
1300                         IO::print("> %02X%s\n", rlength^0xFF, reply_hex);
1301                 }
1302
1303                 unsigned r = process_reply(reply, rlength);
1304                 if(r && !result)
1305                         result = r;
1306         }
1307
1308         return result;
1309 }
1310
1311 unsigned ArduControl::ControlThread::process_reply(const char *reply, unsigned rlength)
1312 {
1313         unsigned char type = reply[0];
1314         if((type&0xE0)==0x80)
1315         {
1316                 if(type!=COMMAND_OK)
1317                         IO::print("Error %02X\n", type);
1318                 return type;
1319         }
1320         else if(type==POWER_STATE && rlength==2)
1321                 set_power(reply[1]);
1322         else if(type==OVERCURRENT)
1323         {
1324                 set_power(false);
1325                 IO::print("Overcurrent detected!\n");
1326         }
1327         else
1328         {
1329                 for(vector<Task *>::iterator i=tasks.begin(); i!=tasks.end(); ++i)
1330                         (*i)->process_reply(reply, rlength);
1331         }
1332
1333         return 0;
1334 }
1335
1336 void ArduControl::ControlThread::set_power(bool p)
1337 {
1338         control.power.set(p);
1339
1340         Tag tag;
1341         tag.type = Tag::GENERAL;
1342         tag.command = POWER;
1343         tag.serial = control.power.serial;
1344         control.completed_commands.push(tag);
1345 }
1346
1347
1348 ArduControl::Loader::Loader(ArduControl &c):
1349         DataFile::ObjectLoader<ArduControl>(c)
1350 {
1351         add("mfx_announce_serial", &Loader::mfx_announce_serial);
1352         add("mfx_locomotive", &Loader::mfx_locomotive);
1353 }
1354
1355 void ArduControl::Loader::mfx_announce_serial(unsigned s)
1356 {
1357         obj.mfx_announce.set_serial(s);
1358 }
1359
1360 void ArduControl::Loader::mfx_locomotive(unsigned id)
1361 {
1362         MfxInfo info;
1363         info.id = id;
1364         info.protocol = "MFX";
1365         load_sub(info);
1366         obj.add_mfx_info(info);
1367 }
1368
1369
1370 ArduControl::MfxInfo::Loader::Loader(MfxInfo &i):
1371         DataFile::ObjectLoader<MfxInfo>(i)
1372 {
1373         add("address", static_cast<unsigned MfxInfo::*>(&MfxInfo::address));
1374         add("name", static_cast<string MfxInfo::*>(&MfxInfo::name));
1375 }
1376
1377 } // namespace R2C2