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