]> git.tdb.fi Git - r2c2.git/blob - source/libr2c2/arducontrol.cpp
Read MFX locomotive name
[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         pending_info(0),
963         read_array(0),
964         read_offset(0),
965         read_length(0),
966         block_size(0)
967 { }
968
969 bool ArduControl::MfxSearchTask::get_work(PendingCommand &cmd)
970 {
971         if(read_length>0)
972         {
973                 cmd.command[0] = MFX_READ;
974                 cmd.command[1] = pending_info->address>>8;
975                 cmd.command[2] = pending_info->address;
976                 unsigned index = read_array*0x40+read_offset;
977                 cmd.command[3] = index>>8;
978                 cmd.command[4] = index;
979                 unsigned length = (read_length>=4 ? 4 : read_length>=2 ? 2 : 1);
980                 cmd.command[5] = length;
981                 cmd.length = 6;
982
983                 sleep(100*Time::msec);
984
985                 return true;
986         }
987         else if(pending_info)
988         {
989                 queue.push(*pending_info);
990                 Tag tag;
991                 tag.type = Tag::GENERAL;
992                 tag.command = NEW_LOCO;
993                 tag.id = pending_info->id;
994                 control.completed_commands.push(tag);
995
996                 if(control.debug>=1)
997                         IO::print("Completed processing locomotive %s at address %d\n", pending_info->name, pending_info->address);
998
999                 delete pending_info;
1000                 pending_info = 0;
1001         }
1002
1003         if(size>32)
1004         {
1005                 if(control.debug>=1)
1006                         IO::print("Assigning MFX address %d to decoder %08X\n", next_address, bits);
1007
1008                 pending_info = new MfxInfo;
1009                 pending_info->protocol = "MFX";
1010                 pending_info->address = next_address;
1011                 pending_info->name = format("%08X", bits);
1012                 pending_info->id = bits;
1013
1014                 cmd.command[0] = MFX_ASSIGN_ADDRESS;
1015                 cmd.command[1] = next_address>>8;
1016                 cmd.command[2] = next_address;
1017                 for(unsigned i=0; i<4; ++i)
1018                         cmd.command[3+i] = bits>>(24-i*8);
1019                 cmd.length = 7;
1020
1021                 size = 0;
1022                 bits = 0;
1023                 misses = 0;
1024                 ++next_address;
1025
1026                 read_array = 0;
1027                 read_offset = 0;
1028                 read_length = 6;
1029
1030                 return true;
1031         }
1032
1033         cmd.command[0] = MFX_SEARCH;
1034         for(unsigned i=0; i<4; ++i)
1035                 cmd.command[1+i] = bits>>(24-i*8);
1036         cmd.command[5] = size;
1037         cmd.length = 6;
1038
1039         sleep(100*Time::msec);
1040
1041         if(control.debug>=1)
1042                 IO::print("Search %08X/%d\n", bits, size);
1043
1044         return true;
1045 }
1046
1047 void ArduControl::MfxSearchTask::process_reply(const char *reply, unsigned length)
1048 {
1049         unsigned char type = reply[0];
1050         if(type==MFX_SEARCH_FEEDBACK && length==2)
1051         {
1052                 if(reply[1])
1053                 {
1054                         misses = 0;
1055                         ++size;
1056                 }
1057                 else if(size>0 && misses<6)
1058                 {
1059                         ++misses;
1060                         bits ^= 1<<(32-size);
1061                 }
1062                 else
1063                 {
1064                         sleep(2*Time::sec);
1065                         bits = 0;
1066                         size = 0;
1067                         misses = 0;
1068                 }
1069         }
1070         else if(type==MFX_READ_FEEDBACK && length>=3)
1071         {
1072                 if(reply[1])
1073                 {
1074                         misses = 0;
1075
1076                         for(unsigned i=2; i<length; ++i)
1077                                 read_data[read_offset+i-2] = reply[i];
1078                         read_offset += length-2;
1079                         read_length -= length-2;
1080
1081                         if(!read_length)
1082                         {
1083                                 if(read_array==0)
1084                                         block_size = static_cast<unsigned char>(read_data[4])*static_cast<unsigned char>(read_data[5]);
1085
1086                                 bool array_handled = false;
1087                                 if(read_data[0]==0x18)
1088                                 {
1089                                         for(unsigned i=1; i<read_offset; ++i)
1090                                                 if(!read_data[i])
1091                                                 {
1092                                                         pending_info->name = string(read_data+1, i-1);
1093                                                         array_handled = true;
1094                                                         break;
1095                                                 }
1096
1097                                         if(!array_handled)
1098                                                 read_length = 4;
1099                                 }
1100                                 else
1101                                         array_handled = true;
1102
1103                                 if(array_handled && control.debug>=1)
1104                                 {
1105                                         IO::print("MFX CA %03X:", read_array);
1106                                         for(unsigned i=0; i<read_offset; ++i)
1107                                                 IO::print(" %02X", static_cast<unsigned char>(read_data[i]));
1108                                         IO::print("\n");
1109                                 }
1110
1111                                 if(array_handled && read_array<block_size)
1112                                 {
1113                                         ++read_array;
1114                                         read_offset = 0;
1115                                         read_length = 1;
1116                                 }
1117                         }
1118                 }
1119                 else
1120                 {
1121                         ++misses;
1122                         if(misses>=10)
1123                         {
1124                                 if(control.debug>=1)
1125                                         IO::print("Failed to read MFX configuration from %d\n", pending_info->address);
1126                                 read_length = 0;
1127                         }
1128                 }
1129         }
1130 }
1131
1132 void ArduControl::MfxSearchTask::set_next_address(unsigned a)
1133 {
1134         next_address = a;
1135 }
1136
1137 bool ArduControl::MfxSearchTask::pop_info(MfxInfo &info)
1138 {
1139         return queue.pop(info);
1140 }
1141
1142
1143 ArduControl::MonitorTask::MonitorTask():
1144         Task("Monitor"),
1145         voltage(0),
1146         current(0),
1147         base_level(0),
1148         peak_level(0),
1149         next_type(0)
1150 { }
1151
1152 bool ArduControl::MonitorTask::get_work(PendingCommand &cmd)
1153 {
1154         if(next_type==0)
1155                 cmd.command[0] = READ_INPUT_VOLTAGE;
1156         else
1157                 cmd.command[0] = READ_TRACK_CURRENT;
1158         cmd.length = 1;
1159
1160         sleep(200*Time::msec);
1161         next_type = (next_type+1)%5;
1162
1163         return true;
1164 }
1165
1166 void ArduControl::MonitorTask::process_reply(const char *reply, unsigned length)
1167 {
1168         unsigned char type = reply[0];
1169         if(type==INPUT_VOLTAGE && length==3)
1170                 voltage = ((static_cast<unsigned char>(reply[1])<<8) | static_cast<unsigned char>(reply[2]))/1000.0f;
1171         else if(type==TRACK_CURRENT && length==5)
1172         {
1173                 current = ((static_cast<unsigned char>(reply[1])<<8) | static_cast<unsigned char>(reply[2]))/1000.0f;
1174                 float peak = ((static_cast<unsigned char>(reply[3])<<8) | static_cast<unsigned char>(reply[4]))/1000.0f;
1175                 peak_level = max(peak_level, peak);
1176                 base_level = min(base_level, current);
1177         }
1178 }
1179
1180 void ArduControl::MonitorTask::reset_peak()
1181 {
1182         base_level = current;
1183         peak_level = current;
1184 }
1185
1186
1187 ArduControl::ControlThread::ControlThread(ArduControl &c):
1188         control(c),
1189         done(false)
1190 {
1191         tasks.push_back(&control.command_queue);
1192         tasks.push_back(&control.monitor);
1193         tasks.push_back(&control.mfx_announce);
1194         tasks.push_back(&control.mfx_search);
1195         tasks.push_back(&control.s88);
1196         tasks.push_back(&control.refresh);
1197
1198         launch();
1199 }
1200
1201 void ArduControl::ControlThread::exit()
1202 {
1203         done = true;
1204         join();
1205 }
1206
1207 void ArduControl::ControlThread::main()
1208 {
1209         init_baud_rate();
1210
1211         while(!done)
1212         {
1213                 PendingCommand cmd;
1214                 if(get_work(cmd))
1215                 {
1216                         bool success = true;
1217                         bool resync = false;
1218                         for(unsigned i=0; (success && i<cmd.repeat_count); ++i)
1219                         {
1220                                 unsigned result = do_command(cmd, control.command_timeout);
1221                                 success = (result==COMMAND_OK);
1222                                 resync = (result==0);
1223                         }
1224
1225                         if(success && cmd.tag)
1226                                 control.completed_commands.push(cmd.tag);
1227
1228                         if(resync)
1229                         {
1230                                 if(control.debug>=1)
1231                                         IO::print("Synchronization with ArduControl lost, attempting to recover\n");
1232                                 for(unsigned i=0; (resync && i<16); ++i)
1233                                 {
1234                                         control.serial.put('\xFF');
1235                                         while(IO::poll(control.serial, IO::P_INPUT, control.command_timeout))
1236                                                 resync = (control.serial.get()!=0xFF);
1237                                 }
1238                                 if(resync)
1239                                 {
1240                                         if(control.debug>=1)
1241                                                 IO::print("Resynchronization failed, giving up\n");
1242                                         done = true;
1243                                 }
1244                                 else
1245                                 {
1246                                         if(control.debug>=1)
1247                                                 IO::print("Resynchronization successful\n");
1248                                         if(cmd.tag)
1249                                                 control.command_queue.push(cmd);
1250                                 }
1251                         }
1252                 }
1253                 else
1254                         Time::sleep(10*Time::msec);
1255         }
1256 }
1257
1258 void ArduControl::ControlThread::init_baud_rate()
1259 {
1260         static unsigned rates[] = { 57600, 9600, 19200, 38400, 0 };
1261         unsigned rate = 0;
1262         control.serial.set_data_bits(8);
1263         control.serial.set_parity(IO::Serial::NONE);
1264         control.serial.set_stop_bits(1);
1265         for(unsigned i=0; rates[i]; ++i)
1266         {
1267                 control.serial.set_baud_rate(rates[i]);
1268                 control.serial.put('\xFF');
1269                 if(IO::poll(control.serial, IO::P_INPUT, 500*Time::msec))
1270                 {
1271                         int c = control.serial.get();
1272                         if(c==0xFF)
1273                         {
1274                                 rate = rates[i];
1275                                 break;
1276                         }
1277                 }
1278         }
1279
1280         if(!rate)
1281         {
1282                 if(control.debug>=1)
1283                         IO::print("ArduControl detection failed\n");
1284                 done = true;
1285                 return;
1286         }
1287
1288         if(control.debug>=1)
1289                 IO::print("ArduControl detected at %d bits/s\n", rate);
1290
1291         if(rate!=rates[0])
1292         {
1293                 PendingCommand cmd;
1294                 cmd.command[0] = SET_BAUD_RATE;
1295                 cmd.command[1] = rates[0]>>8;
1296                 cmd.command[2] = rates[0];
1297                 cmd.length = 3;
1298                 if(do_command(cmd, Time::sec)==COMMAND_OK)
1299                 {
1300                         control.serial.set_baud_rate(rates[0]);
1301                         Time::sleep(Time::sec);
1302                         if(do_command(cmd, Time::sec)==COMMAND_OK)
1303                         {
1304                                 if(control.debug>=1)
1305                                         IO::print("Rate changed to %d bits/s\n", rates[0]);
1306                         }
1307                 }
1308         }
1309 }
1310
1311 bool ArduControl::ControlThread::get_work(PendingCommand &cmd)
1312 {
1313         Time::TimeStamp t = Time::now();
1314
1315         unsigned count = 0;
1316         for(; (count<tasks.size() && tasks[count]->get_sleep_timeout()<=t); ++count) ;
1317
1318         for(; count>0; --count)
1319         {
1320                 unsigned i = 0;
1321                 for(unsigned j=1; j<count; ++j)
1322                         if(tasks[j]->get_priority()<tasks[i]->get_priority())
1323                                 i = j;
1324
1325                 Task *task = tasks[i];
1326                 bool result = task->get_work(cmd);
1327
1328                 Time::TimeStamp st = max(task->get_sleep_timeout(), t);
1329                 for(; (i+1<tasks.size() && tasks[i+1]->get_sleep_timeout()<=st); ++i)
1330                         tasks[i] = tasks[i+1];
1331                 tasks[i] = task;
1332
1333                 if(result)
1334                 {
1335                         if(control.debug>=2)
1336                                 IO::print("Scheduled task %s\n", task->get_name());
1337                         return true;
1338                 }
1339         }
1340
1341         // As fallback, send an idle packet for the MM protocol
1342         cmd.command[0] = MOTOROLA_SPEED;
1343         cmd.command[1] = 80;
1344         cmd.command[2] = 0;
1345         cmd.command[3] = 0;
1346         cmd.length = 4;
1347
1348         return true;
1349 }
1350
1351 unsigned ArduControl::ControlThread::do_command(const PendingCommand &cmd, const Time::TimeDelta &timeout)
1352 {
1353         if(control.debug>=2)
1354         {
1355                 string cmd_hex;
1356                 for(unsigned i=0; i<cmd.length; ++i)
1357                         cmd_hex += format(" %02X", static_cast<unsigned char>(cmd.command[i]));
1358                 IO::print("< %02X%s\n", cmd.length^0xFF, cmd_hex);
1359         }
1360
1361         control.serial.put(cmd.length^0xFF);
1362         control.serial.write(cmd.command, cmd.length);
1363
1364         unsigned result = 0;
1365         while(1)
1366         {
1367                 bool got_data;
1368                 if(result)
1369                         got_data = IO::poll(control.serial, IO::P_INPUT, Time::zero);
1370                 else
1371                         got_data = IO::poll(control.serial, IO::P_INPUT, timeout);
1372
1373                 if(!got_data)
1374                         break;
1375
1376                 unsigned rlength = control.serial.get()^0xFF;
1377                 if(rlength>15)
1378                 {
1379                         IO::print("Invalid length %02X\n", rlength);
1380                         continue;
1381                 }
1382
1383                 char reply[15];
1384                 unsigned pos = 0;
1385                 while(pos<rlength)
1386                 {
1387                         if(!IO::poll(control.serial, IO::P_INPUT, timeout))
1388                                 return 0;
1389                         pos += control.serial.read(reply+pos, rlength-pos);
1390                 }
1391
1392                 if(control.debug>=2)
1393                 {
1394                         string reply_hex;
1395                         for(unsigned i=0; i<rlength; ++i)
1396                                 reply_hex += format(" %02X", static_cast<unsigned char>(reply[i]));
1397                         IO::print("> %02X%s\n", rlength^0xFF, reply_hex);
1398                 }
1399
1400                 unsigned r = process_reply(reply, rlength);
1401                 if(r && !result)
1402                         result = r;
1403         }
1404
1405         return result;
1406 }
1407
1408 unsigned ArduControl::ControlThread::process_reply(const char *reply, unsigned rlength)
1409 {
1410         unsigned char type = reply[0];
1411         if((type&0xE0)==0x80)
1412         {
1413                 if(type!=COMMAND_OK)
1414                         IO::print("Error %02X\n", type);
1415                 return type;
1416         }
1417         else if(type==POWER_STATE && rlength==2)
1418                 set_power(reply[1]);
1419         else if(type==OVERCURRENT)
1420         {
1421                 set_power(false);
1422                 IO::print("Overcurrent detected!\n");
1423         }
1424         else
1425         {
1426                 for(vector<Task *>::iterator i=tasks.begin(); i!=tasks.end(); ++i)
1427                         (*i)->process_reply(reply, rlength);
1428         }
1429
1430         return 0;
1431 }
1432
1433 void ArduControl::ControlThread::set_power(bool p)
1434 {
1435         control.power.set(p);
1436
1437         Tag tag;
1438         tag.type = Tag::GENERAL;
1439         tag.command = POWER;
1440         tag.serial = control.power.serial;
1441         control.completed_commands.push(tag);
1442 }
1443
1444
1445 ArduControl::Loader::Loader(ArduControl &c):
1446         DataFile::ObjectLoader<ArduControl>(c)
1447 {
1448         add("mfx_announce_serial", &Loader::mfx_announce_serial);
1449         add("mfx_locomotive", &Loader::mfx_locomotive);
1450 }
1451
1452 void ArduControl::Loader::mfx_announce_serial(unsigned s)
1453 {
1454         obj.mfx_announce.set_serial(s);
1455 }
1456
1457 void ArduControl::Loader::mfx_locomotive(unsigned id)
1458 {
1459         MfxInfo info;
1460         info.id = id;
1461         info.protocol = "MFX";
1462         load_sub(info);
1463         obj.add_mfx_info(info);
1464 }
1465
1466
1467 ArduControl::MfxInfo::Loader::Loader(MfxInfo &i):
1468         DataFile::ObjectLoader<MfxInfo>(i)
1469 {
1470         add("address", static_cast<unsigned MfxInfo::*>(&MfxInfo::address));
1471         add("name", static_cast<string MfxInfo::*>(&MfxInfo::name));
1472 }
1473
1474 } // namespace R2C2