Open Model Railroad Network (OpenMRN)
Loading...
Searching...
No Matches
SNIPClient.hxx
Go to the documentation of this file.
1
36#ifndef _OPENLCB_SNIPCLIENT_HXX_
37#define _OPENLCB_SNIPCLIENT_HXX_
38
40#include "openlcb/Defs.hxx"
41#include "openlcb/If.hxx"
42#include "os/sleep.h"
43
44namespace openlcb
45{
46
49{
53 void reset(Node *src, NodeHandle dst)
54 {
55 reset_base();
56 resultCode = OPERATION_PENDING;
57 src_ = src;
58 dst_ = dst;
59 }
60
61 enum
62 {
63 OPERATION_PENDING = 0x20000, //< cleared when done is called.
64 ERROR_REJECTED = 0x200000, //< Target node has rejected the request.
65 OPENMRN_TIMEOUT = 0x80000, //< Timeout waiting for ack/nack.
66 };
67
74};
75
76#if !defined(GTEST) || !defined(SNIP_CLIENT_TIMEOUT_NSEC)
79static constexpr long long SNIP_CLIENT_TIMEOUT_NSEC = MSEC_TO_NSEC(2000);
80#endif
81
82class SNIPClient : public CallableFlow<SNIPClientRequest>
83{
84public:
91
93 void shutdown()
94 {
95 while (!is_waiting())
96 {
98 [this]() { timer_.ensure_triggered(); });
99 microsleep(500);
100 }
101 }
102
103 Action entry() override
104 {
105 request()->resultCode = SNIPClientRequest::OPERATION_PENDING;
106 return allocate_and_call(
107 iface()->addressed_message_write_flow(), STATE(write_request));
108 }
109
110private:
111 enum
112 {
115 MASK_1 = ~(MTI_1a ^ MTI_1b),
116 MTI_1 = MTI_1a,
117
119 MASK_2 = Defs::MTI_EXACT,
120 };
121
125 {
126 auto *b =
127 get_allocation_result(iface()->addressed_message_write_flow());
128 b->data()->reset(Defs::MTI_IDENT_INFO_REQUEST,
129 request()->src_->node_id(), request()->dst_, EMPTY_PAYLOAD);
130
132 &responseHandler_, MTI_1, MASK_1);
134 &responseHandler_, MTI_2, MASK_2);
135
137
138 return sleep_and_call(
139 &timer_, SNIP_CLIENT_TIMEOUT_NSEC, STATE(response_came));
140 }
141
145 {
146 auto rb = get_buffer_deleter(message);
147 if (request()->src_ != message->data()->dstNode ||
148 !iface()->matching_node(request()->dst_, message->data()->src))
149 {
150 // Not from the right place.
151 return;
152 }
155 {
156 uint16_t mti, error_code;
158 message->data()->payload, &error_code, &mti, nullptr);
159 LOG(INFO, "rejection err %04x mti %04x", error_code, mti);
160 if (mti && mti != Defs::MTI_IDENT_INFO_REQUEST)
161 {
162 // Got error response for a different interaction. Ignore.
163 return;
164 }
165 request()->resultCode =
166 error_code | SNIPClientRequest::ERROR_REJECTED;
167 }
168 else if (message->data()->mti == Defs::MTI_IDENT_INFO_REPLY)
169 {
170 request()->response = std::move(message->data()->payload);
171 request()->resultCode = 0;
172 }
173 else
174 {
175 // Dunno what this MTI is. Ignore.
176 LOG(INFO, "Unexpected MTI for SNIP response handler: %04x",
177 message->data()->mti);
178 return;
179 }
180 // Wakes up parent flow.
181 request()->resultCode &= ~SNIPClientRequest::OPERATION_PENDING;
182 timer_.trigger();
183 }
184
185 Action response_came()
186 {
188 if (request()->resultCode & SNIPClientRequest::OPERATION_PENDING)
189 {
190 return return_with_error(SNIPClientRequest::OPENMRN_TIMEOUT);
191 }
192 return return_with_error(request()->resultCode);
193 }
194
197 {
198 return request()->src_->iface();
199 }
200
206};
207
208} // namespace openlcb
209
210#endif // _OPENLCB_SNIPCLIENT_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
#define STATE(_fn)
Turns a function name into an argument to be supplied to functions expecting a state.
Definition StateFlow.hxx:61
Base class for all QMember types that hold data in an expandable format.
Definition Buffer.hxx:195
Action return_with_error(int error)
Terminates the flow and returns the request buffer to the caller with an specific error code.
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 sync_run(std::function< void()> fn)
Synchronously runs a closure on this executor.
Definition Executor.cxx:151
virtual void send(MessageType *message, unsigned priority=UINT_MAX)=0
Entry point to the flow.
Collection of related state machines that pend on incoming messages.
ExecutorBase * executor()
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 > * get_allocation_result(FlowInterface< Buffer< T > > *target_flow)
Takes the result of the asynchronous allocation.
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
void ensure_triggered()
Triggers the timer if it is not expired yet.
Definition Timer.hxx:249
Abstract class representing an OpenLCB Interface.
Definition If.hxx:185
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
Action write_request()
Called once the allocation is complete.
StateFlowTimer timer_
Handles the timeout feature.
void handle_response(Buffer< GenMessage > *message)
Callback from the response handler.
Action entry() override
Entry into the StateFlow activity.
void shutdown()
Flushes the pending timed operations.
SNIPClient(Service *s)
Constructor.
IncomingMessageStateFlow::GenericHandler responseHandler_
Registered handler for response messages.
#define LOG(level, message...)
Conditionally write a message to the logging output.
Definition logging.h:99
static const int INFO
Loglevel that is printed by default, reporting some status information.
Definition logging.h:57
string EMPTY_PAYLOAD
A global class / variable for empty or not-yet-initialized payloads.
Definition If.cxx:152
string Payload
Container that carries the data bytes in an NMRAnet message.
void buffer_to_error(const Payload &payload, uint16_t *error_code, uint16_t *mti, string *error_message)
Parses the payload of an Optional Interaction Rejected or Terminate Due To Error message.
Definition If.cxx:112
static constexpr long long SNIP_CLIENT_TIMEOUT_NSEC
Specifies how long to wait for a SNIP request to get a response.
#define MSEC_TO_NSEC(_msec)
Convert a millisecond value to a nanosecond value.
Definition os.h:268
static void microsleep(uint32_t microseconds)
Sleep a given number of microseconds.
All callable flow request objects have to derive from this struct.
void reset_base()
Call this from all instances of reset(...).
int resultCode
If high bits are zero, this is a 16-bit OpenLCB result code.
@ MTI_IDENT_INFO_REPLY
node identity reply
@ MTI_IDENT_INFO_REQUEST
request node identity
@ MTI_EXACT
match mask for a single MTI
@ MTI_OPTIONAL_INTERACTION_REJECTED
rejected request
@ MTI_TERMINATE_DUE_TO_ERROR
terminate due to some error
Container of both a NodeID and NodeAlias.
Buffer contents for invoking the SNIP client.
NodeHandle dst_
Destination node to query.
Payload response
Response payload if successful.
void reset(Node *src, NodeHandle dst)
Helper function for invoke_subflow.
Node * src_
Source node where to send the request from.