Open Model Railroad Network (OpenMRN)
Loading...
Searching...
No Matches
IfCanImpl.hxx
Go to the documentation of this file.
1
35#ifndef _OPENLCB_IFCANIMPL_HXX_
36#define _OPENLCB_IFCANIMPL_HXX_
37
38#include "openlcb/CanDefs.hxx"
40#include "openlcb/IfImpl.hxx"
42
43namespace openlcb
44{
45
47
51{
52public:
53 CanMessageWriteFlow(IfCan *if_can) : WriteFlowBase(if_can)
54 {
55 }
56
57 IfCan *if_can()
58 {
59 return static_cast<IfCan *>(async_if());
60 }
61
62protected:
65 uint8_t dataOffset_;
69 {
70 dataOffset_ = 0;
71 srcAlias_ = 0;
72 dstAlias_ = 0;
73 if (nmsg()->payload.size())
74 {
75 // We have limited space for counting offsets. In practice this
76 // value will be max 10 for certain traction control protocol
77 // messages. Longer data usually travels via datagrams or streams.
78 HASSERT(nmsg()->payload.size() < 256);
79 }
80 return call_immediately(STATE(find_local_alias));
81 }
82
86 {
87 // We are on the IF's executor, so we can access the alias caches.
88 srcAlias_ = if_can()->local_aliases()->lookup(nmsg()->src.id);
89 if (!srcAlias_)
90 {
91 return call_immediately(STATE(allocate_new_alias));
92 }
93 return src_alias_lookup_done();
94 }
95
96private:
98 {
106 HASSERT(if_can()->alias_allocator());
107 NodeAlias alias = if_can()->alias_allocator()->get_allocated_alias(
108 nmsg()->src.id, this);
109 if (!alias)
110 {
111 // wait for notification and re-try this step.
112 return wait();
113 }
114 LOG(INFO, "Allocating new alias %03X for node %012" PRIx64, alias,
115 nmsg()->src.id);
116
117 srcAlias_ = alias;
118 // Take a CAN frame to send off the AMD frame.
119 return allocate_and_call(if_can()->frame_write_flow(),
120 STATE(send_amd_frame));
121 }
122
123 Action send_amd_frame()
124 {
125 auto *b = get_allocation_result(if_can()->frame_write_flow());
126 struct can_frame *f = b->data()->mutable_frame();
128 f->can_dlc = 6;
129 uint64_t rd = htobe64(nmsg()->src.id);
130 memcpy(f->data, reinterpret_cast<uint8_t *>(&rd) + 2, 6);
131 if_can()->frame_write_flow()->send(b);
132 // Source alias is settled.
133 return src_alias_lookup_done();
134 }
135
136 Action src_alias_lookup_done()
137 {
138 return call_immediately(STATE(get_can_frame_buffer));
139 }
140
141protected:
142 Action get_can_frame_buffer()
143 {
144 return allocate_and_call(if_can()->frame_write_flow(),
145 STATE(fill_can_frame_buffer));
146 }
147
148private:
149 virtual Action fill_can_frame_buffer()
150 {
151 auto *b = get_allocation_result(if_can()->frame_write_flow());
152 b->set_done(message()->new_child());
153 struct can_frame *f = b->data()->mutable_frame();
156 {
157 // We don't know how to handle such an MTI in a generic way.
158 b->unref();
159 return call_immediately(STATE(send_finished));
160 }
161 // CAN has only 12 bits of MTI field, so we better fit.
162 HASSERT(!(nmsg()->mti & ~0xfff));
163
164 // Sets the CAN id.
165 uint32_t can_id = 0;
166 CanDefs::set_fields(&can_id, srcAlias_, nmsg()->mti,
169 SET_CAN_FRAME_ID_EFF(*f, can_id);
170
171 const string &data = nmsg()->payload;
172 bool need_more_frames = false;
173 // Sets the destination bytes if needed. Adds the payload.
174 if (Defs::get_mti_address(nmsg()->mti))
175 {
176 f->data[0] = dstAlias_ >> 8;
177 f->data[1] = dstAlias_ & 0xff;
178 if (data.empty())
179 {
180 f->can_dlc = 2;
181 }
182 else
183 {
184 if (dataOffset_)
185 {
186 // This is not the first frame.
187 f->data[0] |= CanDefs::NOT_FIRST_FRAME;
188 }
189 const char *b = data.data();
190 unsigned len = data.size() - dataOffset_;
191 if (len > 6)
192 {
193 len = 6;
194 // This is not the last frame.
195 need_more_frames = true;
196 f->data[0] |= CanDefs::NOT_LAST_FRAME;
197 }
198 memcpy(f->data + 2, b + dataOffset_, len);
199 dataOffset_ += len;
200 f->can_dlc = 2 + len;
201 }
202 }
203 else
204 {
205 if (data.size())
206 {
207 HASSERT(data.size() <= 8); // too big frame for global msg
208 memcpy(f->data, data.data(), data.size());
209 f->can_dlc = data.size();
210 }
211 }
212 if_can()->frame_write_flow()->send(b);
213 if (need_more_frames)
214 {
215 return call_immediately(STATE(get_can_frame_buffer));
216 }
217 else
218 {
219 return call_immediately(STATE(send_finished));
220 }
221 }
222};
223
228{
229public:
231 : CanMessageWriteFlow(if_can)
232 , timer_(this)
233 {
234 }
235
236protected:
240 Action entry() override
241 {
242 return call_immediately(STATE(addressed_entry));
243 }
244
246 {
247 dataOffset_ = 0;
248 srcAlias_ = 0;
249 dstAlias_ = 0;
250 if (nmsg()->payload.size())
251 {
252 // We have limited space for counting offsets. In practice this
253 // value will be max 10 for certain traction control protocol
254 // messages. Longer data usually travels via datagrams or streams.
255 HASSERT(nmsg()->payload.size() < 256);
256 }
257 NodeHandle &dst_ = nmsg()->dst;
258 HASSERT(dst_.id || dst_.alias); // We must have some kind of address.
259 if (dst_.id)
260 {
261 dstAlias_ = if_can()->remote_aliases()->lookup(dst_.id);
263 {
264 LOG(INFO, "AddressedWriteFlow: Could not resolve destination "
265 "address %012" PRIx64
266 " to an alias on the bus. Dropping packet.",
267 nmsg()->dst.id);
268 return call_immediately(STATE(send_finished));
269 }
270 }
271 else if (dst_.alias)
272 {
273 // Check if this is a local node being called by alias.
274 NodeID id = if_can()->local_aliases()->lookup(dst_.alias);
275 if (id)
276 {
277 Node *dst_node = if_can()->lookup_local_node(id);
278 if (dst_node)
279 {
280 dst_.id = id;
281 nmsg()->dstNode = dst_node;
282 return call_immediately(STATE(send_to_local_node));
283 }
284 }
285 }
286 if (dst_.alias && dstAlias_ && dst_.alias != dstAlias_)
287 {
290 LOG(INFO, "AddressedWriteFlow: Caller supplied outdated alias. "
291 "Node id %012" PRIx64 ", Result from cache %03x, "
292 "alias from caller %03x.",
293 dst_.id, dstAlias_, dst_.alias);
294 }
295 else if (!dstAlias_)
296 {
297 dstAlias_ = dst_.alias;
298 }
299 if (dstAlias_)
300 {
301 return call_immediately(STATE(find_local_alias));
302 }
303 else
304 {
305 return call_immediately(STATE(find_remote_alias));
306 }
307 }
308
309 Action find_remote_alias()
310 {
311 aliasListener_.RegisterLocalHandler();
312 srcAlias_ =
313 if_can()->local_aliases()->lookup(nmsg()->src.id);
314 if (srcAlias_)
315 {
316 // We can try to send an AME (alias mapping enquiry) frame.
317 return allocate_and_call(if_can()->frame_write_flow(),
318 STATE(send_ame_frame));
319 }
320 // No local alias -- we need to jump straight to local nodeid
321 // verify. That will allocate a new local alias.
322 return call_immediately(STATE(send_verify_nodeid_global));
323 }
324
325 Action send_ame_frame()
326 {
327 auto *b = get_allocation_result(if_can()->frame_write_flow());
328 struct can_frame *f = b->data();
330 f->can_dlc = 6;
331 uint64_t rd = htobe64(nmsg()->dst.id);
332 memcpy(f->data, reinterpret_cast<uint8_t *>(&rd) + 2, 6);
333 if_can()->frame_write_flow()->send(b);
334 // We wait for a remote response to come via the handler input. If it
335 // doesn't come for 1 sec, go to more global and send a global
336 // unaddressed message inquiring the node id.
337 return sleep_and_call(&timer_, ADDRESSED_MESSAGE_LOOKUP_TIMEOUT_NSEC,
338 STATE(send_verify_nodeid_global));
339 }
340
341 Action send_verify_nodeid_global()
342 {
343 if (dstAlias_)
344 {
345 return call_immediately(STATE(remote_alias_found));
346 }
347 return allocate_and_call(if_can()->global_message_write_flow(),
348 STATE(fill_verify_nodeid_global));
349 }
350
351 Action fill_verify_nodeid_global()
352 {
353 auto *b = get_allocation_result(if_can()->global_message_write_flow());
354 b->data()->reset(Defs::MTI_VERIFY_NODE_ID_GLOBAL,
355 nmsg()->src.id,
356 node_id_to_buffer(nmsg()->dst.id));
357 if_can()->global_message_write_flow()->send(b);
358 return sleep_and_call(&timer_, ADDRESSED_MESSAGE_LOOKUP_TIMEOUT_NSEC,
359 STATE(wait_looking_for_dst));
360 }
361
362 Action wait_looking_for_dst()
363 {
364 if (dstAlias_ && dstAlias_ <= 0xFFF)
365 {
366 return call_immediately(STATE(remote_alias_found));
367 }
368 LOG(INFO, "AddressedWriteFlow: Could not resolve destination "
369 "address %012" PRIx64 " to an alias on the bus. Dropping packet.",
370 nmsg()->dst.id);
371 aliasListener_.UnregisterLocalHandler();
372 if_can()->remote_aliases()->add(nmsg()->dst.id, NOT_RESPONDING);
373 return call_immediately(STATE(timeout_looking_for_dst));
374 }
375
377 {
379 return call_immediately(STATE(send_finished));
380 }
381
382 Action remote_alias_found()
383 {
385 return call_immediately(STATE(find_local_alias));
386 }
387
391 {
392 public:
394 : parent_(parent) {}
395
396 enum
397 {
398 // AMD frames
399 CAN_FILTER1 = CanMessageData::CAN_EXT_FRAME_FILTER | 0x10701000,
400 CAN_MASK1 = CanMessageData::CAN_EXT_FRAME_MASK | 0x1FFFF000,
401 // Initialization complete
402 CAN_FILTER2 = CanMessageData::CAN_EXT_FRAME_FILTER | 0x19100000,
403 CAN_MASK2 = CanMessageData::CAN_EXT_FRAME_MASK | 0x1FFFF000,
404 // Verified node ID number
405 CAN_FILTER3 = CanMessageData::CAN_EXT_FRAME_FILTER | 0x19170000,
406 CAN_MASK3 = CanMessageData::CAN_EXT_FRAME_MASK | 0x1FFFF000,
407 };
408
409 IfCan* if_can() { return parent_->if_can(); }
410
411 // Registers *this to the ifcan to receive alias resolution messages.
412 void RegisterLocalHandler()
413 {
415 this, CAN_FILTER1, CAN_MASK1);
417 this, CAN_FILTER2, CAN_MASK2);
419 this, CAN_FILTER3, CAN_MASK3);
420 }
421
422 // Unregisters *this from the ifcan to not receive alias resolution
423 // messages.
424 void UnregisterLocalHandler()
425 {
427 this, CAN_FILTER1, CAN_MASK1);
429 this, CAN_FILTER2, CAN_MASK2);
431 this, CAN_FILTER3, CAN_MASK3);
432 }
433
435 void send(Buffer<CanMessageData> *message, unsigned priority) OVERRIDE
436 {
437 struct can_frame *f = message->data();
438 uint32_t id = GET_CAN_FRAME_ID_EFF(*f);
439 if (f->can_dlc != 6)
440 {
441 // Not sending a node ID.
442 message->unref();
443 return;
444 }
445 uint64_t nodeid_be = htobe64(parent_->nmsg()->dst.id);
446 uint8_t *nodeid_start = reinterpret_cast<uint8_t *>(&nodeid_be) + 2;
447 if (memcmp(nodeid_start, f->data, 6))
448 {
449 // Node id does not match.
450 message->unref();
451 return;
452 }
453 // Now: we have an alias.
454 parent_->dstAlias_ = id & CanDefs::SRC_MASK;
455 if (!parent_->dstAlias_)
456 {
457 LOG_ERROR("Incoming alias definition message with zero alias. "
458 "CAN frame id %08x",
459 (unsigned)id);
460 message->unref();
461 return;
462 }
463 UnregisterLocalHandler();
464 if_can()->remote_aliases()->add(parent_->nmsg()->dst.id, parent_->dstAlias_);
465 parent_->timer_.trigger();
466 message->unref();
467 }
468
469 private:
471 } aliasListener_{this};
472
473 friend class AliasDefListener;
474
475 StateFlowTimer timer_;
476};
477} // namespace openlcb
478
479#endif // _OPENLCB_IFCANIMPL_HXX_
#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
FrameDispatchFlow * frame_dispatcher()
Definition CanIf.hxx:208
OutgoingFrameHandler * frame_write_flow()
Definition CanIf.hxx:214
void register_handler(HandlerType *handler, ID id, ID mask)
Adds a new handler to this dispatcher.
void unregister_handler(HandlerType *handler, ID id, ID mask)
Removes a specific instance of a handler from this dispatcher.
virtual void send(MessageType *message, unsigned priority=UINT_MAX)=0
Entry point to the flow.
Base::Action Action
Allows using Action without having StateFlowBase:: prefix in front of it.
MessageType * message()
This helper class listend for incoming CAN frames looking for alias mapping definitions.
void send(Buffer< CanMessageData > *message, unsigned priority) OVERRIDE
Handler callback for incoming messages.
The addressed write flow is responsible for sending addressed messages to the CANbus.
Action send_to_hardware() override
This function will be called (on the main executor) to initiate sending this message to the hardware.
Action entry() override
Entry point of message handling flow.
NodeAlias get_allocated_alias(NodeID destination_id, Executable *done)
Allocates an alias from the reserved but unused aliases list.
NodeAlias lookup(NodeID id)
Lookup a node's alias based on its Node ID.
void add(NodeID id, NodeAlias alias)
Add an alias to an alias cache.
Implements the write-side conversion logic from generic messages to CAN frames.
Definition IfCanImpl.hxx:51
uint8_t dataOffset_
for continuation frames: which offset in the Buffer should we start the payload at.
Definition IfCanImpl.hxx:65
Action send_to_hardware() override
This function will be called (on the main executor) to initiate sending this message to the hardware.
Definition IfCanImpl.hxx:68
Action find_local_alias()
Performs the local alias lookup and branches depending on whether we found a local alias or not.
Definition IfCanImpl.hxx:85
NodeAlias dstAlias_
Destination node alias.
Definition IfCanImpl.hxx:64
NodeAlias srcAlias_
Source node alias.
Definition IfCanImpl.hxx:63
Implementation of the OpenLCB interface abstraction for the CAN-bus interface standard.
Definition IfCan.hxx:65
AliasCache * local_aliases()
Definition IfCan.hxx:96
AliasAllocator * alias_allocator()
Definition IfCan.hxx:110
AliasCache * remote_aliases()
Definition IfCan.hxx:103
MessageHandler * global_message_write_flow()
Definition If.hxx:200
Node * lookup_local_node(NodeID id)
Looks up a node ID in the local nodes' registry.
Definition If.hxx:263
Base class for NMRAnet nodes conforming to the asynchronous interface.
Definition Node.hxx:52
Implementation of the hardware-independent parts of the write flows.
Definition IfImpl.hxx:46
virtual Action send_to_local_node()
This state is called when an addressed message's destination is a node that is local to this interfac...
Definition IfImpl.cxx:54
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
Action addressed_entry()
Addressed write flows should call this state BEFORE sending to the hardware.
Definition IfImpl.cxx:41
#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 LOG_ERROR(message...)
Shorthand for LOG(LEVEL_ERROR, message...). See LOG.
Definition logging.h:124
#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
string node_id_to_buffer(NodeID id)
Convenience function to render a 48-bit NMRAnet node ID into a new buffer.
Definition If.cxx:45
uint64_t NodeID
48-bit NMRAnet Node ID type
static const NodeAlias NOT_RESPONDING
Guard value put into the the internal node alias maps when a node ID could not be translated to a val...
uint16_t NodeAlias
Alias to a 48-bit NMRAnet Node ID type.
long long ADDRESSED_MESSAGE_LOOKUP_TIMEOUT_NSEC
Specifies how long to wait for a response to an alias mapping enquiry message when trying to send an ...
Definition IfCan.cxx:57
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 control_init(struct can_frame &frame, NodeAlias src, uint16_t field, int sequence)
Initialize a control frame CAN ID and set DLC to 0.
Definition CanDefs.hxx:379
@ AMD_FRAME
Alias Map Definition frame.
Definition CanDefs.hxx:127
@ AME_FRAME
Alias Mapping Enquiry.
Definition CanDefs.hxx:128
@ GLOBAL_ADDRESSED
most CAN frame types fall in this category
Definition CanDefs.hxx:100
@ NORMAL_PRIORITY
normal priority CAN message
Definition CanDefs.hxx:121
@ SRC_MASK
mask for source field of CAN ID
Definition CanDefs.hxx:62
static void set_fields(uint32_t *can_id, NodeAlias src, Defs::MTI mti, CanFrameType can_type, FrameType type, Priority priority)
Set all the CAN ID fields.
Definition CanDefs.hxx:290
@ MTI_VERIFY_NODE_ID_GLOBAL
verify a Node ID globally
@ MTI_RESERVED_MASK
reserved mask
@ MTI_SPECIAL_MASK
special mask
@ MTI_DATAGRAM_MASK
stream or datagram mask
static bool get_mti_address(MTI mti)
Get the MTI address present value field.
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
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