]> git.tdb.fi Git - r2c2.git/blob - source/libr2c2/arducontrol.h
Restructure ArduControl driver into more manageable units
[r2c2.git] / source / 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/io/serial.h>
7 #include <msp/time/timedelta.h>
8 #include <msp/time/timestamp.h>
9 #include "driver.h"
10
11 namespace R2C2 {
12
13 class ArduControl: public Driver
14 {
15 private:
16         enum Command
17         {
18                 POWER_ON = 0x01,
19                 POWER_OFF = 0x02,
20                 READ_POWER_STATE = 0x03,
21                 READ_TRACK_CURRENT = 0x08,
22                 SET_OVERCURRENT_LIMIT = 0x09,
23                 READ_INPUT_VOLTAGE = 0x0A,
24                 MOTOROLA_SPEED = 0x11,
25                 MOTOROLA_REVERSE = 0x12,
26                 MOTOROLA_SPEED_DIRECTION = 0x13,
27                 MOTOROLA_SPEED_FUNCTION = 0x14,
28                 MOTOROLA_SOLENOID = 0x15,
29                 MFX_SET_STATION_ID = 0x21,
30                 MFX_ANNOUNCE = 0x22,
31                 MFX_SEARCH = 0x23,
32                 MFX_ASSIGN_ADDRESS = 0x24,
33                 MFX_PING = 0x25,
34                 MFX_SPEED = 0x28,
35                 MFX_SPEED_FUNCS8 = 0x29,
36                 MFX_SPEED_FUNCS16 = 0x2A,
37                 S88_READ = 0x30,
38                 COMMAND_OK = 0x80,
39                 RECEIVE_OVERRUN = 0x81,
40                 FRAMING_ERROR = 0x82,
41                 INVALID_COMMAND = 0x83,
42                 LENGTH_ERROR = 0x84,
43                 INVALID_VALUE = 0x85,
44                 OVERCURRENT = 0xA0,
45                 TRACK_CURRENT = 0xC0,
46                 INPUT_VOLTAGE = 0xC1,
47                 POWER_STATE = 0xC2,
48                 S88_DATA = 0xD0,
49                 MFX_FEEDBACK = 0xD1
50         };
51
52         struct Tag
53         {
54                 enum Type
55                 {
56                         NONE,
57                         GENERAL,
58                         LOCOMOTIVE,
59                         ACCESSORY,
60                         SENSOR
61                 };
62
63                 Type type;
64                 unsigned char command;
65                 unsigned short serial;
66                 unsigned id;
67
68                 Tag();
69
70                 operator bool() const { return type!=NONE; }
71         };
72
73         enum GeneralCommand
74         {
75                 POWER
76         };
77
78         enum Protocol
79         {
80                 MM,
81                 MFX
82         };
83
84         struct ProtocolInfo
85         {
86                 unsigned max_address;
87                 unsigned max_speed;
88                 unsigned max_func;
89         };
90
91         template<typename T>
92         struct ControlledVariable
93         {
94                 T current;
95                 T pending;
96                 unsigned short serial;
97
98                 ControlledVariable(): current(), pending(), serial(0) { }
99                 ControlledVariable(T v): current(v), pending(v), serial(0) { }
100
101                 bool set(T v) { if(v==pending) return false; pending = v; ++serial; return true; }
102                 bool commit(unsigned short s) { if(s!=serial) return false; current = pending; return true; }
103
104                 operator T() const { return current; }
105         };
106
107         struct Locomotive
108         {
109                 enum Command
110                 {
111                         SPEED,
112                         REVERSE,
113                         FUNCTIONS
114                 };
115
116                 unsigned id;
117                 Protocol proto;
118                 unsigned address;
119                 ControlledVariable<unsigned> speed;
120                 ControlledVariable<bool> reverse;
121                 ControlledVariable<unsigned> funcs;
122                 unsigned last_change_age;
123
124                 Locomotive(Protocol, unsigned);
125
126                 unsigned create_speed_dir_command(char *) const;
127                 unsigned create_speed_func_command(unsigned, char *) const;
128         };
129
130         struct Accessory
131         {
132                 enum Kind
133                 {
134                         TURNOUT,
135                         SIGNAL
136                 };
137
138                 enum Command
139                 {
140                         ACTIVATE,
141                         DEACTIVATE
142                 };
143
144                 Kind kind;
145                 unsigned address;
146                 unsigned bits;
147                 ControlledVariable<unsigned> state;
148                 unsigned target;
149                 Msp::Time::TimeDelta active_time;
150
151                 Accessory(Kind, unsigned, unsigned);
152
153                 unsigned create_state_command(unsigned, bool, char *) const;
154         };
155
156         struct Sensor
157         {
158                 enum Command
159                 {
160                         STATE
161                 };
162
163                 unsigned address;
164                 ControlledVariable<bool> state;
165
166                 Sensor(unsigned);
167         };
168
169         struct PendingCommand
170         {
171                 Tag tag;
172                 char command[15];
173                 unsigned char length;
174                 unsigned repeat_count;
175
176                 PendingCommand();
177                 PendingCommand(GeneralCommand);
178                 PendingCommand(Locomotive &, Locomotive::Command, unsigned = 0);
179                 PendingCommand(Accessory &, Accessory::Command, unsigned = 0);
180         };
181
182         class Task
183         {
184         protected:
185                 Task() { }
186         public:
187                 virtual ~Task() { }
188
189                 virtual bool get_work(PendingCommand &) = 0;
190                 virtual void process_reply(const char *, unsigned) { }
191         };
192
193         class RefreshTask: public Task
194         {
195         private:
196                 ArduControl &control;
197                 Locomotive *loco;
198                 unsigned phase;
199
200         public:
201                 RefreshTask(ArduControl &);
202
203                 virtual bool get_work(PendingCommand &);
204         };
205
206         class S88Task: public Task
207         {
208         private:
209                 ArduControl &control;
210                 unsigned octets_remaining;
211
212         public:
213                 S88Task(ArduControl &);
214
215                 virtual bool get_work(PendingCommand &);
216                 virtual void process_reply(const char *, unsigned);
217         };
218
219         class MfxAnnounceTask: public Task
220         {
221         private:
222                 ArduControl &control;
223                 Msp::Time::TimeStamp next;
224
225         public:
226                 MfxAnnounceTask(ArduControl &);
227
228                 virtual bool get_work(PendingCommand &);
229         };
230
231         class MfxSearchTask: public Task
232         {
233         private:
234                 ArduControl &control;
235                 Msp::Time::TimeStamp next;
236                 unsigned size;
237                 unsigned bits;
238                 bool pending;
239
240         public:
241                 MfxSearchTask(ArduControl &);
242
243                 virtual bool get_work(PendingCommand &);
244                 virtual void process_reply(const char *, unsigned);
245         };
246
247         class ControlThread: public Msp::Thread
248         {
249         private:
250                 ArduControl &control;
251                 bool done;
252                 std::vector<Task *> tasks;
253
254         public:
255                 ControlThread(ArduControl &);
256
257                 void exit();
258         private:
259                 virtual void main();
260                 bool get_work(PendingCommand &);
261                 unsigned do_command(const PendingCommand &);
262                 unsigned process_reply(const char *, unsigned);
263         };
264
265         typedef std::map<unsigned, Locomotive> LocomotiveMap;
266         typedef std::list<Locomotive *> LocomotivePtrList;
267         typedef std::map<unsigned, Accessory> AccessoryMap;
268         typedef std::list<Accessory *> AccessoryPtrList;
269         typedef std::map<unsigned, Sensor> SensorMap;
270
271         Msp::IO::Serial serial;
272         unsigned debug;
273
274         ControlledVariable<bool> power;
275
276         LocomotiveMap locomotives;
277         LocomotivePtrList refresh_cycle;
278         LocomotivePtrList::iterator next_refresh;
279         unsigned refresh_counter;
280         AccessoryMap accessories;
281         AccessoryPtrList accessory_queue;
282         Accessory *active_accessory;
283         Msp::Time::TimeStamp off_timeout;
284         std::list<PendingCommand> command_queue;
285         std::list<Tag> completed_commands;
286
287         SensorMap sensors;
288         unsigned n_s88_octets;
289
290         unsigned mfx_announce_serial;
291         unsigned next_mfx_address;
292
293         Msp::Mutex mutex;
294         ControlThread thread;
295
296         static ProtocolInfo protocol_info[2];
297
298 public:
299         ArduControl(const std::string &);
300         ~ArduControl();
301
302         virtual void set_power(bool);
303         virtual bool get_power() const { return power; }
304         virtual void halt(bool);
305         virtual bool is_halted() const { return false; }
306
307         virtual const char *enumerate_protocols(unsigned) const;
308 private:
309         static Protocol map_protocol(const std::string &);
310 public:
311         virtual unsigned get_protocol_speed_steps(const std::string &) const;
312
313         virtual unsigned add_loco(unsigned, const std::string &, const VehicleType &);
314         virtual void remove_loco(unsigned);
315         virtual void set_loco_speed(unsigned, unsigned);
316         virtual void set_loco_reverse(unsigned, bool);
317         virtual void set_loco_function(unsigned, unsigned, bool);
318 private:
319         void add_loco_to_refresh(Locomotive &);
320         void remove_loco_from_refresh(Locomotive &);
321         Locomotive *get_loco_to_refresh();
322         void advance_next_refresh();
323
324 public:
325         virtual unsigned add_turnout(unsigned, const TrackType &);
326         virtual void remove_turnout(unsigned);
327         virtual void set_turnout(unsigned, unsigned);
328         virtual unsigned get_turnout(unsigned) const;
329
330         virtual unsigned add_signal(unsigned, const SignalType &);
331         virtual void remove_signal(unsigned);
332         virtual void set_signal(unsigned, unsigned);
333         virtual unsigned get_signal(unsigned) const;
334
335 private:
336         unsigned add_accessory(Accessory::Kind, unsigned, unsigned);
337         void remove_accessory(Accessory::Kind, unsigned);
338         void set_accessory(Accessory::Kind, unsigned, unsigned);
339         unsigned get_accessory(Accessory::Kind, unsigned) const;
340
341 public:
342         virtual unsigned add_sensor(unsigned);
343         virtual void remove_sensor(unsigned);
344         virtual void set_sensor(unsigned, bool) { }
345         virtual bool get_sensor(unsigned) const;
346
347         virtual void tick();
348         virtual void flush();
349
350 private:
351         void push_command(const PendingCommand &);
352         bool pop_command(PendingCommand &);
353         void push_completed_tag(const Tag &);
354         Tag pop_completed_tag();
355 };
356
357 } // namespace R2C2
358
359 #endif