Open Model Railroad Network (OpenMRN)
Loading...
Searching...
No Matches
PIPClient.hxx
Go to the documentation of this file.
1
36#ifndef _OPENLCB_PIPCLIENT_HXX_
37#define _OPENLCB_PIPCLIENT_HXX_
38
40#include "openlcb/Defs.hxx"
41#include "openlcb/If.hxx"
42#include "os/os.h"
43
44namespace openlcb
45{
46
49extern long long PIP_CLIENT_TIMEOUT_NSEC;
50
62{
63public:
64 PIPClient(If *iface)
65 : StateFlowBase(iface)
66 {
67 }
68
78 void request(NodeHandle dst, Node *src, Notifiable *done)
79 {
80 src_ = src;
81 dst_ = dst;
82 done_ = done;
83 errorCode_ = OPERATION_PENDING;
84 pipResponse_ = 0;
85 start_flow(STATE(request_buffer));
86 }
87
90 uint32_t error_code()
91 {
92 return errorCode_;
93 }
94
97 uint64_t response()
98 {
99 return pipResponse_;
100 }
101
102 enum ResultCodes
103 {
104 // Internal error codes generated by the send flow
105 OPERATION_SUCCESS = 0x10000, //< set when the Datagram OK arrives
106 OPERATION_PENDING = 0x20000, //< cleared when done is called.
107 TIMEOUT = 0x80000, //< Timeout waiting for ack/nack.
108
109 IDLE = 0xFFFF0000, //< The current flow is not in use.
110
111 };
112
113private:
114 enum
115 {
118 MASK_1 = ~(MTI_1a ^ MTI_1b),
119 MTI_1 = MTI_1a,
120
122 MASK_2 = Defs::MTI_EXACT,
123 };
124
125 Action request_buffer()
126 {
127 return allocate_and_call(
128 iface()->addressed_message_write_flow(), STATE(write_request));
129 }
130
131 Action write_request()
132 {
133 auto *b =
134 get_allocation_result(iface()->addressed_message_write_flow());
135 b->data()->reset(Defs::MTI_PROTOCOL_SUPPORT_INQUIRY, src_->node_id(),
136 dst_, EMPTY_PAYLOAD);
137
138 iface()->dispatcher()->register_handler(
139 &responseHandler_, MTI_1, MASK_1);
140 iface()->dispatcher()->register_handler(
141 &responseHandler_, MTI_2, MASK_2);
142
143 iface()->addressed_message_write_flow()->send(b);
144
145 return sleep_and_call(
146 &timer_, PIP_CLIENT_TIMEOUT_NSEC, STATE(response_came));
147 }
148
149 // Callback from the response handler.
150 void handle_response(Buffer<GenMessage> *message)
151 {
153 if (src_ != message->data()->dstNode ||
154 !iface()->matching_node(dst_, message->data()->src))
155 {
156 // Not from the right place.
157 return;
158 }
159 if (message->data()->mti == Defs::MTI_OPTIONAL_INTERACTION_REJECTED ||
160 message->data()->mti == Defs::MTI_TERMINATE_DUE_TO_ERROR)
161 {
162 uint16_t mti, error_code;
164 message->data()->payload, &error_code, &mti, nullptr);
165 if (mti && mti != Defs::MTI_PROTOCOL_SUPPORT_INQUIRY)
166 {
167 // Got error response for a different interaction. Ignore.
168 return;
169 }
170 errorCode_ = error_code;
171 }
172 else if (message->data()->mti == Defs::MTI_PROTOCOL_SUPPORT_REPLY)
173 {
174 pipResponse_ = buffer_to_node_id(message->data()->payload);
175 errorCode_ = OPERATION_SUCCESS;
176 }
177 else
178 {
179 // Dunno what this MTI is. Ignore.
180 LOG(INFO, "Unexpected MTI for PIP response handler: %04x",
181 message->data()->mti);
182 return;
183 }
184
185 // Wakes up parent flow.
186 errorCode_ &= ~OPERATION_PENDING;
187 timer_.trigger();
188 }
189
190 Action response_came()
191 {
192 if (errorCode_ & OPERATION_PENDING)
193 {
194 errorCode_ = TIMEOUT;
195 }
196 iface()->dispatcher()->unregister_handler_all(&responseHandler_);
197 done_->notify();
198 return exit();
199 }
200
205 {
206 public:
208 : parent_(parent)
209 {
210 }
211
212 void send(Buffer<GenMessage> *message, unsigned priority) OVERRIDE
213 {
214 parent_->handle_response(message);
215 }
216
217 private:
218 PIPClient *parent_;
219 };
220
221 If *iface()
222 {
223 return static_cast<If *>(service());
224 }
225
226 StateFlowTimer timer_{this};
227 Node *src_;
228 Notifiable *done_;
229 NodeHandle dst_;
230 uint64_t pipResponse_{0};
231 uint32_t errorCode_{IDLE};
232 PIPResponseHandler responseHandler_{this};
233};
234
235} // namespace openlcb
236
237#endif // _OPENLCB_PIPCLIENT_HXX_
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
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
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.
virtual void send(MessageType *message, unsigned priority=UINT_MAX)=0
Entry point to the flow.
Node information.
Definition Devtab.hxx:549
An object that can schedule itself on an executor to run.
virtual void notify()=0
Generic callback.
Base class for state machines.
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.
StateFlowBase()
Default constructor.
Action exit()
Terminate current StateFlow activity.
void start_flow(Callback c)
Resets the flow to the specified state and starts it.
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
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
Message handler for incoming PIP responses.
State flow to request PIP information from a remote node on the OpenLCB network.
Definition PIPClient.hxx:62
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 INFO
Loglevel that is printed by default, reporting some status information.
Definition logging.h:57
#define OVERRIDE
Function attribute for virtual functions declaring that this funciton is overriding a funciton that s...
Definition macros.h:180
long long PIP_CLIENT_TIMEOUT_NSEC
Specifies how long to wait for a PIP request to get a response.
Definition PIPClient.cxx:39
string EMPTY_PAYLOAD
A global class / variable for empty or not-yet-initialized payloads.
Definition If.cxx:152
NodeID buffer_to_node_id(const string &buf)
Converts a 6-byte-long buffer to a node ID.
Definition If.cxx:66
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
@ MTI_EXACT
match mask for a single MTI
@ MTI_PROTOCOL_SUPPORT_INQUIRY
inquire on supported protocols
@ MTI_OPTIONAL_INTERACTION_REJECTED
rejected request
@ MTI_PROTOCOL_SUPPORT_REPLY
reply with supported protocols
@ MTI_TERMINATE_DUE_TO_ERROR
terminate due to some error
Container of both a NodeID and NodeAlias.