AgIsoStack++
A control-function-focused implementation of the major ISOBUS and J1939 protocols
Loading...
Searching...
No Matches
can_network_manager.cpp
Go to the documentation of this file.
1//================================================================================================
10//================================================================================================
11
19#include "isobus/utility/system_timing.hpp"
20#include "isobus/utility/to_string.hpp"
21
22#include <algorithm>
23#include <cassert>
24#include <cstring>
25#include <numeric>
26
27namespace isobus
28{
29 CANNetworkManager CANNetworkManager::CANNetwork;
30
32 {
33 // Clear queues
34 while (!receivedMessageQueue.empty())
35 {
37 }
38 while (!transmittedMessageQueue.empty())
39 {
41 }
42 initialized = true;
43 }
44
45 std::shared_ptr<ControlFunction> CANNetworkManager::get_control_function(std::uint8_t channelIndex, std::uint8_t address, CANLibBadge<AddressClaimStateMachine>) const
46 {
47 return get_control_function(channelIndex, address);
48 }
49
50 void CANNetworkManager::add_global_parameter_group_number_callback(std::uint32_t parameterGroupNumber, CANLibCallback callback, void *parent)
51 {
52 globalParameterGroupNumberCallbacks.emplace_back(parameterGroupNumber, callback, parent, nullptr);
53 }
54
55 void CANNetworkManager::remove_global_parameter_group_number_callback(std::uint32_t parameterGroupNumber, CANLibCallback callback, void *parent)
56 {
57 ParameterGroupNumberCallbackData tempObject(parameterGroupNumber, callback, parent, nullptr);
58 auto callbackLocation = std::find(globalParameterGroupNumberCallbacks.begin(), globalParameterGroupNumberCallbacks.end(), tempObject);
59 if (globalParameterGroupNumberCallbacks.end() != callbackLocation)
60 {
61 globalParameterGroupNumberCallbacks.erase(callbackLocation);
62 }
63 }
64
69
70 void CANNetworkManager::add_any_control_function_parameter_group_number_callback(std::uint32_t parameterGroupNumber, CANLibCallback callback, void *parent)
71 {
72 LOCK_GUARD(Mutex, anyControlFunctionCallbacksMutex);
73 anyControlFunctionParameterGroupNumberCallbacks.emplace_back(parameterGroupNumber, callback, parent, nullptr);
74 }
75
76 void CANNetworkManager::remove_any_control_function_parameter_group_number_callback(std::uint32_t parameterGroupNumber, CANLibCallback callback, void *parent)
77 {
78 ParameterGroupNumberCallbackData tempObject(parameterGroupNumber, callback, parent, nullptr);
79 LOCK_GUARD(Mutex, anyControlFunctionCallbacksMutex);
80 auto callbackLocation = std::find(anyControlFunctionParameterGroupNumberCallbacks.begin(), anyControlFunctionParameterGroupNumberCallbacks.end(), tempObject);
81 if (anyControlFunctionParameterGroupNumberCallbacks.end() != callbackLocation)
82 {
84 }
85 }
86
91
92 std::shared_ptr<InternalControlFunction> CANNetworkManager::get_internal_control_function(std::shared_ptr<ControlFunction> controlFunction)
93 {
94 std::shared_ptr<InternalControlFunction> retVal = nullptr;
95
96 if ((nullptr != controlFunction) &&
97 (ControlFunction::Type::Internal == controlFunction->get_type()))
98 {
99 retVal = std::static_pointer_cast<InternalControlFunction>(controlFunction);
100 }
101 return retVal;
102 }
103
104 float CANNetworkManager::get_estimated_busload(std::uint8_t canChannel)
105 {
106 LOCK_GUARD(Mutex, busloadUpdateMutex);
107 constexpr float ISOBUS_BAUD_RATE_BPS = 250000.0f;
108 float retVal = 0.0f;
109
110 if (canChannel < CAN_PORT_MAXIMUM)
111 {
112 float totalTimeInAccumulatorWindow = (busloadMessageBitsHistory.at(canChannel).size() * BUSLOAD_UPDATE_FREQUENCY_MS) / 1000.0f;
113 std::uint32_t totalBitCount = std::accumulate(busloadMessageBitsHistory.at(canChannel).begin(), busloadMessageBitsHistory.at(canChannel).end(), 0);
114 retVal = (0 != totalTimeInAccumulatorWindow) ? ((totalBitCount / (totalTimeInAccumulatorWindow * ISOBUS_BAUD_RATE_BPS)) * 100.0f) : 0.0f;
115 }
116 return retVal;
117 }
118
119 bool CANNetworkManager::send_can_message(std::uint32_t parameterGroupNumber,
120 const std::uint8_t *dataBuffer,
121 std::uint32_t dataLength,
122 std::shared_ptr<InternalControlFunction> sourceControlFunction,
123 std::shared_ptr<ControlFunction> destinationControlFunction,
125 TransmitCompleteCallback transmitCompleteCallback,
126 void *parentPointer,
127 DataChunkCallback frameChunkCallback)
128 {
129 bool retVal = false;
130
131 if (((nullptr != dataBuffer) ||
132 (nullptr != frameChunkCallback)) &&
133 (dataLength > 0) &&
135 (nullptr != sourceControlFunction) &&
136 ((parameterGroupNumber == static_cast<std::uint32_t>(CANLibParameterGroupNumber::AddressClaim)) ||
137 (sourceControlFunction->get_address_valid())))
138 {
139 std::unique_ptr<CANMessageData> messageData;
140 if (nullptr != frameChunkCallback)
141 {
142 messageData.reset(new CANMessageDataCallback(dataLength, frameChunkCallback, parentPointer));
143 }
144 else
145 {
146 messageData.reset(new CANMessageDataView(dataBuffer, dataLength));
147 }
148 if (transportProtocols[sourceControlFunction->get_can_port()]->protocol_transmit_message(parameterGroupNumber,
149 messageData,
150 sourceControlFunction,
151 destinationControlFunction,
152 transmitCompleteCallback,
153 parentPointer))
154 {
155 // Successfully sent via the transport protocol
156 retVal = true;
157 }
158 else if (extendedTransportProtocols[sourceControlFunction->get_can_port()]->protocol_transmit_message(parameterGroupNumber,
159 messageData,
160 sourceControlFunction,
161 destinationControlFunction,
162 transmitCompleteCallback,
163 parentPointer))
164 {
165 // Successfully sent via the extended transport protocol
166 retVal = true;
167 }
168
170 if ((!retVal) &&
171 (nullptr != dataBuffer))
172 {
173 if (nullptr == destinationControlFunction)
174 {
175 // Todo move binding of dest address to hardware layer
176 retVal = send_can_message_raw(sourceControlFunction->get_can_port(), sourceControlFunction->get_address(), 0xFF, parameterGroupNumber, static_cast<std::uint8_t>(priority), dataBuffer, dataLength);
177 }
178 else if (destinationControlFunction->get_address_valid())
179 {
180 retVal = send_can_message_raw(sourceControlFunction->get_can_port(), sourceControlFunction->get_address(), destinationControlFunction->get_address(), parameterGroupNumber, static_cast<std::uint8_t>(priority), dataBuffer, dataLength);
181 }
182
183 if ((retVal) &&
184 (nullptr != transmitCompleteCallback))
185 {
186 // Message was not sent via a protocol, so handle the tx callback now
187 transmitCompleteCallback(parameterGroupNumber, dataLength, sourceControlFunction, destinationControlFunction, retVal, parentPointer);
188 }
189 }
190 }
191 return retVal;
192 }
193
195 {
197 LOCK_GUARD(Mutex, processingMutex);
198
199 if (!initialized)
200 {
201 initialize();
202 }
203
205
207
208 // Update ISOBUS heartbeats (should be done before process_tx_messages
209 // to minimize latency in safety critical paths)
210 for (std::uint32_t i = 0; i < CAN_PORT_MAXIMUM; i++)
211 {
212 heartBeatInterfaces.at(i)->update();
213 }
214
216
218
220
221 // Update transport protocols
222 for (std::uint32_t i = 0; i < CAN_PORT_MAXIMUM; i++)
223 {
224 transportProtocols[i]->update();
225 extendedTransportProtocols[i]->update();
226 fastPacketProtocol[i]->update();
227 }
229 updateTimestamp_ms = SystemTiming::get_timestamp_ms();
230 }
231
232 bool CANNetworkManager::send_can_message_raw(std::uint32_t portIndex,
233 std::uint8_t sourceAddress,
234 std::uint8_t destAddress,
235 std::uint32_t parameterGroupNumber,
236 std::uint8_t priority,
237 const void *data,
238 std::uint32_t size,
240 {
241 return send_can_message_raw(portIndex, sourceAddress, destAddress, parameterGroupNumber, priority, data, size);
242 }
243
245 {
246 ParameterGroupNumberCallbackData retVal(0, nullptr, nullptr, nullptr);
247
249 {
251 }
252 return retVal;
253 }
254
259
264
269
271 {
273
274 CANIdentifier identifier(rxFrame.identifier);
276 identifier,
277 rxFrame.data,
278 rxFrame.dataLength,
279 get_control_function(rxFrame.channel, identifier.get_source_address()),
281 rxFrame.channel);
282
284
285 if (initialized)
286 {
287 LOCK_GUARD(Mutex, receivedMessageQueueMutex);
288 receivedMessageQueue.push(std::move(message));
289 }
290 }
291
293 {
295
296 CANIdentifier identifier(txFrame.identifier);
298 identifier,
299 txFrame.data,
300 txFrame.dataLength,
301 get_control_function(txFrame.channel, identifier.get_source_address()),
303 txFrame.channel);
304
305 if (initialized)
306 {
307 LOCK_GUARD(Mutex, transmittedMessageQueueMutex);
308 transmittedMessageQueue.push(std::move(message));
309 }
310 }
311
312 void CANNetworkManager::on_control_function_destroyed(std::shared_ptr<ControlFunction> controlFunction, CANLibBadge<ControlFunction>)
313 {
314 if (ControlFunction::Type::Internal == controlFunction->get_type())
315 {
316 heartBeatInterfaces.at(controlFunction->canPortIndex)->on_destroyed_internal_control_function(std::static_pointer_cast<InternalControlFunction>(controlFunction));
317 internalControlFunctions.erase(std::remove(internalControlFunctions.begin(), internalControlFunctions.end(), controlFunction), internalControlFunctions.end());
318 }
319 else if (ControlFunction::Type::Partnered == controlFunction->get_type())
320 {
321 partneredControlFunctions.erase(std::remove(partneredControlFunctions.begin(), partneredControlFunctions.end(), controlFunction), partneredControlFunctions.end());
322 }
323
324 auto result = std::find(inactiveControlFunctions.begin(), inactiveControlFunctions.end(), controlFunction);
325 if (result != inactiveControlFunctions.end())
326 {
327 inactiveControlFunctions.erase(result);
328 }
329
330 for (std::uint8_t i = 0; i < NULL_CAN_ADDRESS; i++)
331 {
332 if (controlFunctionTable[controlFunction->get_can_port()][i] == controlFunction)
333 {
334 if (i != controlFunction->get_address())
335 {
336 LOG_WARNING("[NM]: %s control function with address '%d' was at incorrect address '%d' in the lookup table prior to deletion.",
337 controlFunction->get_type_string().c_str(),
338 controlFunction->get_address(),
339 i);
340 }
341
342 if (controlFunction->get_address() < NULL_CAN_ADDRESS)
343 {
344 if (initialized)
345 {
346 // The control function was active, replace it with an new external control function
347 controlFunctionTable[controlFunction->get_can_port()][controlFunction->address] = ControlFunction::create(controlFunction->get_NAME(), controlFunction->get_address(), controlFunction->get_can_port());
348 }
349 else
350 {
351 // The network manager is not initialized yet, just remove the control function from the table
352 controlFunctionTable[controlFunction->get_can_port()][i] = nullptr;
353 }
354 }
355 }
356 }
357 LOG_INFO("[NM]: %s control function with address '%d' is deleted.",
358 controlFunction->get_type_string().c_str(),
359 controlFunction->get_address());
360 }
361
362 void CANNetworkManager::on_control_function_created(std::shared_ptr<ControlFunction> controlFunction, CANLibBadge<ControlFunction>)
363 {
364 on_control_function_created(controlFunction);
365 }
366
367 void CANNetworkManager::on_control_function_created(std::shared_ptr<ControlFunction> controlFunction, CANLibBadge<InternalControlFunction>)
368 {
369 on_control_function_created(controlFunction);
370 heartBeatInterfaces.at(controlFunction->canPortIndex)->on_new_internal_control_function(std::static_pointer_cast<InternalControlFunction>(controlFunction));
371 }
372
373 void CANNetworkManager::on_control_function_created(std::shared_ptr<ControlFunction> controlFunction, CANLibBadge<PartneredControlFunction>)
374 {
375 on_control_function_created(controlFunction);
376 }
377
379 {
380 if (nullptr != callback)
381 {
382 LOCK_GUARD(Mutex, controlFunctionStatusCallbacksMutex);
383 controlFunctionStateCallbacks.emplace_back(callback);
384 }
385 }
386
388 {
389 if (nullptr != callback)
390 {
391 LOCK_GUARD(Mutex, controlFunctionStatusCallbacksMutex);
392 ControlFunctionStateCallback targetCallback(callback);
393 auto callbackLocation = std::find(controlFunctionStateCallbacks.begin(), controlFunctionStateCallbacks.end(), targetCallback);
394
395 if (controlFunctionStateCallbacks.end() != callbackLocation)
396 {
397 controlFunctionStateCallbacks.erase(callbackLocation);
398 }
399 }
400 }
401
402 const std::list<std::shared_ptr<InternalControlFunction>> &CANNetworkManager::get_internal_control_functions() const
403 {
405 }
406
407 const std::list<std::shared_ptr<PartneredControlFunction>> &CANNetworkManager::get_partnered_control_functions() const
408 {
410 }
411
412 std::list<std::shared_ptr<ControlFunction>> isobus::CANNetworkManager::get_control_functions(bool includingOffline) const
413 {
414 std::list<std::shared_ptr<ControlFunction>> retVal;
415
416 for (std::uint8_t channelIndex = 0; channelIndex < CAN_PORT_MAXIMUM; channelIndex++)
417 {
418 for (std::uint8_t address = 0; address < NULL_CAN_ADDRESS; address++)
419 {
420 if (nullptr != controlFunctionTable[channelIndex][address])
421 {
422 retVal.push_back(controlFunctionTable[channelIndex][address]);
423 }
424 }
425 }
426
427 if (includingOffline)
428 {
429 retVal.insert(retVal.end(), inactiveControlFunctions.begin(), inactiveControlFunctions.end());
430 }
431
432 return retVal;
433 }
434
435 std::list<std::shared_ptr<TransportProtocolSessionBase>> isobus::CANNetworkManager::get_active_transport_protocol_sessions(std::uint8_t canPortIndex) const
436 {
437 std::list<std::shared_ptr<TransportProtocolSessionBase>> retVal;
438 retVal.insert(retVal.end(), transportProtocols[canPortIndex]->get_sessions().begin(), transportProtocols[canPortIndex]->get_sessions().end());
439 retVal.insert(retVal.end(), extendedTransportProtocols[canPortIndex]->get_sessions().begin(), extendedTransportProtocols[canPortIndex]->get_sessions().end());
440 return retVal;
441 }
442
443 std::unique_ptr<FastPacketProtocol> &CANNetworkManager::get_fast_packet_protocol(std::uint8_t canPortIndex)
444 {
445 return fastPacketProtocol[canPortIndex];
446 }
447
449 {
450 assert(canPortIndex < CAN_PORT_MAXIMUM); // You passed in an out of range index!
451 return *heartBeatInterfaces.at(canPortIndex);
452 }
453
458
459 EventDispatcher<std::shared_ptr<InternalControlFunction>> &CANNetworkManager::get_address_violation_event_dispatcher()
460 {
462 }
463
464 bool CANNetworkManager::add_protocol_parameter_group_number_callback(std::uint32_t parameterGroupNumber, CANLibCallback callback, void *parentPointer)
465 {
466 bool retVal = false;
467 ParameterGroupNumberCallbackData callbackInfo(parameterGroupNumber, callback, parentPointer, nullptr);
468 LOCK_GUARD(Mutex, protocolPGNCallbacksMutex);
469 if ((nullptr != callback) && (protocolPGNCallbacks.end() == find(protocolPGNCallbacks.begin(), protocolPGNCallbacks.end(), callbackInfo)))
470 {
471 protocolPGNCallbacks.push_back(callbackInfo);
472 retVal = true;
473 }
474 return retVal;
475 }
476
477 bool CANNetworkManager::remove_protocol_parameter_group_number_callback(std::uint32_t parameterGroupNumber, CANLibCallback callback, void *parentPointer)
478 {
479 bool retVal = false;
480 ParameterGroupNumberCallbackData callbackInfo(parameterGroupNumber, callback, parentPointer, nullptr);
481 LOCK_GUARD(Mutex, protocolPGNCallbacksMutex);
482 if (nullptr != callback)
483 {
484 std::list<ParameterGroupNumberCallbackData>::iterator callbackLocation;
485 callbackLocation = find(protocolPGNCallbacks.begin(), protocolPGNCallbacks.end(), callbackInfo);
486
487 if (protocolPGNCallbacks.end() != callbackLocation)
488 {
489 protocolPGNCallbacks.erase(callbackLocation);
490 retVal = true;
491 }
492 }
493 return retVal;
494 }
495
497 {
500 controlFunctionTable.fill({ nullptr });
501
502 auto send_frame_callback = [this](std::uint32_t parameterGroupNumber,
503 CANDataSpan data,
504 std::shared_ptr<InternalControlFunction> sourceControlFunction,
505 std::shared_ptr<ControlFunction> destinationControlFunction,
506 CANIdentifier::CANPriority priority) { return this->send_can_message(parameterGroupNumber, data.begin(), data.size(), sourceControlFunction, destinationControlFunction, priority); };
507
508 for (std::uint8_t i = 0; i < CAN_PORT_MAXIMUM; i++)
509 {
510 auto receive_message_callback = [this, i](const CANMessage &message) {
511 // TODO: hack port_index for now, once network manager isn't a singleton, this can be removed
513 message.get_identifier(),
514 message.get_data(),
515 message.get_source_control_function(),
516 message.get_destination_control_function(),
517 i);
518 this->protocol_message_callback(message);
519 };
520 transportProtocols.at(i).reset(new TransportProtocolManager(send_frame_callback, receive_message_callback, &configuration));
521 extendedTransportProtocols.at(i).reset(new ExtendedTransportProtocolManager(send_frame_callback, receive_message_callback, &configuration));
522 fastPacketProtocol.at(i).reset(new FastPacketProtocol(send_frame_callback));
523 heartBeatInterfaces.at(i).reset(new HeartbeatInterface(send_frame_callback));
524 }
525 }
526
528 {
529 std::uint8_t channelIndex = message.get_can_port_index();
530
531 if ((static_cast<std::uint32_t>(CANLibParameterGroupNumber::AddressClaim) == message.get_identifier().get_parameter_group_number()) &&
532 (channelIndex < CAN_PORT_MAXIMUM))
533 {
534 std::uint8_t claimedAddress = message.get_identifier().get_source_address();
535 auto targetControlFunction = controlFunctionTable[channelIndex][claimedAddress];
536 if ((nullptr != targetControlFunction) &&
537 (CANIdentifier::NULL_ADDRESS == targetControlFunction->get_address()))
538 {
539 // Someone is at that spot in the table, but their address was stolen
540 // Need to evict them from the table and move them to the inactive list
541 targetControlFunction->address = NULL_CAN_ADDRESS;
542 inactiveControlFunctions.push_back(targetControlFunction);
543 LOG_INFO("[NM]: %s CF '%016llx' is evicted from address '%d' on channel '%d', as their address is probably stolen.",
544 targetControlFunction->get_type_string().c_str(),
545 targetControlFunction->get_NAME().get_full_name(),
546 claimedAddress,
547 channelIndex);
548 targetControlFunction = nullptr;
549 }
550
551 if (targetControlFunction != nullptr)
552 {
553 targetControlFunction->claimedAddressSinceLastAddressClaimRequest = true;
554 }
555 else
556 {
557 // Look through all inactive CFs, maybe one of them has freshly claimed the address
558 for (auto currentControlFunction : inactiveControlFunctions)
559 {
560 if ((currentControlFunction->get_address() == claimedAddress) &&
561 (currentControlFunction->get_can_port() == channelIndex))
562 {
563 controlFunctionTable[channelIndex][claimedAddress] = currentControlFunction;
564 LOG_DEBUG("[NM]: %s CF '%016llx' is now active at address '%d' on channel '%d'.",
565 currentControlFunction->get_type_string().c_str(),
566 currentControlFunction->get_NAME().get_full_name(),
567 claimedAddress,
568 channelIndex);
570 break;
571 }
572 }
573 }
574 }
575 else if ((static_cast<std::uint32_t>(CANLibParameterGroupNumber::ParameterGroupNumberRequest) == message.get_identifier().get_parameter_group_number()) &&
576 (channelIndex < CAN_PORT_MAXIMUM))
577 {
578 auto requestedPGN = message.get_uint24_at(0);
579
580 if (static_cast<std::uint32_t>(CANLibParameterGroupNumber::AddressClaim) == requestedPGN)
581 {
582 lastAddressClaimRequestTimestamp_ms.at(channelIndex) = SystemTiming::get_timestamp_ms();
583
584 // Reset the claimedAddressSinceLastAddressClaimRequest flag for all control functions on the port
585 auto result = std::find_if(inactiveControlFunctions.begin(), inactiveControlFunctions.end(), [channelIndex](std::shared_ptr<ControlFunction> controlFunction) {
586 return (channelIndex == controlFunction->get_can_port());
587 });
588 if (result != inactiveControlFunctions.end())
589 {
590 (*result)->claimedAddressSinceLastAddressClaimRequest = true;
591 }
592 std::for_each(controlFunctionTable[channelIndex].begin(), controlFunctionTable[channelIndex].end(), [](std::shared_ptr<ControlFunction> controlFunction) {
593 if (nullptr != controlFunction)
594 {
595 controlFunction->claimedAddressSinceLastAddressClaimRequest = false;
596 }
597 });
598 }
599 }
600 }
601
603 {
604 for (const auto &currentInternalControlFunction : internalControlFunctions)
605 {
606 if (currentInternalControlFunction->update_address_claiming({}))
607 {
608 std::uint8_t channelIndex = currentInternalControlFunction->get_can_port();
609 std::uint8_t claimedAddress = currentInternalControlFunction->get_address();
610
611 // Check if the internal control function switched addresses, and therefore needs to be moved in the table
612 for (std::uint8_t address = 0; address < NULL_CAN_ADDRESS; address++)
613 {
614 if (controlFunctionTable[channelIndex][address] == currentInternalControlFunction)
615 {
616 controlFunctionTable[channelIndex][address] = nullptr;
617 break;
618 }
619 }
620
621 if (nullptr != controlFunctionTable[channelIndex][claimedAddress])
622 {
623 // Someone is at that spot in the table, but their address was stolen by an internal control function
624 // Need to evict them from the table
625 controlFunctionTable[channelIndex][claimedAddress]->address = NULL_CAN_ADDRESS;
626 controlFunctionTable[channelIndex][claimedAddress] = nullptr;
627 }
628
629 // ECU has claimed since the last update, add it to the table
630 controlFunctionTable[channelIndex][claimedAddress] = currentInternalControlFunction;
631 }
632 }
633 }
634
635 void CANNetworkManager::update_busload(std::uint8_t channelIndex, std::uint32_t numberOfBitsProcessed)
636 {
637 LOCK_GUARD(Mutex, busloadUpdateMutex);
638 currentBusloadBitAccumulator.at(channelIndex) += numberOfBitsProcessed;
639 }
640
642 {
643 LOCK_GUARD(Mutex, busloadUpdateMutex);
644 if (SystemTiming::time_expired_ms(busloadUpdateTimestamp_ms, BUSLOAD_UPDATE_FREQUENCY_MS))
645 {
646 for (std::size_t i = 0; i < busloadMessageBitsHistory.size(); i++)
647 {
649
651 {
652 busloadMessageBitsHistory.at(i).pop_front();
653 }
655 }
656 busloadUpdateTimestamp_ms = SystemTiming::get_timestamp_ms();
657 }
658 }
659
661 {
662 if ((static_cast<std::uint32_t>(CANLibParameterGroupNumber::AddressClaim) == CANIdentifier(rxFrame.identifier).get_parameter_group_number()) &&
663 (CAN_DATA_LENGTH == rxFrame.dataLength) &&
664 (rxFrame.channel < CAN_PORT_MAXIMUM))
665 {
666 std::uint64_t claimedNAME;
667 std::shared_ptr<ControlFunction> foundControlFunction = nullptr;
668 uint8_t claimedAddress = CANIdentifier(rxFrame.identifier).get_source_address();
669
670 claimedNAME = rxFrame.data[0];
671 claimedNAME |= (static_cast<std::uint64_t>(rxFrame.data[1]) << 8);
672 claimedNAME |= (static_cast<std::uint64_t>(rxFrame.data[2]) << 16);
673 claimedNAME |= (static_cast<std::uint64_t>(rxFrame.data[3]) << 24);
674 claimedNAME |= (static_cast<std::uint64_t>(rxFrame.data[4]) << 32);
675 claimedNAME |= (static_cast<std::uint64_t>(rxFrame.data[5]) << 40);
676 claimedNAME |= (static_cast<std::uint64_t>(rxFrame.data[6]) << 48);
677 claimedNAME |= (static_cast<std::uint64_t>(rxFrame.data[7]) << 56);
678
679 // Check if the claimed NAME is someone we already know about
680 auto activeResult = std::find_if(controlFunctionTable[rxFrame.channel].begin(),
681 controlFunctionTable[rxFrame.channel].end(),
682 [claimedNAME](const std::shared_ptr<ControlFunction> &cf) {
683 return (nullptr != cf) && (cf->controlFunctionNAME.get_full_name() == claimedNAME);
684 });
685 if (activeResult != controlFunctionTable[rxFrame.channel].end())
686 {
687 foundControlFunction = *activeResult;
688 }
689 else
690 {
691 auto inActiveResult = std::find_if(inactiveControlFunctions.begin(),
693 [claimedNAME, &rxFrame](const std::shared_ptr<ControlFunction> &cf) {
694 return (cf->controlFunctionNAME.get_full_name() == claimedNAME) && (cf->get_can_port() == rxFrame.channel);
695 });
696 if (inActiveResult != inactiveControlFunctions.end())
697 {
698 foundControlFunction = *inActiveResult;
699 }
700 }
701
702 if (nullptr == foundControlFunction)
703 {
704 // If we still haven't found it, it might be a partner. Check the list of partners.
705 for (const auto &partner : partneredControlFunctions)
706 {
707 if ((partner->get_can_port() == rxFrame.channel) &&
708 (partner->check_matches_name(NAME(claimedNAME))) &&
709 (0 == partner->get_NAME().get_full_name()))
710 {
711 partner->controlFunctionNAME = NAME(claimedNAME);
712 foundControlFunction = partner;
713 controlFunctionTable[rxFrame.channel][claimedAddress] = foundControlFunction;
714 break;
715 }
716 }
717 }
718
719 // Remove any CF that has the same address as the one claiming
720 std::for_each(controlFunctionTable[rxFrame.channel].begin(),
721 controlFunctionTable[rxFrame.channel].end(),
722 [&foundControlFunction, &claimedAddress](const std::shared_ptr<ControlFunction> &cf) {
723 if ((nullptr != cf) && (foundControlFunction != cf) && (cf->address == claimedAddress))
724 cf->address = CANIdentifier::NULL_ADDRESS;
725 });
726
727 std::for_each(inactiveControlFunctions.begin(),
729 [&rxFrame, &foundControlFunction, &claimedAddress](const std::shared_ptr<ControlFunction> &cf) {
730 if ((foundControlFunction != cf) && (cf->address == claimedAddress) && (cf->get_can_port() == rxFrame.channel))
731 cf->address = CANIdentifier::NULL_ADDRESS;
732 });
733
734 if (nullptr == foundControlFunction)
735 {
736 // New device, need to start keeping track of it
737 foundControlFunction = ControlFunction::create(NAME(claimedNAME), claimedAddress, rxFrame.channel);
738 controlFunctionTable[rxFrame.channel][foundControlFunction->get_address()] = foundControlFunction;
739 LOG_DEBUG("[NM]: A control function claimed address %u on channel %u", foundControlFunction->get_address(), foundControlFunction->get_can_port());
740 }
741 else if (foundControlFunction->address != claimedAddress)
742 {
743 if (foundControlFunction->get_address_valid())
744 {
745 controlFunctionTable[rxFrame.channel][claimedAddress] = foundControlFunction;
746 controlFunctionTable[rxFrame.channel][foundControlFunction->get_address()] = nullptr;
747 LOG_INFO("[NM]: The %s control function at address %d changed it's address to %d on channel %u.",
748 foundControlFunction->get_type_string().c_str(),
749 foundControlFunction->get_address(),
750 claimedAddress,
751 foundControlFunction->get_can_port());
752 }
753 else
754 {
755 LOG_INFO("[NM]: %s control function with name %016llx has claimed address %u on channel %u.",
756 foundControlFunction->get_type_string().c_str(),
757 foundControlFunction->get_NAME().get_full_name(),
758 claimedAddress,
759 foundControlFunction->get_can_port());
761 }
762 foundControlFunction->address = claimedAddress;
763 }
764 }
765 }
766
768 {
769 for (const auto &partner : partneredControlFunctions)
770 {
771 if (!partner->initialized)
772 {
773 // Remove any inactive CF that matches the partner's name
774 for (auto currentInactiveControlFunction = inactiveControlFunctions.begin(); currentInactiveControlFunction != inactiveControlFunctions.end(); currentInactiveControlFunction++)
775 {
776 if ((partner->check_matches_name((*currentInactiveControlFunction)->get_NAME())) &&
777 (partner->get_can_port() == (*currentInactiveControlFunction)->get_can_port()) &&
778 (ControlFunction::Type::External == (*currentInactiveControlFunction)->get_type()))
779 {
780 inactiveControlFunctions.erase(currentInactiveControlFunction);
781 break;
782 }
783 }
784
785 for (const auto &currentActiveControlFunction : controlFunctionTable[partner->get_can_port()])
786 {
787 if ((nullptr != currentActiveControlFunction) &&
788 (partner->check_matches_name(currentActiveControlFunction->get_NAME())) &&
789 (ControlFunction::Type::External == currentActiveControlFunction->get_type()))
790 {
791 // This CF matches the filter and is not an internal or already partnered CF
792 // Populate the partner's data
793 partner->address = currentActiveControlFunction->get_address();
794 partner->controlFunctionNAME = currentActiveControlFunction->get_NAME();
795 partner->initialized = true;
796 controlFunctionTable[partner->get_can_port()][partner->address] = std::shared_ptr<ControlFunction>(partner);
798
799 LOG_INFO("[NM]: A partner with name %016llx has claimed address %u on channel %u.",
800 partner->get_NAME().get_full_name(),
801 partner->get_address(),
802 partner->get_can_port());
803 break;
804 }
805 }
806 partner->initialized = true;
807 }
808 }
809 }
810
812 std::uint8_t sourceAddress,
813 std::uint8_t destAddress,
814 std::uint32_t parameterGroupNumber,
815 std::uint8_t priority,
816 const void *data,
817 std::uint32_t size) const
818 {
819 CANMessageFrame txFrame;
821
822 if ((NULL_CAN_ADDRESS != destAddress) && (priority <= static_cast<std::uint8_t>(CANIdentifier::CANPriority::PriorityLowest7)) && (size <= CAN_DATA_LENGTH) && (nullptr != data))
823 {
824 std::uint32_t identifier = 0;
825
826 // Manually encode an identifier
827 identifier |= ((priority & 0x07) << 26);
828 identifier |= (sourceAddress & 0xFF);
829
830 if (BROADCAST_CAN_ADDRESS == destAddress)
831 {
832 if ((parameterGroupNumber & 0xF000) >= 0xF000)
833 {
834 identifier |= ((parameterGroupNumber & 0x3FFFF) << 8);
835 }
836 else
837 {
838 identifier |= (destAddress << 8);
839 identifier |= ((parameterGroupNumber & 0x3FF00) << 8);
840 }
841 }
842 else
843 {
844 if ((parameterGroupNumber & 0xF000) < 0xF000)
845 {
846 identifier |= (destAddress << 8);
847 identifier |= ((parameterGroupNumber & 0x3FF00) << 8);
848 }
849 else
850 {
851 LOG_WARNING("[NM]: Cannot send a message with PGN " +
852 isobus::to_string(static_cast<int>(parameterGroupNumber)) +
853 " as a destination specific message. " +
854 "Try resending it using nullptr as your destination control function.");
855 identifier = DEFAULT_IDENTIFIER;
856 }
857 }
858
859 if (DEFAULT_IDENTIFIER != identifier)
860 {
861 txFrame.channel = portIndex;
862 memcpy(reinterpret_cast<void *>(txFrame.data), data, size);
863 txFrame.dataLength = size;
864 txFrame.isExtendedFrame = true;
865 txFrame.identifier = identifier & 0x1FFFFFFF;
866 }
867 }
868 return txFrame;
869 }
870
871 std::shared_ptr<ControlFunction> CANNetworkManager::get_control_function(std::uint8_t channelIndex, std::uint8_t address) const
872 {
873 std::shared_ptr<ControlFunction> retVal = nullptr;
874
875 if ((address < NULL_CAN_ADDRESS) && (channelIndex < CAN_PORT_MAXIMUM))
876 {
877 retVal = controlFunctionTable[channelIndex][address];
878 }
879 return retVal;
880 }
881
883 {
884 LOCK_GUARD(Mutex, receivedMessageQueueMutex);
885 if (!receivedMessageQueue.empty())
886 {
887 CANMessage retVal = std::move(receivedMessageQueue.front());
889 return retVal;
890 }
892 }
893
895 {
896 LOCK_GUARD(Mutex, transmittedMessageQueueMutex);
897 if (!transmittedMessageQueue.empty())
898 {
899 CANMessage retVal = std::move(transmittedMessageQueue.front());
901 return retVal;
902 }
904 }
905
906 void CANNetworkManager::on_control_function_created(std::shared_ptr<ControlFunction> controlFunction)
907 {
908 if (ControlFunction::Type::Internal == controlFunction->get_type())
909 {
910 internalControlFunctions.push_back(std::static_pointer_cast<InternalControlFunction>(controlFunction));
911 }
912 else if (ControlFunction::Type::Partnered == controlFunction->get_type())
913 {
914 partneredControlFunctions.push_back(std::static_pointer_cast<PartneredControlFunction>(controlFunction));
915 }
916 }
917
919 {
920 LOCK_GUARD(Mutex, anyControlFunctionCallbacksMutex);
921 for (const auto &currentCallback : anyControlFunctionParameterGroupNumberCallbacks)
922 {
923 if ((currentCallback.get_parameter_group_number() == currentMessage.get_identifier().get_parameter_group_number()) &&
924 ((nullptr == currentMessage.get_destination_control_function()) ||
925 (ControlFunction::Type::Internal == currentMessage.get_destination_control_function()->get_type())))
926 {
927 currentCallback.get_callback()(currentMessage, currentCallback.get_parent());
928 }
929 }
930 }
931
933 {
934 auto sourceAddress = currentMessage.get_identifier().get_source_address();
935
936 if ((BROADCAST_CAN_ADDRESS != sourceAddress) &&
937 (NULL_CAN_ADDRESS != sourceAddress))
938 {
939 for (auto &internalCF : internalControlFunctions)
940 {
941 if ((nullptr != internalCF) &&
942 (internalCF->get_address() == sourceAddress) &&
943 (currentMessage.get_can_port_index() == internalCF->get_can_port()))
944 {
945 internalCF->on_address_violation({});
946 addressViolationEventDispatcher.call(internalCF);
947 }
948 }
949 }
950 }
951
952 void CANNetworkManager::process_control_function_state_change_callback(std::shared_ptr<ControlFunction> controlFunction, ControlFunctionState state)
953 {
954 LOCK_GUARD(Mutex, controlFunctionStatusCallbacksMutex);
955 for (const auto &callback : controlFunctionStateCallbacks)
956 {
957 callback(controlFunction, state);
958 }
959 }
960
962 {
963 LOCK_GUARD(Mutex, protocolPGNCallbacksMutex);
964 for (const auto &currentCallback : protocolPGNCallbacks)
965 {
966 if (currentCallback.get_parameter_group_number() == currentMessage.get_identifier().get_parameter_group_number())
967 {
968 currentCallback.get_callback()(currentMessage, currentCallback.get_parent());
969 }
970 }
971 }
972
974 {
975 std::shared_ptr<ControlFunction> messageDestination = message.get_destination_control_function();
976 if ((nullptr == messageDestination) &&
977 ((nullptr != message.get_source_control_function()) ||
978 ((static_cast<std::uint32_t>(CANLibParameterGroupNumber::ParameterGroupNumberRequest) == message.get_identifier().get_parameter_group_number()) &&
980 {
981 // Message destined to global
982 for (std::size_t i = 0; i < get_number_global_parameter_group_number_callbacks(); i++)
983 {
984 if ((message.get_identifier().get_parameter_group_number() == get_global_parameter_group_number_callback(i).get_parameter_group_number()) &&
986 {
987 // We have a callback that matches this PGN
989 }
990 }
991 }
992 else if ((messageDestination != nullptr) && (messageDestination->get_type() == ControlFunction::Type::Internal))
993 {
994 // Message is destined to us
995 for (const auto &partner : partneredControlFunctions)
996 {
997 if ((nullptr != partner) &&
998 (partner->get_can_port() == message.get_can_port_index()))
999 {
1000 // Message matches CAN port for a partnered control function
1001 for (std::size_t k = 0; k < partner->get_number_parameter_group_number_callbacks(); k++)
1002 {
1003 if ((message.get_identifier().get_parameter_group_number() == partner->get_parameter_group_number_callback(k).get_parameter_group_number()) &&
1004 (nullptr != partner->get_parameter_group_number_callback(k).get_callback()) &&
1005 ((nullptr == partner->get_parameter_group_number_callback(k).get_internal_control_function()) ||
1006 (partner->get_parameter_group_number_callback(k).get_internal_control_function()->get_address() == message.get_identifier().get_destination_address())))
1007 {
1008 // We have a callback matching this message
1009 partner->get_parameter_group_number_callback(k).get_callback()(message, partner->get_parameter_group_number_callback(k).get_parent());
1010 }
1011 }
1012 }
1013 }
1014 }
1015 }
1016
1018 {
1019 constexpr std::uint8_t COMMANDED_ADDRESS_LENGTH = 9;
1020
1021 if ((nullptr == message.get_destination_control_function()) &&
1022 (static_cast<std::uint32_t>(CANLibParameterGroupNumber::CommandedAddress) == message.get_identifier().get_parameter_group_number()) &&
1023 (COMMANDED_ADDRESS_LENGTH == message.get_data_length()))
1024 {
1025 std::uint64_t targetNAME = message.get_uint64_at(0);
1026
1027 for (const auto &currentICF : internalControlFunctions)
1028 {
1029 if ((message.get_can_port_index() == currentICF->get_can_port()) &&
1030 (currentICF->get_NAME().get_full_name() == targetNAME))
1031 {
1032 currentICF->process_commanded_address(message.get_uint8_at(8), {});
1033 }
1034 }
1035 }
1036 }
1037
1039 {
1040 // We may miss a message without locking the mutex when checking if empty, but that's okay. It will be picked up on the next iteration
1041 while (!receivedMessageQueue.empty())
1042 {
1044
1045 update_address_table(currentMessage);
1047
1048 // Update Special Callbacks, like protocols and non-cf specific ones
1049 transportProtocols.at(currentMessage.get_can_port_index())->process_message(currentMessage);
1050 extendedTransportProtocols.at(currentMessage.get_can_port_index())->process_message(currentMessage);
1051 fastPacketProtocol.at(currentMessage.get_can_port_index())->process_message(currentMessage);
1052 heartBeatInterfaces.at(currentMessage.get_can_port_index())->process_rx_message(currentMessage);
1053 process_protocol_pgn_callbacks(currentMessage);
1055
1056 // Update Others
1058 }
1059 }
1060
1062 {
1063 // We may miss a message without locking the mutex when checking if empty, but that's okay. It will be picked up on the next iteration
1064 while (!transmittedMessageQueue.empty())
1065 {
1067
1068 // Update listen-only callbacks
1069 messageTransmittedEventDispatcher.call(currentMessage);
1070 }
1071 }
1072
1074 {
1075 for (std::uint_fast8_t channelIndex = 0; channelIndex < CAN_PORT_MAXIMUM; channelIndex++)
1076 {
1077 constexpr std::uint32_t MAX_ADDRESS_CLAIM_RESOLUTION_TIME = 755; // This is 250ms + RTxD + 250ms
1078 if ((0 != lastAddressClaimRequestTimestamp_ms.at(channelIndex)) &&
1079 (SystemTiming::time_expired_ms(lastAddressClaimRequestTimestamp_ms.at(channelIndex), MAX_ADDRESS_CLAIM_RESOLUTION_TIME)))
1080 {
1081 for (std::uint_fast8_t i = 0; i < NULL_CAN_ADDRESS; i++)
1082 {
1083 auto controlFunction = controlFunctionTable[channelIndex][i];
1084 if ((nullptr != controlFunction) &&
1085 (!controlFunction->claimedAddressSinceLastAddressClaimRequest) &&
1086 (ControlFunction::Type::Internal != controlFunction->get_type()))
1087 {
1088 inactiveControlFunctions.push_back(controlFunction);
1089 LOG_INFO("[NM]: Control function with address %u and NAME %016llx is now offline on channel %u.", controlFunction->get_address(), controlFunction->get_NAME(), channelIndex);
1090 controlFunctionTable[channelIndex][i] = nullptr;
1091 controlFunction->address = NULL_CAN_ADDRESS;
1093 }
1094 else if ((nullptr != controlFunction) &&
1095 (!controlFunction->claimedAddressSinceLastAddressClaimRequest))
1096 {
1098 }
1099 }
1100 lastAddressClaimRequestTimestamp_ms.at(channelIndex) = 0;
1101 }
1102 }
1103 }
1104
1105 bool CANNetworkManager::send_can_message_raw(std::uint32_t portIndex, std::uint8_t sourceAddress, std::uint8_t destAddress, std::uint32_t parameterGroupNumber, std::uint8_t priority, const void *data, std::uint32_t size) const
1106 {
1107 CANMessageFrame tempFrame = construct_frame(portIndex, sourceAddress, destAddress, parameterGroupNumber, priority, data, size);
1108 bool retVal = false;
1109
1110 if ((DEFAULT_IDENTIFIER != tempFrame.identifier) &&
1111 (portIndex < CAN_PORT_MAXIMUM))
1112 {
1113 retVal = send_can_message_frame_to_hardware(tempFrame);
1114 }
1115 return retVal;
1116 }
1117
1124
1125} // namespace isobus
General constants used throughout this library.
Defines some PGNs that are used in the library or are very common.
An abstraction between this CAN stack and any hardware layer.
An abstraction of a CAN message, could be > 8 data bytes.
The main class that manages the ISOBUS stack including: callbacks, Name to Address management,...
A class that describes a control function on the bus that the stack should communicate with....
A class that acts as a logging sink. The intent is that someone could make their own derived class of...
This is a way to protect functions on public interfaces from being accessed by objects that shouldn't...
A utility class that allows easy interpretation of a 32 bit CAN identifier.
CANPriority
Defines all the CAN frame priorities that can be encoded in a frame ID.
@ PriorityLowest7
The lowest priority.
static constexpr std::uint8_t NULL_ADDRESS
The NULL CAN address as defined by ISO11783.
std::uint8_t get_source_address() const
Returns the source address of the frame encoded in the identifier.
std::uint32_t get_parameter_group_number() const
Returns the PGN encoded in the identifier.
std::uint8_t get_destination_address() const
Returns the destination address of the frame encoded in the identifier.
A class that represents data of a CAN message by using a callback function.
A class that represents data of a CAN message by holding a view of an array of bytes....
A CAN frame for interfacing with a hardware layer, like socket CAN or other interface.
std::uint8_t dataLength
The length of the data used in the frame.
std::uint32_t identifier
The 32 bit identifier of the frame.
bool isExtendedFrame
Denotes if the frame is extended format.
std::uint32_t get_number_bits_in_message() const
std::uint8_t data[8]
The data payload of the frame.
std::uint8_t channel
The CAN channel index associated with the frame.
A class that represents a generic CAN message of arbitrary length.
static const std::uint32_t ABSOLUTE_MAX_MESSAGE_LENGTH
ISO11783-3 defines this: The maximum number of packets that can be sent in a single connection with e...
@ Receive
Message is being received.
@ Transmit
Message is to be transmitted from the stack.
static CANMessage create_invalid_message()
Factory method to construct an intentionally invalid CANMessage.
std::uint8_t get_can_port_index() const
Returns the CAN channel index associated with the message.
std::uint32_t get_data_length() const
Returns the length of the data in the CAN message.
std::uint32_t get_uint24_at(const std::uint32_t index, const ByteFormat format=ByteFormat::LittleEndian) const
Get a right-aligned 24-bit integer from the buffer (returned as a uint32_t) at a specific index....
std::shared_ptr< ControlFunction > get_source_control_function() const
Gets the source control function that the message is from.
std::uint8_t get_uint8_at(const std::uint32_t index) const
Get a 8-bit unsigned byte from the buffer at a specific index. A 8-bit unsigned byte can hold a value...
CANIdentifier get_identifier() const
Returns the identifier of the message.
std::shared_ptr< ControlFunction > get_destination_control_function() const
Gets the destination control function that the message is to.
std::uint64_t get_uint64_at(const std::uint32_t index, const ByteFormat format=ByteFormat::LittleEndian) const
Get a 64-bit unsigned integer from the buffer at a specific index. A 64-bit unsigned integer can hold...
A class that defines stack-wide configuration data. You can set the values in there to suit your spec...
std::unique_ptr< FastPacketProtocol > & get_fast_packet_protocol(std::uint8_t canPortIndex)
Returns the class instance of the NMEA2k fast packet protocol. Use this to register for FP multipacke...
friend class TransportProtocolManager
Allows the network manager to work closely with the transport protocol manager object.
std::list< std::shared_ptr< ControlFunction > > inactiveControlFunctions
A list of the control function that currently don't have a valid address.
std::queue< CANMessage > transmittedMessageQueue
A queue of transmitted messages to process (already sent, so changes to the message won't affect the ...
EventDispatcher< std::shared_ptr< InternalControlFunction > > & get_address_violation_event_dispatcher()
Returns the network manager's event dispatcher for notifying consumers whenever an address violation ...
void update_address_table(const CANMessage &message)
Updates the internal address table based on a received CAN message.
static constexpr std::uint32_t BUSLOAD_SAMPLE_WINDOW_MS
Using a 1s window to average the bus load, otherwise it's very erratic.
void on_control_function_destroyed(std::shared_ptr< ControlFunction > controlFunction, CANLibBadge< ControlFunction >)
Informs the network manager that a control function object has been destroyed, so that it can be purg...
Mutex anyControlFunctionCallbacksMutex
Mutex to protect the "any CF" callbacks.
void add_global_parameter_group_number_callback(std::uint32_t parameterGroupNumber, CANLibCallback callback, void *parent)
This is how you register a callback for any PGN destined for the global address (0xFF)
EventDispatcher< CANMessage > messageTransmittedEventDispatcher
An event dispatcher for notifying consumers about transmitted messages by our application.
void remove_control_function_status_change_callback(ControlFunctionStateCallback callback)
Used to remove callbacks added with add_control_function_status_change_callback.
void update_new_partners()
Checks if new partners have been created and matches them to existing control functions.
bool send_can_message_raw(std::uint32_t portIndex, std::uint8_t sourceAddress, std::uint8_t destAddress, std::uint32_t parameterGroupNumber, std::uint8_t priority, const void *data, std::uint32_t size, CANLibBadge< AddressClaimStateMachine >) const
Sends a CAN message using raw addresses. Used only by the stack.
std::array< std::deque< std::uint32_t >, CAN_PORT_MAXIMUM > busloadMessageBitsHistory
Stores the approximate number of bits processed on each channel over multiple previous time windows.
std::shared_ptr< ControlFunction > get_control_function(std::uint8_t channelIndex, std::uint8_t address, CANLibBadge< AddressClaimStateMachine >) const
Called only by the stack, returns a control function based on certain port and address.
CANNetworkConfiguration & get_configuration()
Returns the configuration of this network manager.
void process_tx_messages()
Processes the internal transmitted message queue.
std::array< std::uint32_t, CAN_PORT_MAXIMUM > lastAddressClaimRequestTimestamp_ms
Stores timestamps for when the last request for the address claim PGN was received....
void process_receive_can_message_frame(const CANMessageFrame &rxFrame)
Used to tell the network manager when frames are received on the bus.
void process_protocol_pgn_callbacks(const CANMessage &currentMessage)
Processes a can message for callbacks added with add_protocol_parameter_group_number_callback.
void process_can_message_for_address_violations(const CANMessage &currentMessage)
Validates that a CAN message has not caused an address violation. If a violation is found,...
std::vector< ParameterGroupNumberCallbackData > anyControlFunctionParameterGroupNumberCallbacks
A list of all global PGN callbacks.
std::array< std::array< std::shared_ptr< ControlFunction >, NULL_CAN_ADDRESS >, CAN_PORT_MAXIMUM > controlFunctionTable
Table to maintain address to NAME mappings.
static CANNetworkManager CANNetwork
Static singleton of the one network manager. Use this to access stack functionality.
void on_control_function_created(std::shared_ptr< ControlFunction > controlFunction, CANLibBadge< ControlFunction >)
Informs the network manager that a control function object has been created, so that it can be added ...
EventDispatcher< CANMessage > & get_transmitted_message_event_dispatcher()
Returns the network manager's event dispatcher for notifying consumers whenever a message is transmit...
std::vector< ParameterGroupNumberCallbackData > globalParameterGroupNumberCallbacks
A list of all global PGN callbacks.
bool remove_protocol_parameter_group_number_callback(std::uint32_t parameterGroupNumber, CANLibCallback callback, void *parentPointer)
Removes a PGN callback for a protocol class.
EventDispatcher< std::shared_ptr< InternalControlFunction > > addressViolationEventDispatcher
An event dispatcher for notifying consumers about address violations.
CANNetworkManager()
Constructor for the network manager. Sets default values for members.
std::list< std::shared_ptr< InternalControlFunction > > internalControlFunctions
A list of the internal control functions.
void process_can_message_for_commanded_address(const CANMessage &message)
Processes a CAN message to see if it's a commanded address message, and if it is, it attempts to set ...
bool add_protocol_parameter_group_number_callback(std::uint32_t parameterGroupNumber, CANLibCallback callback, void *parentPointer)
Adds a PGN callback for a protocol class.
std::list< std::shared_ptr< PartneredControlFunction > > partneredControlFunctions
A list of the partnered control functions.
std::list< ControlFunctionStateCallback > controlFunctionStateCallbacks
List of all control function state callbacks.
void update_internal_cfs()
Updates the internal address table based on updates to internal cfs addresses.
std::shared_ptr< InternalControlFunction > get_internal_control_function(std::shared_ptr< ControlFunction > controlFunction)
Returns an internal control function if the passed-in control function is an internal type.
std::array< std::unique_ptr< ExtendedTransportProtocolManager >, CAN_PORT_MAXIMUM > extendedTransportProtocols
One instance of the extended transport protocol manager for each channel.
void add_any_control_function_parameter_group_number_callback(std::uint32_t parameterGroupNumber, CANLibCallback callback, void *parent)
Registers a callback for ANY control function sending the associated PGN.
std::list< ParameterGroupNumberCallbackData > protocolPGNCallbacks
A list of PGN callback registered by CAN protocols.
void add_control_function_status_change_callback(ControlFunctionStateCallback callback)
Use this to get a callback when a control function goes online or offline. This could be useful if yo...
void initialize()
Initializer function for the network manager.
const std::list< std::shared_ptr< PartneredControlFunction > > & get_partnered_control_functions() const
Gets all the partnered control functions that are currently registered in the network manager.
void process_can_message_for_global_and_partner_callbacks(const CANMessage &message)
Matches a CAN message to any matching PGN callback, and calls that callback.
void remove_global_parameter_group_number_callback(std::uint32_t parameterGroupNumber, CANLibCallback callback, void *parent)
This is how you remove a callback for any PGN destined for the global address (0xFF)
static constexpr std::uint32_t BUSLOAD_UPDATE_FREQUENCY_MS
Bus load bit accumulation happens over a 100ms window.
CANMessage get_next_can_message_from_rx_queue()
Get the next CAN message from the received message queue, and remove it from the queue.
CANNetworkConfiguration configuration
The configuration for this network manager.
void process_any_control_function_pgn_callbacks(const CANMessage &currentMessage)
Processes a can message for callbacks added with add_any_control_function_parameter_group_number_call...
const std::list< std::shared_ptr< InternalControlFunction > > & get_internal_control_functions() const
Gets all the internal control functions that are currently registered in the network manager.
bool initialized
True if the network manager has been initialized by the update function.
void update_busload(std::uint8_t channelIndex, std::uint32_t numberOfBitsProcessed)
Processes a CAN message's contribution to the current busload.
std::list< std::shared_ptr< ControlFunction > > get_control_functions(bool includingOffline) const
Gets all the control functions that are known to the network manager.
std::list< std::shared_ptr< TransportProtocolSessionBase > > get_active_transport_protocol_sessions(std::uint8_t canPortIndex) const
Gets all the active transport protocol sessions that are currently active.
void process_rx_messages()
Processes the internal received message queue.
void prune_inactive_control_functions()
Checks to see if any control function didn't claim during a round of address claiming and removes it ...
void process_transmitted_can_message_frame(const CANMessageFrame &txFrame)
Used to tell the network manager when frames are emitted on the bus.
bool send_can_message(std::uint32_t parameterGroupNumber, const std::uint8_t *dataBuffer, std::uint32_t dataLength, std::shared_ptr< InternalControlFunction > sourceControlFunction, std::shared_ptr< ControlFunction > destinationControlFunction=nullptr, CANIdentifier::CANPriority priority=CANIdentifier::CANPriority::PriorityDefault6, TransmitCompleteCallback txCompleteCallback=nullptr, void *parentPointer=nullptr, DataChunkCallback frameChunkCallback=nullptr)
This is the main way to send a CAN message of any length.
void process_control_function_state_change_callback(std::shared_ptr< ControlFunction > controlFunction, ControlFunctionState state)
Checks the control function state callback list to see if we need to call a control function state ca...
std::array< std::unique_ptr< TransportProtocolManager >, CAN_PORT_MAXIMUM > transportProtocols
One instance of the transport protocol manager for each channel.
std::queue< CANMessage > receivedMessageQueue
A queue of received messages to process.
void protocol_message_callback(const CANMessage &message)
Processes completed protocol messages. Causes PGN callbacks to trigger.
CANMessageFrame construct_frame(std::uint32_t portIndex, std::uint8_t sourceAddress, std::uint8_t destAddress, std::uint32_t parameterGroupNumber, std::uint8_t priority, const void *data, std::uint32_t size) const
Builds a CAN frame from a frame's discrete components.
HeartbeatInterface & get_heartbeat_interface(std::uint8_t canPortIndex)
Returns an interface which can be used to manage ISO11783-7 heartbeat messages.
friend class ExtendedTransportProtocolManager
Allows the network manager to access the ETP manager.
void update()
The main update function for the network manager. Updates all protocols.
Mutex controlFunctionStatusCallbacksMutex
A Mutex that protects access to the control function status callback list.
ParameterGroupNumberCallbackData get_global_parameter_group_number_callback(std::uint32_t index) const
Gets a PGN callback for the global address by index.
void update_busload_history()
Updates the stored bit accumulators for calculating the bus load over a multiple sample windows.
friend class FastPacketProtocol
Allows the FP protocol to access the network manager protected functions.
std::uint32_t busloadUpdateTimestamp_ms
Tracks a time window for determining approximate busload.
CANMessage get_next_can_message_from_tx_queue()
Get the next CAN message from the received message queue, and remove it from the queue.
std::array< std::uint32_t, CAN_PORT_MAXIMUM > currentBusloadBitAccumulator
Accumulates the approximate number of bits processed on each channel during the current time window.
std::array< std::unique_ptr< HeartbeatInterface >, CAN_PORT_MAXIMUM > heartBeatInterfaces
Manages ISOBUS heartbeat requests, one per channel.
std::uint32_t updateTimestamp_ms
Keeps track of the last time the CAN stack was update in milliseconds.
void remove_any_control_function_parameter_group_number_callback(std::uint32_t parameterGroupNumber, CANLibCallback callback, void *parent)
This is how you remove a callback added with add_any_control_function_parameter_group_number_callback...
std::size_t get_number_global_parameter_group_number_callbacks() const
Returns the number of global PGN callbacks that have been registered with the network manager.
Mutex protocolPGNCallbacksMutex
A mutex for PGN callback thread safety.
Mutex busloadUpdateMutex
A mutex that protects the busload metrics since we calculate it on our own thread.
Mutex transmittedMessageQueueMutex
A mutex for protecting the transmitted message queue.
void update_control_functions(const CANMessageFrame &rxFrame)
Creates new control function classes based on the frames coming in from the bus.
Mutex receivedMessageQueueMutex
A mutex for receive messages thread safety.
float get_estimated_busload(std::uint8_t canChannel)
Returns an estimated busload between 0.0f and 100.0f.
std::array< std::unique_ptr< FastPacketProtocol >, CAN_PORT_MAXIMUM > fastPacketProtocol
One instance of the fast packet protocol for each channel.
static Mutex controlFunctionProcessingMutex
Protects the control function tables.
static std::shared_ptr< ControlFunction > create(NAME NAMEValue, std::uint8_t addressValue, std::uint8_t CANPort)
The factory function to construct a control function.
@ Internal
The control function is part of our stack and can address claim.
@ External
The control function is some other device on the bus.
@ Partnered
An external control function that you explicitly want to talk to.
This class is used to send and receive ISOBUS heartbeats.
A class that represents an ISO11783 control function NAME from an address claim.
Definition can_NAME.hpp:24
A storage class to hold data about callbacks for a specific PGN.
void * get_parent() const
Returns the parent pointer for this data object.
CANLibCallback get_callback() const
Returns the callback pointer for this data object.
This namespace encompasses all of the ISO11783 stack's functionality to reduce global namespace pollu...
DataSpan< const std::uint8_t > CANDataSpan
A read-only span of data for a CAN message.
constexpr std::uint8_t NULL_CAN_ADDRESS
The NULL CAN address defined by J1939 and ISO11783.
void(*)(std::uint32_t parameterGroupNumber, std::uint32_t dataLength, std::shared_ptr< InternalControlFunction > sourceControlFunction, std::shared_ptr< ControlFunction > destinationControlFunction, bool successful, void *parentPointer) TransmitCompleteCallback
A callback for when a transmit is completed by the stack.
constexpr std::uint8_t CAN_DATA_LENGTH
The length of a classical CAN frame.
void(*)(const CANMessage &message, void *parentPointer) CANLibCallback
A callback for control functions to get CAN messages.
bool send_can_message_frame_to_hardware(const CANMessageFrame &frame)
The sending abstraction layer between the hardware and the stack.
constexpr std::uint8_t BROADCAST_CAN_ADDRESS
The global/broadcast CAN address.
void on_transmit_can_message_frame_from_hardware(const CANMessageFrame &txFrame)
Informs the network manager whenever messages are emitted on the bus.
void periodic_update_from_hardware()
The periodic update abstraction layer between the hardware and the stack.
bool(*)(std::uint32_t callbackIndex, std::uint32_t bytesOffset, std::uint32_t numberOfBytesNeeded, std::uint8_t *chunkBuffer, void *parentPointer) DataChunkCallback
A callback to get chunks of data for transfer by a protocol.
constexpr std::uint32_t DEFAULT_IDENTIFIER
An invalid identifier used as a default.
void(*)(std::shared_ptr< ControlFunction > controlFunction, ControlFunctionState state) ControlFunctionStateCallback
A callback that can inform you when a control function changes state between online and offline.
constexpr std::uint32_t CAN_PORT_MAXIMUM
An arbitrary limit for memory consumption.
void receive_can_message_frame_from_hardware(const CANMessageFrame &frame)
The receiving abstraction layer between the hardware and the stack.
ControlFunctionState
Enumerates the "online" states of a control function.
@ Online
The CF's address claim state is valid.
@ Offline
The CF's address claim state is not valid.