]> git.tdb.fi Git - r2c2.git/blob - source/libr2c2/intellibox.h
Implement signals in Intellibox driver as well
[r2c2.git] / source / libr2c2 / intellibox.h
1 #ifndef LIBR2C2_INTELLIBOX_H_
2 #define LIBR2C2_INTELLIBOX_H_
3
4 #include <map>
5 #include <msp/io/serial.h>
6 #include <msp/time/timestamp.h>
7 #include "driver.h"
8
9 namespace R2C2 {
10
11 /**
12 Driver for Uhlenbrock Intellibox.  Uses the P50X binary protocol over RS232.
13
14 Motorola decoders with 27 speed steps are supported by manually generating the
15 commands necessary to reach the "half-steps".  However, sending a rapid stream
16 of speed changes to the same locomotive seems to cause excessive lag, so we
17 cheat a bit; instead of sending the half-step command immediately, we send it
18 with a 500ms delay, but only if no new set_loco_speed calls have occurred.  As
19 a downside from this accelerations and decelerations are still jerky.
20 */
21 class Intellibox: public Driver
22 {
23 private:
24         enum Command
25         {
26                 CMD_LOK=0x80,
27                 CMD_LOK_STATUS=0x84,
28                 CMD_LOK_CONFIG=0x85,
29                 CMD_FUNC=0x88,
30                 CMD_FUNC_STATUS=0x8C,
31                 CMD_TURNOUT=0x90,
32                 CMD_TURNOUT_FREE=0x93,
33                 CMD_TURNOUT_STATUS=0x94,
34                 CMD_TURNOUT_GROUP_STATUS=0x95,
35                 CMD_SENSOR_STATUS=0x98,
36                 CMD_SENSOR_REPORT=0x99,
37                 CMD_SENSOR_PARAM_SET=0x9D,
38                 CMD_STATUS=0xA2,
39                 CMD_POWER_OFF=0xA6,
40                 CMD_POWER_ON=0xA7,
41                 CMD_NOP=0xC4,
42                 CMD_EVENT=0xC8,
43                 CMD_EVENT_LOK=0xC9,
44                 CMD_EVENT_TURNOUT=0xCA,
45                 CMD_EVENT_SENSOR=0xCB
46         };
47
48         enum Error
49         {
50                 ERR_NO_ERROR=0,
51                 ERR_SYS_ERROR,
52                 ERR_BAD_PARAM,
53                 ERR_POWER_OFF=0x6,
54                 ERR_NO_LOK_SPACE=0x8,  // No space in lok command buffer
55                 ERR_NO_TURNOUT_SPACE,  // No space in turnout command buffer
56                 ERR_NO_DATA,           // "no Lok status available (Lok is not in a slot)"
57                 ERR_NO_SLOT,           // "there is no slot available"
58                 ERR_BAD_LOK_ADDR,
59                 ERR_LOK_BUSY,
60                 ERR_BAD_TURNOUT_ADDR,
61                 ERR_BAD_SO_VALUE,
62                 ERR_NO_I2C_SPACE,
63                 ERR_LOW_TURNOUT_SPACE=0x40,
64                 ERR_LOK_HALTED,
65                 ERR_LOK_POWER_OFF
66         };
67
68         enum Protocol
69         {
70                 NONE,
71                 MM,
72                 MM_27
73         };
74
75         struct Locomotive
76         {
77                 Protocol protocol;
78                 bool ext_func;
79                 unsigned speed;
80                 bool reverse;
81                 unsigned funcs;
82                 int pending_half_step;
83                 Msp::Time::TimeStamp half_step_delay;
84
85                 Locomotive();
86         };
87
88         // These are used for signals as well, but Intellibox calls them turnouts.
89         struct Turnout
90         {
91                 unsigned bits;
92                 unsigned state;
93                 bool active;
94                 bool synced;
95                 unsigned pending;
96                 Msp::Time::TimeStamp off_timeout;
97                 bool signal;
98
99                 Turnout();
100         };
101
102         struct Sensor
103         {
104                 bool state;
105                 Msp::Time::TimeStamp off_timeout;
106
107                 Sensor();
108         };
109
110         struct CommandSlot
111         {
112                 Command cmd;
113                 unsigned addr;
114                 unsigned char data[8];
115                 unsigned length;
116         };
117
118         Msp::IO::Serial serial;
119         bool power;
120         bool halted;
121         std::map<unsigned, Locomotive> locos;
122         std::map<unsigned, Turnout> turnouts;
123         std::map<unsigned, Sensor> sensors;
124         bool update_sensors;
125         std::list<CommandSlot> queue;
126         bool command_sent;
127         Msp::Time::TimeStamp next_event_query;
128
129 public:
130         Intellibox(const std::string &);
131
132         virtual void set_power(bool);
133         virtual bool get_power() const { return power; }
134         virtual void halt(bool);
135         virtual bool is_halted() const { return halted; }
136
137         virtual const char *enumerate_protocols(unsigned) const;
138         virtual unsigned get_protocol_speed_steps(const std::string &) const;
139         virtual void add_loco(unsigned, const std::string &, const VehicleType &);
140         virtual void set_loco_speed(unsigned, unsigned);
141         virtual void set_loco_reverse(unsigned, bool);
142         virtual void set_loco_function(unsigned, unsigned, bool);
143
144         virtual void add_turnout(unsigned, const TrackType &);
145 private:
146         void add_turnout(unsigned, unsigned, bool);
147         void turnout_state_changed(unsigned, const Turnout &) const;
148 public:
149         virtual void set_turnout(unsigned, unsigned);
150         virtual unsigned get_turnout(unsigned) const;
151
152         virtual void add_signal(unsigned, const SignalType &);
153         virtual void set_signal(unsigned, unsigned);
154         virtual unsigned get_signal(unsigned) const;
155
156 public:
157         virtual void add_sensor(unsigned);
158         virtual void set_sensor(unsigned, bool) { }
159         virtual bool get_sensor(unsigned) const;
160
161         virtual void tick();
162         virtual void flush();
163
164 private:
165         Protocol map_protocol(const std::string &) const;
166         void command(Command);
167         void command(Command, const unsigned char *, unsigned);
168         void command(Command, unsigned, const unsigned char *, unsigned);
169         void loco_command(unsigned, unsigned, bool, unsigned, bool);
170         void turnout_command(unsigned, bool, bool);
171         void process_reply(const Msp::Time::TimeStamp &);
172         unsigned read_all(unsigned char *, unsigned);
173         unsigned read_status(Error *);
174         void error(Command, Error);
175 };
176
177 } // namespace R2C2
178
179 #endif