Open Model Railroad Network (OpenMRN)
Loading...
Searching...
No Matches
MemoryConfigClient.hxx
Go to the documentation of this file.
1
35#ifndef _OPENLCB_MEMORYCONFIGCLIENT_HXX_
36#define _OPENLCB_MEMORYCONFIGCLIENT_HXX_
37
40#include "openlcb/IfCan.hxx"
44
45namespace openlcb
46{
47
49{
50 enum ReadCmd
51 {
52 READ
53 };
54
55 enum ReadStreamCmd
56 {
57 READ_STREAM
58 };
59
60 enum ReadPartCmd
61 {
62 READ_PART
63 };
64
65 enum ReadPartStreamCmd
66 {
67 READ_PART_STREAM
68 };
69
70 enum WriteCmd
71 {
72 WRITE
73 };
74
75 enum UpdateCompleteCmd
76 {
77 UPDATE_COMPLETE
78 };
79
80 enum RebootCmd
81 {
82 REBOOT
83 };
84
85 enum FactoryResetCmd
86 {
87 FACTORY_RESET
88 };
89
90 enum FreezeCmd
91 {
92 FREEZE
93 };
94
95 enum UnfreezeCmd
96 {
97 UNFREEZE
98 };
99
106 void reset(ReadCmd, NodeHandle d, uint8_t space,
107 std::function<void(MemoryConfigClientRequest *)> cb = nullptr)
108 {
109 reset_base();
110 cmd = CMD_READ;
111 memory_space = space;
112 dst = d;
113 address = 0;
114 size = 0xffffffffu;
115 payload.clear();
116 progressCb = std::move(cb);
117 }
118
125 void reset(ReadStreamCmd, NodeHandle d, uint8_t space,
126 std::function<void(MemoryConfigClientRequest *)> cb = nullptr)
127 {
128 reset(READ, d, space, std::move(cb));
129 use_stream = true;
130 }
131
138 void reset(ReadPartCmd, NodeHandle d, uint8_t space, unsigned offset,
139 unsigned size)
140 {
141 reset_base();
142 cmd = CMD_READ_PART;
143 memory_space = space;
144 dst = d;
145 this->address = offset;
146 this->size = size;
147 payload.clear();
148 }
149
158 void reset(ReadPartStreamCmd, NodeHandle d, uint8_t space, unsigned offset,
159 unsigned size)
160 {
161 reset(READ_PART, d, space, offset, size);
162 use_stream = true;
163 }
164
171 void reset(
172 WriteCmd, NodeHandle d, uint8_t space, unsigned offset, string data)
173 {
174 reset_base();
175 cmd = CMD_WRITE;
176 memory_space = space;
177 dst = d;
178 this->address = offset;
179 this->size = data.size();
180 payload = std::move(data);
181 }
182
187 void reset(UpdateCompleteCmd, NodeHandle d)
188 {
189 reset_base();
190 cmd = CMD_META_REQUEST;
191 dst = d;
192 payload.clear();
193 payload.reserve(2);
194 payload.push_back(DatagramDefs::CONFIGURATION);
196 }
197
202 void reset(RebootCmd, NodeHandle d)
203 {
204 reset_base();
205 cmd = CMD_META_REQUEST;
206 dst = d;
207 payload.clear();
208 payload.reserve(2);
209 payload.push_back(DatagramDefs::CONFIGURATION);
210 payload.push_back(MemoryConfigDefs::COMMAND_RESET);
211 }
212
217 void reset(FactoryResetCmd, NodeHandle d)
218 {
219 reset_base();
220 cmd = CMD_FACTORY_RESET;
221 dst = d;
222 payload.clear();
223 payload.reserve(8);
224 payload.push_back(DatagramDefs::CONFIGURATION);
226 }
227
233 void reset(FreezeCmd, NodeHandle d, uint8_t space)
234 {
235 reset_base();
236 cmd = CMD_META_REQUEST;
237 dst = d;
238 payload.clear();
239 payload.reserve(3);
240 payload.push_back(DatagramDefs::CONFIGURATION);
241 payload.push_back(MemoryConfigDefs::COMMAND_FREEZE);
242 payload.push_back(space);
243 }
244
250 void reset(UnfreezeCmd, NodeHandle d, uint8_t space)
251 {
252 reset_base();
253 cmd = CMD_META_REQUEST;
254 dst = d;
255 payload.clear();
256 payload.reserve(3);
257 payload.push_back(DatagramDefs::CONFIGURATION);
258 payload.push_back(MemoryConfigDefs::COMMAND_UNFREEZE);
259 payload.push_back(space);
260 }
261
262 enum Command : uint8_t
263 {
264 CMD_READ,
265 CMD_READ_PART,
266 CMD_WRITE,
267 CMD_META_REQUEST,
268 CMD_FACTORY_RESET
269 };
270
273 {
275 progressCb = nullptr;
276 payload.clear();
277 size = 0;
278 address = 0;
279 use_stream = false;
280 }
281
282 Command cmd;
283 uint8_t memory_space;
284 bool use_stream;
285 unsigned address;
286 unsigned size;
289 string payload;
291 std::function<void(MemoryConfigClientRequest *)> progressCb;
292};
293
294class MemoryConfigClient : public CallableFlow<MemoryConfigClientRequest>
295{
296public:
298 : CallableFlow<MemoryConfigClientRequest>(memcfg->dg_service())
299 , node_(node)
300 , memoryConfigHandler_(memcfg)
301 { }
302
306 {
307 // Internal error codes generated by the send flow
309 DatagramClient::OPERATION_PENDING,
310 TIMEOUT = Defs::ERROR_PERMANENT |
311 Defs::OPENMRN_TIMEOUT,
312 };
313
316 {
317 return node_;
318 }
319
326
327protected:
328 Action entry() override
329 {
330 request()->resultCode = OPERATION_PENDING;
331 switch (request()->cmd)
332 {
333 case MemoryConfigClientRequest::CMD_READ:
334 case MemoryConfigClientRequest::CMD_READ_PART:
335 return allocate_and_call(
336 STATE(do_read), dg_service()->client_allocator());
337 case MemoryConfigClientRequest::CMD_WRITE:
338 return allocate_and_call(
339 STATE(do_write), dg_service()->client_allocator());
340 case MemoryConfigClientRequest::CMD_META_REQUEST:
341 return allocate_and_call(
342 STATE(do_meta_request), dg_service()->client_allocator());
343 case MemoryConfigClientRequest::CMD_FACTORY_RESET:
345 default:
346 break;
347 }
348 return return_with_error(Defs::ERROR_UNIMPLEMENTED_SUBCMD);
349 }
350
351private:
352 Action do_read()
353 {
354 dgClient_ = full_allocation_result(dg_service()->client_allocator());
355 offset_ = request()->address;
357 return call_immediately(STATE(send_next_read));
358 }
359
360 Action send_next_read()
361 {
362 return allocate_and_call(
363 dg_service()->iface()->dispatcher(), STATE(send_read_datagram));
364 }
365
366 Action send_read_datagram()
367 {
368 auto *b = get_allocation_result(dg_service()->iface()->dispatcher());
369 b->set_done(bn_.reset(this));
370 unsigned sz = request()->size > 64 ? 64 : request()->size;
371 b->data()->reset(Defs::MTI_DATAGRAM, node_->node_id(), request()->dst,
372 MemoryConfigDefs::read_datagram(
373 request()->memory_space, offset_, sz));
374 if (request()->size < 0xffffffffu)
375 {
376 request()->size -= sz;
377 }
379 responseCode_ = DatagramClient::OPERATION_PENDING;
381 return wait_and_call(STATE(read_complete));
382 }
383
384 Action read_complete()
385 {
386 if (!(dgClient_->result() & DatagramClient::OPERATION_SUCCESS))
387 {
388 // some error occurred.
389 return handle_read_error(dgClient_->result());
390 }
391 if (responseCode_ & DatagramClient::OPERATION_PENDING)
392 {
394 return sleep_and_call(
395 &timer_, SEC_TO_NSEC(3), STATE(read_response_timeout));
396 }
397 else
398 {
399 return call_immediately(STATE(read_response_timeout));
400 }
401 }
402
403 Action read_response_timeout()
404 {
405 if (responseCode_ & DatagramClient::OPERATION_PENDING)
406 {
407 return handle_read_error(Defs::OPENMRN_TIMEOUT);
408 }
409 size_t len = responsePayload_.size();
410 const uint8_t *bytes =
413 {
414 LOG(INFO,
415 "Memory Config client: response datagram payload not "
416 "long enough");
417 return handle_read_error(
418 Defs::ERROR_INVALID_ARGS_MESSAGE_TOO_SHORT);
419 }
420 unsigned ofs = MemoryConfigDefs::get_payload_offset(responsePayload_);
423 uint8_t cmd = bytes[1] & MemoryConfigDefs::COMMAND_MASK;
424 if (address != offset_)
425 {
426 return handle_read_error(Defs::ERROR_OUT_OF_ORDER);
427 }
428 if (space != request()->memory_space)
429 {
430 return handle_read_error(Defs::ERROR_OUT_OF_ORDER);
431 }
433 {
434 if (len < ofs + 2)
435 {
436 return handle_read_error(
437 Defs::ERROR_INVALID_ARGS_MESSAGE_TOO_SHORT);
438 }
439 uint16_t error = bytes[ofs++];
440 error <<= 8;
441 error |= bytes[ofs];
442 return handle_read_error(error);
443 }
445 {
446 return handle_read_error(Defs::ERROR_UNIMPLEMENTED);
447 }
448 unsigned dlen = len - ofs;
449 request()->payload.append((char *)(bytes + ofs), dlen);
450 offset_ += dlen;
451 if (request()->progressCb)
452 {
453 request()->progressCb(request());
454 }
455 if ((dlen < 64) || (request()->size == 0))
456 {
457 return call_immediately(STATE(finish_read));
458 }
459 return call_immediately(STATE(send_next_read));
460 }
461
462protected:
463 Action handle_read_error(int error)
464 {
465 if (error == MemoryConfigDefs::ERROR_OUT_OF_BOUNDS)
466 {
467 return finish_read();
468 }
469 cleanup_read();
470 return return_with_error(error);
471 }
472
473 void cleanup_read()
474 {
475 responsePayload_.clear();
476 dg_service()->client_allocator()->typed_insert(dgClient_);
478 dgClient_ = nullptr;
479 }
480
481 Action finish_read()
482 {
483 cleanup_read();
484 return return_ok();
485 }
486
487private:
488 Action do_write()
489 {
490 dgClient_ = full_allocation_result(dg_service()->client_allocator());
491 offset_ = request()->address;
492 payloadOffset_ = 0;
494 return call_immediately(STATE(send_next_write));
495 }
496
497 Action send_next_write()
498 {
499 return allocate_and_call(
500 dg_service()->iface()->dispatcher(), STATE(send_write_datagram));
501 }
502
503 Action send_write_datagram()
504 {
505 auto *b = get_allocation_result(dg_service()->iface()->dispatcher());
506 b->set_done(bn_.reset(this));
507 unsigned sz = request()->payload.size() - payloadOffset_;
508 if (sz > MemoryConfigDefs::MAX_DATAGRAM_RW_BYTES)
509 {
510 sz = MemoryConfigDefs::MAX_DATAGRAM_RW_BYTES;
511 }
512 writeLength_ = sz;
513 b->data()->reset(Defs::MTI_DATAGRAM, node_->node_id(), request()->dst,
514 MemoryConfigDefs::write_datagram(request()->memory_space, offset_,
515 request()->payload.substr(payloadOffset_, sz)));
517 responseCode_ = DatagramClient::OPERATION_PENDING;
519 return wait_and_call(STATE(write_complete));
520 }
521
522 Action write_complete()
523 {
524 if (!(dgClient_->result() & DatagramClient::OPERATION_SUCCESS))
525 {
526 // some error occurred.
527 return handle_write_error(dgClient_->result());
528 }
529 if (responseCode_ & DatagramClient::OPERATION_PENDING)
530 {
532 return sleep_and_call(
533 &timer_, SEC_TO_NSEC(3), STATE(write_response_timeout));
534 }
535 else
536 {
537 return call_immediately(STATE(write_response_timeout));
538 }
539 }
540
541 Action write_response_timeout()
542 {
543 if (responseCode_ & DatagramClient::OPERATION_PENDING)
544 {
545 return handle_write_error(Defs::OPENMRN_TIMEOUT);
546 }
547 size_t len = responsePayload_.size();
548 const uint8_t *bytes =
551 {
552 LOG(INFO,
553 "Memory Config client: response datagram payload not "
554 "long enough");
555 return handle_write_error(
556 Defs::ERROR_INVALID_ARGS_MESSAGE_TOO_SHORT);
557 }
558 unsigned ofs = MemoryConfigDefs::get_payload_offset(responsePayload_);
561 uint8_t cmd = bytes[1] & MemoryConfigDefs::COMMAND_MASK;
562 if (address != offset_)
563 {
564 return handle_write_error(Defs::ERROR_OUT_OF_ORDER);
565 }
566 if (space != request()->memory_space)
567 {
568 return handle_write_error(Defs::ERROR_OUT_OF_ORDER);
569 }
571 {
572 if (len < ofs + 2)
573 {
574 return handle_write_error(
575 Defs::ERROR_INVALID_ARGS_MESSAGE_TOO_SHORT);
576 }
577 uint16_t error = bytes[ofs++];
578 error <<= 8;
579 error |= bytes[ofs];
580 return handle_write_error(error);
581 }
583 {
584 return handle_write_error(Defs::ERROR_UNIMPLEMENTED);
585 }
586 // write success.
589 if (payloadOffset_ >= request()->payload.size())
590 {
591 return call_immediately(STATE(finish_write));
592 }
593 return call_immediately(STATE(send_next_write));
594 }
595
596 Action handle_write_error(int error)
597 {
598 if (error == MemoryConfigDefs::ERROR_OUT_OF_BOUNDS)
599 {
600 return finish_write();
601 }
602 cleanup_write();
603 return return_with_error(error);
604 }
605
606 void cleanup_write()
607 {
608 responsePayload_.clear();
609 dg_service()->client_allocator()->typed_insert(dgClient_);
611 dgClient_ = nullptr;
612 }
613
614 Action finish_write()
615 {
616 cleanup_write();
617 return return_ok();
618 }
619
620 Action do_meta_request()
621 {
622 dgClient_ = full_allocation_result(dg_service()->client_allocator());
623 // Meta requests do not have a response, so we are not registering the
624 // response flow here.
625 return allocate_and_call(
626 dg_service()->iface()->dispatcher(), STATE(send_meta_datagram));
627 }
628
629 Action send_meta_datagram()
630 {
631 auto *b = get_allocation_result(dg_service()->iface()->dispatcher());
632 b->set_done(bn_.reset(this));
633 b->data()->reset(Defs::MTI_DATAGRAM, node_->node_id(), request()->dst,
634 std::move(request()->payload));
637 return wait_and_call(STATE(meta_complete));
638 }
639
640 Action meta_complete()
641 {
642 auto result = dgClient_->result();
643 dg_service()->client_allocator()->typed_insert(dgClient_);
644 dgClient_ = nullptr;
645 result &= DatagramClient::RESPONSE_CODE_MASK;
646 if (result == DatagramClient::DST_REBOOT ||
647 result == DatagramClient::OPERATION_SUCCESS)
648 {
649 return return_ok();
650 }
651 else
652 {
653 return return_with_error(result);
654 }
655 }
656
660 {
661 node()->iface()->canonicalize_handle(&request()->dst);
662 if (request()->dst.id)
663 {
665 }
666 // Now: we have a dst with alias only, so we must be running on CAN-bus.
667 IfCan *iface = (IfCan *)node()->iface();
669 {
670 // This is so rarely used that we rather allocate it dynamically.
671 nodeIdlookupFlow_.reset(new NodeIdLookupFlow(iface));
672 }
674 STATE(dst_id_complete), node(), request()->dst);
675 }
676
679 {
680 auto rb =
682 request()->dst = rb->data()->handle;
683 // Object not needed anymore.
684 nodeIdlookupFlow_.reset();
685 if (request()->dst.id)
686 {
688 }
689 return return_with_error(Defs::ERROR_OPENMRN_NOT_FOUND);
690 }
691
695 {
696 request()->payload.resize(8);
697 node_id_to_data(request()->dst.id, &request()->payload[2]);
698 return allocate_and_call(
699 STATE(do_meta_request), dg_service()->client_allocator());
700 }
701
703 {
704 public:
706 : DefaultDatagramHandler(parent->memoryConfigHandler_->dg_service())
707 , parent_(parent)
708 { }
709
710 private:
711 Action entry() override
712 {
713 if (!parent_->has_request())
714 {
715 return respond_reject(Defs::ERROR_OUT_OF_ORDER);
716 }
717 if (!parent_->node_->iface()->matching_node(
718 parent_->request()->dst, message()->data()->src))
719 {
720 return respond_reject(Defs::ERROR_OUT_OF_ORDER);
721 }
722 if (size() < 2)
723 {
724 return respond_reject(
725 Defs::ERROR_INVALID_ARGS_MESSAGE_TOO_SHORT);
726 }
727 auto *bytes = payload();
728 uint8_t cmd = bytes[1] & ~3;
729 switch (cmd)
730 {
733 {
734 if (parent_->request()->cmd !=
735 MemoryConfigClientRequest::CMD_READ &&
736 parent_->request()->cmd !=
737 MemoryConfigClientRequest::CMD_READ_PART)
738 {
739 break;
740 }
741 parent_->responseCode_ = 0;
742 message()->data()->payload.swap(parent_->responsePayload_);
743 if (parent_->isWaitingForTimer_)
744 {
745 parent_->timer_.trigger();
746 }
747 return respond_ok(0);
748 }
751 {
752 if (parent_->request()->cmd !=
753 MemoryConfigClientRequest::CMD_READ &&
754 parent_->request()->cmd !=
755 MemoryConfigClientRequest::CMD_READ_PART)
756 {
757 break;
758 }
759 if (!parent_->request()->use_stream)
760 {
761 break;
762 }
763 parent_->responseCode_ = 0;
764 message()->data()->payload.swap(parent_->responsePayload_);
765 if (parent_->isWaitingForTimer_)
766 {
767 parent_->timer_.trigger();
768 }
769 return respond_ok(0);
770 }
773 if (parent_->request()->cmd !=
774 MemoryConfigClientRequest::CMD_WRITE)
775 {
776 break;
777 }
778 parent_->responseCode_ = 0;
779 message()->data()->payload.swap(parent_->responsePayload_);
780 if (parent_->isWaitingForTimer_)
781 {
782 parent_->timer_.trigger();
783 }
784 return respond_ok(0);
785 }
786 return respond_reject(Defs::ERROR_UNIMPLEMENTED_SUBCMD);
787 }
788
789 private:
790 MemoryConfigClient *parent_;
791 };
792
793protected:
794 DatagramService *dg_service()
795 {
796 return static_cast<DatagramService *>(service());
797 }
798
811 std::unique_ptr<openlcb::NodeIdLookupFlow> nodeIdlookupFlow_;
813 uint32_t offset_;
817 uint16_t writeLength_;
826}; // class MemoryConfigClient
827
829{
830public:
832 Node *node, MemoryConfigHandler *memcfg, uint8_t local_stream_id)
833 : MemoryConfigClient(node, memcfg)
834 {
837 IfCan *iface = static_cast<IfCan *>(node_->iface());
838 HASSERT(iface);
839 HASSERT(iface->stream_transport());
841 receiver_.reset(new StreamReceiverCan(iface, dstStreamId_));
842 }
843
844protected:
845 Action entry() override
846 {
847 if (!request()->use_stream)
848 {
850 }
851 request()->resultCode = OPERATION_PENDING;
852 switch (request()->cmd)
853 {
854 case MemoryConfigClientRequest::CMD_READ:
855 case MemoryConfigClientRequest::CMD_READ_PART:
856 return allocate_and_call(
857 STATE(do_stream_read), dg_service()->client_allocator());
858 default:
859 return return_with_error(Defs::ERROR_UNIMPLEMENTED_SUBCMD);
860 }
861 }
862
864 {
865 dgClient_ = full_allocation_result(dg_service()->client_allocator());
867 {
868 // Opens the stream receiver.
869 receiver_->pool()->alloc(&streamRecvRequest_);
871 streamRecvRequest_->data()->reset(&defaultSink_, node_,
873 streamRecvRequest_->data()->done.reset(this);
874 // We keep an extra reference.
875 streamRecvRequest_->data()->done.new_child();
876 receiver_->send(streamRecvRequest_->ref());
877 }
878 return allocate_and_call(dg_service()->iface()->dispatcher(),
879 STATE(send_stream_read_datagram));
880 }
881
882 Action send_stream_read_datagram()
883 {
884 auto *b = get_allocation_result(dg_service()->iface()->dispatcher());
885 b->set_done(bn_.reset(this));
886 b->data()->reset(Defs::MTI_DATAGRAM, node_->node_id(), request()->dst,
887 MemoryConfigDefs::read_stream_datagram(request()->memory_space,
888 request()->address, dstStreamId_, request()->size));
889
891 responseCode_ = DatagramClient::OPERATION_PENDING;
893 return wait_and_call(STATE(stream_read_dg_complete));
894 }
895
896 Action stream_read_dg_complete()
897 {
898 if (!(dgClient_->result() & DatagramClient::OPERATION_SUCCESS))
899 {
900 // some error occurred.
902 }
903 if (responseCode_ & DatagramClient::OPERATION_PENDING)
904 {
906 return sleep_and_call(
907 &timer_, SEC_TO_NSEC(3), STATE(stream_read_response_timeout));
908 }
909 else
910 {
911 return call_immediately(STATE(stream_read_response_timeout));
912 }
913 }
914
915 Action stream_read_response_timeout()
916 {
917 if (responseCode_ & DatagramClient::OPERATION_PENDING)
918 {
919 return handle_read_error(Defs::OPENMRN_TIMEOUT);
920 }
921 const uint8_t *bytes =
924 {
925 LOG(INFO,
926 "Memory Config client: response datagram payload not "
927 "long enough");
928 return handle_read_error(
929 Defs::ERROR_INVALID_ARGS_MESSAGE_TOO_SHORT);
930 }
931 unsigned ofs = MemoryConfigDefs::get_payload_offset(responsePayload_);
934 uint8_t cmd = bytes[1] & MemoryConfigDefs::COMMAND_MASK;
935 if (address != request()->address)
936 {
937 LOG(VERBOSE, "mismatched address a %u o %u", (unsigned)address,
938 (unsigned)request()->address);
939 return handle_read_error(Defs::ERROR_OUT_OF_ORDER);
940 }
941 if (space != request()->memory_space)
942 {
943 LOG(VERBOSE, "mismatched space");
944 return handle_read_error(Defs::ERROR_OUT_OF_ORDER);
945 }
947 {
948 uint16_t error = bytes[ofs++];
949 error <<= 8;
950 error |= bytes[ofs];
951 return handle_read_error(error);
952 }
954 {
955 return handle_read_error(Defs::ERROR_UNIMPLEMENTED);
956 }
957 // Now: we have a read success, and the stream is probably happening.
958 return call_immediately(STATE(wait_for_stream_complete));
959 }
960
961 Action wait_for_stream_complete()
962 {
963 if (streamRecvRequest_->data()->done.abort_if_almost_done()) {
964 // There was already a close request.
965 return call_immediately(STATE(recv_stream_closed));
966 } else {
967 // We don't need to hold on to our extra ref anymore.
968 streamRecvRequest_->data()->done.notify();
969 return wait_and_call(STATE(recv_stream_closed));
970 }
971 }
972
973 Action recv_stream_closed()
974 {
975 return finish_read();
976 }
977
981 {
982 // We don't need to hold on to our extra ref anymore.
983 streamRecvRequest_->data()->done.notify();
984 receiver_->cancel_request();
985 request()->resultCode = error;
986 return wait_and_call(STATE(cleanup_after_error));
987 }
988
989 Action cleanup_after_error()
990 {
991 cleanup_read();
992 return return_with_error(request()->resultCode);
993 }
994
997 struct DefaultSink : public ByteSink
998 {
1000 : parent_(parent)
1001 { }
1002
1003 void send(ByteBuffer *msg, unsigned prio) override
1004 {
1005 auto rb = get_buffer_deleter(msg);
1006 parent_->request()->payload.append(
1007 (char *)msg->data()->data_, msg->data()->size());
1008 if (parent_->request()->progressCb)
1009 {
1010 parent_->request()->progressCb(parent_->request());
1011 }
1012 }
1013
1015 } defaultSink_{this};
1016
1017 std::unique_ptr<StreamReceiverInterface> receiver_;
1022}; // class MemoryConfigClientWithStream
1023
1024} // namespace openlcb
1025
1026#endif
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
#define STATE(_fn)
Turns a function name into an argument to be supplied to functions expecting a state.
Definition StateFlow.hxx:61
A BarrierNotifiable allows to create a number of child Notifiable and wait for all of them to finish.
BarrierNotifiable * reset(Notifiable *done)
Resets the barrier. Returns &*this. Asserts that is_done().
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.
MemoryConfigClientRequest * request()
Return type for a state flow callback.
Use this timer class to deliver the timeout notification to a stateflow.
Service * service()
Return a pointer to the service I am bound to.
Action allocate_and_call(FlowInterface< Buffer< T > > *target_flow, Callback c, Pool *pool=nullptr)
Allocates a buffer from a pool and proceed to the next state when allocation is successful.
Buffer< T > * full_allocation_result(FlowInterface< Buffer< T > > *target_flow)
Takes the result of the asynchronous allocation without resetting the object.
Buffer< T > * get_allocation_result(FlowInterface< Buffer< T > > *target_flow)
Takes the result of the asynchronous allocation.
Action invoke_subflow_and_wait(FlowInterface< Buffer< T > > *target_flow, Callback c, Args &&... args)
Calls a helper flow to perform some actions.
Action wait_and_call(Callback c)
Wait for resource to become available before proceeding to next state.
Action sleep_and_call(::Timer *timer, long long timeout_nsec, Callback c)
Suspends execution of this control flow for a specified time.
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.
Use this class to send datagrams.
Definition Datagram.hxx:79
uint32_t result()
Returns a bitmask of ResultCodes for the transmission operation.
Definition Datagram.hxx:105
virtual void write_datagram(Buffer< GenMessage > *b, unsigned priority=UINT_MAX)=0
Triggers sending a datagram.
Transport-agnostic dispatcher of datagrams.
Definition Datagram.hxx:161
TypedQAsync< DatagramClient > * client_allocator()
Datagram clients.
Definition Datagram.hxx:186
Base class with utility functionality that implements some common functionality.
Action respond_reject(uint16_t error_code)
Sends a DATAGRAM_REJECT response to the datagram originator node.
Action respond_ok(uint8_t flags)
Sends a DATAGRAM_OK response to the datagram originator node.
Implementation of the OpenLCB interface abstraction for the CAN-bus interface standard.
Definition IfCan.hxx:65
virtual void canonicalize_handle(NodeHandle *h)
Canonicalizes the node handle: fills in id and/or alias from the maps the interface holds internally.
Definition If.hxx:327
StreamTransport * stream_transport()
Definition If.hxx:345
virtual bool matching_node(NodeHandle expected, NodeHandle actual)=0
BufferPtr< StreamReceiveRequest > streamRecvRequest_
Holds a ref to the stream receiver request.
uint8_t dstStreamId_
stream ID on the local device.
Action entry() override
Entry into the StateFlow activity.
MemoryConfigClientWithStream(Node *node, MemoryConfigHandler *memcfg, uint8_t local_stream_id)
Action handle_read_error(int error)
Called upon various error conditions, typically before opening the stream.
Action entry() override
Entry into the StateFlow activity.
MemoryConfigHandler * mem_cfg()
Action dst_id_complete()
Completed the ID lookup flow.
std::unique_ptr< openlcb::NodeIdLookupFlow > nodeIdlookupFlow_
Rarely used helper flow to look up full node IDs from aliases.
BarrierNotifiable bn_
Notify helper.
Node * node_
Node from which to send the requests out.
DatagramClient * dgClient_
The allocated datagram client which we hold on for the time that we are querying.
Action factory_reset_have_id()
Called from different places to do the factory reset request once we have the node ID filled in the d...
MemoryConfigHandler * memoryConfigHandler_
Hook into the parent node's memory config handler service.
uint32_t offset_
Next byte to read from the memory space.
uint32_t payloadOffset_
Next byte in the payload to write.
StateFlowTimer timer_
timing helper
string responsePayload_
The data that came back from reading.
ResultCodes
These result codes are written into request()->resultCode during and as a return from the flow.
@ OPERATION_PENDING
cleared when done is called.
@ TIMEOUT
Timeout waiting for ack/nack.
uint8_t isWaitingForTimer_
1 if we are pending on the timer.
Action entry() override
Entry into the StateFlow activity.
Action prepare_factory_reset()
Before we send out a factory reset command, we have to ensure that we know the target node's node ID,...
int responseCode_
error code that came with the response. 0 for success.
uint16_t writeLength_
How many bytes we wrote in this datagram.
ResponseFlow responseFlow_
Handler for the incoming reply datagrams.
Implementation of the Memory Access Configuration Protocol for OpenLCB.
void clear_client(DatagramHandlerFlow *client)
Unregisters the previously registered second handler.
void set_client(DatagramHandlerFlow *client)
Registers a second handler to forward all the client interactions, i.e.
Child flow to be used in parents that need translation from node alias to node id.
Definition IfCan.hxx:207
Base class for NMRAnet nodes conforming to the asynchronous interface.
Definition Node.hxx:52
#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 INFO
Loglevel that is printed by default, reporting some status information.
Definition logging.h:57
#define HASSERT(x)
Checks that the value of expression x is true, else terminates the current process.
Definition macros.h:138
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
#define SEC_TO_NSEC(_sec)
Convert a second value to a nanosecond value.
Definition os.h:286
All callable flow request objects have to derive from this struct.
void reset_base()
Call this from all instances of reset(...).
@ CONFIGURATION
configuration message
@ MTI_DATAGRAM
datagram
void reset(ReadCmd, NodeHandle d, uint8_t space, std::function< void(MemoryConfigClientRequest *)> cb=nullptr)
Sets up a command to read an entire memory space.
void reset_base()
Helper function invoked at every other reset call.
void reset(ReadPartCmd, NodeHandle d, uint8_t space, unsigned offset, unsigned size)
Sets up a command to read a part of a memory space.
void reset(WriteCmd, NodeHandle d, uint8_t space, unsigned offset, string data)
Sets up a command to write a part of a memory space.
void reset(ReadStreamCmd, NodeHandle d, uint8_t space, std::function< void(MemoryConfigClientRequest *)> cb=nullptr)
Sets up a command to read an entire memory space using stream transport.
std::function< void(MemoryConfigClientRequest *)> progressCb
Callback to execute as progress is being made.
NodeHandle dst
Node to send the request to.
void reset(FactoryResetCmd, NodeHandle d)
Sets up a command to send a Factory Reset request to a remote node.
void reset(ReadPartStreamCmd, NodeHandle d, uint8_t space, unsigned offset, unsigned size)
Sets up a command to read a part of a memory space using stream transport.
void reset(RebootCmd, NodeHandle d)
Sets up a command to send a Reboot request to a remote node.
void reset(FreezeCmd, NodeHandle d, uint8_t space)
Sets up a command to send a Freeze request to a remote node.
void reset(UpdateCompleteCmd, NodeHandle d)
Sets up a command to send an Update Complete request to a remote node.
void reset(UnfreezeCmd, NodeHandle d, uint8_t space)
Sets up a command to send a Unfreeze request to a remote node.
Stores incoming stream data into the request()->payload object (which is a string).
void send(ByteBuffer *msg, unsigned prio) override
Entry point to the flow.
@ COMMAND_READ_REPLY
reply to read data from address space
@ COMMAND_RESET
reset node to its power on state
@ COMMAND_READ_STREAM_FAILED
failed to read data using a stream
@ COMMAND_UNFREEZE
unfreeze operation of node
@ COMMAND_FREEZE
freeze operation of node
@ COMMAND_UPDATE_COMPLETE
indicate that a sequence of commands is complete
@ COMMAND_FACTORY_RESET
reset node to factory defaults
@ COMMAND_WRITE_REPLY
reply to write data to address space
@ COMMAND_READ_FAILED
failed to read data from address space
@ COMMAND_READ_STREAM_REPLY
reply to read data using a stream
@ COMMAND_WRITE_FAILED
failed to write data to address space
static bool payload_min_length_check(const DatagramPayload &payload, unsigned extra)
static const uint8_t * payload_bytes(const DatagramPayload &payload)
Type casts a DatagramPayload to an array of bytes.
static uint32_t get_address(const DatagramPayload &payload)
static uint8_t get_space(const DatagramPayload &payload)
Container of both a NodeID and NodeAlias.
static constexpr uint8_t INVALID_STREAM_ID
This value is invalid as a source or destination stream ID.