AgIsoStack++
A control-function-focused implementation of the major ISOBUS and J1939 protocols
Loading...
Searching...
No Matches
can_hardware_interface.cpp
Go to the documentation of this file.
1//================================================================================================
9//================================================================================================
12#include "isobus/utility/system_timing.hpp"
13#include "isobus/utility/to_string.hpp"
14
15#include <algorithm>
16#include <limits>
17
18namespace isobus
19{
20#if !defined CAN_STACK_DISABLE_THREADS && !defined ARDUINO
21 std::unique_ptr<std::thread> CANHardwareInterface::updateThread;
23#endif
24 std::uint32_t CANHardwareInterface::periodicUpdateInterval = PERIODIC_UPDATE_INTERVAL;
26
27 EventDispatcher<const CANMessageFrame &> CANHardwareInterface::frameReceivedEventDispatcher;
28 EventDispatcher<const CANMessageFrame &> CANHardwareInterface::frameTransmittedEventDispatcher;
30
31 std::vector<std::unique_ptr<CANHardwareInterface::CANHardware>> CANHardwareInterface::hardwareChannels;
35
36 CANHardwareInterface CANHardwareInterface::SINGLETON;
37
39 messagesToBeTransmittedQueue(queueCapacity), receivedMessagesQueue(queueCapacity)
40 {
41 }
42
44 {
45#if !defined CAN_STACK_DISABLE_THREADS && !defined ARDUINO
47#endif
48 }
49
51 {
52 bool retVal = false;
53 if (nullptr != frameHandler)
54 {
55 frameHandler->open();
56 if (frameHandler->get_is_valid())
57 {
58#if !defined CAN_STACK_DISABLE_THREADS && !defined ARDUINO
60#endif
61 retVal = true;
62 }
63 }
64 return retVal;
65 }
66
68 {
69#if !defined CAN_STACK_DISABLE_THREADS && !defined ARDUINO
71#endif
72 if (nullptr != frameHandler)
73 {
74 frameHandler = nullptr;
75 }
76 messagesToBeTransmittedQueue.clear();
77 receivedMessagesQueue.clear();
78 return false;
79 }
80
82 {
83 if ((nullptr != frameHandler) && frameHandler->get_is_valid() && frameHandler->write_frame(frame))
84 {
85 return true;
86 }
87 return false;
88 }
89
91 {
92 if ((nullptr != frameHandler) && frameHandler->get_is_valid() && (!receivedMessagesQueue.is_full()))
93 {
94 CANMessageFrame frame;
95 if (frameHandler->read_frame(frame))
96 {
97 receivedMessagesQueue.push(frame);
98 return true; // Indicate that a frame was read
99 }
100 }
101 return false;
102 }
103
104#if !defined CAN_STACK_DISABLE_THREADS && !defined ARDUINO
109
111 {
112 receiveThreadRunning = true;
113 if (nullptr == receiveMessageThread)
114 {
115 receiveMessageThread.reset(new std::thread([this]() { receive_thread_function(); }));
116 }
117 }
118
120 {
121 receiveThreadRunning = false;
122 if (nullptr != receiveMessageThread)
123 {
124 if (receiveMessageThread->joinable())
125 {
126 receiveMessageThread->join();
127 }
128 receiveMessageThread = nullptr;
129 }
130 }
131
133 {
134 while (receiveThreadRunning)
135 {
136 if ((nullptr != frameHandler) && frameHandler->get_is_valid())
137 {
138 if (!receive_can_frame())
139 {
140 // There was no frame to receive, so if any other thread wants to do something, let it.
141 std::this_thread::yield();
142 }
143 else
144 {
146 }
147 }
148 else
149 {
150 std::this_thread::sleep_for(std::chrono::milliseconds(1000)); // Arbitrary, but don't want to infinite loop on the validity check.
151 }
152 }
153 }
154#endif
155
160
161 bool CANHardwareInterface::set_number_of_can_channels(std::uint8_t value, std::size_t queueCapacity)
162 {
163 LOCK_GUARD(Mutex, hardwareChannelsMutex);
164
165 if (started)
166 {
167 LOG_ERROR("[HardwareInterface] Cannot set number of channels after interface is started.");
168 return false;
169 }
170
171 while (value > static_cast<std::uint8_t>(hardwareChannels.size()))
172 {
173 hardwareChannels.emplace_back(new CANHardware(queueCapacity));
174 }
175 while (value < static_cast<std::uint8_t>(hardwareChannels.size()))
176 {
177 hardwareChannels.pop_back();
178 }
179 return true;
180 }
181
182 bool CANHardwareInterface::assign_can_channel_frame_handler(std::uint8_t channelIndex, std::shared_ptr<CANHardwarePlugin> driver)
183 {
184 LOCK_GUARD(Mutex, hardwareChannelsMutex);
185
186 if (started)
187 {
188 LOG_ERROR("[HardwareInterface] Cannot assign frame handlers after interface is started.");
189 return false;
190 }
191
192 if (channelIndex >= static_cast<std::uint8_t>(hardwareChannels.size()))
193 {
194 LOG_ERROR("[HardwareInterface] Unable to set frame handler at channel " + to_string(channelIndex) +
195 ", because there are only " + to_string(hardwareChannels.size()) + " channels set. " +
196 "Use set_number_of_can_channels() to increase the number of channels before assigning frame handlers.");
197 return false;
198 }
199
200 if (nullptr != hardwareChannels[channelIndex]->frameHandler)
201 {
202 LOG_ERROR("[HardwareInterface] Unable to set frame handler at channel " + to_string(channelIndex) + ", because it is already assigned.");
203 return false;
204 }
205
206 hardwareChannels[channelIndex]->frameHandler = driver;
207 return true;
208 }
209
211 {
212 return static_cast<std::uint8_t>(hardwareChannels.size() & std::numeric_limits<std::uint8_t>::max());
213 }
214
216 {
217 LOCK_GUARD(Mutex, hardwareChannelsMutex);
218
219 if (started)
220 {
221 LOG_ERROR("[HardwareInterface] Cannot remove frame handlers after interface is started.");
222 return false;
223 }
224
225 if (channelIndex >= static_cast<std::uint8_t>(hardwareChannels.size()))
226 {
227 LOG_ERROR("[HardwareInterface] Unable to remove frame handler at channel " + to_string(channelIndex) +
228 ", because there are only " + to_string(hardwareChannels.size()) + " channels set.");
229 return false;
230 }
231
232 if (nullptr == hardwareChannels[channelIndex]->frameHandler)
233 {
234 LOG_ERROR("[HardwareInterface] Unable to remove frame handler at channel " + to_string(channelIndex) + ", because it is not assigned.");
235 return false;
236 }
237
238 hardwareChannels[channelIndex]->frameHandler = nullptr;
239 return true;
240 }
241
242 std::shared_ptr<CANHardwarePlugin> CANHardwareInterface::get_assigned_can_channel_frame_handler(std::uint8_t channelIndex)
243 {
244 std::shared_ptr<CANHardwarePlugin> retVal;
245
246 if (channelIndex < static_cast<std::uint8_t>(hardwareChannels.size()))
247 {
248 retVal = hardwareChannels.at(channelIndex)->frameHandler;
249 }
250 return retVal;
251 }
252
254 {
255 LOCK_GUARD(Mutex, hardwareChannelsMutex);
256
257#if !defined CAN_STACK_DISABLE_THREADS && !defined ARDUINO
259#endif
260 std::for_each(hardwareChannels.begin(), hardwareChannels.end(), [](const std::unique_ptr<CANHardware> &channel) {
261 channel->start();
262 });
263
264 started = true;
265 return true;
266 }
267
269 {
270 if (!started)
271 {
272 LOG_ERROR("[HardwareInterface] Cannot stop interface before it is started.");
273 return false;
274 }
275 frameReceivedEventDispatcher.clear_listeners();
276 frameTransmittedEventDispatcher.clear_listeners();
277 periodicUpdateEventDispatcher.clear_listeners();
278
279#if !defined CAN_STACK_DISABLE_THREADS && !defined ARDUINO
280 stop_threads();
281#endif
282
283 LOCK_GUARD(Mutex, hardwareChannelsMutex);
284 std::for_each(hardwareChannels.begin(), hardwareChannels.end(), [](const std::unique_ptr<CANHardware> &channel) {
285 channel->stop();
286 });
287 return true;
288 }
289
291 {
292 return started;
293 }
294
296 {
297 if (!started)
298 {
299 LOG_ERROR("[HardwareInterface] Cannot transmit message before interface is started.");
300 return false;
301 }
302
303 if (frame.channel >= static_cast<std::uint8_t>(hardwareChannels.size()))
304 {
305 LOG_ERROR("[HardwareInterface] Cannot transmit message on channel " + to_string(frame.channel) +
306 ", because there are only " + to_string(hardwareChannels.size()) + " channels set.");
307 return false;
308 }
309
310 const std::unique_ptr<CANHardware> &channel = hardwareChannels[frame.channel];
311 if (nullptr == channel->frameHandler)
312 {
313 LOG_ERROR("[HardwareInterface] Cannot transmit message on channel " + to_string(frame.channel) + ", because it is not assigned.");
314 return false;
315 }
316
317 if (channel->frameHandler->get_is_valid())
318 {
319 channel->messagesToBeTransmittedQueue.push(frame);
320#if !defined CAN_STACK_DISABLE_THREADS && !defined ARDUINO
321 updateThreadWakeupCondition.notify_all();
322#endif
323 return true;
324 }
325 return false;
326 }
327
328 EventDispatcher<const CANMessageFrame &> &CANHardwareInterface::get_can_frame_received_event_dispatcher()
329 {
331 }
332
334 {
336 }
337
342
344 {
346 }
347
352
354 {
355 if (started)
356 {
357 {
358 // Stage 1 - Receiving messages from hardware
359 LOCK_GUARD(Mutex, hardwareChannelsMutex);
360 for (std::uint8_t i = 0; i < hardwareChannels.size(); i++)
361 {
362#if defined CAN_STACK_DISABLE_THREADS || defined ARDUINO
363 // If we don't have threads, we need to poll the hardware for messages here
364 hardwareChannels[i]->receive_can_frame();
365#endif
366
368 while (hardwareChannels[i]->receivedMessagesQueue.peek(frame))
369 {
370 frame.channel = i;
371 frameReceivedEventDispatcher.invoke(frame);
373 hardwareChannels[i]->receivedMessagesQueue.pop();
374 }
375 }
376 }
377
378 // Stage 2 - Update stack. That will fill up the transmit queues if needed
379 if (SystemTiming::time_expired_ms(lastUpdateTimestamp, periodicUpdateInterval))
380 {
383 lastUpdateTimestamp = SystemTiming::get_timestamp_ms();
384 }
385
386 // Stage 3 - Transmitting messages to hardware
387 {
388 LOCK_GUARD(Mutex, hardwareChannelsMutex);
389 std::for_each(hardwareChannels.begin(), hardwareChannels.end(), [](const std::unique_ptr<CANHardware> &channel) {
390 isobus::CANMessageFrame frame;
391 while (channel->messagesToBeTransmittedQueue.peek(frame))
392 {
393 if (channel->transmit_can_frame(frame))
394 {
395 frameTransmittedEventDispatcher.invoke(frame);
396 on_transmit_can_message_frame_from_hardware(frame);
397 channel->messagesToBeTransmittedQueue.pop();
398 }
399 else
400 {
401 break;
402 }
403 }
404 });
405 }
406 }
407 }
408
409#if !defined CAN_STACK_DISABLE_THREADS && !defined ARDUINO
411 {
412 std::unique_lock<std::mutex> hardwareLock(hardwareChannelsMutex);
413 // Wait until everything is running
414 hardwareLock.unlock();
415
416 while (started)
417 {
418 std::unique_lock<std::mutex> threadLock(updateMutex);
419 updateThreadWakeupCondition.wait_for(threadLock, std::chrono::milliseconds(periodicUpdateInterval)); // Update with at least the periodic interval
420 update();
421 }
422 }
423
425 {
426 started = true;
427 if (nullptr == updateThread)
428 {
429 updateThread.reset(new std::thread(update_thread_function));
430 }
431 }
432
434 {
435 started = false;
436 if (nullptr != updateThread)
437 {
438 if (updateThread->joinable())
439 {
440 updateThreadWakeupCondition.notify_all();
441 updateThread->join();
442 }
443 updateThread = nullptr;
444 }
445 }
446#endif
447}
The hardware abstraction layer that separates the stack from the underlying CAN driver.
A class that acts as a logging sink. The intent is that someone could make their own derived class of...
Stores the data for a single CAN channel.
CANHardware(std::size_t queueCapacity)
Constructor for the CANHardware.
virtual ~CANHardware()
Destructor for the CANHardware.
void receive_thread_function()
The receiving thread loop for this CAN channel.
bool receive_can_frame()
Receives a frame from the hardware and adds it to the receive queue.
void start_threads()
Starts the receiving thread for this CAN channel.
bool start()
Starts this hardware channel.
void stop_threads()
Stops the receiving thread for this CAN channel.
bool transmit_can_frame(const CANMessageFrame &frame) const
Try to transmit the frame to the hardware.
static bool stop()
Stops all CAN management threads and discards all remaining messages in the Tx and Rx queues.
static std::vector< std::unique_ptr< CANHardware > > hardwareChannels
A list of all CAN channel's metadata.
static EventDispatcher< const CANMessageFrame & > frameReceivedEventDispatcher
The event dispatcher for when a CAN message frame is received from hardware event.
static EventDispatcher< const CANMessageFrame & > frameTransmittedEventDispatcher
The event dispatcher for when a CAN message has been transmitted via hardware.
static std::uint8_t get_number_of_can_channels()
Returns the number of configured CAN channels that the class is managing.
static EventDispatcher< const CANMessageFrame & > & get_can_frame_received_event_dispatcher()
Get the event dispatcher for when a CAN message frame is received from hardware event.
static EventDispatcher periodicUpdateEventDispatcher
The event dispatcher for when a periodic update is called.
static Mutex updateMutex
A mutex for the main thread.
static std::uint32_t periodicUpdateInterval
The period between calls to the network manager update function in milliseconds.
static EventDispatcher & get_periodic_update_event_dispatcher()
Get the event dispatcher for when a periodic update is called.
static void stop_threads()
Stops all threads related to the hardware interface.
static bool started
Stores if the threads have been started.
static bool unassign_can_channel_frame_handler(std::uint8_t channelIndex)
Removes a CAN driver from a channel.
static void update()
Call this periodically if you have threads disabled.
static bool set_number_of_can_channels(std::uint8_t value, std::size_t queueCapacity=40)
Sets the number of CAN channels to manage.
static void start_threads()
Starts all threads related to the hardware interface.
static std::shared_ptr< CANHardwarePlugin > get_assigned_can_channel_frame_handler(std::uint8_t channelIndex)
Gets the CAN driver assigned to a channel.
static bool transmit_can_frame(const CANMessageFrame &frame)
Called externally, adds a message to a CAN channel's Tx queue.
static Mutex hardwareChannelsMutex
Mutex to protect hardwareChannels
static std::uint32_t lastUpdateTimestamp
The last time the network manager was updated.
static CANHardwareInterface SINGLETON
Singleton instance of the CANHardwareInterface class.
virtual ~CANHardwareInterface()
Deconstructor for the CANHardwareInterface class for stopping threads.
static std::uint32_t get_periodic_update_interval()
Get the interval between periodic updates to the network manager.
static EventDispatcher< const CANMessageFrame & > & get_can_frame_transmitted_event_dispatcher()
Get the event dispatcher for when a CAN message frame will be send to hardware event.
static bool is_running()
Checks if the CAN stack and CAN drivers are running.
static bool assign_can_channel_frame_handler(std::uint8_t channelIndex, std::shared_ptr< CANHardwarePlugin > canDriver)
Assigns a CAN driver to a channel.
static void update_thread_function()
The main thread loop for updating the stack.
static std::condition_variable updateThreadWakeupCondition
A condition variable to allow for signaling the updateThread to wakeup.
static std::unique_ptr< std::thread > updateThread
The main thread.
static void set_periodic_update_interval(std::uint32_t value)
Set the interval between periodic updates to the network manager.
static bool start()
Starts the threads for managing the CAN stack and CAN drivers.
A CAN frame for interfacing with a hardware layer, like socket CAN or other interface.
std::uint8_t channel
The CAN channel index associated with the frame.
This namespace encompasses all of the ISO11783 stack's functionality to reduce global namespace pollu...
bool send_can_message_frame_to_hardware(const CANMessageFrame &frame)
The sending abstraction layer between the hardware and the stack.
void periodic_update_from_hardware()
The periodic update abstraction layer between the hardware and the stack.
void receive_can_message_frame_from_hardware(const CANMessageFrame &frame)
The receiving abstraction layer between the hardware and the stack.