35#ifndef _OPENLCB_MEMORYCONFIGCLIENT_HXX_
36#define _OPENLCB_MEMORYCONFIGCLIENT_HXX_
65 enum ReadPartStreamCmd
75 enum UpdateCompleteCmd
111 memory_space = space;
128 reset(READ, d, space, std::move(cb));
143 memory_space = space;
145 this->address = offset;
161 reset(READ_PART, d, space, offset, size);
172 WriteCmd,
NodeHandle d, uint8_t space,
unsigned offset,
string data)
176 memory_space = space;
178 this->address = offset;
179 this->size = data.size();
180 payload = std::move(data);
190 cmd = CMD_META_REQUEST;
205 cmd = CMD_META_REQUEST;
220 cmd = CMD_FACTORY_RESET;
236 cmd = CMD_META_REQUEST;
242 payload.push_back(space);
253 cmd = CMD_META_REQUEST;
259 payload.push_back(space);
262 enum Command : uint8_t
283 uint8_t memory_space;
309 DatagramClient::OPERATION_PENDING,
310 TIMEOUT = Defs::ERROR_PERMANENT |
311 Defs::OPENMRN_TIMEOUT,
333 case MemoryConfigClientRequest::CMD_READ:
334 case MemoryConfigClientRequest::CMD_READ_PART:
336 STATE(do_read), dg_service()->client_allocator());
337 case MemoryConfigClientRequest::CMD_WRITE:
339 STATE(do_write), dg_service()->client_allocator());
340 case MemoryConfigClientRequest::CMD_META_REQUEST:
342 STATE(do_meta_request), dg_service()->client_allocator());
343 case MemoryConfigClientRequest::CMD_FACTORY_RESET:
360 Action send_next_read()
363 dg_service()->iface()->dispatcher(),
STATE(send_read_datagram));
366 Action send_read_datagram()
372 MemoryConfigDefs::read_datagram(
374 if (
request()->size < 0xffffffffu)
384 Action read_complete()
403 Action read_response_timeout()
407 return handle_read_error(Defs::OPENMRN_TIMEOUT);
410 const uint8_t *bytes =
415 "Memory Config client: response datagram payload not "
417 return handle_read_error(
418 Defs::ERROR_INVALID_ARGS_MESSAGE_TOO_SHORT);
423 uint8_t cmd = bytes[1] & MemoryConfigDefs::COMMAND_MASK;
426 return handle_read_error(Defs::ERROR_OUT_OF_ORDER);
428 if (space !=
request()->memory_space)
430 return handle_read_error(Defs::ERROR_OUT_OF_ORDER);
436 return handle_read_error(
437 Defs::ERROR_INVALID_ARGS_MESSAGE_TOO_SHORT);
439 uint16_t error = bytes[ofs++];
442 return handle_read_error(error);
446 return handle_read_error(Defs::ERROR_UNIMPLEMENTED);
448 unsigned dlen = len - ofs;
449 request()->payload.append((
char *)(bytes + ofs), dlen);
455 if ((dlen < 64) || (
request()->size == 0))
463 Action handle_read_error(
int error)
465 if (error == MemoryConfigDefs::ERROR_OUT_OF_BOUNDS)
467 return finish_read();
497 Action send_next_write()
500 dg_service()->iface()->dispatcher(),
STATE(send_write_datagram));
503 Action send_write_datagram()
508 if (sz > MemoryConfigDefs::MAX_DATAGRAM_RW_BYTES)
510 sz = MemoryConfigDefs::MAX_DATAGRAM_RW_BYTES;
514 MemoryConfigDefs::write_datagram(
request()->memory_space,
offset_,
522 Action write_complete()
541 Action write_response_timeout()
545 return handle_write_error(Defs::OPENMRN_TIMEOUT);
548 const uint8_t *bytes =
553 "Memory Config client: response datagram payload not "
555 return handle_write_error(
556 Defs::ERROR_INVALID_ARGS_MESSAGE_TOO_SHORT);
561 uint8_t cmd = bytes[1] & MemoryConfigDefs::COMMAND_MASK;
564 return handle_write_error(Defs::ERROR_OUT_OF_ORDER);
566 if (space !=
request()->memory_space)
568 return handle_write_error(Defs::ERROR_OUT_OF_ORDER);
574 return handle_write_error(
575 Defs::ERROR_INVALID_ARGS_MESSAGE_TOO_SHORT);
577 uint16_t error = bytes[ofs++];
580 return handle_write_error(error);
584 return handle_write_error(Defs::ERROR_UNIMPLEMENTED);
596 Action handle_write_error(
int error)
598 if (error == MemoryConfigDefs::ERROR_OUT_OF_BOUNDS)
600 return finish_write();
614 Action finish_write()
620 Action do_meta_request()
626 dg_service()->iface()->dispatcher(),
STATE(send_meta_datagram));
629 Action send_meta_datagram()
634 std::move(
request()->payload));
640 Action meta_complete()
645 result &= DatagramClient::RESPONSE_CODE_MASK;
646 if (result == DatagramClient::DST_REBOOT ||
647 result == DatagramClient::OPERATION_SUCCESS)
682 request()->dst = rb->data()->handle;
699 STATE(do_meta_request), dg_service()->client_allocator());
725 Defs::ERROR_INVALID_ARGS_MESSAGE_TOO_SHORT);
728 uint8_t cmd = bytes[1] & ~3;
735 MemoryConfigClientRequest::CMD_READ &&
737 MemoryConfigClientRequest::CMD_READ_PART)
753 MemoryConfigClientRequest::CMD_READ &&
755 MemoryConfigClientRequest::CMD_READ_PART)
759 if (!parent_->
request()->use_stream)
774 MemoryConfigClientRequest::CMD_WRITE)
854 case MemoryConfigClientRequest::CMD_READ:
855 case MemoryConfigClientRequest::CMD_READ_PART:
879 STATE(send_stream_read_datagram));
882 Action send_stream_read_datagram()
887 MemoryConfigDefs::read_stream_datagram(
request()->memory_space,
896 Action stream_read_dg_complete()
915 Action stream_read_response_timeout()
921 const uint8_t *bytes =
926 "Memory Config client: response datagram payload not "
929 Defs::ERROR_INVALID_ARGS_MESSAGE_TOO_SHORT);
934 uint8_t cmd = bytes[1] & MemoryConfigDefs::COMMAND_MASK;
935 if (address !=
request()->address)
937 LOG(
VERBOSE,
"mismatched address a %u o %u", (
unsigned)address,
941 if (space !=
request()->memory_space)
948 uint16_t error = bytes[ofs++];
961 Action wait_for_stream_complete()
973 Action recv_stream_closed()
975 return finish_read();
984 receiver_->cancel_request();
989 Action cleanup_after_error()
1006 parent_->
request()->payload.append(
1007 (
char *)msg->
data()->data_, msg->
data()->size());
1008 if (parent_->
request()->progressCb)
1015 } defaultSink_{
this};
1017 std::unique_ptr<StreamReceiverInterface> receiver_;
BufferPtr< T > get_buffer_deleter(Buffer< T > *b)
Helper function to create a BufferPtr of an appropriate type without having to explicitly specify the...
AutoReleaseBuffer< T > BufferPtr
Smart pointer for buffers.
#define STATE(_fn)
Turns a function name into an argument to be supplied to functions expecting a state.
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.
T * data()
get a pointer to the start of the data.
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.
Action call_immediately(Callback c)
Imediately call the next state upon return.
Use this class to send datagrams.
uint32_t result()
Returns a bitmask of ResultCodes for the transmission operation.
virtual void write_datagram(Buffer< GenMessage > *b, unsigned priority=UINT_MAX)=0
Triggers sending a datagram.
Transport-agnostic dispatcher of datagrams.
TypedQAsync< DatagramClient > * client_allocator()
Datagram clients.
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.
const uint8_t * payload()
Implementation of the OpenLCB interface abstraction for the CAN-bus interface standard.
virtual void canonicalize_handle(NodeHandle *h)
Canonicalizes the node handle: fills in id and/or alias from the maps the interface holds internally.
StreamTransport * stream_transport()
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.
Base class for NMRAnet nodes conforming to the asynchronous interface.
uint8_t get_next_stream_receive_id()
#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 INFO
Loglevel that is printed by default, reporting some status information.
#define HASSERT(x)
Checks that the value of expression x is true, else terminates the current process.
void node_id_to_data(NodeID id, void *data)
Convenience function to render a 48-bit NMRAnet node ID into an existing buffer.
#define SEC_TO_NSEC(_sec)
Convert a second value to a nanosecond value.
All callable flow request objects have to derive from this struct.
void reset_base()
Call this from all instances of reset(...).
@ CONFIGURATION
configuration message
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.