Open Model Railroad Network (OpenMRN)
Loading...
Searching...
No Matches
CanRoutingHub.hxx
1
36#ifndef _OPENLCB_CANROUTNGHUB_HXX_
37#define _OPENLCB_CANROUTNGHUB_HXX_
38
40#include "openlcb/CanDefs.hxx"
41#include "openlcb/Defs.hxx"
42#include "openlcb/If.hxx"
43#include "utils/Hub.hxx"
45#include "utils/gc_format.h"
46
47namespace openlcb
48{
49
59{
60public:
61 typedef HubData value_type;
64
66 : deliveryFlow_(s, this)
67 {
68 }
69
70 void send(Buffer<HubData> *b, unsigned priority = UINT_MAX) override
71 {
72 OSMutexLock l(&lock_);
73 void *port = b->data()->skipMember_;
74 auto it = ports_.find(port);
75 if (it == ports_.end())
76 {
77 LOG(INFO, "Arrived packet to routing hub without recognized source "
78 "designation (%p). Dropped packet.",
79 b->data()->skipMember_);
80 b->unref();
81 return;
82 }
83 const string &p = *b->data();
84 for (unsigned i = 0; i < p.size(); ++i)
85 {
86 if (it->second.segmenter_.consume_byte(p[i]))
87 {
88 // We have a frame.
89 string ret;
90 it->second.segmenter_.frame_buffer(&ret);
91 LOG(VERBOSE, "sending frame: %s", ret.c_str());
92 auto *cb = deliveryFlow_.alloc();
93 HASSERT(it->second.segmenter_.parse_frame_to_output(
94 cb->data()->mutable_frame()));
95 cb->data()->skipMember_ = reinterpret_cast<
97 b->data()->skipMember_);
98 deliveryFlow_.send(
99 cb, reprioritize_frame(cb->data()->frame(), priority));
100 }
101 }
102 }
103
105 {
107 // deliveryFlow_.send(b, reprioritize_frame(b->data()->frame(),
108 // priority));
109 return &deliveryFlow_;
110 }
111
112 void register_port(HubPortInterface *port)
113 {
114 OSMutexLock l(&lock_);
115 HASSERT(port);
116 ports_[port].hubPort_ = port;
117 }
118
119 void unregister_port(HubPortInterface *port)
120 {
121 OSMutexLock l(&lock_);
122 auto it = ports_.find(port);
123 if (it == ports_.end())
124 {
125 LOG(INFO, "Trying to remove a nonexistant port: %p", port);
126 return;
127 }
128 it->second.inactive_ = true;
129 pendingRemove_.push_back(port);
130 }
131
132private:
133 class PortParser;
134 typedef std::map<void *, PortParser> PortsMap;
144 const struct can_frame &frame, unsigned old_priority)
145 {
146 // @TODO(balazs.racz): check actual priority.
147 return old_priority;
148 }
149
152 class DeliveryFlow : public StateFlow<Buffer<CanHubData>, QList<5>>
153 {
154 public:
157 , parent_(parent)
158 {
159 }
160
161 private:
162 Action entry() override
163 {
164 OSMutexLock l(&parent_->lock_);
165 // First we apply any pending removes.
166 for (void *p : parent_->pendingRemove_)
167 {
168 parent_->ports_.erase(p);
169 }
170 parent_->pendingRemove_.clear();
171
172 // Classifies the packet.
173 srcAddress_ = 0;
174 dstAddress_ = 0;
175 const struct can_frame &frame = message()->data()->frame();
176 if (IS_CAN_FRAME_ERR(frame) || IS_CAN_FRAME_RTR(frame))
177 {
178 return release_and_exit();
179 }
180 classify_frame(frame);
181
182 if (srcAddress_ != 0)
183 {
184 parent_->routingTable_.add_node_id_to_route(
185 message()->data()->skipMember_, srcAddress_);
186 }
187
188 gcBuf_ = nullptr;
189
190 if (forwardType_ == ADDRESSED && dstAddress_ != 0)
191 {
192 void *port =
193 parent_->routingTable_.lookup_port_for_address(dstAddress_);
194 nextIt_ = parent_->ports_.find(port);
195 if (nextIt_ != parent_->ports_.end())
196 {
197 // We found the desired port in the routing table.
198 return call_immediately(STATE(forward_addressed));
199 }
200 else
201 {
202 forwardType_ = FORWARD_ALL;
203 }
204 }
205
206 nextIt_ = parent_->ports_.begin();
207
208 return call_immediately(STATE(try_next_entry));
209 }
210
215 void classify_frame(const can_frame &frame)
216 {
217 if (!IS_CAN_FRAME_EFF(frame))
218 {
219 forwardType_ = FORWARD_ALL;
220 return;
221 }
222 uint32_t can_id = GET_CAN_FRAME_ID_EFF(frame);
223 // At this point: all frames belong to openlcb protocols thus the
224 // last 12 bits are the source alias.
225 srcAddress_ = CanDefs::get_src(can_id);
227 {
228 // control frame
229 forwardType_ = FORWARD_ALL;
230 if (CanDefs::is_cid_frame(can_id))
231 {
232 // We do not record source address of CHECK_ID frames,
233 // because they could be in conflict. We only record the ID
234 // at the reserve alias frame 200 msec later.
235 srcAddress_ = 0;
236 }
237 return;
238 }
239 // At this point: OpenLCB message.
240 if (CanDefs::get_can_frame_type(can_id) == 6 ||
241 CanDefs::get_can_frame_type(can_id) == 0)
242 {
243 // unknown can frame type
244 forwardType_ = FORWARD_ALL;
245 return;
246 }
247 // At this point: openlcb message with a known frame type (1, 2..5,
248 // 7)
249 if (CanDefs::get_can_frame_type(can_id) !=
251 {
252 // Datagram and stream frames.
253 forwardType_ = ADDRESSED;
254 dstAddress_ = CanDefs::get_dst(can_id);
255 return;
256 }
257 // At this point: global or addressed message
258 Defs::MTI mti = static_cast<Defs::MTI>(CanDefs::get_mti(can_id));
259 if (Defs::get_mti_address(mti) && frame.can_dlc >= 2)
260 {
261 // address present (really).
262 dstAddress_ = frame.data[0] & 0xf;
263 dstAddress_ <<= 8;
264 dstAddress_ |= frame.data[1];
265 forwardType_ = ADDRESSED;
266 return;
267 }
268 bool has_event = false;
269 if (Defs::get_mti_event(mti) && frame.can_dlc == 8)
270 {
271 event_ = data_to_eventid(frame.data);
272 has_event = true;
273 }
274 if (mti == Defs::MTI_EVENT_REPORT && has_event)
275 {
276 forwardType_ = EVENT;
277 return;
278 }
279 if (has_event)
280 {
281 switch (mti & ~Defs::MTI_MODIFIER_MASK)
282 {
284 ~Defs::MTI_MODIFIER_MASK:
285 parent_->routingTable_.register_consumer(
286 message()->data()->skipMember_, event_);
287 break;
289 ~Defs::MTI_MODIFIER_MASK:
290 parent_->routingTable_.register_producer(
291 message()->data()->skipMember_, event_);
292 break;
293 default:
294 break;
295 }
296 switch (mti)
297 {
299 parent_->routingTable_.register_producer_range(
300 message()->data()->skipMember_, event_);
301 break;
303 parent_->routingTable_.register_consumer_range(
304 message()->data()->skipMember_, event_);
305 break;
306 default:
307 break;
308 }
309 }
310 // Now: we have a non-event global message or a message with an
311 // invalid format.
312 forwardType_ = FORWARD_ALL;
313 }
314
315 Action try_next_entry()
316 {
317 OSMutexLock l(&parent_->lock_);
318 if (nextIt_ == parent_->ports_.end())
319 {
320 return done_processing();
321 }
322
323 if (forwardType_ == EVENT)
324 {
325 if (parent_->routingTable_.check_pcer(
326 static_cast<CanHubPortInterface *>(nextIt_->first),
327 event_))
328 {
329 forward_to_port();
330 }
331 }
332 else
333 {
334 // forward all
335 forward_to_port();
336 }
337
338 nextIt_++;
339 return again();
340 }
341
342 Action forward_addressed()
343 {
344 OSMutexLock l(&parent_->lock_);
345 forward_to_port();
346 return done_processing();
347 }
348
349 Action done_processing()
350 {
351 if (gcBuf_)
352 {
353 gcBuf_->unref();
354 gcBuf_ = nullptr;
355 }
356 return release_and_exit();
357 }
358
359 void forward_to_port()
360 {
361 if (nextIt_->second.inactive_)
362 return;
363 if (nextIt_->second.canPort_)
364 {
365 if (nextIt_->second.canPort_ == message()->data()->skipMember_)
366 return;
367 nextIt_->second.canPort_->send(message()->ref(), priority());
368 }
369 else
370 {
371 HASSERT(nextIt_->second.hubPort_);
373 reinterpret_cast<CanHubPortInterface *>(
374 nextIt_->second.hubPort_);
375 if (hpi == message()->data()->skipMember_)
376 return;
377 ensure_gc_buf_available();
378 nextIt_->second.hubPort_->send(gcBuf_->ref());
379 }
380 }
381
382 void ensure_gc_buf_available()
383 {
384 if (gcBuf_ != nullptr)
385 return;
387 char buf[29];
388 char *end = gc_format_generate(&message()->data()->frame(), buf, 0);
389 gcBuf_->data()->assign(buf, end - buf);
390 gcBuf_->data()->skipMember_ = reinterpret_cast<HubPortInterface *>(
391 message()->data()->skipMember_);
392 }
393
404
405 ForwardType forwardType_; //< what to do with this frame
406 NodeAlias srcAddress_; //< for all OpenLCB frames
407 NodeAlias dstAddress_; //< for addressed frames
408 EventId event_; //< for PCER messages
409 PortsMap::iterator nextIt_; //< which port to consider next
410 GcCanRoutingHub *parent_;
413 };
414
415 DeliveryFlow deliveryFlow_;
416
417 friend class DeliveryFlow;
418
421 {
424 bool inactive_{false};
425 GcStreamParser segmenter_;
426 CanHubPortInterface *canPort_{nullptr};
427 HubPortInterface *hubPort_{nullptr};
428 };
430 std::map<void *, PortParser> ports_;
431 OSMutex lock_;
435 std::vector<void *> pendingRemove_;
436
438};
439
440} // namespace openlcb
441
442#endif // _OPENLCB_CANROUTNGHUB_HXX_
DynamicPool * mainBufferPool
main buffer pool instance
Definition Buffer.cxx:37
#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
void unref()
Decrement count.
Definition Buffer.hxx:675
T * data()
get a pointer to the start of the data.
Definition Buffer.hxx:215
MessageType * alloc()
Synchronously allocates a message buffer from the pool of this flow.
Parses a sequence of characters; finds GridConnect protocol packet boundaries in the sequence of pack...
Data type wrapper for sending data through a Hub.
Definition Hub.hxx:101
Class to allow convenient locking and unlocking of mutexes in a C context.
Definition OS.hxx:494
This class provides a mutex API.
Definition OS.hxx:427
void alloc(Buffer< BufferType > **result, Executable *flow=NULL)
Get a free item out of the pool.
Definition Buffer.hxx:292
A list of queues.
Definition Queue.hxx:466
Collection of related state machines that pend on incoming messages.
State flow with a given typed input queue.
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()
Flow responsible for queuing outgoing CAN frames as well as sending out the actual frames to the reci...
void classify_frame(const can_frame &frame)
Classifies the incoming frame and sets the class variables determining what to do with it.
Action entry() override
Entry into the StateFlow activity.
@ ADDRESSED
Addressed packet that needs to check the routing table.
@ EVENT
Event report packet that needs to check the routing table.
@ FORWARD_ALL
Broadcast packet that needs to go out to all ports, unfiltered.
Buffer< HubData > * gcBuf_
Gridconnect-rendered frame.
A hub flow that accepts string HUB ports sending CAN frames via the GridConnect protocol,...
unsigned reprioritize_frame(const struct can_frame &frame, unsigned old_priority)
Computes the desired priority of a CAN frame.
void send(Buffer< HubData > *b, unsigned priority=UINT_MAX) override
Entry point to the flow.
CanHubPortInterface * can_hub()
std::map< void *, PortParser > ports_
Keyed by the skipMember_ value of the incoming data from a given port.
std::vector< void * > pendingRemove_
Due to race conditions involving iteration and add/remove calls, we delay applying unregister request...
Routing table for gateways and routers in OpenLCB.
char * gc_format_generate(const struct can_frame *can_frame, char *buf, int double_format)
Formats a can frame in the GridConnect protocol.
#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 INFO
Loglevel that is printed by default, reporting some status information.
Definition logging.h:57
#define HASSERT(x)
Checks that the value of expression x is true, else terminates the current process.
Definition macros.h:138
uint64_t data_to_eventid(const void *data)
Takes 8 bytes (big-endian) from *data, and returns the event id they represent.
Definition Convert.hxx:81
uint16_t NodeAlias
Alias to a 48-bit NMRAnet Node ID type.
static bool is_cid_frame(uint32_t can_id)
Tests if the incoming frame is a CID frame.
Definition CanDefs.hxx:208
static CanFrameType get_can_frame_type(uint32_t can_id)
Get the CAN frame type field value of the CAN ID.
Definition CanDefs.hxx:181
@ CONTROL_MSG
CAN control frame message.
Definition CanDefs.hxx:111
static NodeAlias get_dst(uint32_t can_id)
Get the destination field value of the CAN ID.
Definition CanDefs.hxx:172
static FrameType get_frame_type(uint32_t can_id)
Get the frame type field value of the CAN ID.
Definition CanDefs.hxx:190
static CanMTI get_mti(uint32_t can_id)
Get the MTI field value of the CAN ID.
Definition CanDefs.hxx:163
@ GLOBAL_ADDRESSED
most CAN frame types fall in this category
Definition CanDefs.hxx:100
static NodeAlias get_src(uint32_t can_id)
Get the source field value of the CAN ID.
Definition CanDefs.hxx:154
The generic interface for NMRAnet network interfaces.
MTI
Known Message type indicators.
@ MTI_PRODUCER_IDENTIFIED_RANGE
producer broadcast about a range of producers
@ MTI_MODIFIER_MASK
modifier within Priority/Type mask
@ MTI_PRODUCER_IDENTIFIED_VALID
producer broadcast, valid state
@ MTI_CONSUMER_IDENTIFIED_VALID
consumer broadcast, valid state
@ MTI_CONSUMER_IDENTIFIED_RANGE
consumer broadcast about a range of consumers
static bool get_mti_event(MTI mti)
Get the MTI event present value field.
static bool get_mti_address(MTI mti)
Get the MTI address present value field.
Data and objects we keep for each port.
bool inactive_
If true, we must not send any data to this target, because it has been unregistered.