45DefaultTrainNode::DefaultTrainNode(TrainService *service, TrainImpl *train)
49 , controllerNodeId_({0, 0})
53TrainNode::~TrainNode()
57TrainNodeWithConsist::~TrainNodeWithConsist()
59 while (!consistSlaves_.empty())
61 delete consistSlaves_.pop_front();
65DefaultTrainNode::~DefaultTrainNode()
103If *DefaultTrainNode::iface()
127 , trainService_(service)
131 this, Defs::MTI_TRACTION_CONTROL_COMMAND, 0xffff);
137 this, Defs::MTI_TRACTION_CONTROL_COMMAND, 0xffff);
146 Action maybe_alloc_response(Callback c)
150 return call_immediately(c);
154 return allocate_and_call(
155 iface()->addressed_message_write_flow(), c);
159 void ensure_response_exists()
163 response_ = get_allocation_result(
164 iface()->addressed_message_write_flow());
171 if (!
nmsg()->dstNode)
173 LOG(
VERBOSE,
"Traction message for unknown node.");
174 return release_and_exit();
177 if (!trainService_->
nodes_->is_node_registered(train_node()))
179 LOG(
VERBOSE,
"Traction message for node %p that is not "
185 return release_and_exit();
191 LOG(
VERBOSE,
"Traction message with no command byte.");
200 case TractionDefs::REQ_SET_SPEED:
204 nextConsistIndex_ = 0;
205 return call_immediately(
STATE(maybe_forward_consist));
207 case TractionDefs::REQ_SET_FN:
209 uint32_t address =
payload()[1];
235 nextConsistIndex_ = 0;
236 return call_immediately(
STATE(maybe_forward_consist));
238 case TractionDefs::REQ_EMERGENCY_STOP:
241 nextConsistIndex_ = 0;
242 return call_immediately(
STATE(maybe_forward_consist));
244 case TractionDefs::REQ_QUERY_SPEED:
245 case TractionDefs::REQ_QUERY_FN:
248 return maybe_alloc_response(
STATE(handle_query));
250 case TractionDefs::REQ_CONTROLLER_CONFIG:
252 return maybe_alloc_response(
255 case TractionDefs::REQ_CONSIST_CONFIG:
257 return maybe_alloc_response(
258 STATE(handle_consist_config));
260 case TractionDefs::REQ_TRACTION_MGMT:
262 return maybe_alloc_response(
STATE(handle_traction_mgmt));
266 LOG(
VERBOSE,
"Rejecting unknown traction message.");
278 case TractionDefs::REQ_QUERY_SPEED:
281 uint8_t *d =
reinterpret_cast<uint8_t *
>(&(*p)[0]);
282 d[0] = TractionDefs::RESP_QUERY_SPEED;
285 if (train_node()->train()->get_emergencystop())
287 status |= TractionDefs::SPEEDRESP_STATUS_IS_ESTOP;
296 case TractionDefs::REQ_QUERY_FN:
299 uint8_t *d =
reinterpret_cast<uint8_t *
>(&(*p)[0]);
300 d[0] = TractionDefs::RESP_QUERY_FN;
304 uint32_t address =
payload()[1];
309 uint16_t fn_value = train_node()->
train()->
get_fn(address);
310 d[4] = fn_value >> 8;
311 d[5] = fn_value & 0xff;
315 DIE(
"unexpected call to handle_query.");
324 case TractionDefs::CTRLREQ_ASSIGN_CONTROLLER:
327 p[0] = TractionDefs::RESP_CONTROLLER_CONFIG;
328 p[1] = TractionDefs::CTRLRESP_ASSIGN_CONTROLLER;
338 supplied_controller.
alias = alias;
343 existing_controller.
id &&
344 !iface()->matching_node(existing_controller,
345 supplied_controller))
349 p[2] = TractionDefs::CTRLRESP_ASSIGN_ERROR_CONTROLLER;
356 case TractionDefs::CTRLREQ_QUERY_CONTROLLER:
361 p[0] = TractionDefs::RESP_CONTROLLER_CONFIG;
362 p[1] = TractionDefs::CTRLRESP_QUERY_CONTROLLER;
368 p.push_back(h.
alias >> 8);
369 p.push_back(h.
alias & 0xff);
373 case TractionDefs::CTRLREQ_RELEASE_CONTROLLER:
384 supplied_controller.
alias = alias;
388 if (!iface()->matching_node(existing_controller,
389 supplied_controller))
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();
398 existing_controller = {0, 0};
400 return release_and_exit();
403 LOG(
VERBOSE,
"Rejecting unknown traction message.");
407 Action handle_consist_config()
412 case TractionDefs::CNSTREQ_ATTACH_NODE:
416 Defs::ERROR_INVALID_ARGS_MESSAGE_TOO_SHORT);
419 if (target == train_node()->node_id())
421 return init_and_send_response(
422 TractionDefs::consist_add_response(
423 target, Defs::ERROR_OPENMRN_ALREADY_EXISTS));
426 return init_and_send_response(
427 TractionDefs::consist_add_response(target, 0));
429 case TractionDefs::CNSTREQ_DETACH_NODE:
433 Defs::ERROR_INVALID_ARGS_MESSAGE_TOO_SHORT);
436 return init_and_send_response(
437 TractionDefs::consist_del_response(
438 target, resp ? 0 :
Defs::ERROR_OPENMRN_NOT_FOUND));
440 case TractionDefs::CNSTREQ_QUERY_NODES:
443 if (sz > 255) sz = 255;
449 return init_and_send_response(
450 TractionDefs::consist_qry_response_long(
454 return init_and_send_response(
455 TractionDefs::consist_qry_response_short(sz));
458 LOG(
VERBOSE,
"Unknown Traction consist subcommand %x", cmd);
463 Action maybe_forward_consist()
465 auto* train_node = this->train_node();
467 if (count <= nextConsistIndex_)
468 return release_and_exit();
471 if (iface()->matching_node(
nmsg()->src, NodeHandle(dst)))
477 bool flip_speed =
false;
478 if (cmd == TractionDefs::REQ_SET_SPEED) {
479 if (flags & TractionDefs::CNSTFLAGS_REVERSE) {
482 }
else if (cmd == TractionDefs::REQ_SET_FN) {
483 uint32_t address =
payload()[1];
489 if ((flags & TractionDefs::CNSTFLAGS_LINKF0) == 0) {
495 if ((flags & TractionDefs::CNSTFLAGS_LINKFN) == 0) {
502 if (count == nextConsistIndex_ + 1u)
506 b->data()->src = NodeHandle(train_node->node_id());
507 b->data()->dst = NodeHandle(dst);
508 b->data()->dstNode =
nullptr;
510 b->data()->payload[1] ^= 0x80;
518 return allocate_and_call(
519 iface()->addressed_message_write_flow(),
520 STATE(forward_consist));
527 get_allocation_result(iface()->addressed_message_write_flow());
535 return release_and_exit();
537 b->data()->reset(
message()->data()->mti, train_node()->node_id(),
541 TractionDefs::REQ_SET_SPEED) &&
542 (flags & TractionDefs::CNSTFLAGS_REVERSE))
544 b->data()->payload[1] ^= 0x80;
548 return call_immediately(
STATE(maybe_forward_consist));
551 Action handle_traction_mgmt()
557 case TractionDefs::MGMTREQ_RESERVE:
569 p.push_back(TractionDefs::RESP_TRACTION_MGMT);
570 p.push_back(TractionDefs::MGMTRESP_RESERVE);
574 case TractionDefs::MGMTREQ_RELEASE:
577 return release_and_exit();
579 case TractionDefs::MGMTREQ_NOOP:
582 return release_and_exit();
585 LOG(
VERBOSE,
"Unknown Traction management subcommand %x",
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;
606 ensure_response_exists();
607 response_->data()->reset(Defs::MTI_TRACTION_CONTROL_REPLY,
608 train_node()->node_id(),
nmsg()->src,
618 return release_and_exit();
630 return reinterpret_cast<const uint8_t *
>(
nmsg()->
payload.data());
636 return maybe_alloc_response(
STATE(send_reject_permanent));
639 Action send_reject_permanent()
641 ensure_response_exists();
643 response_->data()->reset(
645 nmsg()->dstNode->node_id(),
nmsg()->src,
653 unsigned nextConsistIndex_ : 8;
667 , nodes_(train_node_registry)
672TrainService::~TrainService()
683 nodes_->register_node(node);
684 LOG(
VERBOSE,
"Registered node %p for traction.", node);
692 nodes_->unregister_node(node);
#define STATE(_fn)
Turns a function name into an argument to be supplied to functions expecting a state.
See OSMutexLock in os/OS.hxx.
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.
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.
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.
virtual void delete_local_node(Node *node)=0
Removes a local node from this interface.
MessageDispatchFlow * dispatcher()
void add_local_node(Node *node)
Registers a new local node on this interface.
MessageHandler * addressed_message_write_flow()
Base class for incoming message handler flows.
GenMessage * nmsg()
Returns the NMRAnet message we received.
Base class for NMRAnet nodes conforming to the asynchronous interface.
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
~TrainNodeForProxy()
Destructor.
NodeID node_id() OVERRIDE
~TrainNodeWithId()
Destructor.
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.
Action handle_controller_config()
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.
#define LOG(level, message...)
Conditionally write a message to the logging output.
static const int VERBOSE
Loglevel that is usually not printed, reporting debugging information.
static const int WARNING
Loglevel that is always printed, reporting a warning or a retryable error.
#define OVERRIDE
Function attribute for virtual functions declaring that this funciton is overriding a funciton that s...
#define HASSERT(x)
Checks that the value of expression x is true, else terminates the current process.
#define DIE(MSG)
Unconditionally terminates the current process with a message.
string EMPTY_PAYLOAD
A global class / variable for empty or not-yet-initialized payloads.
void node_id_to_data(NodeID id, void *data)
Convenience function to render a 48-bit NMRAnet node ID into an existing buffer.
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.
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...
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.
string payload
Data content in the message body.
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.