]> git.tdb.fi Git - r2c2.git/blob - source/libr2c2/arducontrol.h
78b360018de17c245629059b789ab6be55c9ba4d
[r2c2.git] / source / libr2c2 / arducontrol.h
1 #ifndef LIBR2C2_ARDUCONTROL_H_
2 #define LIBR2C2_ARDUCONTROL_H_
3
4 #include <deque>
5 #include <msp/core/mutex.h>
6 #include <msp/core/thread.h>
7 #include <msp/datafile/objectloader.h>
8 #include <msp/fs/path.h>
9 #include <msp/io/serial.h>
10 #include <msp/time/timedelta.h>
11 #include <msp/time/timestamp.h>
12 #include "driver.h"
13
14 namespace R2C2 {
15
16 class ArduControl: public Driver
17 {
18 public:
19         class Loader: public Msp::DataFile::ObjectLoader<ArduControl>
20         {
21         public:
22                 Loader(ArduControl &);
23
24         private:
25                 void mfx_announce_serial(unsigned);
26                 void mfx_locomotive(unsigned);
27         };
28
29 private:
30         enum Command
31         {
32                 POWER_ON = 0x01,
33                 POWER_OFF = 0x02,
34                 READ_POWER_STATE = 0x03,
35                 READ_TRACK_CURRENT = 0x08,
36                 SET_OVERCURRENT_LIMIT = 0x09,
37                 READ_INPUT_VOLTAGE = 0x0A,
38                 MOTOROLA_SPEED = 0x11,
39                 MOTOROLA_REVERSE = 0x12,
40                 MOTOROLA_SPEED_DIRECTION = 0x13,
41                 MOTOROLA_SPEED_FUNCTION = 0x14,
42                 MOTOROLA_SOLENOID = 0x15,
43                 MFX_SET_STATION_ID = 0x21,
44                 MFX_ANNOUNCE = 0x22,
45                 MFX_SEARCH = 0x23,
46                 MFX_ASSIGN_ADDRESS = 0x24,
47                 MFX_PING = 0x25,
48                 MFX_READ = 0x26,
49                 MFX_SPEED = 0x28,
50                 MFX_SPEED_FUNCS8 = 0x29,
51                 MFX_SPEED_FUNCS16 = 0x2A,
52                 S88_READ = 0x30,
53                 SET_BAUD_RATE = 0x70,
54                 COMMAND_OK = 0x80,
55                 RECEIVE_OVERRUN = 0x81,
56                 FRAMING_ERROR = 0x82,
57                 INVALID_COMMAND = 0x83,
58                 LENGTH_ERROR = 0x84,
59                 INVALID_VALUE = 0x85,
60                 OVERCURRENT = 0xA0,
61                 BAUD_CHANGE_FAILED = 0xA1,
62                 TRACK_CURRENT = 0xC0,
63                 INPUT_VOLTAGE = 0xC1,
64                 POWER_STATE = 0xC2,
65                 S88_DATA = 0xD0,
66                 MFX_SEARCH_FEEDBACK = 0xD1,
67                 MFX_PING_FEEDBACK = 0xD2,
68                 MFX_READ_FEEDBACK = 0xD3
69         };
70
71         struct Tag
72         {
73                 enum Type
74                 {
75                         NONE,
76                         GENERAL,
77                         LOCOMOTIVE,
78                         ACCESSORY,
79                         SENSOR
80                 };
81
82                 Type type;
83                 unsigned char command;
84                 unsigned short serial;
85                 unsigned id;
86
87                 Tag();
88
89                 operator bool() const { return type!=NONE; }
90         };
91
92         enum GeneralCommand
93         {
94                 POWER,
95                 NEW_LOCO
96         };
97
98         enum Protocol
99         {
100                 MM,
101                 MFX
102         };
103
104         struct ProtocolInfo
105         {
106                 unsigned max_address;
107                 unsigned max_speed;
108                 unsigned max_func;
109         };
110
111         template<typename T>
112         struct ControlledVariable
113         {
114                 T current;
115                 T pending;
116                 unsigned short serial;
117
118                 ControlledVariable(): current(), pending(), serial(0) { }
119                 ControlledVariable(T v): current(v), pending(v), serial(0) { }
120
121                 bool set(T v) { if(v==pending) return false; pending = v; ++serial; return true; }
122                 bool commit(unsigned short s) { if(s!=serial) return false; current = pending; return true; }
123                 void rollback() { pending = current; ++serial; }
124
125                 operator T() const { return current; }
126         };
127
128         struct Locomotive
129         {
130                 enum Command
131                 {
132                         SPEED,
133                         REVERSE,
134                         FUNCTIONS
135                 };
136
137                 unsigned id;
138                 Protocol proto;
139                 unsigned address;
140                 ControlledVariable<unsigned> speed;
141                 ControlledVariable<bool> reverse;
142                 ControlledVariable<unsigned> funcs;
143                 unsigned last_change_age;
144
145                 Locomotive(Protocol, unsigned);
146
147                 unsigned create_speed_dir_command(char *) const;
148                 unsigned create_speed_func_command(unsigned, char *) const;
149         };
150
151         struct MfxInfo: public DetectedLocomotive
152         {
153                 class Loader: public Msp::DataFile::ObjectLoader<MfxInfo>
154                 {
155                 public:
156                         Loader(MfxInfo &);
157                 };
158
159                 unsigned id;
160         };
161
162         struct Accessory
163         {
164                 enum Kind
165                 {
166                         TURNOUT,
167                         SIGNAL
168                 };
169
170                 enum Command
171                 {
172                         ACTIVATE,
173                         DEACTIVATE
174                 };
175
176                 Kind kind;
177                 unsigned address;
178                 unsigned bits;
179                 unsigned valid_states;
180                 ControlledVariable<unsigned> state;
181                 unsigned uncertain;
182                 unsigned target;
183                 Msp::Time::TimeDelta active_time;
184
185                 Accessory(Kind, unsigned, unsigned, unsigned);
186
187                 unsigned create_state_command(unsigned, bool, char *) const;
188         };
189
190         struct Sensor
191         {
192                 enum Command
193                 {
194                         STATE
195                 };
196
197                 unsigned address;
198                 ControlledVariable<bool> state;
199
200                 Sensor(unsigned);
201         };
202
203         struct PendingCommand
204         {
205                 Tag tag;
206                 char command[15];
207                 unsigned char length;
208                 unsigned repeat_count;
209
210                 PendingCommand();
211                 PendingCommand(GeneralCommand);
212                 PendingCommand(Locomotive &, Locomotive::Command, unsigned = 0);
213                 PendingCommand(Accessory &, Accessory::Command, unsigned = 0);
214         };
215
216         template<typename T>
217         class Queue
218         {
219         private:
220                 std::deque<T> items;
221                 Msp::Mutex mutex;
222
223         public:
224                 void push(const T &);
225                 bool pop(T &);
226                 unsigned size() const;
227                 bool empty() const;
228         };
229
230         class Task
231         {
232         protected:
233                 std::string name;
234                 unsigned priority;
235                 Msp::Time::TimeStamp sleep_timeout;
236
237                 Task(const std::string &, unsigned = 0);
238         public:
239                 virtual ~Task() { }
240
241                 const std::string &get_name() const { return name; }
242
243                 virtual bool get_work(PendingCommand &) = 0;
244                 virtual void process_reply(const char *, unsigned) { }
245
246                 unsigned get_priority() const { return priority; }
247                 const Msp::Time::TimeStamp &get_sleep_timeout() const { return sleep_timeout; }
248         protected:
249                 void sleep(const Msp::Time::TimeDelta &);
250         };
251
252         class CommandQueueTask: public Task
253         {
254         private:
255                 Queue<PendingCommand> queue;
256
257         public:
258                 CommandQueueTask();
259
260                 virtual bool get_work(PendingCommand &);
261
262                 void push(const PendingCommand &);
263                 unsigned size() const { return queue.size(); }
264                 bool empty() const { return queue.empty(); }
265         };
266
267         class RefreshTask: public Task
268         {
269         private:
270                 typedef std::list<Locomotive *> LocomotivePtrList;
271
272                 LocomotivePtrList cycle;
273                 LocomotivePtrList::iterator next;
274                 unsigned round;
275                 Locomotive *loco;
276                 unsigned phase;
277                 Msp::Mutex mutex;
278
279         public:
280                 RefreshTask();
281
282                 virtual bool get_work(PendingCommand &);
283
284                 void add_loco(Locomotive &);
285                 void remove_loco(Locomotive &);
286         private:
287                 Locomotive *get_next_loco();
288                 void advance();
289         };
290
291         class S88Task: public Task
292         {
293         private:
294                 ArduControl &control;
295                 unsigned n_octets;
296                 unsigned octets_remaining;
297                 Msp::Time::TimeStamp last_poll;
298                 Msp::Time::TimeDelta latency;
299
300         public:
301                 S88Task(ArduControl &);
302
303                 virtual bool get_work(PendingCommand &);
304                 virtual void process_reply(const char *, unsigned);
305
306                 void set_n_octets(unsigned);
307                 void grow_n_octets(unsigned);
308
309                 const Msp::Time::TimeDelta &get_latency() const { return latency; }
310         };
311
312         class MfxAnnounceTask: public Task
313         {
314         private:
315                 unsigned serial;
316
317         public:
318                 MfxAnnounceTask();
319
320                 virtual bool get_work(PendingCommand &);
321
322                 void set_serial(unsigned);
323                 unsigned get_serial() const { return serial; }
324         };
325
326         class MfxSearchTask: public Task
327         {
328         private:
329                 ArduControl &control;
330                 unsigned next_address;
331                 unsigned size;
332                 unsigned bits;
333                 unsigned misses;
334                 Queue<MfxInfo> queue;
335
336                 MfxInfo *pending_info;
337                 unsigned read_array;
338                 unsigned read_offset;
339                 unsigned read_length;
340                 char read_data[0x40];
341                 unsigned block_size;
342
343         public:
344                 MfxSearchTask(ArduControl &);
345
346                 virtual bool get_work(PendingCommand &);
347                 virtual void process_reply(const char *, unsigned);
348
349                 void set_next_address(unsigned);
350                 bool pop_info(MfxInfo &);
351         };
352
353         class MonitorTask: public Task
354         {
355         private:
356                 float voltage;
357                 float current;
358                 float base_level;
359                 float peak_level;
360                 unsigned next_type;
361
362         public:
363                 MonitorTask();
364
365                 virtual bool get_work(PendingCommand &);
366                 virtual void process_reply(const char *, unsigned);
367
368                 float get_voltage() const { return voltage; }
369                 float get_current() const { return current; }
370                 void reset_peak();
371                 float get_peak() const { return peak_level-base_level; }
372         };
373
374         class ControlThread: public Msp::Thread
375         {
376         private:
377                 ArduControl &control;
378                 bool done;
379                 std::vector<Task *> tasks;
380
381         public:
382                 ControlThread(ArduControl &);
383
384                 void exit();
385         private:
386                 virtual void main();
387                 void init_baud_rate();
388                 bool get_work(PendingCommand &);
389                 unsigned do_command(const PendingCommand &, const Msp::Time::TimeDelta &);
390                 unsigned process_reply(const char *, unsigned);
391                 void set_power(bool);
392         };
393
394         typedef std::map<unsigned, Locomotive> LocomotiveMap;
395         typedef std::vector<MfxInfo> MfxInfoArray;
396         typedef std::map<unsigned, Accessory> AccessoryMap;
397         typedef std::list<Accessory *> AccessoryPtrList;
398         typedef std::map<unsigned, Sensor> SensorMap;
399
400         Msp::IO::Serial serial;
401         unsigned debug;
402         Msp::FS::Path state_file;
403
404         ControlledVariable<bool> power;
405         bool halted;
406
407         LocomotiveMap locomotives;
408         MfxInfoArray mfx_info;
409         AccessoryMap accessories;
410         AccessoryPtrList accessory_queue;
411         Accessory *active_accessory;
412         unsigned char active_index;
413         Msp::Time::TimeStamp off_timeout;
414
415         SensorMap sensors;
416
417         Msp::Time::TimeDelta command_timeout;
418         CommandQueueTask command_queue;
419         Queue<Tag> completed_commands;
420         RefreshTask refresh;
421         S88Task s88;
422         MfxAnnounceTask mfx_announce;
423         MfxSearchTask mfx_search;
424         MonitorTask monitor;
425         ControlThread thread;
426
427         static ProtocolInfo protocol_info[2];
428         static TelemetryInfo telemetry_info[4];
429
430 public:
431         ArduControl(const Options &);
432         ~ArduControl();
433
434         virtual void set_power(bool);
435         virtual bool get_power() const { return power; }
436         virtual void halt(bool);
437         virtual bool is_halted() const { return halted; }
438
439         virtual const char *enumerate_protocols(unsigned) const;
440 private:
441         static Protocol map_protocol(const std::string &);
442 public:
443         virtual unsigned get_protocol_speed_steps(const std::string &) const;
444
445         virtual const DetectedLocomotive *enumerate_detected_locos(unsigned) const;
446         virtual unsigned add_loco(unsigned, const std::string &, const VehicleType &);
447 private:
448         MfxInfoArray::iterator add_mfx_info(const MfxInfo &);
449         MfxInfo *find_mfx_info(unsigned);
450 public:
451         virtual void remove_loco(unsigned);
452         virtual void set_loco_speed(unsigned, unsigned);
453         virtual void set_loco_reverse(unsigned, bool);
454         virtual void set_loco_function(unsigned, unsigned, bool);
455
456         virtual unsigned add_turnout(unsigned, const TrackType &);
457         virtual void remove_turnout(unsigned);
458         virtual void set_turnout(unsigned, unsigned);
459         virtual unsigned get_turnout(unsigned) const;
460
461         virtual unsigned add_signal(unsigned, const SignalType &);
462         virtual void remove_signal(unsigned);
463         virtual void set_signal(unsigned, unsigned);
464         virtual unsigned get_signal(unsigned) const;
465
466 private:
467         unsigned add_accessory(Accessory::Kind, unsigned, unsigned, unsigned);
468         void remove_accessory(Accessory::Kind, unsigned);
469         void set_accessory(Accessory::Kind, unsigned, unsigned);
470         unsigned get_accessory(Accessory::Kind, unsigned) const;
471         void activate_accessory_by_mask(Accessory &, unsigned);
472
473 public:
474         virtual unsigned add_sensor(unsigned);
475         virtual void remove_sensor(unsigned);
476         virtual void set_sensor(unsigned, bool) { }
477         virtual bool get_sensor(unsigned) const;
478
479         virtual const TelemetryInfo *enumerate_telemetry(unsigned) const;
480         virtual float get_telemetry_value(const std::string &) const;
481
482         virtual void tick();
483         virtual void flush();
484 private:
485         void save_state() const;
486 };
487
488 } // namespace R2C2
489
490 #endif