]> git.tdb.fi Git - r2c2.git/blob - source/libr2c2/intellibox.h
46c769b925053e60553596841663816c371d7144
[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 remove_loco(unsigned);
141         virtual void set_loco_speed(unsigned, unsigned);
142         virtual void set_loco_reverse(unsigned, bool);
143         virtual void set_loco_function(unsigned, unsigned, bool);
144
145         virtual void add_turnout(unsigned, const TrackType &);
146         virtual void remove_turnout(unsigned);
147 private:
148         void add_turnout(unsigned, unsigned, bool);
149         void turnout_state_changed(unsigned, const Turnout &) const;
150 public:
151         virtual void set_turnout(unsigned, unsigned);
152         virtual unsigned get_turnout(unsigned) const;
153
154         virtual void add_signal(unsigned, const SignalType &);
155         virtual void remove_signal(unsigned);
156         virtual void set_signal(unsigned, unsigned);
157         virtual unsigned get_signal(unsigned) const;
158
159 public:
160         virtual void add_sensor(unsigned);
161         virtual void remove_sensor(unsigned);
162         virtual void set_sensor(unsigned, bool) { }
163         virtual bool get_sensor(unsigned) const;
164
165         virtual void tick();
166         virtual void flush();
167
168 private:
169         Protocol map_protocol(const std::string &) const;
170         void command(Command);
171         void command(Command, const unsigned char *, unsigned);
172         void command(Command, unsigned, const unsigned char *, unsigned);
173         void loco_command(unsigned, unsigned, bool, unsigned, bool);
174         void turnout_command(unsigned, bool, bool);
175         void process_reply(const Msp::Time::TimeStamp &);
176         unsigned read_all(unsigned char *, unsigned);
177         unsigned read_status(Error *);
178         void error(Command, Error);
179 };
180
181 } // namespace R2C2
182
183 #endif