Open Model Railroad Network (OpenMRN)
Loading...
Searching...
No Matches
DatagramCan.cxx
Go to the documentation of this file.
1
36
38#include "openlcb/DatagramImpl.hxx"
39#include "openlcb/IfCanImpl.hxx"
40
41namespace openlcb
42{
43
46extern long long DATAGRAM_RESPONSE_TIMEOUT_NSEC;
47
56{
57public:
60 {
61 }
62
63
64private:
65 Action fill_can_frame_buffer() OVERRIDE
66 {
67 LOG(VERBOSE, "fill can frame buffer");
68 auto *b = get_allocation_result(if_can()->frame_write_flow());
69 struct can_frame *f = b->data()->mutable_frame();
71
72 // Sets the CAN id.
73 uint32_t can_id = 0x1A000000;
75 LOG(VERBOSE, "dst alias %x", dstAlias_);
77
78 bool need_more_frames = false;
79 unsigned len = nmsg()->payload.size() - dataOffset_;
80 if (len > 8)
81 {
82 len = 8;
83 // This is not the last frame.
84 need_more_frames = true;
85 if (dataOffset_)
86 {
89 }
90 else
91 {
94 }
95 }
96 else
97 {
98 // No more data after this frame.
99 if (dataOffset_)
100 {
103 }
104 else
105 {
108 }
109 }
110
111 memcpy(f->data, &nmsg()->payload[dataOffset_], len);
112 dataOffset_ += len;
113 f->can_dlc = len;
114
115 SET_CAN_FRAME_ID_EFF(*f, can_id);
116 if_can()->frame_write_flow()->send(b);
117
118 if (need_more_frames)
119 {
120 return call_immediately(STATE(get_can_frame_buffer));
121 }
122 else
123 {
124 return call_immediately(STATE(send_finished));
125 }
126 }
127}; // CanDatagramWriteFlow
128
132{
133public:
134 enum
135 {
142 };
143
144 CanDatagramParser(IfCan *iface);
146
148 Action entry() override
149 {
150 errorCode_ = 0;
151 const struct can_frame *f = &message()->data()->frame();
152
153 uint32_t id = GET_CAN_FRAME_ID_EFF(*f);
154 unsigned can_frame_type = (id & CanDefs::CAN_FRAME_TYPE_MASK) >>
156
157 if (can_frame_type < 2 || can_frame_type > 5)
158 {
159 // Not datagram frame.
160 return release_and_exit();
161 }
162
163 srcAlias_ = (id & CanDefs::SRC_MASK) >> CanDefs::SRC_SHIFT;
164
165 uint64_t buffer_key = id & (CanDefs::DST_MASK | CanDefs::SRC_MASK);
166
167 dst_.alias = buffer_key >> (CanDefs::DST_SHIFT);
168 dstNode_ = nullptr;
169 dst_.id = if_can()->local_aliases()->lookup(NodeAlias(dst_.alias));
170 if (dst_.id)
171 {
172 dstNode_ = if_can()->lookup_local_node(dst_.id);
173 }
174 if (!dstNode_)
175 {
176 // Destination not local node.
177 return release_and_exit();
178 }
179
180 DatagramPayload *buf = nullptr;
181 bool last_frame = true;
182
183 switch (can_frame_type)
184 {
185 case 2:
186 // Single-frame datagram. Let's allocate one small buffer for
187 // it.
188 localBuffer_.clear();
189 localBuffer_.reserve(f->can_dlc);
190 buf = &localBuffer_;
191 break;
192 case 3:
193 {
194 // Datagram first frame
195 auto it = pendingBuffers_.find(buffer_key);
196 if (it != pendingBuffers_.end())
197 {
201 errorCode_ = DatagramClient::RESEND_OK |
202 DatagramClient::OUT_OF_ORDER;
203 break;
204 }
205
206 buf = &pendingBuffers_[buffer_key];
207 buf->clear();
208
209 // Datagram first frame. Get a full buffer.
210 buf->reserve(72);
211 last_frame = false;
212 break;
213 }
214 case 4:
215 // Datagram middle frame
216 last_frame = false;
217 // Fall through
218 case 5:
219 {
220 // Datagram last frame
221 auto it = pendingBuffers_.find(buffer_key);
222 if (it != pendingBuffers_.end())
223 {
224 buf = &it->second;
225 if (last_frame)
226 {
227 localBuffer_.clear();
228 // Moves ownership of the allocated data to the local
229 // buffer.
230 localBuffer_.swap(*buf);
231 buf = &localBuffer_;
233 }
234 }
235 break;
236 }
237 default:
238 // Not datagram frame.
239 return release_and_exit();
240 }
241
242 if (!buf)
243 {
244 errorCode_ =
245 DatagramClient::RESEND_OK | DatagramClient::OUT_OF_ORDER;
246 }
247 else if (buf->size() + f->can_dlc > DatagramDefs::MAX_SIZE)
248 {
249 // Too long datagram arrived.
250 LOG(WARNING, "AsyncDatagramCan: too long incoming datagram arrived."
251 " Size: %d",
252 (int)(buf->size() + f->can_dlc));
253 errorCode_ = DatagramClient::PERMANENT_ERROR;
254 // Since we reject the datagram, let's not keep the buffer
255 // around. This call should not crash if the buffer was already
256 // deleted.
257 pendingBuffers_.erase(buffer_key);
258 }
259
260 if (errorCode_)
261 {
262 release();
263 // Gets the send flow to send rejection.
264 return allocate_and_call(if_can()->addressed_message_write_flow(),
266 }
267
268 // Copies new data into buf.
269 buf->append(reinterpret_cast<const char *>(&f->data[0]), f->can_dlc);
270 release();
271 if (last_frame)
272 {
273 HASSERT(buf == &localBuffer_);
274 // Datagram is complete; let's send it to higher level If.
275 return allocate_and_call(if_can()->dispatcher(),
277 }
278 else
279 {
280 return exit();
281 }
282 }
283
287 {
289 HASSERT(dstNode_);
290 auto *f =
291 get_allocation_result(if_can()->addressed_message_write_flow());
292 f->data()->reset(Defs::MTI_DATAGRAM_REJECTED, dst_.id, {0, srcAlias_},
294 if_can()->addressed_message_write_flow()->send(f);
295 return exit();
296 }
297
302 {
304 auto *f = get_allocation_result(if_can()->dispatcher());
305 GenMessage *m = f->data();
307 m->payload.swap(localBuffer_);
308 m->dst = dst_;
309 m->dstNode = dstNode_;
310 m->src.alias = srcAlias_;
311 // This will be zero if the alias is not known.
312 m->src.id =
313 m->src.alias ? if_can()->remote_aliases()->lookup(m->src.alias) : 0;
314 if (!m->src.id && m->src.alias)
315 {
316 // It's unlikely to have a datagram coming in on the interface with
317 // a local alias and still framed into CAN frames. But we still
318 // handle it.
319 m->src.id = if_can()->local_aliases()->lookup(m->src.alias);
320 }
321 if_can()->dispatcher()->send(f);
322 return exit();
323 }
324
325private:
329
330 Node *dstNode_;
331 NodeHandle dst_;
332 unsigned short srcAlias_ : 12;
335 uint16_t errorCode_;
336
343};
344CanDatagramService::CanDatagramService(IfCan *iface,
345 int num_registry_entries,
346 int num_clients)
347 : DatagramService(iface, num_registry_entries)
348{
349 if_can()->add_owned_flow(new CanDatagramParser(if_can()));
350 auto* dg_send = new CanDatagramWriteFlow(if_can());
351 if_can()->add_owned_flow(dg_send);
352 for (int i = 0; i < num_clients; ++i)
353 {
354 auto *client_flow = new DatagramClientImpl(if_can(), dg_send);
355 if_can()->add_owned_flow(client_flow);
356 client_allocator()->insert(static_cast<DatagramClient *>(client_flow));
357 }
358}
359
361{
362 return new CanDatagramParser(if_can);
363}
364
365CanDatagramService::~CanDatagramService()
366{
367}
368
369CanDatagramParser::CanDatagramParser(IfCan *iface)
370 : CanFrameStateFlow(iface)
371{
372 if_can()->frame_dispatcher()->register_handler(this,
373 CAN_FILTER |
375 CAN_MASK &
378 if_can()->frame_dispatcher()->register_handler(this,
379 CAN_FILTER |
381 CAN_MASK &
384}
385
386CanDatagramParser::~CanDatagramParser()
387{
388 if_can()->frame_dispatcher()->unregister_handler_all(this);
389}
390
391} // namespace openlcb
#define STATE(_fn)
Turns a function name into an argument to be supplied to functions expecting a state.
Definition StateFlow.hxx:61
OutgoingFrameHandler * frame_write_flow()
Definition CanIf.hxx:214
An object that can be scheduled on an executor to run.
virtual void send(MessageType *message, unsigned priority=UINT_MAX)=0
Entry point to the flow.
Though at the surface, this may seem like an unnecessary abstraction of std::map, it has the purpose ...
Definition StlMap.hxx:50
Iterator end()
Get an iterator index pointing one past the last element in mapping.
Definition StlMap.hxx:152
void clear()
Removes all elements in the map.
Definition StlMap.hxx:90
size_t erase(Key key)
Remove an element from the tree.
Definition StlMap.hxx:102
Iterator find(const Key &key)
Find an element matching the given key.
Definition StlMap.hxx:144
void release() OVERRIDE
Unrefs the current buffer.
Base::Action Action
Allows using Action without having StateFlowBase:: prefix in front of it.
void send(MessageType *msg, unsigned priority=UINT_MAX) OVERRIDE
Sends a message to the state flow for processing.
MessageType * message()
The addressed write flow is responsible for sending addressed messages to the CANbus.
NodeAlias lookup(NodeID id)
Lookup a node's alias based on its Node ID.
Frame handler that assembles incoming datagram fragments into a single datagram message.
Action datagram_complete()
Requests the datagram in buf_, dstNode_ etc... to be sent to the AsyncIf for processing.
uint16_t errorCode_
If non-zero, contains a Rejection error code and the datagram should not be forwarded to the upper la...
StlMap< uint64_t, DatagramPayload > pendingBuffers_
Open datagram buffers.
Action entry() override
Handler callback for incoming frames.
DatagramPayload localBuffer_
A local buffer that owns the datagram payload bytes after we took the entry from the pending buffers ...
Action send_rejection()
Sends a datagram rejection.
Datagram client implementation for CANbus-based datagram protocol.
Base class for incoming CAN frame handlers.
Definition IfCan.hxx:164
uint8_t dataOffset_
for continuation frames: which offset in the Buffer should we start the payload at.
Definition IfCanImpl.hxx:65
NodeAlias dstAlias_
Destination node alias.
Definition IfCanImpl.hxx:64
NodeAlias srcAlias_
Source node alias.
Definition IfCanImpl.hxx:63
Datagram client implementation for CANbus-based datagram protocol.
Use this class to send datagrams.
Definition Datagram.hxx:79
Transport-agnostic dispatcher of datagrams.
Definition Datagram.hxx:161
Implementation of the OpenLCB interface abstraction for the CAN-bus interface standard.
Definition IfCan.hxx:65
AliasCache * local_aliases()
Definition IfCan.hxx:96
AliasCache * remote_aliases()
Definition IfCan.hxx:103
void add_owned_flow(Executable *e) override
Transfers ownership of a module to the interface.
Definition IfCan.cxx:746
MessageDispatchFlow * dispatcher()
Definition If.hxx:224
Node * lookup_local_node(NodeID id)
Looks up a node ID in the local nodes' registry.
Definition If.hxx:263
MessageHandler * addressed_message_write_flow()
Definition If.hxx:210
Base class for NMRAnet nodes conforming to the asynchronous interface.
Definition Node.hxx:52
virtual Action send_finished()
Virtual method called after the send is completed, i.e., all the frames are generated and sent to the...
Definition IfImpl.hxx:71
GenMessage * nmsg()
Implementations shall call this function when they are done with sending the packet.
Definition IfImpl.hxx:88
#define LOG(level, message...)
Conditionally write a message to the logging output.
Definition logging.h:99
static const int VERBOSE
Loglevel that is usually not printed, reporting debugging information.
Definition logging.h:59
static const int WARNING
Loglevel that is always printed, reporting a warning or a retryable error.
Definition logging.h:55
#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
long long DATAGRAM_RESPONSE_TIMEOUT_NSEC
Defines how long the datagram client flow should wait for the datagram ack/nack response message.
Definition Datagram.cxx:42
Executable * TEST_CreateCanDatagramParser(IfCan *if_can)
Creates a CAN datagram parser flow. Exposed for testing only.
uint16_t NodeAlias
Alias to a 48-bit NMRAnet Node ID type.
Payload DatagramPayload
Contents of a Datagram message.
Definition Datagram.hxx:51
string error_to_buffer(uint16_t error_code, uint16_t mti)
Formats a payload for response of error response messages such as OPtioanl Interaction Rejected or Te...
Definition If.cxx:90
static const uint32_t CAN_EXT_FRAME_MASK
Mask to OR onto a can mask to tell the dispatcher to only consider extended can frames.
Definition CanIf.hxx:69
static const uint32_t CAN_EXT_FRAME_FILTER
Filter to OR onto a can ID to tell the dispatcher to only consider extended can frames.
Definition CanIf.hxx:66
@ NMRANET_MSG
normal NMRAnet message
Definition CanDefs.hxx:112
static void set_dst(uint32_t *can_id, NodeAlias dst)
Set the destination field value of the CAN ID.
Definition CanDefs.hxx:246
static void set_src(uint32_t *can_id, NodeAlias src)
Set the source field value of the CAN ID.
Definition CanDefs.hxx:236
static void set_can_frame_type(uint32_t *can_id, CanFrameType type)
Set the CAN frame type field value of the CAN ID.
Definition CanDefs.hxx:256
@ DATAGRAM_MIDDLE_FRAME
middle frame of multi-frame datagram
Definition CanDefs.hxx:103
@ DATAGRAM_FINAL_FRAME
last frame of multi-frame datagram
Definition CanDefs.hxx:104
@ DATAGRAM_FIRST_FRAME
first frame of multi-frame datagram
Definition CanDefs.hxx:102
@ DATAGRAM_ONE_FRAME
a single frame datagram
Definition CanDefs.hxx:101
@ NORMAL_PRIORITY
normal priority CAN message
Definition CanDefs.hxx:121
@ FRAME_TYPE_MASK
mask for frame type field of CAN ID
Definition CanDefs.hxx:66
@ SRC_MASK
mask for source field of CAN ID
Definition CanDefs.hxx:62
@ FRAME_TYPE_SHIFT
shift for frame type field of CAN ID
Definition CanDefs.hxx:75
@ CAN_FRAME_TYPE_SHIFT
shift for can frame type field of CAN ID
Definition CanDefs.hxx:74
@ CAN_FRAME_TYPE_MASK
mask for can frame type field of CAN ID
Definition CanDefs.hxx:65
@ PRIORITY_SHIFT
shift for priority field of CAN ID
Definition CanDefs.hxx:76
@ DST_MASK
mask for MTI field of CAN ID
Definition CanDefs.hxx:64
@ PRIORITY_MASK
mask for priority field of CAN ID
Definition CanDefs.hxx:67
@ DST_SHIFT
shift for MTI field of CAN ID
Definition CanDefs.hxx:73
@ SRC_SHIFT
shift for source field of CAN ID
Definition CanDefs.hxx:71
@ MAX_SIZE
maximum size in bytes of a datagram
@ MTI_DATAGRAM_REJECTED
datagram rejected by receiver
@ MTI_DATAGRAM
datagram
This class is used in the dispatching of incoming or outgoing NMRAnet messages to the message handler...
Definition If.hxx:72
NodeHandle dst
Destination node.
Definition If.hxx:106
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
Defs::MTI mti
OpenLCB MTI of the incoming message.
Definition If.hxx:108
string payload
Data content in the message body.
Definition If.hxx:113
Container of both a NodeID and NodeAlias.
NodeID id
48-bit NMRAnet Node ID
NodeAlias alias
alias to NMRAnet Node ID