Open Model Railroad Network (OpenMRN)
Loading...
Searching...
No Matches
BulkAliasAllocator.cxx
Go to the documentation of this file.
1
36#include "openlcb/CanDefs.hxx"
37#include "utils/MakeUnique.hxx"
38
39namespace openlcb
40{
41
44class BulkAliasAllocator : public CallableFlow<BulkAliasRequest>
45{
46public:
51 {
52 }
53
66
69 {
70 unsigned needed = std::min(request()->numAliases_,
71 (unsigned)(config_bulk_alias_num_can_frames() + 3) / 4);
72 if (!needed)
73 {
75 }
76 bn_.reset(this);
77 for (unsigned i = 0; i < needed; ++i)
78 {
79 NodeAlias next_alias = if_can()->alias_allocator()->get_new_seed();
80 auto if_id = if_can()->alias_allocator()->if_node_id();
81 send_can_frame(next_alias, (if_id >> 36) & 0xfff, 7);
82 send_can_frame(next_alias, (if_id >> 24) & 0xfff, 6);
83 send_can_frame(next_alias, (if_id >> 12) & 0xfff, 5);
84 send_can_frame(next_alias, (if_id >> 0) & 0xfff, 4);
85 --request()->numAliases_;
86 pendingAliasesByTime_.push_back({next_alias});
87 pendingAliasesByKey_.insert({next_alias});
88 }
89 bn_.notify();
91 }
92
95 {
96 auto ctime = relative_time();
97 for (unsigned i = nextToStampTime_; i < pendingAliasesByTime_.size();
98 ++i)
99 {
100 pendingAliasesByTime_[i].cidTime_ = ctime;
101 }
103 // Go back to sending more CID frames as needed.
105 }
106
110 {
112 {
113 return complete();
114 }
115 if (request()->numAliases_)
116 {
117 // Some conflicts were identified, go and allocate more.
119 }
120 auto ctime = relative_time();
121 unsigned num_sent = 0;
122 bn_.reset(this);
123 while ((nextToClaim_ < pendingAliasesByTime_.size()) &&
124 (num_sent < (unsigned)(config_bulk_alias_num_can_frames())) &&
126 ctime))
127 {
128 NodeAlias a =
130 ++nextToClaim_;
131 auto it = pendingAliasesByKey_.find(a);
132 if (it->hasConflict_)
133 {
134 // we skip this alias because there was a conflict.
135 continue;
136 }
138 ++num_sent;
140 }
142 {
143 // no frame sent
144 return sleep_and_call(
146 }
147 else
148 {
149 bn_.notify();
150 // Wait for outgoing frames to be gone and call this again.
151 return wait();
152 }
153 }
154
163
164private:
170 {
171 auto rb = get_buffer_deleter(message);
172 auto alias = CanDefs::get_src(GET_CAN_FRAME_ID_EFF(*message->data()));
173 auto it = pendingAliasesByKey_.find(alias);
174 if (it != pendingAliasesByKey_.end() && !it->hasConflict_)
175 {
176 it->hasConflict_ = 1;
177 ++request()->numAliases_;
178 }
179 }
180
182 IncomingFrameHandler::GenericHandler conflictHandler_ {
184
187 static constexpr unsigned ALLOCATE_DELAY = 20;
188
195 void send_can_frame(NodeAlias src, uint16_t control_field, int sequence)
196 {
197 auto *b = if_can()->frame_write_flow()->alloc();
198 b->set_done(bn_.new_child());
199 CanDefs::control_init(*b->data(), src, control_field, sequence);
200 if_can()->frame_write_flow()->send(b, 0);
201 }
202
205 {
206 return static_cast<IfCan *>(service());
207 }
208
210 unsigned relative_time()
211 {
213 }
214
217 {
221 : alias_(alias)
222 , cidTime_(0)
223 {
224 }
225
227 unsigned alias_ : 12;
230 unsigned cidTime_ : 8;
231 };
232 static_assert(sizeof(PendingAliasInfo) == 4, "memory bloat");
233
236 {
240 : alias_(alias)
241 , hasConflict_(0)
242 {
243 }
244
246 uint16_t alias_ : 12;
248 uint16_t hasConflict_ : 1;
249 };
250 static_assert(sizeof(AliasLookupInfo) == 2, "memory bloat");
253 {
254 bool operator()(AliasLookupInfo a, AliasLookupInfo b)
255 {
256 return a.alias_ < b.alias_;
257 }
258 };
259
266 long long startTime_;
269 std::vector<PendingAliasInfo> pendingAliasesByTime_;
277 uint16_t nextToClaim_;
278};
279
280std::unique_ptr<BulkAliasAllocatorInterface> create_bulk_alias_allocator(
281 IfCan *can_if)
282{
283 return std::make_unique<BulkAliasAllocator>(can_if);
284}
285
286} // namespace openlcb
BufferPtr< T > get_buffer_deleter(Buffer< T > *b)
Helper function to create a BufferPtr of an appropriate type without having to explicitly specify the...
Definition Buffer.hxx:272
C++11 version of std::make_unique which is only available from c++14 or later.
#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.
void notify() override
Implementation of the barrier semantics.
BarrierNotifiable * reset(Notifiable *done)
Resets the barrier. Returns &*this. Asserts that is_done().
BarrierNotifiable * new_child()
Call this for each child task.
bool abort_if_almost_done()
Checks if there is exactly one outstanding notification left in the barrier.
Base class for all QMember types that hold data in an expandable format.
Definition Buffer.hxx:195
Action return_ok()
Terminates the flow and returns the request buffer to the caller with an error code of OK (zero).
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_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.
MessageType * alloc()
Synchronously allocates a message buffer from the pool of this flow.
An mostly std::set<> compatible class that stores the internal data in a sorted vector.
Return type for a state flow callback.
Use this timer class to deliver the timeout notification to a stateflow.
Service * service()
Return a pointer to the service I am bound to.
Action wait()
Wait for an asynchronous call.
Action wait_and_call(Callback c)
Wait for resource to become available before proceeding to next state.
Action sleep_and_call(::Timer *timer, long long timeout_nsec, Callback c)
Suspends execution of this control flow for a specified time.
Action call_immediately(Callback c)
Imediately call the next state upon return.
void add_allocated_alias(NodeAlias alias)
Call from an alternate alias allocator.
NodeAlias get_new_seed()
Returns a new alias to check from the random sequence.
Implementation of the BulkAliasAllocatorInterface to allocate many aliases at the same time.
SortedListSet< AliasLookupInfo, LookupCompare > pendingAliasesByKey_
Stores the aliases we are trying to allocate in the alias order.
uint16_t nextToStampTime_
Index into the pendingAliasesByTime_ vector where we need to stmap time.
BulkAliasAllocator(IfCan *iface)
Constructor.
void send_can_frame(NodeAlias src, uint16_t control_field, int sequence)
Sends a CAN control frame to the bus.
Action complete()
Called when all RID frames are sent out.
Action send_cid_frames()
Picks a bunch of random aliases, sends CID frames for them to the bus.
Action entry() override
Start of flow when a request arrives to allocate many aliases.
void handle_conflict(Buffer< CanMessageData > *message)
Callback from the stack for all incoming frames while we are operating.
long long startTime_
We measure time elapsed relative to this point.
std::vector< PendingAliasInfo > pendingAliasesByTime_
Stores the aliases we are trying to allocate in time order of picking them.
uint16_t nextToClaim_
Index into the pendingAliasesByTime_ vector where we need to send out the reserve frame.
BarrierNotifiable bn_
Helper object to determine when the CAN frames have flushed from the system.
StateFlowTimer timer_
Helper object for sleeping.
Action stamp_time()
Adds the timestamps when the CID requests were sent out.
IncomingFrameHandler::GenericHandler conflictHandler_
Listens to incoming CAN frames and handles alias conflicts.
Action wait_for_results()
Sends out the RID frames for any alias that the 200 msec has already elapsed, then waits a bit and tr...
static constexpr unsigned ALLOCATE_DELAY
How many count to wait before sending out the RID frames.
Implementation of the OpenLCB interface abstraction for the CAN-bus interface standard.
Definition IfCan.hxx:65
AliasAllocator * alias_allocator()
Definition IfCan.hxx:110
std::unique_ptr< BulkAliasAllocatorInterface > create_bulk_alias_allocator(IfCan *can_if)
Creates a bulk alias allocator.
uint16_t NodeAlias
Alias to a 48-bit NMRAnet Node ID type.
long long os_get_time_monotonic(void)
Get the monotonic time since the system started.
Definition os.c:571
#define MSEC_TO_NSEC(_msec)
Convert a millisecond value to a nanosecond value.
Definition os.h:268
We store this type in the sorted map lookup structure.
uint16_t hasConflict_
1 if we have seen a conflict
Comparator function on AliasLookupInfo objects.
We store this type in the time-ordered aliases structure.
unsigned cidTime_
The time when the CID requests were sent.
Message type to request allocating many aliases for an interface.
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
@ RID_FRAME
Reserve ID Frame.
Definition CanDefs.hxx:126
static NodeAlias get_src(uint32_t can_id)
Get the source field value of the CAN ID.
Definition CanDefs.hxx:154