]> git.tdb.fi Git - r2c2.git/blob - source/libr2c2/arducontrol.h
Use a common implementation for the three different queues
[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                 SET_BAUD_RATE = 0x70,
39                 COMMAND_OK = 0x80,
40                 RECEIVE_OVERRUN = 0x81,
41                 FRAMING_ERROR = 0x82,
42                 INVALID_COMMAND = 0x83,
43                 LENGTH_ERROR = 0x84,
44                 INVALID_VALUE = 0x85,
45                 OVERCURRENT = 0xA0,
46                 BAUD_CHANGE_FAILED = 0xA1,
47                 TRACK_CURRENT = 0xC0,
48                 INPUT_VOLTAGE = 0xC1,
49                 POWER_STATE = 0xC2,
50                 S88_DATA = 0xD0,
51                 MFX_SEARCH_FEEDBACK = 0xD1,
52                 MFX_PING_FEEDBACK = 0xD2
53         };
54
55         struct Tag
56         {
57                 enum Type
58                 {
59                         NONE,
60                         GENERAL,
61                         LOCOMOTIVE,
62                         ACCESSORY,
63                         SENSOR
64                 };
65
66                 Type type;
67                 unsigned char command;
68                 unsigned short serial;
69                 unsigned id;
70
71                 Tag();
72
73                 operator bool() const { return type!=NONE; }
74         };
75
76         enum GeneralCommand
77         {
78                 POWER,
79                 NEW_LOCO
80         };
81
82         enum Protocol
83         {
84                 MM,
85                 MFX
86         };
87
88         struct ProtocolInfo
89         {
90                 unsigned max_address;
91                 unsigned max_speed;
92                 unsigned max_func;
93         };
94
95         template<typename T>
96         struct ControlledVariable
97         {
98                 T current;
99                 T pending;
100                 unsigned short serial;
101
102                 ControlledVariable(): current(), pending(), serial(0) { }
103                 ControlledVariable(T v): current(v), pending(v), serial(0) { }
104
105                 bool set(T v) { if(v==pending) return false; pending = v; ++serial; return true; }
106                 bool commit(unsigned short s) { if(s!=serial) return false; current = pending; return true; }
107
108                 operator T() const { return current; }
109         };
110
111         struct Locomotive
112         {
113                 enum Command
114                 {
115                         SPEED,
116                         REVERSE,
117                         FUNCTIONS
118                 };
119
120                 unsigned id;
121                 Protocol proto;
122                 unsigned address;
123                 ControlledVariable<unsigned> speed;
124                 ControlledVariable<bool> reverse;
125                 ControlledVariable<unsigned> funcs;
126                 unsigned last_change_age;
127
128                 Locomotive(Protocol, unsigned);
129
130                 unsigned create_speed_dir_command(char *) const;
131                 unsigned create_speed_func_command(unsigned, char *) const;
132         };
133
134         struct MfxInfo: public DetectedLocomotive
135         {
136                 unsigned id;
137         };
138
139         struct Accessory
140         {
141                 enum Kind
142                 {
143                         TURNOUT,
144                         SIGNAL
145                 };
146
147                 enum Command
148                 {
149                         ACTIVATE,
150                         DEACTIVATE
151                 };
152
153                 Kind kind;
154                 unsigned address;
155                 unsigned bits;
156                 ControlledVariable<unsigned> state;
157                 unsigned target;
158                 Msp::Time::TimeDelta active_time;
159
160                 Accessory(Kind, unsigned, unsigned);
161
162                 unsigned create_state_command(unsigned, bool, char *) const;
163         };
164
165         struct Sensor
166         {
167                 enum Command
168                 {
169                         STATE
170                 };
171
172                 unsigned address;
173                 ControlledVariable<bool> state;
174
175                 Sensor(unsigned);
176         };
177
178         struct PendingCommand
179         {
180                 Tag tag;
181                 char command[15];
182                 unsigned char length;
183                 unsigned repeat_count;
184
185                 PendingCommand();
186                 PendingCommand(GeneralCommand);
187                 PendingCommand(Locomotive &, Locomotive::Command, unsigned = 0);
188                 PendingCommand(Accessory &, Accessory::Command, unsigned = 0);
189         };
190
191         template<typename T>
192         class Queue
193         {
194         private:
195                 std::list<T> items;
196                 Msp::Mutex mutex;
197
198         public:
199                 void push(const T &);
200                 bool pop(T &);
201         };
202
203         class Task
204         {
205         protected:
206                 Task() { }
207         public:
208                 virtual ~Task() { }
209
210                 virtual bool get_work(PendingCommand &) = 0;
211                 virtual void process_reply(const char *, unsigned) { }
212         };
213
214         class RefreshTask: public Task
215         {
216         private:
217                 typedef std::list<Locomotive *> LocomotivePtrList;
218
219                 LocomotivePtrList cycle;
220                 LocomotivePtrList::iterator next;
221                 unsigned round;
222                 Locomotive *loco;
223                 unsigned phase;
224                 Msp::Mutex mutex;
225
226         public:
227                 RefreshTask();
228
229                 virtual bool get_work(PendingCommand &);
230
231                 void add_loco(Locomotive &);
232                 void remove_loco(Locomotive &);
233         private:
234                 Locomotive *get_next_loco();
235                 void advance();
236         };
237
238         class S88Task: public Task
239         {
240         private:
241                 ArduControl &control;
242                 unsigned n_octets;
243                 unsigned octets_remaining;
244
245         public:
246                 S88Task(ArduControl &);
247
248                 virtual bool get_work(PendingCommand &);
249                 virtual void process_reply(const char *, unsigned);
250
251                 void set_n_octets(unsigned);
252                 void grow_n_octets(unsigned);
253         };
254
255         class MfxAnnounceTask: public Task
256         {
257         private:
258                 unsigned serial;
259                 Msp::Time::TimeStamp next;
260
261         public:
262                 MfxAnnounceTask();
263
264                 virtual bool get_work(PendingCommand &);
265
266                 void set_serial(unsigned);
267         };
268
269         class MfxSearchTask: public Task
270         {
271         private:
272                 ArduControl &control;
273                 unsigned next_address;
274                 Msp::Time::TimeStamp next;
275                 unsigned size;
276                 unsigned bits;
277                 unsigned misses;
278                 Queue<MfxInfo> queue;
279
280         public:
281                 MfxSearchTask(ArduControl &);
282
283                 virtual bool get_work(PendingCommand &);
284                 virtual void process_reply(const char *, unsigned);
285
286                 bool pop_info(MfxInfo &);
287         };
288
289         class ControlThread: public Msp::Thread
290         {
291         private:
292                 ArduControl &control;
293                 bool done;
294                 std::vector<Task *> tasks;
295
296         public:
297                 ControlThread(ArduControl &);
298
299                 void exit();
300         private:
301                 virtual void main();
302                 void init_baud_rate();
303                 bool get_work(PendingCommand &);
304                 unsigned do_command(const PendingCommand &);
305                 unsigned process_reply(const char *, unsigned);
306         };
307
308         typedef std::map<unsigned, Locomotive> LocomotiveMap;
309         typedef std::vector<MfxInfo> MfxInfoArray;
310         typedef std::map<unsigned, Accessory> AccessoryMap;
311         typedef std::list<Accessory *> AccessoryPtrList;
312         typedef std::map<unsigned, Sensor> SensorMap;
313
314         Msp::IO::Serial serial;
315         unsigned debug;
316
317         ControlledVariable<bool> power;
318
319         LocomotiveMap locomotives;
320         MfxInfoArray mfx_info;
321         AccessoryMap accessories;
322         AccessoryPtrList accessory_queue;
323         Accessory *active_accessory;
324         Msp::Time::TimeStamp off_timeout;
325
326         SensorMap sensors;
327
328         Msp::Mutex mutex;
329         Queue<PendingCommand> command_queue;
330         Queue<Tag> completed_commands;
331         RefreshTask refresh;
332         S88Task s88;
333         MfxAnnounceTask mfx_announce;
334         MfxSearchTask mfx_search;
335         ControlThread thread;
336
337         static ProtocolInfo protocol_info[2];
338
339 public:
340         ArduControl(const std::string &);
341         ~ArduControl();
342
343         virtual void set_power(bool);
344         virtual bool get_power() const { return power; }
345         virtual void halt(bool);
346         virtual bool is_halted() const { return false; }
347
348         virtual const char *enumerate_protocols(unsigned) const;
349 private:
350         static Protocol map_protocol(const std::string &);
351 public:
352         virtual unsigned get_protocol_speed_steps(const std::string &) const;
353
354         virtual const DetectedLocomotive *enumerate_detected_locos(unsigned) const;
355         virtual unsigned add_loco(unsigned, const std::string &, const VehicleType &);
356         virtual void remove_loco(unsigned);
357         virtual void set_loco_speed(unsigned, unsigned);
358         virtual void set_loco_reverse(unsigned, bool);
359         virtual void set_loco_function(unsigned, unsigned, bool);
360
361         virtual unsigned add_turnout(unsigned, const TrackType &);
362         virtual void remove_turnout(unsigned);
363         virtual void set_turnout(unsigned, unsigned);
364         virtual unsigned get_turnout(unsigned) const;
365
366         virtual unsigned add_signal(unsigned, const SignalType &);
367         virtual void remove_signal(unsigned);
368         virtual void set_signal(unsigned, unsigned);
369         virtual unsigned get_signal(unsigned) const;
370
371 private:
372         unsigned add_accessory(Accessory::Kind, unsigned, unsigned);
373         void remove_accessory(Accessory::Kind, unsigned);
374         void set_accessory(Accessory::Kind, unsigned, unsigned);
375         unsigned get_accessory(Accessory::Kind, unsigned) const;
376
377 public:
378         virtual unsigned add_sensor(unsigned);
379         virtual void remove_sensor(unsigned);
380         virtual void set_sensor(unsigned, bool) { }
381         virtual bool get_sensor(unsigned) const;
382
383         virtual void tick();
384         virtual void flush();
385 };
386
387 } // namespace R2C2
388
389 #endif