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