Open Model Railroad Network (OpenMRN)
Loading...
Searching...
No Matches
TractionTrain.cxx
Go to the documentation of this file.
1
35//#define LOGLEVEL VERBOSE
36
38
39#include "utils/logging.h"
40#include "openlcb/If.hxx"
41
42namespace openlcb
43{
44
45DefaultTrainNode::DefaultTrainNode(TrainService *service, TrainImpl *train)
46 : service_(service)
47 , train_(train)
48 , isInitialized_(0)
49 , controllerNodeId_({0, 0})
50{
51}
52
53TrainNode::~TrainNode()
54{
55}
56
57TrainNodeWithConsist::~TrainNodeWithConsist()
58{
59 while (!consistSlaves_.empty())
60 {
61 delete consistSlaves_.pop_front();
62 }
63}
64
65DefaultTrainNode::~DefaultTrainNode()
66{
67}
68
69TrainNodeForProxy::TrainNodeForProxy(TrainService *service, TrainImpl *train)
70 : DefaultTrainNode(service, train)
71{
72 service->register_train(this);
73}
74
76{
79 // service_->unregister_train(this);
80}
81
83 TrainService *service, TrainImpl *train, NodeID node_id)
84 : DefaultTrainNode(service, train)
85 , nodeId_(node_id)
86{
87 service->register_train(this);
88}
89
91{
94 // service_->unregister_train(this);
95}
96
102
103If *DefaultTrainNode::iface()
104{
105 return service_->iface();
106}
107
112{
114
115 Impl(TrainService *parent) : traction_(parent)
116 {
117 }
118
122 {
123 public:
125 : IncomingMessageStateFlow(service->iface())
126 , reserved_(0)
127 , trainService_(service)
128 , response_(nullptr)
129 {
130 iface()->dispatcher()->register_handler(
131 this, Defs::MTI_TRACTION_CONTROL_COMMAND, 0xffff);
132 }
133
135 {
136 iface()->dispatcher()->unregister_handler(
137 this, Defs::MTI_TRACTION_CONTROL_COMMAND, 0xffff);
138 }
139
140 protected:
141 TrainNode *train_node()
142 {
143 return static_cast<TrainNode *>(nmsg()->dstNode);
144 }
145
146 Action maybe_alloc_response(Callback c)
147 {
148 if (response_)
149 {
150 return call_immediately(c);
151 }
152 else
153 {
154 return allocate_and_call(
155 iface()->addressed_message_write_flow(), c);
156 }
157 }
158
159 void ensure_response_exists()
160 {
161 if (!response_)
162 {
163 response_ = get_allocation_result(
164 iface()->addressed_message_write_flow());
165 }
166 }
167
169 {
170 // If the message is not for a local node, ignore.
171 if (!nmsg()->dstNode)
172 {
173 LOG(VERBOSE, "Traction message for unknown node.");
174 return release_and_exit();
175 }
176 // Checks if destination is a local traction-enabled node.
177 if (!trainService_->nodes_->is_node_registered(train_node()))
178 {
179 LOG(VERBOSE, "Traction message for node %p that is not "
180 "traction enabled.",
181 train_node());
185 return release_and_exit();
186 }
187 train_node()->command_hook(nmsg()->src, nmsg()->payload);
188 // No command byte?
189 if (size() < 1)
190 {
191 LOG(VERBOSE, "Traction message with no command byte.");
192 return reject_permanent();
193 }
194 uint8_t cmd = payload()[0] & TractionDefs::REQ_MASK;
195 switch (cmd)
196 {
200 case TractionDefs::REQ_SET_SPEED:
201 {
202 SpeedType sp = fp16_to_speed(payload() + 1);
203 train_node()->train()->set_speed(sp);
204 nextConsistIndex_ = 0;
205 return call_immediately(STATE(maybe_forward_consist));
206 }
207 case TractionDefs::REQ_SET_FN:
208 {
209 uint32_t address = payload()[1];
210 address <<= 8;
211 address |= payload()[2];
212 address <<= 8;
213 address |= payload()[3];
214 uint16_t value = payload()[4];
215 value <<= 8;
216 value |= payload()[5];
217 bn_.reset(this);
218 bool should_apply =
219 train_node()->function_policy(nmsg()->src, payload()[0],
220 address, value, bn_.new_child());
221 // The function_policy call may have completed inline. We
222 // can inquire from the barrier. If it was not completed
223 // inline, we have to wait for the notification and re-try
224 // the call.
225 if (!bn_.abort_if_almost_done())
226 {
227 // Not notified inline.
228 bn_.notify(); // consumes our share
229 return wait();
230 }
231 if (should_apply)
232 {
233 train_node()->train()->set_fn(address, value);
234 }
235 nextConsistIndex_ = 0;
236 return call_immediately(STATE(maybe_forward_consist));
237 }
238 case TractionDefs::REQ_EMERGENCY_STOP:
239 {
240 train_node()->train()->set_emergencystop();
241 nextConsistIndex_ = 0;
242 return call_immediately(STATE(maybe_forward_consist));
243 }
244 case TractionDefs::REQ_QUERY_SPEED: // fall through
245 case TractionDefs::REQ_QUERY_FN:
246 {
247 // Need a response message first.
248 return maybe_alloc_response(STATE(handle_query));
249 }
250 case TractionDefs::REQ_CONTROLLER_CONFIG:
251 {
252 return maybe_alloc_response(
254 }
255 case TractionDefs::REQ_CONSIST_CONFIG:
256 {
257 return maybe_alloc_response(
258 STATE(handle_consist_config));
259 }
260 case TractionDefs::REQ_TRACTION_MGMT:
261 {
262 return maybe_alloc_response(STATE(handle_traction_mgmt));
263 }
264 default:
265 {
266 LOG(VERBOSE, "Rejecting unknown traction message.");
267 return reject_permanent();
268 }
269 }
270 }
271
272 Action handle_query()
273 {
275 uint8_t cmd = payload()[0];
276 switch (cmd)
277 {
278 case TractionDefs::REQ_QUERY_SPEED:
279 {
280 p->resize(8);
281 uint8_t *d = reinterpret_cast<uint8_t *>(&(*p)[0]);
282 d[0] = TractionDefs::RESP_QUERY_SPEED;
283 speed_to_fp16(train_node()->train()->get_speed(), d + 1);
284 uint8_t status = 0;
285 if (train_node()->train()->get_emergencystop())
286 {
287 status |= TractionDefs::SPEEDRESP_STATUS_IS_ESTOP;
288 }
289 d[3] = status;
290 speed_to_fp16(train_node()->train()->get_commanded_speed(),
291 d + 4);
292 speed_to_fp16(train_node()->train()->get_actual_speed(),
293 d + 6);
294 return send_response();
295 }
296 case TractionDefs::REQ_QUERY_FN:
297 {
298 p->resize(6);
299 uint8_t *d = reinterpret_cast<uint8_t *>(&(*p)[0]);
300 d[0] = TractionDefs::RESP_QUERY_FN;
301 d[1] = payload()[1];
302 d[2] = payload()[2];
303 d[3] = payload()[3];
304 uint32_t address = payload()[1];
305 address <<= 8;
306 address |= payload()[2];
307 address <<= 8;
308 address |= payload()[3];
309 uint16_t fn_value = train_node()->train()->get_fn(address);
310 d[4] = fn_value >> 8;
311 d[5] = fn_value & 0xff;
312 return send_response();
313 }
314 }
315 DIE("unexpected call to handle_query.");
316 }
317
319 {
321 uint8_t subcmd = payload()[1];
322 switch (subcmd)
323 {
324 case TractionDefs::CTRLREQ_ASSIGN_CONTROLLER:
325 {
326 p.resize(3);
327 p[0] = TractionDefs::RESP_CONTROLLER_CONFIG;
328 p[1] = TractionDefs::CTRLRESP_ASSIGN_CONTROLLER;
329 NodeHandle supplied_controller = {0, 0};
330 if (size() < 9)
331 return reject_permanent();
332 supplied_controller.id = data_to_node_id(payload() + 3);
333 if (size() >= 11 && (payload()[2] & 0x01))
334 {
335 uint16_t alias = payload()[9];
336 alias <<= 8;
337 alias |= payload()[10];
338 supplied_controller.alias = alias;
339 }
340 NodeHandle existing_controller =
341 train_node()->get_controller();
342 if (false && // TODO(balazs.racz) this will automatically "steal" the loco but forgets to notify the old controller that it's stolen.
343 existing_controller.id &&
344 !iface()->matching_node(existing_controller,
345 supplied_controller))
346 {
349 p[2] = TractionDefs::CTRLRESP_ASSIGN_ERROR_CONTROLLER;
350 return send_response();
351 }
352 train_node()->set_controller(supplied_controller);
353 p[2] = 0;
354 return send_response();
355 }
356 case TractionDefs::CTRLREQ_QUERY_CONTROLLER:
357 {
358 NodeHandle h = train_node()->get_controller();
359 p.reserve(11);
360 p.resize(9);
361 p[0] = TractionDefs::RESP_CONTROLLER_CONFIG;
362 p[1] = TractionDefs::CTRLRESP_QUERY_CONTROLLER;
363 p[2] = 0;
364 node_id_to_data(h.id, &p[3]);
365 if (h.alias)
366 {
367 p[2] |= 1;
368 p.push_back(h.alias >> 8);
369 p.push_back(h.alias & 0xff);
370 }
371 return send_response();
372 }
373 case TractionDefs::CTRLREQ_RELEASE_CONTROLLER:
374 {
375 NodeHandle supplied_controller = {0, 0};
376 if (size() < 9)
377 return reject_permanent();
378 supplied_controller.id = data_to_node_id(payload() + 3);
379 if (size() >= 11 && (payload()[2] & 0x01))
380 {
381 uint16_t alias = payload()[9];
382 alias <<= 8;
383 alias |= payload()[10];
384 supplied_controller.alias = alias;
385 }
386 NodeHandle existing_controller =
387 train_node()->get_controller();
388 if (!iface()->matching_node(existing_controller,
389 supplied_controller))
390 {
391 LOG(WARNING,
392 "Tried to release a train that was not held: "
393 "train's controller %012" PRIx64
394 ", release command's controller %012" PRIx64,
395 existing_controller.id, supplied_controller.id);
396 return release_and_exit();
397 }
398 existing_controller = {0, 0};
399 train_node()->set_controller(existing_controller);
400 return release_and_exit();
401 }
402 }
403 LOG(VERBOSE, "Rejecting unknown traction message.");
404 return reject_permanent();
405 }
406
407 Action handle_consist_config()
408 {
409 uint8_t cmd = payload()[1];
410 switch (cmd)
411 {
412 case TractionDefs::CNSTREQ_ATTACH_NODE:
413 {
414 if (size() < 9)
415 return reject_permanent(
416 Defs::ERROR_INVALID_ARGS_MESSAGE_TOO_SHORT);
417 NodeID target = data_to_node_id(payload() + 3);
418 uint8_t flags = payload()[2];
419 if (target == train_node()->node_id())
420 {
421 return init_and_send_response(
422 TractionDefs::consist_add_response(
423 target, Defs::ERROR_OPENMRN_ALREADY_EXISTS));
424 }
425 train_node()->add_consist(target, flags);
426 return init_and_send_response(
427 TractionDefs::consist_add_response(target, 0));
428 }
429 case TractionDefs::CNSTREQ_DETACH_NODE:
430 {
431 if (size() < 9)
432 return reject_permanent(
433 Defs::ERROR_INVALID_ARGS_MESSAGE_TOO_SHORT);
434 NodeID target = data_to_node_id(payload() + 3);
435 bool resp = train_node()->remove_consist(target);
436 return init_and_send_response(
437 TractionDefs::consist_del_response(
438 target, resp ? 0 : Defs::ERROR_OPENMRN_NOT_FOUND));
439 }
440 case TractionDefs::CNSTREQ_QUERY_NODES:
441 {
442 int sz = train_node()->query_consist_length();
443 if (sz > 255) sz = 255;
444 if (size() > 2) {
445 uint8_t id = payload()[2];
446 if (id < sz) {
447 uint8_t flags = 0;
448 NodeID rp = train_node()->query_consist(id, &flags);
449 return init_and_send_response(
450 TractionDefs::consist_qry_response_long(
451 sz, id, flags, rp));
452 }
453 }
454 return init_and_send_response(
455 TractionDefs::consist_qry_response_short(sz));
456 }
457 default:
458 LOG(VERBOSE, "Unknown Traction consist subcommand %x", cmd);
459 return reject_permanent(Defs::ERROR_UNIMPLEMENTED_SUBCMD);
460 }
461 }
462
463 Action maybe_forward_consist()
464 {
465 auto* train_node = this->train_node();
466 unsigned count = train_node->query_consist_length();
467 if (count <= nextConsistIndex_)
468 return release_and_exit();
469 uint8_t flags = 0;
470 NodeID dst = train_node->query_consist(nextConsistIndex_, &flags);
471 if (iface()->matching_node(nmsg()->src, NodeHandle(dst)))
472 {
473 ++nextConsistIndex_;
474 return again();
475 }
476 uint8_t cmd = payload()[0] & TractionDefs::REQ_MASK;
477 bool flip_speed = false;
478 if (cmd == TractionDefs::REQ_SET_SPEED) {
479 if (flags & TractionDefs::CNSTFLAGS_REVERSE) {
480 flip_speed = true;
481 }
482 } else if (cmd == TractionDefs::REQ_SET_FN) {
483 uint32_t address = payload()[1];
484 address <<= 8;
485 address |= payload()[2];
486 address <<= 8;
487 address |= payload()[3];
488 if (address == 0) {
489 if ((flags & TractionDefs::CNSTFLAGS_LINKF0) == 0) {
490 // skip
491 ++nextConsistIndex_;
492 return again();
493 }
494 } else {
495 if ((flags & TractionDefs::CNSTFLAGS_LINKFN) == 0) {
496 // skip
497 ++nextConsistIndex_;
498 return again();
499 }
500 }
501 }
502 if (count == nextConsistIndex_ + 1u)
503 {
504 // last node: we can transfer the message.
505 auto *b = transfer_message();
506 b->data()->src = NodeHandle(train_node->node_id());
507 b->data()->dst = NodeHandle(dst);
508 b->data()->dstNode = nullptr;
509 if (flip_speed) {
510 b->data()->payload[1] ^= 0x80;
511 }
512 b->data()->payload[0] |= TractionDefs::REQ_LISTENER;
513 iface()->addressed_message_write_flow()->send(b);
514 return exit();
515 }
516 else
517 {
518 return allocate_and_call(
519 iface()->addressed_message_write_flow(),
520 STATE(forward_consist));
521 }
522 }
523
524 Action forward_consist()
525 {
526 auto *b =
527 get_allocation_result(iface()->addressed_message_write_flow());
528 uint8_t flags;
529 NodeID dst = train_node()->query_consist(nextConsistIndex_, &flags);
530 if (!dst)
531 {
532 // Strange. The consist destination should exist and never be
533 // zero once we got here.
534 b->unref();
535 return release_and_exit();
536 }
537 b->data()->reset(message()->data()->mti, train_node()->node_id(),
538 NodeHandle(dst), message()->data()->payload);
539 b->data()->payload[0] |= TractionDefs::REQ_LISTENER;
540 if (((payload()[0] & TractionDefs::REQ_MASK) ==
541 TractionDefs::REQ_SET_SPEED) &&
542 (flags & TractionDefs::CNSTFLAGS_REVERSE))
543 {
544 b->data()->payload[1] ^= 0x80;
545 }
546 iface()->addressed_message_write_flow()->send(b);
547 ++nextConsistIndex_;
548 return call_immediately(STATE(maybe_forward_consist));
549 }
550
551 Action handle_traction_mgmt()
552 {
554 uint8_t cmd = payload()[1];
555 switch (cmd)
556 {
557 case TractionDefs::MGMTREQ_RESERVE:
558 {
559 uint8_t code = 0xff;
560 if (reserved_)
561 {
562 code = 0x1;
563 }
564 else
565 {
566 code = 0;
567 reserved_ = 1;
568 }
569 p.push_back(TractionDefs::RESP_TRACTION_MGMT);
570 p.push_back(TractionDefs::MGMTRESP_RESERVE);
571 p.push_back(code);
572 return send_response();
573 }
574 case TractionDefs::MGMTREQ_RELEASE:
575 {
576 reserved_ = 0;
577 return release_and_exit();
578 }
579 case TractionDefs::MGMTREQ_NOOP:
580 {
581 // Nothing to do.
582 return release_and_exit();
583 }
584 default:
585 LOG(VERBOSE, "Unknown Traction management subcommand %x",
586 cmd);
587 return reject_permanent();
588 }
589 }
590
596 {
597 ensure_response_exists();
598 response_->data()->reset(Defs::MTI_TRACTION_CONTROL_REPLY,
599 train_node()->node_id(), nmsg()->src,
601 return &response_->data()->payload;
602 }
603
604 Action init_and_send_response(Payload p)
605 {
606 ensure_response_exists();
607 response_->data()->reset(Defs::MTI_TRACTION_CONTROL_REPLY,
608 train_node()->node_id(), nmsg()->src,
609 std::move(p));
610 return send_response();
611 }
612
615 {
616 iface()->addressed_message_write_flow()->send(response_);
617 response_ = nullptr;
618 return release_and_exit();
619 }
620
622 size_t size()
623 {
624 return nmsg()->payload.size();
625 }
626
628 const uint8_t *payload()
629 {
630 return reinterpret_cast<const uint8_t *>(nmsg()->payload.data());
631 }
632
634 Action reject_permanent(uint16_t code = Defs::ERROR_PERMANENT)
635 {
636 return maybe_alloc_response(STATE(send_reject_permanent));
637 }
638
639 Action send_reject_permanent()
640 {
641 ensure_response_exists();
642 // An alternative would be to send TERMINATE_DUE_TO_ERROR here.
643 response_->data()->reset(
645 nmsg()->dstNode->node_id(), nmsg()->src,
647 return send_response();
648 }
649
650 private:
652 unsigned errorCode_ : 16;
653 unsigned nextConsistIndex_ : 8;
655 unsigned reserved_ : 1;
656 TrainService *trainService_;
657 Buffer<GenMessage> *response_;
659 };
660
661 TractionRequestFlow traction_;
662};
663
664TrainService::TrainService(If *iface, NodeRegistry *train_node_registry)
665 : Service(iface->executor())
666 , iface_(iface)
667 , nodes_(train_node_registry)
668{
669 impl_ = new Impl(this);
670}
671
672TrainService::~TrainService()
673{
674 delete impl_;
675}
676
678{
679 iface_->add_local_node(node);
680 extern void StartInitializationFlow(Node * node);
682 AtomicHolder h(this);
683 nodes_->register_node(node);
684 LOG(VERBOSE, "Registered node %p for traction.", node);
685}
686
688{
689 HASSERT(nodes_->is_node_registered(node));
691 AtomicHolder h(this);
692 nodes_->unregister_node(node);
693}
694
695} // namespace openlcb
#define STATE(_fn)
Turns a function name into an argument to be supplied to functions expecting a state.
Definition StateFlow.hxx:61
See OSMutexLock in os/OS.hxx.
Definition Atomic.hxx:153
A BarrierNotifiable allows to create a number of child Notifiable and wait for all of them to finish.
void notify() override
Implementation of the barrier semantics.
BarrierNotifiable * reset(Notifiable *done)
Resets the barrier. Returns &*this. Asserts that is_done().
BarrierNotifiable * new_child()
Call this for each child task.
bool abort_if_almost_done()
Checks if there is exactly one outstanding notification left in the barrier.
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.
Collection of related state machines that pend on incoming messages.
Base::Action Action
Allows using Action without having StateFlowBase:: prefix in front of it.
MessageType * transfer_message()
Releases ownership of the current message.
MessageType * message()
Default implementation of a train node.
TrainService * service_
Pointer to the traction service.
TrainImpl * train_
Pointer to the train implementation object.
Abstract class representing an OpenLCB Interface.
Definition If.hxx:185
virtual void delete_local_node(Node *node)=0
Removes a local node from this interface.
MessageDispatchFlow * dispatcher()
Definition If.hxx:224
void add_local_node(Node *node)
Registers a new local node on this interface.
Definition If.hxx:242
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
Abstract base class for train implementations.
virtual void set_speed(SpeedType speed)=0
Sets the speed of the locomotive.
virtual void set_emergencystop()=0
Sets the train to emergency stop.
virtual void set_fn(uint32_t address, uint16_t value)=0
Sets the value of a function.
virtual uint16_t get_fn(uint32_t address)=0
virtual uint32_t legacy_address()=0
virtual dcc::TrainAddressType legacy_address_type()=0
TrainNodeWithId(TrainService *service, TrainImpl *train, NodeID node_id)
Constructor.
Virtual node class for an OpenLCB train protocol node.
virtual void set_controller(NodeHandle id)=0
virtual bool add_consist(NodeID tgt, uint8_t flags)=0
Adds a node ID to the consist targets.
virtual bool remove_consist(NodeID tgt)=0
Removes a node ID from the consist targets.
virtual NodeHandle get_controller()=0
virtual bool function_policy(NodeHandle src, uint8_t command_byte, uint32_t fnum, uint16_t value, Notifiable *done)=0
Applies a policy to function change requests coming in from the OpenLCB bus.
virtual NodeID query_consist(int id, uint8_t *flags)=0
Fetch a given consist link.
virtual int query_consist_length()=0
virtual void command_hook(NodeHandle src, const Payload &p)=0
Invoked for every incoming traction command targeted to this node.
virtual TrainImpl * train()=0
Handler for incoming OpenLCB messages of MTI == Traction Protocol Request.
Action send_response()
Sends off the response buffer to the client.
Action reject_permanent(uint16_t code=Defs::ERROR_PERMANENT)
Rejects the incoming message with a permanent error.
unsigned reserved_
1 if the voluntary lock protocol has set this train to be reserved.
size_t size()
Returns the size of the incoming message payload.
Payload * initialize_response()
Takes the allocation result of a response buffer (addressed write flow) and fills in src,...
Action entry() OVERRIDE
Entry into the StateFlow activity.
const uint8_t * payload()
Returns the incoming message payload (bytes).
unsigned errorCode_
error code for reject_permanent().
Collection of control flows necessary for implementing the Traction Protocol.
void unregister_train(TrainNode *node)
Removes a train node from the local interface.
void register_train(TrainNode *node)
Registers a new train with the train service.
Impl * impl_
Implementation flows.
std::unique_ptr< NodeRegistry > nodes_
List of train nodes managed by this Service.
If * iface_
OpenLCB interface.
TrainService(If *iface, NodeRegistry *train_node_registry=new DefaultNodeRegistry)
Constructor.
This class provides a mechanism for working with velocity in different forms.
Definition Velocity.hxx:73
#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
static const int WARNING
Loglevel that is always printed, reporting a warning or a retryable error.
Definition logging.h:55
#define OVERRIDE
Function attribute for virtual functions declaring that this funciton is overriding a funciton that s...
Definition macros.h:180
#define HASSERT(x)
Checks that the value of expression x is true, else terminates the current process.
Definition macros.h:138
#define DIE(MSG)
Unconditionally terminates the current process with a message.
Definition macros.h:143
string EMPTY_PAYLOAD
A global class / variable for empty or not-yet-initialized payloads.
Definition If.cxx:152
void node_id_to_data(NodeID id, void *data)
Convenience function to render a 48-bit NMRAnet node ID into an existing buffer.
Definition If.cxx:52
void speed_to_fp16(SpeedType speed, void *fp16)
Renders a SpeedType value to an unaligned memory address, typically to the output buffer.
NodeID data_to_node_id(const void *d)
Converts 6 bytes of big-endian data to a node ID.
Definition If.cxx:59
uint64_t NodeID
48-bit NMRAnet Node ID type
SpeedType fp16_to_speed(const void *fp16)
Parses a SpeedType value from an unaligned memory address, typically from the input buffer.
string Payload
Container that carries the data bytes in an NMRAnet message.
void StartInitializationFlow(Node *node)
Helper function that sends a local virtual node to the static InitializeFlow.
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
The generic interface for NMRAnet network interfaces.
@ MTI_OPTIONAL_INTERACTION_REJECTED
rejected request
Node * dstNode
If the destination node is local, this value is non-NULL.
Definition If.hxx:110
string payload
Data content in the message body.
Definition If.hxx:113
Container of both a NodeID and NodeAlias.
NodeID id
48-bit NMRAnet Node ID
NodeAlias alias
alias to NMRAnet Node ID
static NodeID train_node_id_from_legacy(dcc::TrainAddressType type, uint32_t addr)
Converts a legacy address to an NMRAnet node ID.
@ REQ_MASK
Mask to apply to the command byte of the requests.
@ REQ_LISTENER
Flag bit in the command byte set when a listener command is forwarded.
Implementation structure for TrainService.