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