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