Open Model Railroad Network (OpenMRN)
Loading...
Searching...
No Matches
BootloaderClient.hxx
Go to the documentation of this file.
1
36#include <time.h>
37
41#include "openlcb/PIPClient.hxx"
42#include "openlcb/CanDefs.hxx"
44#include "openlcb/IfCan.hxx"
45#include "utils/Ewma.hxx"
46
47namespace openlcb
48{
49
53{
54 // Response error code. Zero if request successful.
55 uint16_t error_code{0};
56 // Human-readable error string.
57 string error_details;
58};
59
63{
68 // Nonzero: request the target to reboot into bootloader mode before
69 // flashing.
70 uint8_t request_reboot{1};
71 // Nonzero: request the target to reboot after flashing.
72 uint8_t request_reboot_after{1};
73 // Nonzero: skip the PIP request to the bootloader. Use streams.
74 uint8_t skip_pip{0};
76 uint32_t offset{0};
78 string data;
81 std::function<void(float)> progress_callback;
85};
86
90
102class BootloaderClient : public StateFlow<Buffer<BootloaderRequest>, QList<1>>
103{
104public:
106 Node *node, DatagramService *if_datagram_service, IfCan *if_can)
108 , node_(node)
109 , datagramService_(if_datagram_service)
110 , ifCan_(if_can)
111 {
112 }
113
114 Action entry() override
115 {
116 return allocate_and_call(
117 STATE(got_dg_client), datagramService_->client_allocator());
118 }
119
120 DatagramService *datagram_service()
121 {
122 return datagramService_;
123 }
124
125 Node *node()
126 {
127 return node_;
128 }
129
130 const NodeHandle &dst()
131 {
132 return message()->data()->dst;
133 }
134
135 const BootloaderRequest *request()
136 {
137 return message()->data();
138 }
139
140 void response_datagram_arrived(Buffer<IncomingDatagram> *datagram)
141 {
142 if (responseDatagram_)
143 {
144 LOG_ERROR("Multiple response datagrams arrived from the "
145 "target node.");
146 responseDatagram_->unref();
147 }
148 responseDatagram_ = datagram;
149 if (sleeping_)
150 {
151 timer_.trigger();
152 } // else we will be woken up by the datagram client.
153 }
154
155private:
157 {
158 dgClient_ =
159 full_allocation_result(datagramService_->client_allocator());
160 if (!message()->data()->request_reboot)
161 {
162 return call_immediately(STATE(send_pip_request));
163 }
166 DatagramPayload payload;
167 payload.push_back(DatagramDefs::CONFIGURATION);
168 payload.push_back(MemoryConfigDefs::COMMAND_FREEZE);
169 payload.push_back(message()->data()->memory_space);
170 b->data()->reset(Defs::MTI_DATAGRAM, node_->node_id(),
171 message()->data()->dst, payload);
172 b->set_done(n_.reset(this));
173 dgClient_->write_datagram(b);
177 return wait_and_call(STATE(reboot_dg_done));
178 }
179
180 Action reboot_dg_done()
181 {
182 uint32_t dg_result = dgClient_->result();
183 LOG(INFO, "Reboot command result: %04x", dg_result);
184 return call_immediately(STATE(send_pip_request));
185 }
186
187 Action send_pip_request()
188 {
189 if (message()->data()->skip_pip) {
190 LOG(INFO, "Skipping PIP request. Using streams.");
191 return call_immediately(STATE(bootload_using_stream));
192 }
193 pipClient_.request(message()->data()->dst, node_, this);
194 return wait_and_call(STATE(pip_response));
195 }
196
197 Action pip_response()
198 {
199 if (pipClient_.error_code() != PIPClient::OPERATION_SUCCESS) {
200 LOG(INFO,
201 "PIP request failed. Error code: %" PRIx32 ". Using streams.",
202 pipClient_.error_code());
203 return call_immediately(STATE(bootload_using_stream));
204 }
205 if (pipClient_.response() & Defs::STREAM) {
206 LOG(INFO, "Using streams for bootloading.");
207 return call_immediately(STATE(bootload_using_stream));
208 } else {
209 LOG(INFO, "Using datagrams for bootloading.");
210 return call_immediately(STATE(bootload_using_datagrams));
211 }
212 }
213
214 Action bootload_using_stream()
215 {
218 DatagramPayload payload;
219 payload.push_back(DatagramDefs::CONFIGURATION);
221 payload.push_back(message()->data()->offset >> 24);
222 payload.push_back(message()->data()->offset >> 16);
223 payload.push_back(message()->data()->offset >> 8);
224 payload.push_back(message()->data()->offset);
225 payload.push_back(message()->data()->memory_space);
226 localStreamId_ = allocate_local_stream_id();
227 payload.push_back(localStreamId_);
228 b->data()->reset(Defs::MTI_DATAGRAM, node_->node_id(),
229 message()->data()->dst, payload);
230 b->set_done(n_.reset(this));
231 dgClient_->write_datagram(b);
232
233 responseDatagram_ = nullptr;
234 sleeping_ = false;
235 register_write_response_handler();
236 return wait_and_call(STATE(write_request_sent));
237 }
238
242 {
243 public:
245 : DefaultDatagramHandler(parent->datagram_service())
246 , parent_(parent)
247 {
248 }
249
250 Action entry() override
251 {
252 IncomingDatagram *datagram = message()->data();
253
254 if (datagram->dst != parent_->node() ||
255 !parent_->node()->iface()->matching_node(
256 parent_->dst(), datagram->src) ||
257 datagram->payload.size() < 6 ||
258 datagram->payload[0] != DatagramDefs::CONFIGURATION ||
259 ((datagram->payload[1] & 0xF4) !=
261 {
262 // Uninteresting datagram.
264 }
266 }
267
269 {
270 parent_->response_datagram_arrived(transfer_message());
271 return exit();
272 }
273
274 private:
275 BootloaderClient *parent_;
276 };
277
278 Action write_request_sent()
279 {
280 uint32_t dg_result = dgClient_->result();
281 datagramService_->client_allocator()->typed_insert(dgClient_);
282
283 if (responseDatagram_)
284 {
285 // Response has already arrived.
286 return call_immediately(STATE(wait_for_response_dg));
287 }
288 else if (dg_result & DatagramClient::OPERATION_SUCCESS)
289 {
290 sleeping_ = true;
291 return sleep_and_call(&timer_,
293 STATE(wait_for_response_dg));
294 }
295 else
296 {
297 // Some error happened on sending the datagram.
298 string error_details;
299 if (dg_result & DatagramClient::DST_NOT_FOUND)
300 {
301 error_details = "Destination node not found.";
302 }
303 else if (dg_result & DatagramClient::TIMEOUT)
304 {
305 error_details = "Timeout waiting for destination node to ACK "
306 "the write request datagram.";
307 }
308 else if (dg_result & DatagramClient::PERMANENT_ERROR)
309 {
310 error_details = "Write request: Permanent error.";
311 }
312 else if (dg_result & DatagramClient::RESEND_OK)
313 {
314 error_details =
315 "Write request: Temporary error, please try again.";
316 }
317 return return_error(dg_result & 0xffff, error_details);
318 }
319 }
320
321 Action wait_for_response_dg()
322 {
323 sleeping_ = false;
324 if (!responseDatagram_)
325 {
326 return return_error(DatagramClient::RESEND_OK,
327 "Timed out waiting for response datagram.");
328 }
329 unregister_write_response_handler();
330 const auto &payload = responseDatagram_->data()->payload;
331 if ((payload[1] & 0xFC) ==
333 {
334 // Write error.
335 uint16_t error_code = DatagramClient::PERMANENT_ERROR;
336 unsigned error_ofs = 6;
338 {
339 ++error_ofs;
340 }
341 LOG(WARNING, "payload length %" PRIdPTR " error offset %u data %02x %02x",
342 payload.size(), error_ofs, payload[error_ofs],
343 payload[error_ofs + 1]);
344 error_code =
345 (payload[error_ofs] << 8) | ((uint8_t)payload[error_ofs + 1]);
346 error_ofs += 2;
347 return return_error(
348 error_code, "Write rejected " + payload.substr(error_ofs));
349 }
350 else if ((payload[1] & 0xFC) ==
352 {
353 // Write OK. proceed to stream acquisition.
354 responseDatagram_->unref();
355 responseDatagram_ = nullptr;
356 return call_immediately(STATE(initiate_stream));
357 }
358 else
359 {
360 // don't know what happened.
361 return return_error(
362 DatagramClient::PERMANENT_ERROR, "internal inconsistency.");
363 }
364 }
365
366 uint8_t allocate_local_stream_id()
367 {
368 return 0x55;
369 }
370
371 Action return_error(uint16_t error_code, const string &error_details)
372 {
373 unregister_write_response_handler();
374 message()->data()->response->error_code = error_code;
375 message()->data()->response->error_details = error_details;
376 if (responseDatagram_)
377 {
378 responseDatagram_->unref();
379 responseDatagram_ = nullptr;
380 }
381 return release_and_exit();
382 }
383
384 void register_write_response_handler()
385 {
386 datagramService_->registry()->insert(
387 node_, DatagramDefs::CONFIGURATION, &writeResponseHandler_);
388 writeResponseRegistered_ = true;
389 }
390
391 void unregister_write_response_handler()
392 {
393 if (writeResponseRegistered_)
394 {
395 writeResponseRegistered_ = false;
396 datagramService_->registry()->erase(
397 node_, DatagramDefs::CONFIGURATION, &writeResponseHandler_);
398 }
399 }
400
401 Action initiate_stream()
402 {
403 return allocate_and_call(
404 node_->iface()->addressed_message_write_flow(),
405 STATE(send_init_stream));
406 }
407
408 Action send_init_stream()
409 {
410 auto *b = get_allocation_result(
411 node_->iface()->addressed_message_write_flow());
412 b->data()->reset(Defs::MTI_STREAM_INITIATE_REQUEST, node_->node_id(),
413 message()->data()->dst,
415 StreamDefs::MAX_PAYLOAD, false, localStreamId_));
416 node_->iface()->addressed_message_write_flow()->send(b);
417 sleeping_ = true;
418 node_->iface()->dispatcher()->register_handler(
419 &streamInitiateReplyHandler_, Defs::MTI_STREAM_INITIATE_REPLY,
421 return sleep_and_call(&timer_, SEC_TO_NSEC(g_bootloader_timeout_sec),
422 STATE(received_init_stream));
423 }
424
425 void stream_initiate_replied(Buffer<GenMessage> *message)
426 {
427 if (message->data()->dstNode != node_ ||
428 !node_->iface()->matching_node(dst(), message->data()->src))
429 {
430 // Not for me.
431 return message->unref();
432 }
433 const auto &payload = message->data()->payload;
434 if (payload.size() < 6 || payload[4] != localStreamId_)
435 {
436 // Talking about another stream or incorrect data.
437 return message->unref();
438 }
439 remoteStreamId_ = payload[5];
440 streamFlags_ = payload[2];
441 streamAdditionalFlags_ = payload[3];
442 maxBufferSize_ = (payload[0] << 8) | payload[1];
443 // We save the remote alias here if we haven't got any yet.
444 if (message->data()->src.alias)
445 {
446 this->message()->data()->dst.alias = message->data()->src.alias;
447 }
448 message->unref();
449 timer_.trigger();
450 }
451
452 Action received_init_stream()
453 {
454 sleeping_ = false;
455 node_->iface()->dispatcher()->unregister_handler(
456 &streamInitiateReplyHandler_, Defs::MTI_STREAM_INITIATE_REPLY,
458 if (!(streamFlags_ & StreamDefs::FLAG_ACCEPT))
459 {
460 if (streamFlags_ & StreamDefs::FLAG_PERMANENT_ERROR)
461 {
462 return return_error(
463 DatagramDefs::PERMANENT_ERROR | streamAdditionalFlags_,
464 "Stream initiate request was denied (permanent error).");
465 }
466 else
467 {
468 return return_error(
469 Defs::ERROR_TEMPORARY | streamAdditionalFlags_,
470 "Stream initiate request was denied (temporary error).");
471 }
472 }
473 if (!maxBufferSize_)
474 {
475 return return_error(DatagramDefs::PERMANENT_ERROR,
476 "Inconsistency: zero buffer length but "
477 "accepted stream request.");
478 }
479 availableBufferSize_ = maxBufferSize_;
480 bufferOffset_ = 0;
481 speed_ = 0;
482 lastMeasurementOffset_ = 0;
483 lastMeasurementTimeNsec_ = os_get_time_monotonic();
484 node_->iface()->dispatcher()->register_handler(
485 &streamProceedHandler_, Defs::MTI_STREAM_PROCEED, Defs::MTI_EXACT);
486 return call_immediately(STATE(send_stream_data));
487 }
488
489 Action send_stream_data()
490 {
491 if (bufferOffset_ >= message()->data()->data.size())
492 {
493 return call_immediately(STATE(close_stream));
494 }
495 return allocate_and_call(
496 ifCan_->frame_write_flow(), STATE(fill_outgoing_stream_frame));
497 }
498
499 Action fill_outgoing_stream_frame()
500 {
501 auto *b = get_allocation_result(ifCan_->frame_write_flow());
502 uint32_t can_id;
503 NodeAlias local_alias =
504 ifCan_->local_aliases()->lookup(node()->node_id());
505 NodeAlias remote_alias = dst().alias;
507 &can_id, local_alias, remote_alias, CanDefs::STREAM_DATA);
508 auto *frame = b->data()->mutable_frame();
509 SET_CAN_FRAME_ID_EFF(*frame, can_id);
510 size_t len =
511 std::min(size_t(7), message()->data()->data.size() - bufferOffset_);
512 if (availableBufferSize_ < len)
513 {
514 len = availableBufferSize_;
515 }
516 frame->can_dlc = len + 1;
517 frame->data[0] = remoteStreamId_;
518 memcpy(&frame->data[1], &message()->data()->data[bufferOffset_], len);
519 bufferOffset_ += len;
520 availableBufferSize_ -= len;
521 // LOG(INFO, "available buffer: %d", availableBufferSize_);
522 b->set_done(n_.reset(this));
523 ifCan_->frame_write_flow()->send(b);
524
525 if (availableBufferSize_)
526 {
527 return wait_and_call(STATE(send_stream_data));
528 }
529 else
530 {
531 return wait_and_call(STATE(wait_for_stream_proceed));
532 }
533 }
534
535 Action wait_for_stream_proceed()
536 {
537 if (availableBufferSize_)
538 {
539 // received early stream_proceed response
540 return call_immediately(STATE(stream_proceed_timeout));
541 }
542 sleeping_ = true;
543 sleepStartTimeNsec_ = os_get_time_monotonic();
544 return sleep_and_call(&timer_, SEC_TO_NSEC(g_bootloader_timeout_sec),
546 }
547
548 void stream_proceed_received(Buffer<GenMessage> *message)
549 {
550 if (message->data()->dstNode != node_ ||
551 !node_->iface()->matching_node(dst(), message->data()->src))
552 {
553 // Not for me.
554 return message->unref();
555 }
556 size_t bytes_sent = bufferOffset_ - lastMeasurementOffset_;
557 long long next_time = os_get_time_monotonic();
558 float new_speed = next_time - lastMeasurementTimeNsec_;
559 new_speed = float(bytes_sent) * 1e9 / new_speed;
560 if (!lastMeasurementOffset_)
561 {
562 speed_ = new_speed;
563 }
564 else
565 {
566 speed_ = speed_ * 0.8 + new_speed * 0.2;
567 }
568 struct timespec ts;
569 clock_gettime(CLOCK_REALTIME, &ts);
570 if (request()->progress_callback)
571 {
572 float ofs = bufferOffset_;
573 ofs /= request()->data.size();
574 request()->progress_callback(ofs);
575 }
576 LOG(INFO,
577 "%02ld.%06ld stream offset: %" PRIdPTR "; wrote %.0lld usec slept "
578 "%.0lld usec, speed=%.0f bytes/sec",
579 ts.tv_sec % 60, ts.tv_nsec / 1000, bufferOffset_,
580 (sleepStartTimeNsec_ - lastMeasurementTimeNsec_) / 1000,
581 (next_time - sleepStartTimeNsec_) / 1000, speed_);
582 lastMeasurementOffset_ = bufferOffset_;
583 lastMeasurementTimeNsec_ = next_time;
584
585 const auto &payload = message->data()->payload;
586 if (payload.size() < 2 || payload[0] != localStreamId_)
587 {
588 // Talking about another stream or incorrect data.
589 return message->unref();
590 }
591 availableBufferSize_ += maxBufferSize_;
592 message->unref();
593 if (sleeping_)
594 {
595 timer_.trigger();
596 }
597 }
598
600 {
601 sleeping_ = false;
602 if (!availableBufferSize_) // no proceed arrived
603 {
606 return return_error(Defs::ERROR_TEMPORARY,
607 "Times out waiting for stream proceed message.");
608 return call_immediately(STATE(close_stream));
609 }
610 return call_immediately(STATE(send_stream_data));
611 }
612
613 Action close_stream()
614 {
615 node_->iface()->dispatcher()->unregister_handler(
616 &streamProceedHandler_, Defs::MTI_STREAM_PROCEED, Defs::MTI_EXACT);
617 return allocate_and_call(
618 node_->iface()->addressed_message_write_flow(),
619 STATE(send_close_stream));
620 }
621
622 Action send_close_stream()
623 {
624 auto *b = get_allocation_result(
625 node_->iface()->addressed_message_write_flow());
626 b->data()->reset(Defs::MTI_STREAM_COMPLETE, node_->node_id(),
627 message()->data()->dst,
628 StreamDefs::create_close_request(localStreamId_, remoteStreamId_));
629 node_->iface()->addressed_message_write_flow()->send(b);
630 // wait some time before sending the reset command.
631 return sleep_and_call(
632 &timer_, MSEC_TO_NSEC(200), STATE(send_reboot_request));
633 }
634
635 Action send_reboot_request()
636 {
637 if (message()->data()->request_reboot_after) {
638 return allocate_and_call(
639 STATE(reboot_dg_client), datagramService_->client_allocator());
640 } else {
641 return return_error(0, "Remote node left in bootloader.");
642 }
643 }
644
645 Action bootload_using_datagrams()
646 {
647 // dgClient_ is active currently.
648 bufferOffset_ = 0;
649 return call_immediately(STATE(next_dg_write_datagram));
650 }
651
653 {
656 DatagramPayload payload = MemoryConfigDefs::write_datagram(message()->data()->memory_space, message()->data()->offset + bufferOffset_);
657 unsigned len = message()->data()->data.size() - bufferOffset_;
658 if (len > 64) len = 64;
659 payload.append(&message()->data()->data[bufferOffset_], len);
660 b->set_done(n_.reset(this));
661 b->data()->reset(Defs::MTI_DATAGRAM, node_->node_id(),
662 message()->data()->dst, payload);
663 dgClient_->write_datagram(b);
664
665 responseDatagram_ = nullptr;
666 sleeping_ = false;
668 return wait_and_call(STATE(dg_write_request_sent));
669 }
670
671 Action dg_write_request_sent()
672 {
673 uint32_t dg_result =
674 dgClient_->result() & DatagramClient::RESPONSE_CODE_MASK;
675 if (dg_result != DatagramClient::OPERATION_SUCCESS) {
676 datagramService_->client_allocator()->typed_insert(dgClient_);
677 return return_error(dg_result, "Write rejected.");
678 }
679
680 if (dgClient_->result() & DatagramClient::OK_REPLY_PENDING) {
681 DIE("Write datagram results with reply pending not supported for "
682 "bootloader yet.");
683 }
684
685 unsigned len = message()->data()->data.size() - bufferOffset_;
686 if (len > 64) len = 64;
687 bufferOffset_ += len;
688
689 if ((bufferOffset_ & ~0xFF) != ((bufferOffset_ - len) & ~0xFF)) {
690 speedAvg_.add_absolute(bufferOffset_);
691 LOG(INFO, "write offset: %" PRIdPTR "; speed=%.0f bytes/sec",
692 bufferOffset_, speedAvg_.avg());
693 if (request()->progress_callback)
694 {
695 float ofs = bufferOffset_;
696 ofs /= request()->data.size();
697 request()->progress_callback(ofs);
698 }
699 }
700
701 if (bufferOffset_ < message()->data()->data.size()) {
702 return call_immediately(STATE(next_dg_write_datagram));
703 }
704 if (message()->data()->request_reboot_after) {
705 return call_immediately(STATE(reboot_with_dg_client));
706 } else {
707 return return_error(0, "Remote node left in bootloader.");
708 }
709 }
710
711 Action reboot_dg_client()
712 {
713 dgClient_ =
714 full_allocation_result(datagramService_->client_allocator());
715 return reboot_with_dg_client();
716 }
717
718 Action reboot_with_dg_client()
719 {
722 DatagramPayload payload;
723 payload.push_back(DatagramDefs::CONFIGURATION);
724 payload.push_back(MemoryConfigDefs::COMMAND_UNFREEZE);
725 payload.push_back(message()->data()->memory_space);
726 b->data()->reset(Defs::MTI_DATAGRAM, node_->node_id(),
727 message()->data()->dst, payload);
728 b->set_done(n_.reset(this));
729 dgClient_->write_datagram(b);
730 return wait_and_call(STATE(finish));
731 }
732
733 Action finish()
734 {
735 auto result = dgClient_->result();
736 result &= DatagramClient::RESPONSE_CODE_MASK;
737 if (result == DatagramClient::DST_REBOOT ||
738 result == DatagramClient::OPERATION_SUCCESS)
739 {
740 // this is fine
741 result = 0;
742 }
743 uint16_t olcb_error = result & 0xffff;
744 if (olcb_error == FirmwareUpgradeDefs::ERROR_INCOMPATIBLE_FIRMWARE)
745 {
746 return return_error(olcb_error,
747 "The firmware data is incompatible with this hardware.");
748 }
749 if (olcb_error == FirmwareUpgradeDefs::ERROR_CORRUPTED_DATA)
750 {
751 return return_error(
752 olcb_error, "The firmware data is invalid or corrupted.");
753 }
754 if (olcb_error == FirmwareUpgradeDefs::ERROR_WRITE_CHECKSUM_FAILED)
755 {
756 return return_error(olcb_error,
757 "The firmware written has failed checksum. Try again.");
758 }
759 if (olcb_error & DatagramClient::PERMANENT_ERROR)
760 {
761 return return_error(result & 0xffff, "");
762 }
763 // Not sure what this is.
764 datagramService_->client_allocator()->typed_insert(dgClient_);
765 return return_error(0, "");
766 }
767
768private:
769 Node *node_;
770 DatagramService *datagramService_;
771 IfCan *ifCan_;
772 DatagramClient *dgClient_ = nullptr;
773 Buffer<IncomingDatagram> *responseDatagram_ = nullptr;
774 uint8_t localStreamId_;
775 uint8_t remoteStreamId_;
776 // maximum size of pending stream data.
777 uint16_t maxBufferSize_;
778 // Flags from the stream initiate response.
779 uint8_t streamFlags_;
780 // Additional flags from the initiate response.
781 uint8_t streamAdditionalFlags_;
782
783 // How many bytes can we send before needing to wait for stream data
784 // proceed message.
785 uint32_t availableBufferSize_;
786 // The next byte we need to send from the input data.
787 size_t bufferOffset_;
788
789 Ewma speedAvg_;
790 // The Average speed (ewma) in bytes/second.
791 float speed_;
792 // The offset at which the last speed measurement took place.
793 size_t lastMeasurementOffset_;
794 // The time in nsec at which the last speed measurement took place.
795 long long lastMeasurementTimeNsec_;
796 // Snapshots the time at which we start to sleep to wait for a stream
797 // proceed flag.
798 long long sleepStartTimeNsec_;
799
800 WriteResponseHandler writeResponseHandler_{this};
801 bool writeResponseRegistered_ = false;
802 MessageHandler::GenericHandler streamInitiateReplyHandler_{
803 this, &BootloaderClient::stream_initiate_replied};
804 MessageHandler::GenericHandler streamProceedHandler_{
805 this, &BootloaderClient::stream_proceed_received};
806 StateFlowTimer timer_{this};
807 // true if we are waiting for a timeout, false if we haven't started
808 // sleeping yet.
809 bool sleeping_ = false;
811 PIPClient pipClient_{ifCan_};
812};
813
814} // namespace openlcb
DynamicPool * mainBufferPool
main buffer pool instance
Definition Buffer.cxx:37
#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().
void set_done(BarrierNotifiable *done)
Specifies that a given BarrierNotifiable must be called when the Buffer is deallocated (unreffed to z...
Definition Buffer.hxx:97
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
OutgoingFrameHandler * frame_write_flow()
Definition CanIf.hxx:214
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.
Exponentially weighted moving average.
Definition Ewma.hxx:57
float avg()
Definition Ewma.hxx:99
void add_absolute(uint32_t offset)
Sets the absolute value where the transfer is.
Definition Ewma.hxx:71
virtual void send(MessageType *message, unsigned priority=UINT_MAX)=0
Entry point to the flow.
Node information.
Definition Devtab.hxx:549
void alloc(Buffer< BufferType > **result, Executable *flow=NULL)
Get a free item out of the pool.
Definition Buffer.hxx:292
A list of queues.
Definition Queue.hxx:466
Return type for a state flow callback.
Action exit()
Terminates the processing of this flow.
State flow with a given typed input queue.
void insert(Node *node, uint32_t id, Handler *handler)
Inserts a handler into the map.
void erase(Node *node, uint32_t id, Handler *handler)
Removes a handler from the map.
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()
NodeAlias lookup(NodeID id)
Lookup a node's alias based on its Node ID.
Datagram handler that listens to the incoming memoryconfig datagram for the write stream response mes...
Action entry() override
Entry into the StateFlow activity.
Action ok_response_sent() override
This state is where the handling will end up after a respond_ok call.
StateFlow performing the bootloading process.
Action entry() override
Entry into the StateFlow activity.
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
AliasCache * local_aliases()
Definition IfCan.hxx:96
MessageDispatchFlow * dispatcher()
Definition If.hxx:224
MessageHandler * addressed_message_write_flow()
Definition If.hxx:210
virtual bool matching_node(NodeHandle expected, NodeHandle actual)=0
Base class for NMRAnet nodes conforming to the asynchronous interface.
Definition Node.hxx:52
uint32_t error_code()
Definition PIPClient.hxx:90
uint64_t response()
Returns the response of the last request out, or unspecified if the last request has not succeeded.
Definition PIPClient.hxx:97
void request(NodeHandle dst, Node *src, Notifiable *done)
Sends a PIP request to the specified node.
Definition PIPClient.hxx:78
#define LOG(level, message...)
Conditionally write a message to the logging output.
Definition logging.h:99
static const int WARNING
Loglevel that is always printed, reporting a warning or a retryable error.
Definition logging.h:55
static const int INFO
Loglevel that is printed by default, reporting some status information.
Definition logging.h:57
#define LOG_ERROR(message...)
Shorthand for LOG(LEVEL_ERROR, message...). See LOG.
Definition logging.h:124
#define DIE(MSG)
Unconditionally terminates the current process with a message.
Definition macros.h:143
int g_bootloader_timeout_sec
How long to wait for a write datagram's response in the bootloader client.
uint16_t NodeAlias
Alias to a 48-bit NMRAnet Node ID type.
Payload DatagramPayload
Contents of a Datagram message.
Definition Datagram.hxx:51
long long os_get_time_monotonic(void)
Get the monotonic time since the system started.
Definition os.c:571
#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
Send a structure of this type to the BootloaderClient state flow to perform the bootloading process o...
string data
Payload to write.
uint32_t offset
Offset at which to start writing.
std::function< void(float)> progress_callback
If set, will be called with floats [0.0, 1.0] as the download is progressing.
uint8_t memory_space
Memory space ID to write into.
BootloaderResponse * response
This structure will be filled with the returning error code, or zero if the bootloading was successfu...
NodeHandle dst
Node to send the bootload request to.
This structure will be filled in by BootloaderClient when the bootloading operation is complete.
static void set_datagram_fields(uint32_t *can_id, NodeAlias src, NodeAlias dst, CanFrameType can_type)
Set all the CAN ID fields for datagram or stream message.
Definition CanDefs.hxx:305
@ STREAM_DATA
stream data frame
Definition CanDefs.hxx:105
@ FLAGS_NONE
no flags set
@ PERMANENT_ERROR
Permanent error occurred.
@ CONFIGURATION
configuration message
@ MTI_STREAM_PROCEED
stream flow control
@ MTI_EXACT
match mask for a single MTI
@ MTI_STREAM_INITIATE_REPLY
Stream initiate reply.
@ MTI_STREAM_INITIATE_REQUEST
Stream initiate request.
@ MTI_STREAM_COMPLETE
stream terminate connection
@ MTI_DATAGRAM
datagram
Message structure for incoming datagram handlers.
Definition Datagram.hxx:55
DatagramPayload payload
Owned by the current IncomingDatagram object.
Definition Datagram.hxx:62
Node * dst
Virtual node that the datagram was addressed to.
Definition Datagram.hxx:59
NodeHandle src
Originator of the incoming datagram.
Definition Datagram.hxx:57
@ COMMAND_WRITE_STREAM_FAILED
failed to write data using a stream
@ COMMAND_UNFREEZE
unfreeze operation of node
@ COMMAND_FREEZE
freeze operation of node
@ COMMAND_WRITE_STREAM
command to write data using a stream
@ COMMAND_WRITE_STREAM_REPLY
reply to write data using a stream
@ SPACE_FIRMWARE
firmware upgrade space
Container of both a NodeID and NodeAlias.
NodeAlias alias
alias to NMRAnet Node ID
static Payload create_close_request(uint8_t src_stream_id, uint8_t dst_stream_id, uint32_t total_bytes=INVALID_TOTAL_BYTE_COUNT)
Creates the payload for a stream close message.
static Payload create_initiate_request(uint16_t max_buffer_size, bool has_ident, uint8_t src_stream_id, uint8_t dst_stream_id=INVALID_STREAM_ID)
Creates a Stream Initiate Request message payload.
static constexpr uint16_t MAX_PAYLOAD
Maximum window size for stream send.