Open Model Railroad Network (OpenMRN)
Loading...
Searching...
No Matches
SimpleInfoProtocol.hxx
Go to the documentation of this file.
1
35#ifndef _OPENLCB_SIMPLEINFOPROTOCOL_HXX_
36#define _OPENLCB_SIMPLEINFOPROTOCOL_HXX_
37
38#include <fcntl.h>
39#include <unistd.h>
40
41#include "openmrn_features.h"
42#include "openlcb/If.hxx"
44
45namespace openlcb
46{
47
48struct SimpleInfoDescriptor;
49
53{
55 : src(nullptr)
56 , dst({0, 0})
57 , descriptor(nullptr)
58 {
59 }
64 void reset(const GenMessage *msg_to_respond,
65 const SimpleInfoDescriptor *desc,
66 Defs::MTI response_mti)
67 {
68 HASSERT(msg_to_respond->dstNode);
69 src = msg_to_respond->dstNode;
70 dst = msg_to_respond->src;
71 descriptor = desc;
72 mti = response_mti;
73 }
82};
83
89{
90 enum Cmd
91 {
92 END_OF_DATA = 0, //< End of the data sequence
93 C_STRING = 1, //< pointer points to a C-string with NULL term. arg, if non-zero, specifies max length including the 0 byte.
94 LITERAL_BYTE = 2, //< transfer argument as byte (e.g. version). Pointer, if not null, will be checked to match the transferred value.
95 CHAR_ARRAY = 3, //< len = argument, pointer in data. No null termination.
96 FILE_C_STRING = 4,//< pointer is filename, offset is used for file offset. arg is the maximum length including nul termination.
97 FILE_LITERAL_BYTE = 5,//< transfer argument as byte (e.g. version). pointer is filename, offset is used for file offset, used to check against the expected value.
98 FILE_CHAR_ARRAY = 6,//< pointer is filename, offset is used for file offset, arg is length of value, no null termination.
99 };
100
101 uint8_t cmd; //< Command. See enum Cmd.
102 uint8_t arg; //< Argument to the command.
103 uint16_t arg2; //< Additional argument.
105 const char *data;
106};
107
110
130{
131public:
144 SimpleInfoFlow(Service *s, unsigned max_bytes_per_message = 255,
145 bool use_continue_bits = true)
148 max_bytes_per_message > 255 ? 255 : max_bytes_per_message)
149 , useContinueBits_(use_continue_bits ? 1 : 0)
150 {
151 }
152
154 {
155 if (fd_ >= 0) {
156 ::close(fd_);
157 fd_ = -1;
158 fileName_ = nullptr;
159 }
160 }
161
162private:
164 {
165 HASSERT(message()->data()->src);
166 HASSERT(message()->data()->descriptor);
167 entryOffset_ = 0;
168 byteOffset_ = 0;
169 isFirstMessage_ = 1;
171 return call_immediately(STATE(continue_send));
172 }
173
174 const SimpleInfoDescriptor &current_descriptor()
175 {
176 return message()->data()->descriptor[entryOffset_];
177 }
178
180 bool is_eof()
181 {
182 return (current_descriptor().cmd == SimpleInfoDescriptor::END_OF_DATA);
183 }
184
188 {
189 const SimpleInfoDescriptor &d = current_descriptor();
190 const char* new_file_name = reinterpret_cast<const char*>(d.data);
191 HASSERT(new_file_name);
192 if (!(fileName_ == new_file_name ||
193 (fileName_ && d.data && !strcmp(fileName_, new_file_name)))) {
194 fileName_ = new_file_name;
195 if (fd_ >= 0) {
196 ::close(fd_);
197 }
198 fd_ = ::open(fileName_, O_RDONLY);
199 HASSERT(fd_ >= 0);
200 }
201 int ret = lseek(fd_, d.arg2, SEEK_SET);
202 HASSERT(ret != -1);
203 }
204
207 {
208 const SimpleInfoDescriptor &d = current_descriptor();
209 switch (d.cmd) {
210 case SimpleInfoDescriptor::C_STRING:
211 {
212 byteOffset_ = 0;
213 currentLength_ = strlen((const char *)d.data) + 1;
214 if (d.arg && d.arg < currentLength_) {
215 // Clips too long messages.
216 currentLength_ = d.arg;
217 LOG(INFO, "message clipped to length %d", currentLength_);
218 }
219 break;
220 }
221#if OPENMRN_HAVE_POSIX_FD
222 case SimpleInfoDescriptor::FILE_CHAR_ARRAY:
224#endif
225 // Fall through
226 case SimpleInfoDescriptor::CHAR_ARRAY:
227 byteOffset_ = 0;
228 currentLength_ = d.arg;
230 break;
231#if OPENMRN_HAVE_POSIX_FD
232 case SimpleInfoDescriptor::FILE_LITERAL_BYTE:
233 {
235 break;
236 }
237 case SimpleInfoDescriptor::FILE_C_STRING:
238 {
240 currentLength_ = d.arg;
241 byteOffset_ = 0;
242 break;
243 }
244#endif // if have fd
245 default:
246 currentLength_ = 0;
247 }
248 }
249
252 {
253 HASSERT(fd_ >= 0);
254 uint8_t ret;
255 int result = ::read(fd_, &ret, 1);
256 HASSERT(result >= 0);
257 if (result == 0) ret = 0;
258 return ret;
259 }
260
262 uint8_t current_byte()
263 {
264 const SimpleInfoDescriptor &d = current_descriptor();
265 switch (d.cmd)
266 {
267 case SimpleInfoDescriptor::END_OF_DATA:
268 return 0;
269 case SimpleInfoDescriptor::LITERAL_BYTE: {
270 if (d.data) {
271 HASSERT(d.arg == *d.data);
272 }
273 return d.arg;
274 }
275 case SimpleInfoDescriptor::C_STRING:
276 if (byteOffset_ >= currentLength_ - 1)
277 {
278 return 0;
279 }
280 else
281 {
282 return d.data[byteOffset_];
283 }
284 case SimpleInfoDescriptor::CHAR_ARRAY:
286 {
287 return 0;
288 }
289 else
290 {
291 return d.data[byteOffset_];
292 }
293 case SimpleInfoDescriptor::FILE_C_STRING:
294 {
295 uint8_t fdata = file_read_current_byte();
296 if (!fdata) {
298 }
299 if (byteOffset_ >= currentLength_ - 1)
300 {
301 fdata = 0;
302 }
303 return fdata;
304 }
305 case SimpleInfoDescriptor::FILE_LITERAL_BYTE:
306 {
307 uint8_t fdata = file_read_current_byte();
308 HASSERT(d.arg == fdata);
309 return d.arg;
310 }
311 case SimpleInfoDescriptor::FILE_CHAR_ARRAY:
312 {
313 return file_read_current_byte();
314 }
315 default:
316 DIE("Unexpected descriptor type.");
317 }
318 }
319
322 {
323 const SimpleInfoDescriptor &d = current_descriptor();
324 switch (d.cmd)
325 {
326 case SimpleInfoDescriptor::END_OF_DATA:
327 return;
328 case SimpleInfoDescriptor::LITERAL_BYTE:
329 case SimpleInfoDescriptor::FILE_LITERAL_BYTE:
330 break;
331 case SimpleInfoDescriptor::C_STRING:
332 case SimpleInfoDescriptor::FILE_C_STRING:
333 case SimpleInfoDescriptor::CHAR_ARRAY:
334 case SimpleInfoDescriptor::FILE_CHAR_ARRAY:
336 {
337 break;
338 }
339 else
340 {
341 return;
342 }
343 default:
344 DIE("Unexpected descriptor type.");
345 }
346 ++entryOffset_;
348 }
349
350 Action continue_send()
351 {
352 if (is_eof())
353 {
354 return release_and_exit();
355 }
356 return allocate_and_call(
357 message()->data()->src->iface()->addressed_message_write_flow(),
358 STATE(fill_buffer));
359 }
360
361 Action fill_buffer()
362 {
364 ->data()
365 ->src->iface()
366 ->addressed_message_write_flow());
367 const SimpleInfoResponse &r = *message()->data();
368 b->data()->reset(r.mti, r.src->node_id(), r.dst, EMPTY_PAYLOAD);
369 for (uint8_t offset = 0; offset < maxBytesPerMessage_ && !is_eof();
370 ++offset, step_byte())
371 {
372 b->data()->payload.push_back(current_byte());
373 }
374 b->data()->set_flag_dst(GenMessage::WAIT_FOR_LOCAL_LOOPBACK);
376 {
377 if (!is_eof())
378 {
379 b->data()->set_flag_dst(
381 }
382
383 if (isFirstMessage_)
384 {
385 isFirstMessage_ = 0;
386 }
387 else
388 {
389 b->data()->set_flag_dst(
391 }
392 }
393 b->set_done(n_.reset(this));
394 message()->data()->src->iface()->addressed_message_write_flow()->send(
395 b);
396 return wait_and_call(STATE(continue_send));
397 }
398
402 uint8_t useContinueBits_ : 1;
403
406 uint8_t isFirstMessage_ : 1;
408 uint8_t entryOffset_ : 5;
409
411 uint8_t byteOffset_;
415
417 const char* fileName_{nullptr};
419 int fd_{-1};
420
422};
423
424} // namespace openlcb
425
426#endif // _OPENLCB_SIMPLEINFOPROTOCOL_HXX_
#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().
A list of queues.
Definition Queue.hxx:466
Collection of related state machines that pend on incoming messages.
Return type for a state flow callback.
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 wait_and_call(Callback c)
Wait for resource to become available before proceeding to next state.
Action release_and_exit()
Terminates the processing of the current message.
State flow with a given typed input queue.
Base::Action Action
Allows using Action without having StateFlowBase:: prefix in front of it.
Action call_immediately(Callback c)
Imediately call the next state upon return.
Base class for NMRAnet nodes conforming to the asynchronous interface.
Definition Node.hxx:52
StateFlow for sending out medium-sized data payloads like the Simple Node Ident Info protocol.
const char * fileName_
Last file name we opened.
Action entry() OVERRIDE
Entry into the StateFlow activity.
uint8_t file_read_current_byte()
Returns the next byte from the file data, advancing the file offset.
uint8_t byteOffset_
Byte offset within a descriptor entry.
uint8_t isFirstMessage_
Whether this is the first reply message we are sending out.
uint8_t entryOffset_
Tells which descriptor entry we are processing.
uint8_t maxBytesPerMessage_
Configuration option.
void update_for_next_entry()
Call this function after updating entryOffset_.
void open_and_seek_next_file()
Assumes that the current descriptor is a file argument.
uint8_t useContinueBits_
Configuration option.
int fd_
fd of the last file we opened.
SimpleInfoFlow(Service *s, unsigned max_bytes_per_message=255, bool use_continue_bits=true)
Creates a simple ident flow handler.
uint8_t current_byte()
Returns the current byte in the stream of data.
void step_byte()
Increments to the next byte in the stream of data.
uint8_t currentLength_
Total / max length of the current block.
#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
#define HASSERT(x)
Checks that the value of expression x is true, else terminates the current process.
Definition macros.h:138
#define DIE(MSG)
Unconditionally terminates the current process with a message.
Definition macros.h:143
string EMPTY_PAYLOAD
A global class / variable for empty or not-yet-initialized payloads.
Definition If.cxx:152
StateFlow< Buffer< SimpleInfoResponse >, QList< 1 > > SimpleInfoFlowBase
Base class for the SimpleInfoFlow.
MTI
Known Message type indicators.
This class is used in the dispatching of incoming or outgoing NMRAnet messages to the message handler...
Definition If.hxx:72
@ DSTFLAG_NOT_FIRST_MESSAGE
Signals to the stack that we need to set the continuation bits in the outgoing message to indicate th...
Definition If.hxx:163
@ WAIT_FOR_LOCAL_LOOPBACK
Specifies that the stack should wait for the local loopback processing before invoking the done notif...
Definition If.hxx:159
@ DSTFLAG_NOT_LAST_MESSAGE
Signals to the stack that we need to set the continuation bits in the outgoing message to indicate th...
Definition If.hxx:167
Node * dstNode
If the destination node is local, this value is non-NULL.
Definition If.hxx:110
NodeHandle src
Source node.
Definition If.hxx:104
Container of both a NodeID and NodeAlias.
This structure defines how to piece together a reply to a Simple Info request, sich as SNIP.
const char * data
Points to a string if the command requires so.
Use a Buffer<SimpleInfoResponse> to send a SNIP response to a particular destination host.
Defs::MTI mti
MTI of the response to be sent.
Node * src
Source node to send the response from.
NodeHandle dst
Destination node to send the response to.
void reset(const GenMessage *msg_to_respond, const SimpleInfoDescriptor *desc, Defs::MTI response_mti)
Initializes the fields of this message to be a response to an NMRAnetMessage (i.e....
const SimpleInfoDescriptor * descriptor
Descriptor of payload to send.