Open Model Railroad Network (OpenMRN)
Loading...
Searching...
No Matches
TractionThrottle.hxx
Go to the documentation of this file.
1
35#ifndef _OPENLCB_TRACTIONTHROTTLE_HXX_
36#define _OPENLCB_TRACTIONTHROTTLE_HXX_
37
44
45namespace openlcb
46{
47
52 public LinkedObject<TractionThrottle>
53{
54public:
58 : TractionThrottleBase(node->iface())
59 , node_(node)
60 {
61 clear_cache();
62 }
63
65 {
66 iface()->dispatcher()->unregister_handler_all(&listenReplyHandler_);
67 iface()->dispatcher()->unregister_handler_all(&speedReplyHandler_);
68 }
69
70 using Command = TractionThrottleInput::Command;
71
72 enum
73 {
78 ERROR_UNASSIGNED = 0x4000000,
79 ERROR_ASSIGNED = 0x4010000,
80 };
81
82 void set_speed(SpeedType speed) override
83 {
85 TractionDefs::speed_set_payload(speed));
86 lastSetSpeed_ = speed;
87 estopActive_ = false;
88 }
89
91 {
92 // TODO: if we don't know the current speed, we should probably go and
93 // ask.
94 return lastSetSpeed_;
95 }
96
97 void set_emergencystop() override
98 {
99 send_traction_message_with_loopback(TractionDefs::estop_set_payload());
100 estopActive_ = true;
102 }
103
106 bool get_emergencystop() override
107 {
108 return estopActive_;
109 }
110
111 void set_fn(uint32_t address, uint16_t value) override
112 {
114 TractionDefs::fn_set_payload(address, value));
115 lastKnownFn_[address] = value;
116 }
117
118 uint16_t get_fn(uint32_t address) override
119 {
120 auto it = lastKnownFn_.find(address);
121 if (it != lastKnownFn_.end())
122 {
123 return it->second;
124 }
125 return FN_NOT_KNOWN;
126 }
127
128 void toggle_fn(uint32_t fn) override
129 {
130 auto fnstate = get_fn(fn);
131 if (fnstate == FN_NOT_KNOWN)
132 {
133 fnstate = 1;
134 }
135 else
136 {
137 fnstate = !fnstate;
138 }
139 set_fn(fn, fnstate);
140 }
141
145 void query_fn(uint32_t address) override
146 {
147 send_traction_message(TractionDefs::fn_get_payload(address));
148 }
149
150 uint32_t legacy_address() override
151 {
152 return 0;
153 }
154
156 {
157 return dcc::TrainAddressType::DCC_SHORT_ADDRESS;
158 }
159
162 bool is_train_assigned() override
163 {
164 return assigned_;
165 }
166
169 {
170 return node_;
171 }
172
175 {
176 return dst_;
177 }
178
188 void set_throttle_listener(std::function<void(int fn)> update_callback) override
189 {
190 updateCallback_ = std::move(update_callback);
191 }
192
193#ifdef GTEST
194 void TEST_assign_listening_node(openlcb::NodeID dst)
195 {
196 dst_ = dst;
197 set_assigned();
198 set_listening();
199 }
200#endif
201
202private:
203 Action entry() override
204 {
205 switch (message()->data()->cmd)
206 {
207 case Command::CMD_SET_DST:
208 {
209 if (assigned_)
210 {
211 return return_with_error(ERROR_ASSIGNED);
212 }
213 dst_ = input()->dst;
214 return return_ok();
215 }
216 case Command::CMD_ASSIGN_TRAIN:
217 {
218 if (assigned_)
219 {
220 return call_immediately(STATE(release_train));
221 }
222 else
223 {
224 return call_immediately(STATE(assign_train));
225 }
226 break;
227 }
228 case Command::CMD_RELEASE_TRAIN:
229 {
230 if (assigned_)
231 {
232 return call_immediately(STATE(release_train));
233 }
234 else
235 {
236 return return_ok();
237 }
238 }
239 case Command::CMD_LOAD_STATE:
240 {
241 if (!dst_)
242 {
243 return return_with_error(ERROR_UNASSIGNED);
244 }
245 return call_immediately(STATE(load_state));
246 }
247 case Command::CMD_CONSIST_ADD:
248 {
249 if (!dst_)
250 {
251 return return_with_error(ERROR_UNASSIGNED);
252 }
253 if (!input()->dst)
254 {
255 return return_with_error(Defs::ERROR_INVALID_ARGS);
256 }
257 return call_immediately(STATE(consist_add));
258 }
259 case Command::CMD_CONSIST_DEL:
260 {
261 if (!dst_)
262 {
263 return return_with_error(ERROR_UNASSIGNED);
264 }
265 if (!input()->dst)
266 {
267 return return_with_error(Defs::ERROR_INVALID_ARGS);
268 }
269 return call_immediately(STATE(consist_del));
270 }
271 case Command::CMD_CONSIST_QRY:
272 {
273 if (!dst_)
274 {
275 return return_with_error(ERROR_UNASSIGNED);
276 }
277 return call_immediately(STATE(consist_qry));
278 }
279 default:
280 LOG_ERROR("Unknown traction throttle command %d received.",
281 input()->cmd);
282 return return_with_error(Defs::ERROR_INVALID_ARGS);
283 }
284 }
285
286 Action release_train()
287 {
288 if (listenConsist_)
289 {
290 // Checks if there is another throttle listening to the same
291 // train. If so, we will not unregister ourselves. The last local
292 // throttle to release will do the unregistering.
293 bool found_other_throttle = false;
294 {
296 for (TractionThrottle *p =
298 p && !found_other_throttle;
299 p = p->LinkedObject<TractionThrottle>::link_next())
300 {
301 if (p == this)
302 {
303 // self, ignore
304 continue;
305 }
306 if (p->node_ != node_)
307 {
308 // Different virtual node, will get regular
309 // feedback
310 continue;
311 }
312 if (p->dst_ != dst_)
313 {
314 // Target node ID is different.
315 continue;
316 }
317 if (!p->listenConsist_)
318 {
319 // other throttle was not set as listener to begin with
320 continue;
321 }
322 found_other_throttle = true;
323 }
324 }
325 if (!found_other_throttle)
326 {
327 LOG(VERBOSE, "unregister listener");
328 handler_.wait_for_response(NodeHandle(dst_),
329 TractionDefs::RESP_CONSIST_CONFIG, &timer_);
331 TractionDefs::consist_del_payload(node_->node_id()));
332 return sleep_and_call(
333 &timer_, TIMEOUT_NSEC, STATE(release_listener_response));
334 }
335 LOG(VERBOSE,
336 "skipping unregister consist because of another throttle.");
337 // we do have to remove the handler though.
338 clear_listening();
339 }
340 return call_immediately(STATE(release_step_2));
341 }
342
343 Action release_listener_response()
344 {
346 if (handler_.response())
347 {
348 handler_.response()->unref();
349 }
350 clear_listening();
351 return call_immediately(STATE(release_step_2));
352 }
353
354 Action release_step_2()
355 {
356 send_traction_message(TractionDefs::release_controller_payload(node_));
357 clear_assigned();
358 clear_cache();
359 if (input()->cmd == Command::CMD_ASSIGN_TRAIN)
360 {
361 return sleep_and_call(
362 &timer_, MSEC_TO_NSEC(50), STATE(assign_train));
363 }
364 else
365 {
366 return return_ok();
367 }
368 }
369
370 Action assign_train()
371 {
372 dst_ = input()->dst;
374 NodeHandle(dst_), TractionDefs::RESP_CONTROLLER_CONFIG, &timer_);
375 send_traction_message(TractionDefs::assign_controller_payload(node_));
376 return sleep_and_call(&timer_, TIMEOUT_NSEC, STATE(assign_response));
377 }
378
379 Action assign_response()
380 {
382 if (!handler_.response())
383 {
384 return return_with_error(Defs::OPENMRN_TIMEOUT);
385 }
386
388 const string &payload = handler_.response()->data()->payload;
389 if (payload.size() < 3)
390 {
391 return return_with_error(Defs::ERROR_INVALID_ARGS);
392 }
393 if (payload[1] != TractionDefs::CTRLRESP_ASSIGN_CONTROLLER)
394 {
395 // spurious reply message
396 return return_with_error(Defs::ERROR_OUT_OF_ORDER);
397 }
398 input()->replyCause = payload[2];
399 if (payload[2] != 0)
400 {
401 return return_with_error(Defs::ERROR_REJECTED);
402 }
403 set_assigned();
404 if (input()->flags)
405 {
406 // need to add consist listener
408 NodeHandle(dst_), TractionDefs::RESP_CONSIST_CONFIG, &timer_);
409 send_traction_message(TractionDefs::consist_add_payload(
410 node_->node_id(),
411 TractionDefs::CNSTFLAGS_HIDE | TractionDefs::CNSTFLAGS_LINKF0 |
412 TractionDefs::CNSTFLAGS_LINKFN));
413 return sleep_and_call(
414 &timer_, TIMEOUT_NSEC, STATE(assign_consist_response));
415 }
416 return return_ok();
417 }
418
419 Action assign_consist_response()
420 {
421 // All error responses are actually okay here; we succeeded in the
422 // assignment but the listener setup didn't work.
424 if (!handler_.response())
425 {
426 return return_ok();
427 }
428
430 // Marks that we are owning the listener.
431 set_listening();
432 return return_ok();
433 }
434
435 Action load_state()
436 {
437 pendingQueries_ = 1;
438 send_traction_message(TractionDefs::speed_get_payload());
439 for (int i = 0; i <= MAX_FN_QUERY; ++i)
440 {
442 send_traction_message(TractionDefs::fn_get_payload(i));
443 }
444 return sleep_and_call(&timer_, TIMEOUT_NSEC, STATE(load_done));
445 }
446
447 Action load_done()
448 {
449 if (!timer_.is_triggered())
450 {
451 // timed out
452 pendingQueries_ = 0;
453 return return_with_error(Defs::OPENMRN_TIMEOUT);
454 }
455 else
456 {
457 return return_ok();
458 }
459 }
460
464 {
465 if (pendingQueries_ > 0)
466 {
467 if (!--pendingQueries_)
468 {
469 timer_.trigger();
470 }
471 return true;
472 }
473 return false;
474 }
475
479 {
481 if (msg->data()->dstNode != node_)
482 {
483 // For a different throttle.
484 return;
485 }
486 if (!iface()->matching_node(msg->data()->src, NodeHandle(dst_)))
487 {
488 return;
489 }
490 const Payload &p = msg->data()->payload;
491 if (p.size() < 1)
492 return;
493 switch (p[0])
494 {
495 case TractionDefs::RESP_QUERY_SPEED:
496 {
497 bool expected = pending_reply_arrived();
498 Velocity v;
499 bool is_estop;
500 if (TractionDefs::speed_get_parse_last(p, &v, &is_estop))
501 {
502 lastSetSpeed_ = v;
503 estopActive_ = is_estop;
504 if (updateCallback_ && !expected)
505 {
506 updateCallback_(-1);
507 }
508 }
509 return;
510 }
511 case TractionDefs::RESP_QUERY_FN:
512 {
513 bool expected = pending_reply_arrived();
514 uint16_t v;
515 unsigned num;
516 if (TractionDefs::fn_get_parse(p, &v, &num))
517 {
518 lastKnownFn_[num] = v;
519 if (updateCallback_ && !expected)
520 {
521 updateCallback_(num);
522 }
523 }
524 }
525 case TractionDefs::RESP_TRACTION_MGMT:
526 {
527 if (p.size() >= 2 && p[1] == TractionDefs::MGMTRESP_HEARTBEAT)
528 {
529 // Automatically responds to heartbeat requests.
531 }
532 }
533 }
534 }
535
536 Action consist_add()
537 {
539 NodeHandle(dst_), TractionDefs::RESP_CONSIST_CONFIG, &timer_);
541 TractionDefs::consist_add_payload(input()->dst, input()->flags));
542 return sleep_and_call(&timer_, TIMEOUT_NSEC, STATE(consist_add_response));
543 }
544
545 Action consist_del()
546 {
548 NodeHandle(dst_), TractionDefs::RESP_CONSIST_CONFIG, &timer_);
549 send_traction_message(TractionDefs::consist_del_payload(input()->dst));
550 return sleep_and_call(&timer_, TIMEOUT_NSEC, STATE(consist_add_response));
551 }
552
553 Action consist_add_response()
554 {
556 if (!handler_.response())
557 {
558 return return_with_error(Defs::OPENMRN_TIMEOUT);
559 }
560
562 const string &payload = handler_.response()->data()->payload;
563 if (payload.size() < 9)
564 {
565 return return_with_error(Defs::ERROR_INVALID_ARGS);
566 }
567 if (message()->data()->cmd == Command::CMD_CONSIST_ADD)
568 {
569 if (payload[1] != TractionDefs::CNSTRESP_ATTACH_NODE)
570 {
571 // spurious reply message
572 return return_with_error(Defs::ERROR_OUT_OF_ORDER);
573 }
574 }
575 else if (message()->data()->cmd == Command::CMD_CONSIST_DEL)
576 {
577 if (payload[1] != TractionDefs::CNSTRESP_DETACH_NODE)
578 {
579 // spurious reply message
580 return return_with_error(Defs::ERROR_OUT_OF_ORDER);
581 }
582 } else if (data_to_node_id(&payload[2]) != input()->dst) {
583 // spurious reply message
584 return return_with_error(Defs::ERROR_OUT_OF_ORDER);
585 }
586 uint16_t e = payload[8];
587 e <<= 8;
588 e |= payload[9];
589 input()->replyCause = e ? 1 : 0;
590 return return_with_error(e);
591 }
592
593 Action consist_qry()
594 {
596 NodeHandle(dst_), TractionDefs::RESP_CONSIST_CONFIG, &timer_);
597 if (input()->replyCause == 0xff)
598 {
599 send_traction_message(TractionDefs::consist_qry_payload());
600 }
601 else
602 {
604 TractionDefs::consist_qry_payload(input()->consistIndex));
605 }
606 return sleep_and_call(
607 &timer_, TIMEOUT_NSEC, STATE(consist_qry_response));
608 }
609
610 Action consist_qry_response()
611 {
613 if (!handler_.response())
614 {
615 return return_with_error(Defs::OPENMRN_TIMEOUT);
616 }
617
619 const string &payload = handler_.response()->data()->payload;
620 if (payload.size() < 3)
621 {
622 return return_with_error(Defs::ERROR_INVALID_ARGS_MESSAGE_TOO_SHORT);
623 }
624 if (payload[1] != TractionDefs::CNSTRESP_QUERY_NODES)
625 {
626 // spurious reply message
627 return return_with_error(Defs::ERROR_OUT_OF_ORDER);
628 }
629 input()->consistCount = payload[2];
630 if (payload.size() >= 11) {
631 input()->consistIndex = payload[3];
632 input()->flags = payload[4];
633 input()->dst = data_to_node_id(&payload[5]);
634 } else {
635 input()->consistIndex = 0xff;
636 input()->flags = 0xff;
637 input()->dst = 0;
638 }
639 input()->replyCause = 0;
640 return return_ok();
641 }
642
647 {
649 if (msg->data()->dstNode != node_)
650 {
651 // For a different throttle.
652 return;
653 }
654 if (!iface()->matching_node(msg->data()->src, NodeHandle(dst_)))
655 {
656 return;
657 }
658 listen_reply_process(msg->data()->payload);
659 }
660
666 {
667 if (p.size() < 1)
668 return;
669 switch (p[0] & TractionDefs::REQ_MASK)
670 {
671 case TractionDefs::REQ_SET_SPEED:
672 {
673 Velocity v;
674 // speed get and set have the same signature for what we care
676 {
677 lastSetSpeed_ = v;
678 estopActive_ = false;
679 if (updateCallback_)
680 {
681 updateCallback_(-1);
682 }
683 }
684 return;
685 }
686 case TractionDefs::REQ_EMERGENCY_STOP:
687 {
688 estopActive_ = true;
689 if (updateCallback_)
690 {
691 updateCallback_(-1);
692 }
693 return;
694 }
695 case TractionDefs::REQ_SET_FN:
696 {
697 uint16_t v;
698 unsigned num;
699 // function get and set have the same signature
700 if (TractionDefs::fn_get_parse(p, &v, &num))
701 {
702 lastKnownFn_[num] = v;
703 if (updateCallback_)
704 {
705 updateCallback_(num);
706 }
707 }
708 return;
709 }
710 }
711 }
712
723 {
724 auto b = send_traction_message_helper(std::move(payload));
725 std::function<void()> f = std::bind(
727 iface()->executor()->add(new CallbackExecutable(std::move(f)));
728 }
729
735 {
736 auto rb = get_buffer_deleter(b);
737 // Walks all TractionThrottle objects.
738 TractionThrottle *p = nullptr;
739 do
740 {
741 {
742 // finds next instance that's interesting
744 while (true)
745 {
746 if (!p)
747 {
749 }
750 else
751 {
752 p = p->LinkedObject<TractionThrottle>::link_next();
753 }
754 if (!p)
755 {
756 break;
757 }
758 if (p == this)
759 {
760 // self, ignore
761 continue;
762 }
763 if (p->node_ != node_)
764 {
765 // Differnet virtual node, will get regular
766 // feedback
767 continue;
768 }
769 if (p->dst_ != dst_)
770 {
771 // Target node ID is different.
772 continue;
773 }
774 if (!p->listenConsist_)
775 {
776 // other throttle was not set as listener to begin with
777 continue;
778 }
779
780 // Will call p, but we need to get out of the
781 // atomic first.
782 break;
783 }
784 } // atomic
785 if (p)
786 {
787 p->listen_reply_process(b->data()->payload);
788 }
789 } while (p != nullptr);
790 }
791
799 {
800 send_traction_message_helper(std::move(payload));
801 }
802
809 {
810 HASSERT(dst_ != 0);
811 auto *b = iface()->addressed_message_write_flow()->alloc();
812 b->data()->reset(Defs::MTI_TRACTION_CONTROL_COMMAND, node_->node_id(),
813 NodeHandle(dst_), std::move(payload));
814 iface()->addressed_message_write_flow()->send(b->ref());
815 return get_buffer_deleter(b);
816 }
817
818 void set_listening()
819 {
820 listenConsist_ = true;
821 iface()->dispatcher()->register_handler(&listenReplyHandler_,
822 Defs::MTI_TRACTION_CONTROL_COMMAND, Defs::MTI_EXACT);
823 }
824
825 void clear_listening()
826 {
827 listenConsist_ = false;
828 iface()->dispatcher()->unregister_handler(&listenReplyHandler_,
829 Defs::MTI_TRACTION_CONTROL_COMMAND, Defs::MTI_EXACT);
830 }
831
832 void set_assigned()
833 {
834 iface()->dispatcher()->register_handler(&speedReplyHandler_, Defs::MTI_TRACTION_CONTROL_REPLY, Defs::MTI_EXACT);
835 assigned_ = true;
836 }
837
838 void clear_assigned()
839 {
840 if (!assigned_)
841 {
842 return;
843 }
844 assigned_ = false;
845 iface()->dispatcher()->unregister_handler(&speedReplyHandler_, Defs::MTI_TRACTION_CONTROL_REPLY, Defs::MTI_EXACT);
846 }
847
848 void clear_cache()
849 {
851 estopActive_ = false;
852 lastKnownFn_.clear();
853 }
854
855 TractionThrottleInput *input()
856 {
857 return message()->data();
858 }
859
860 If *iface()
861 {
862 // We know that the service pointer is the node's interface from the
863 // constructor.
864 return static_cast<If *>(service());
865 }
866
867 MessageHandler::GenericHandler speedReplyHandler_{
869 MessageHandler::GenericHandler listenReplyHandler_{
873 unsigned pendingQueries_{0};
874 StateFlowTimer timer_{this};
876 bool assigned_{false};
878 bool listenConsist_{false};
880 bool estopActive_{false};
881 NodeID dst_;
882 Node *node_;
886 std::function<void(int fn)> updateCallback_;
890 std::map<uint32_t, uint16_t> lastKnownFn_;
891};
892
893} // namespace openlcb
894
895#endif // _OPENLCB_TRACTIONTHROTTLE_HXX_
BufferPtr< T > get_buffer_deleter(Buffer< T > *b)
Helper function to create a BufferPtr of an appropriate type without having to explicitly specify the...
Definition Buffer.hxx:272
AutoReleaseBuffer< T > BufferPtr
Smart pointer for buffers.
Definition Buffer.hxx:259
std::unique_ptr< Buffer< T >, BufferDelete< T > > AutoReleaseBuffer
This class will automatically unref a Buffer when going out of scope.
Definition Buffer.hxx:256
#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
Base class for all QMember types that hold data in an expandable format.
Definition Buffer.hxx:195
T * data()
get a pointer to the start of the data.
Definition Buffer.hxx:215
Action return_ok()
Terminates the flow and returns the request buffer to the caller with an error code of OK (zero).
Action return_with_error(int error)
Terminates the flow and returns the request buffer to the caller with an specific error code.
A notifiable class that calls a particular function object once when it is invoked,...
void register_handler(HandlerType *handler, ID id, ID mask)
Adds a new handler to this dispatcher.
void unregister_handler_all(HandlerType *handler)
Removes all instances of a handler from this dispatcher.
void unregister_handler(HandlerType *handler, ID id, ID mask)
Removes a specific instance of a handler from this dispatcher.
virtual void add(Executable *action, unsigned priority=UINT_MAX)=0
Send a message to this Executor's queue.
virtual void send(MessageType *message, unsigned priority=UINT_MAX)=0
Entry point to the flow.
MessageType * alloc()
Synchronously allocates a message buffer from the pool of this flow.
Using this class as a base class will cause the given class to have all its instances linked up in a ...
static T * link_head()
ExecutorBase * executor()
Return type for a state flow callback.
Service * service()
Return a pointer to the service I am bound to.
Action sleep_and_call(::Timer *timer, long long timeout_nsec, Callback c)
Suspends execution of this control flow for a specified time.
bool is_triggered()
Definition Timer.hxx:273
void trigger()
This will wakeup the timer prematurely, immediately.
Definition Timer.hxx:237
Action call_immediately(Callback c)
Imediately call the next state upon return.
MessageDispatchFlow * dispatcher()
Definition If.hxx:224
MessageHandler * addressed_message_write_flow()
Definition If.hxx:210
Base class for NMRAnet nodes conforming to the asynchronous interface.
Definition Node.hxx:52
This class helps waiting for traction responses.
void wait_for_response(NodeHandle target_node, uint8_t expected_type, ::Timer *trigger)
Starts waiting for a traction control reply from a given node with the first byte 'expected_type'.
void wait_timeout()
Call this if the timeout has expired.
Buffer< GenMessage > * response()
Caller must unref this buffer when done with it.
@ FN_NOT_KNOWN
Returned from get_fn() when we don't have a cahced value for a function.
Interface for a single throttle for running a train node.
void query_fn(uint32_t address) override
Sends out a function query command.
SpeedType get_speed() override
dcc::TrainAddressType legacy_address_type() override
std::map< uint32_t, uint16_t > lastKnownFn_
Cache: all known function values.
void send_traction_message_with_loopback(Payload payload)
Allocates (synchronously) an outgoing openlcb buffer with traction request MTI and the given payload ...
void listen_reply_process(const Payload &p)
Business logic for interpreting a proxied traction command payload.
bool estopActive_
keep track if E-Stop is active
bool assigned_
True if the assign controller has returned positive.
void set_throttle_listener(std::function< void(int fn)> update_callback) override
Sets up a callback for listening for remote throttle updates.
SpeedType lastSetSpeed_
Cache: Velocity value that we last commanded to the train.
Action entry() override
Entry into the StateFlow activity.
openlcb::NodeID target_node() override
unsigned pendingQueries_
How many speed/fn query requests I have sent off to the train node that have not yet seen a reply.
void set_speed(SpeedType speed) override
Sets the speed of the locomotive.
uint32_t legacy_address() override
void speed_reply(Buffer< GenMessage > *msg)
Invoked for TRACTION_CONTROL_REPLY messages coming in via the dispatcher.
TractionResponseHandler handler_
Helper class for stateful query/return flows.
bool pending_reply_arrived()
Notifies that a pending query during load has gotten a reply.
void loopback_traction_message(Buffer< GenMessage > *b)
Performs loopback processing of an outgoing traction message.
bool listenConsist_
True if we also have a consist link with the assigned loco.
void set_emergencystop() override
Sets the train to emergency stop.
void toggle_fn(uint32_t fn) override
Flips a function on<>off.
void set_fn(uint32_t address, uint16_t value) override
Sets the value of a function.
BufferPtr< GenMessage > send_traction_message_helper(Payload payload)
Allocates (synchronously) an outgoing openlcb buffer with traction request MTI and the given payload ...
@ TIMEOUT_NSEC
Timeout for assign controller request.
@ MAX_FN_QUERY
Upon a load state request, how far do we go into the function list?
uint16_t get_fn(uint32_t address) override
void listen_reply(Buffer< GenMessage > *msg)
Invoked for TRACTION_CONTROL_COMMAND messages coming in via the dispatcher.
bool is_train_assigned() override
Determine if a train is currently assigned to this trottle.
bool get_emergencystop() override
Get the current E-Stop state.
std::function< void(int fn)> updateCallback_
Function to call when a different controller updates the train.
openlcb::Node * throttle_node() override
void send_traction_message(Payload payload)
Allocates (synchronously) an outgoing openlcb buffer with traction request MTI and the given payload ...
This class provides a mechanism for working with velocity in different forms.
Definition Velocity.hxx:73
void set_mph(float mph)
Sets the speed value from a given mph value.
Definition Velocity.hxx:213
TrainAddressType
Which address type this legacy train node uses.
Definition dcc/Defs.hxx:46
#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 LOG_ERROR(message...)
Shorthand for LOG(LEVEL_ERROR, message...). See LOG.
Definition logging.h:124
#define HASSERT(x)
Checks that the value of expression x is true, else terminates the current process.
Definition macros.h:138
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
Velocity nan_to_speed()
Definition Velocity.hxx:491
string Payload
Container that carries the data bytes in an NMRAnet message.
#define MSEC_TO_NSEC(_msec)
Convert a millisecond value to a nanosecond value.
Definition os.h:268
#define SEC_TO_NSEC(_sec)
Convert a second value to a nanosecond value.
Definition os.h:286
@ MTI_EXACT
match mask for a single MTI
Container of both a NodeID and NodeAlias.
static bool fn_get_parse(const Payload &p, uint16_t *value, unsigned *address)
Parses the response payload of a GET_FN packet.
static Payload noop_payload()
Generates a Noop message, to be sent from the throttle to the train node.
static bool speed_get_parse_last(const Payload &p, Velocity *v, bool *is_estop=nullptr)
Parses the response payload of a GET_SPEED packet.
@ REQ_MASK
Mask to apply to the command byte of the requests.
uint8_t flags
Contains the flags for the consist listener.
NodeID dst
For assign, this carries the destination node ID.
uint8_t replyCause
For assign controller reply REJECTED, this is 1 for controller refused connection,...
uint8_t consistIndex
Index of the entry in the consisting list that needs to be returned.
uint8_t consistCount
Total number of entries in the consisting list.