Open Model Railroad Network (OpenMRN)
Loading...
Searching...
No Matches
TractionProxy.cxx
Go to the documentation of this file.
1
37
39#include "dcc/Loco.hxx"
40#include "openlcb/If.hxx"
41
42namespace openlcb
43{
44
47Node *allocate_train_node(uint8_t system, uint8_t addr_hi, uint8_t addr_lo,
48 TrainService* traction_service)
49 __attribute__((weak));
50
51Node *allocate_train_node(uint8_t system, uint8_t addr_hi, uint8_t addr_lo, TrainService* traction_service)
52{
53 TrainImpl *impl = nullptr;
54 if (system == TractionDefs::PROXYTYPE_MARKLIN_DIGITAL)
55 {
56 impl = new dcc::MMNewTrain(dcc::MMAddress(addr_lo));
57 }
58 else if (system == TractionDefs::PROXYTYPE_DCC)
59 {
61 if (addr_hi == 0 && addr_lo < 128)
62 {
63 impl = new dcc::Dcc28Train(dcc::DccShortAddress(addr_lo));
64 }
65 else
66 {
68 (static_cast<uint16_t>(addr_hi) << 8) | addr_lo));
69 }
70 }
71 if (!impl) return nullptr;
72 TrainNode *train_node = new TrainNodeForProxy(traction_service, impl);
73 return train_node;
74}
75
80{
81public:
82 Impl(TrainService *train_service, Node *proxy_node)
83 : trainService_(train_service)
84 , proxyNode_(proxy_node)
85 , proxyEventProducer_(proxyNode_)
86 , handlerFlow_(this)
87 {
88 }
89
90 TrainService *traction_service()
91 {
92 return trainService_;
93 }
94
95 If *iface()
96 {
97 return traction_service()->iface();
98 }
99
100 Node *node()
101 {
102 return proxyNode_;
103 }
104
108 {
109 public:
111 : IncomingMessageStateFlow(impl->iface())
112 , impl_(impl)
113 , response_(nullptr)
114 {
115 iface()->dispatcher()->register_handler(
116 this, Defs::MTI_TRACTION_PROXY_COMMAND, 0xffff);
117 }
118
120 {
121 iface()->dispatcher()->unregister_handler(
122 this, Defs::MTI_TRACTION_PROXY_COMMAND, 0xffff);
123 }
124
125 protected:
127 {
128 if (!nmsg()->dstNode)
129 {
130 LOG(VERBOSE, "Traction proxy message for unknown node.");
131 return release_and_exit();
132 }
133 if (nmsg()->dstNode != impl_->node())
134 {
135 // Wrong node.
136 return release_and_exit();
137 }
138 if (response_)
139 {
140 return call_immediately(STATE(handle_cmd));
141 }
142 else
143 {
144 return allocate_and_call(
145 impl_->iface()->addressed_message_write_flow(),
146 STATE(handle_cmd));
147 }
148 }
149
150 Action handle_cmd()
151 {
152 if (!response_)
153 {
154 response_ = get_allocation_result(
155 impl_->iface()->addressed_message_write_flow());
156 }
157 // No command byte?
158 if (size() < 1)
159 {
160 LOG(VERBOSE, "Traction proxy message with no command byte.");
161 return reject_permanent();
162 }
163 uint8_t cmd = payload()[0];
164 switch (cmd)
165 {
166 case TractionDefs::PROXYREQ_MANAGE:
167 {
168 return call_immediately(STATE(handle_manage));
169 }
170 case TractionDefs::PROXYREQ_ALLOCATE:
171 {
172 return call_immediately(STATE(handle_allocate));
173 }
174 default:
175 {
176 LOG(VERBOSE, "Unknown traction proxy command %x.", cmd);
177 return reject_permanent();
178 }
179 }
180 }
181
182 Action handle_manage()
183 {
184 if (size() < 2)
185 {
186 return reject_permanent();
187 }
188 uint8_t cmd = payload()[1];
189 switch (cmd)
190 {
191 case TractionDefs::PROXYREQ_MANAGE_RESERVE:
192 {
193 uint8_t code = 0xff;
194 if (reserved_)
195 {
196 code = 0x1;
197 }
198 else
199 {
200 code = 0;
201 reserved_ = 1;
202 }
203 Payload p;
204 p.push_back(TractionDefs::PROXYRESP_MANAGE);
205 p.push_back(TractionDefs::PROXYRESP_MANAGE_RESERVE_REPLY);
206 p.push_back(code);
207 response_->data()->reset(Defs::MTI_TRACTION_PROXY_REPLY,
208 nmsg()->dstNode->node_id(),
209 nmsg()->src, p);
210 impl_->iface()->addressed_message_write_flow()->send(
211 response_);
212 response_ = nullptr;
213 return release_and_exit();
214 }
215 case TractionDefs::PROXYREQ_MANAGE_RELEASE:
216 {
217 reserved_ = 0;
218 return release_and_exit();
219 }
220 default:
221 LOG(VERBOSE, "Unknown Traction proxy manage subcommand %x",
222 cmd);
223 return reject_permanent();
224 }
225 }
226
227 Action handle_allocate()
228 {
229 if (size() < 5)
230 {
231 LOG(VERBOSE, "proxy allocate with too short message or not DCC "
232 "legacy technology ID.");
233 return reject_permanent();
234 }
235 uint8_t system = payload()[1];
236 uint8_t addr_hi = payload()[2];
237 uint8_t addr_lo = payload()[3];
238 Node *train_node =
239 allocate_train_node(system, addr_hi, addr_lo, impl_->traction_service());
240
241 if (!train_node)
242 {
243 LOG(VERBOSE, "proxy allocate could not create impl.");
244 return reject_permanent();
245 }
246 Payload b;
247 b.push_back(TractionDefs::PROXYRESP_ALLOCATE);
248 b.push_back(0);
249 b.push_back(system);
250 b.push_back(addr_hi);
251 b.push_back(addr_lo);
252 auto node_id = train_node->node_id();
253 for (int i = 56; i >= 0; i -= 8)
254 {
255 b.push_back((node_id >> i) & 0xff);
256 }
257 response_->data()->reset(Defs::MTI_TRACTION_PROXY_REPLY,
258 nmsg()->dstNode->node_id(), nmsg()->src,
259 b);
260 impl_->iface()->addressed_message_write_flow()->send(response_);
261 response_ = nullptr;
262 return release_and_exit();
263 }
264
266 size_t size()
267 {
268 return nmsg()->payload.size();
269 }
270
272 const uint8_t *payload()
273 {
274 return reinterpret_cast<const uint8_t *>(nmsg()->payload.data());
275 }
276
279 {
280 // An alternative would be to send TERMINATE_DUE_TO_ERROR here.
281 response_->data()->reset(
283 nmsg()->dstNode->node_id(), nmsg()->src,
284 error_to_buffer(Defs::ERROR_PERMANENT, nmsg()->mti));
285 impl_->iface()->addressed_message_write_flow()->send(response_);
286 response_ = nullptr;
287 return release_and_exit();
288 }
289
290 private:
291 Impl *impl_;
292 Buffer<GenMessage> *response_;
293 unsigned reserved_ : 1;
294 };
295
296 TrainService *trainService_;
297 Node *proxyNode_;
299 ProxyRequestFlow handlerFlow_;
300};
301
302TractionProxyService::TractionProxyService(TrainService *train_service,
303 Node *proxy_node)
304 : Service(train_service->iface()->executor())
305{
306 impl_ = new Impl(train_service, proxy_node);
307}
308
309TractionProxyService::~TractionProxyService()
310{
311 delete impl_;
312}
313
314} // namespace openlcb
#define STATE(_fn)
Turns a function name into an argument to be supplied to functions expecting a state.
Definition StateFlow.hxx:61
Base class for all QMember types that hold data in an expandable format.
Definition Buffer.hxx:195
void register_handler(HandlerType *handler, ID id, ID mask)
Adds a new handler to this dispatcher.
void unregister_handler(HandlerType *handler, ID id, ID mask)
Removes a specific instance of a handler from this dispatcher.
virtual void send(MessageType *message, unsigned priority=UINT_MAX)=0
Entry point to the flow.
Node information.
Definition Devtab.hxx:549
Collection of related state machines that pend on incoming messages.
Base::Action Action
Allows using Action without having StateFlowBase:: prefix in front of it.
TrainImpl class for a DCC locomotive.
Definition Loco.hxx:493
TrainImpl structure for Marklin-Motorola v2 protocol locomotives.
Definition Loco.hxx:747
Class that advertises an event ID to be produced.
Abstract class representing an OpenLCB Interface.
Definition If.hxx:185
MessageDispatchFlow * dispatcher()
Definition If.hxx:224
MessageHandler * addressed_message_write_flow()
Definition If.hxx:210
Base class for incoming message handler flows.
Definition If.hxx:400
GenMessage * nmsg()
Returns the NMRAnet message we received.
Definition If.hxx:413
Base class for NMRAnet nodes conforming to the asynchronous interface.
Definition Node.hxx:52
State flow handling incoming OpenLCB messages of MTI == Traction proxy Request.
Action reject_permanent()
Rejects the incoming message with a permanent error.
const uint8_t * payload()
Returns the incoming message payload (bytes).
Action entry() OVERRIDE
Entry into the StateFlow activity.
size_t size()
Returns the size of the incoming message payload.
Abstract base class for train implementations.
Train node class with a an OpenLCB Node ID from the DCC pool. Used for command stations.
Virtual node class for an OpenLCB train protocol node.
Collection of control flows necessary for implementing the Traction Protocol.
#define LOG(level, message...)
Conditionally write a message to the logging output.
Definition logging.h:99
static const int VERBOSE
Loglevel that is usually not printed, reporting debugging information.
Definition logging.h:59
#define OVERRIDE
Function attribute for virtual functions declaring that this funciton is overriding a funciton that s...
Definition macros.h:180
string Payload
Container that carries the data bytes in an NMRAnet message.
Node * allocate_train_node(uint8_t system, uint8_t addr_hi, uint8_t addr_lo, TrainService *traction_service)
string error_to_buffer(uint16_t error_code, uint16_t mti)
Formats a payload for response of error response messages such as OPtioanl Interaction Rejected or Te...
Definition If.cxx:90
Strongly typed wrapper representing a long DCC address.
Definition Address.hxx:66
Strongly typed wrapper representing a short DCC address.
Definition Address.hxx:49
Strongly typed wrapper representing a marklin-motorola protocol address.
Definition Address.hxx:82
@ MTI_OPTIONAL_INTERACTION_REJECTED
rejected request
string payload
Data content in the message body.
Definition If.hxx:113
PImpl Implementation structure for the Traction Proxy service.