AgIsoStack++
A control-function-focused implementation of the major ISOBUS and J1939 protocols
Loading...
Searching...
No Matches
isobus_diagnostic_protocol.cpp
Go to the documentation of this file.
1//================================================================================================
34//================================================================================================
35
37
42#include "isobus/utility/system_timing.hpp"
43
44#include <algorithm>
45
46namespace isobus
47{
49 suspectParameterNumber(spn),
50 failureModeIdentifier(failureMode),
51 lampState(lamp)
52 {
53 }
54
56 {
57 return ((suspectParameterNumber == obj.suspectParameterNumber) &&
58 (failureModeIdentifier == obj.failureModeIdentifier) &&
59 (lampState == obj.lampState));
60 }
61
63 {
64 return occurrenceCount;
65 }
66
68 {
69 return suspectParameterNumber;
70 }
71
76
77 DiagnosticProtocol::DiagnosticProtocol(std::shared_ptr<InternalControlFunction> internalControlFunction, NetworkType networkType) :
79 myControlFunction(internalControlFunction),
81 txFlags(static_cast<std::uint32_t>(TransmitFlags::NumberOfFlags), process_flags, this)
82 {
84
85 for (auto &ecuIDField : ecuIdentificationFields)
86 {
87 ecuIDField = "*";
88 }
89 }
90
95
97 {
98 bool retVal = false;
99 if (!initialized)
100 {
101 initialized = true;
102 CANNetworkManager::CANNetwork.add_protocol_parameter_group_number_callback(static_cast<std::uint32_t>(CANLibParameterGroupNumber::DiagnosticMessage22), process_message, this);
103 CANNetworkManager::CANNetwork.add_protocol_parameter_group_number_callback(static_cast<std::uint32_t>(CANLibParameterGroupNumber::DiagnosticMessage13), process_message, this);
104 CANNetworkManager::CANNetwork.add_global_parameter_group_number_callback(static_cast<std::uint32_t>(CANLibParameterGroupNumber::DiagnosticMessage13), process_message, this);
105 addressViolationEventHandle = CANNetworkManager::CANNetwork.get_address_violation_event_dispatcher().add_listener([this](std::shared_ptr<InternalControlFunction> affectedCF) { this->on_address_violation(affectedCF); });
106
107 if (auto requestProtocol = myControlFunction->get_pgn_request_protocol().lock())
108 {
109 requestProtocol->register_pgn_request_callback(static_cast<std::uint32_t>(CANLibParameterGroupNumber::DiagnosticMessage1), process_parameter_group_number_request, this);
110 requestProtocol->register_pgn_request_callback(static_cast<std::uint32_t>(CANLibParameterGroupNumber::DiagnosticMessage2), process_parameter_group_number_request, this);
111 requestProtocol->register_pgn_request_callback(static_cast<std::uint32_t>(CANLibParameterGroupNumber::DiagnosticMessage3), process_parameter_group_number_request, this);
112 requestProtocol->register_pgn_request_callback(static_cast<std::uint32_t>(CANLibParameterGroupNumber::DiagnosticMessage11), process_parameter_group_number_request, this);
113 requestProtocol->register_pgn_request_callback(static_cast<std::uint32_t>(CANLibParameterGroupNumber::ProductIdentification), process_parameter_group_number_request, this);
114 requestProtocol->register_pgn_request_callback(static_cast<std::uint32_t>(CANLibParameterGroupNumber::DiagnosticProtocolIdentification), process_parameter_group_number_request, this);
115 requestProtocol->register_pgn_request_callback(static_cast<std::uint32_t>(CANLibParameterGroupNumber::SoftwareIdentification), process_parameter_group_number_request, this);
116 requestProtocol->register_pgn_request_callback(static_cast<std::uint32_t>(CANLibParameterGroupNumber::ECUIdentificationInformation), process_parameter_group_number_request, this);
117 }
118 retVal = true;
119 }
120 else
121 {
122 LOG_WARNING("[DP] DiagnosticProtocol's initialize() called when already initialized");
123 }
124 return retVal;
125 }
126
128 {
129 return initialized;
130 }
131
133 {
134 if (initialized)
135 {
136 initialized = false;
137
138 if (auto requestProtocol = myControlFunction->get_pgn_request_protocol().lock())
139 {
140 requestProtocol->remove_pgn_request_callback(static_cast<std::uint32_t>(CANLibParameterGroupNumber::DiagnosticMessage1), process_parameter_group_number_request, this);
141 requestProtocol->remove_pgn_request_callback(static_cast<std::uint32_t>(CANLibParameterGroupNumber::DiagnosticMessage2), process_parameter_group_number_request, this);
142 requestProtocol->remove_pgn_request_callback(static_cast<std::uint32_t>(CANLibParameterGroupNumber::DiagnosticMessage3), process_parameter_group_number_request, this);
143 requestProtocol->remove_pgn_request_callback(static_cast<std::uint32_t>(CANLibParameterGroupNumber::DiagnosticMessage11), process_parameter_group_number_request, this);
144 requestProtocol->remove_pgn_request_callback(static_cast<std::uint32_t>(CANLibParameterGroupNumber::ProductIdentification), process_parameter_group_number_request, this);
145 requestProtocol->remove_pgn_request_callback(static_cast<std::uint32_t>(CANLibParameterGroupNumber::DiagnosticProtocolIdentification), process_parameter_group_number_request, this);
146 requestProtocol->remove_pgn_request_callback(static_cast<std::uint32_t>(CANLibParameterGroupNumber::SoftwareIdentification), process_parameter_group_number_request, this);
147 requestProtocol->remove_pgn_request_callback(static_cast<std::uint32_t>(CANLibParameterGroupNumber::ECUIdentificationInformation), process_parameter_group_number_request, this);
148 }
149 CANNetworkManager::CANNetwork.remove_protocol_parameter_group_number_callback(static_cast<std::uint32_t>(CANLibParameterGroupNumber::DiagnosticMessage22), process_message, this);
150 CANNetworkManager::CANNetwork.remove_protocol_parameter_group_number_callback(static_cast<std::uint32_t>(CANLibParameterGroupNumber::DiagnosticMessage13), process_message, this);
151 CANNetworkManager::CANNetwork.remove_global_parameter_group_number_callback(static_cast<std::uint32_t>(CANLibParameterGroupNumber::DiagnosticMessage13), process_message, this);
153 }
154 }
155
157 {
159 {
160 if (SystemTiming::time_expired_ms(lastDM13ReceivedTimestamp, customDM13SuspensionTime))
161 {
162 broadcastState = true;
164 }
165 }
166 else if (SystemTiming::time_expired_ms(lastDM13ReceivedTimestamp, DM13_TIMEOUT_MS))
167 {
168 broadcastState = true;
169 }
170
171 if (broadcastState)
172 {
173 if (j1939Mode)
174 {
175 if (SystemTiming::time_expired_ms(lastDM1SentTimestamp, DM_MAX_FREQUENCY_MS))
176 {
177 txFlags.set_flag(static_cast<std::uint32_t>(TransmitFlags::DM1));
178 lastDM1SentTimestamp = SystemTiming::get_timestamp_ms();
179 }
180 }
181 else
182 {
183 if ((0 != activeDTCList.size()) &&
184 (SystemTiming::time_expired_ms(lastDM1SentTimestamp, DM_MAX_FREQUENCY_MS)))
185 {
186 txFlags.set_flag(static_cast<std::uint32_t>(TransmitFlags::DM1));
187 lastDM1SentTimestamp = SystemTiming::get_timestamp_ms();
188 }
189 }
190 }
191 txFlags.process_all_flags();
193 }
194
196 {
197 j1939Mode = value;
198 }
199
201 {
202 return j1939Mode;
203 }
204
206 {
207 inactiveDTCList.insert(std::end(inactiveDTCList), std::begin(activeDTCList), std::end(activeDTCList));
208 activeDTCList.clear();
209
210 if (broadcastState)
211 {
212 txFlags.set_flag(static_cast<std::uint32_t>(TransmitFlags::DM1));
213 }
214 }
215
220
225
227 {
229 {
230 ecuIdentificationFields[static_cast<std::size_t>(field)] = value + "*";
231 }
232 }
233
235 {
236 bool retVal = false;
237
238 if (active)
239 {
240 // First check to see if it's already active
241 auto activeLocation = std::find(activeDTCList.begin(), activeDTCList.end(), dtc);
242
243 if (activeDTCList.end() == activeLocation)
244 {
245 // Not already active. This is valid
246 retVal = true;
247 auto inactiveLocation = std::find(inactiveDTCList.begin(), inactiveDTCList.end(), dtc);
248
249 if (inactiveDTCList.end() != inactiveLocation)
250 {
251 inactiveLocation->occurrenceCount++;
252 activeDTCList.push_back(*inactiveLocation);
253 inactiveDTCList.erase(inactiveLocation);
254 }
255 else
256 {
257 activeDTCList.push_back(dtc);
258 activeDTCList[activeDTCList.size() - 1].occurrenceCount = 1;
259
260 if ((SystemTiming::get_time_elapsed_ms(lastDM1SentTimestamp) > DM_MAX_FREQUENCY_MS) &&
262 {
263 txFlags.set_flag(static_cast<std::uint32_t>(TransmitFlags::DM1));
264 lastDM1SentTimestamp = SystemTiming::get_timestamp_ms();
265 }
266 }
267 }
268 else
269 {
270 // Already active!
271 retVal = false;
272 }
273 }
274 else
275 {
277 auto inactiveLocation = std::find(inactiveDTCList.begin(), inactiveDTCList.end(), dtc);
278
279 if (inactiveDTCList.end() == inactiveLocation)
280 {
281 retVal = true;
282 auto activeLocation = std::find(activeDTCList.begin(), activeDTCList.end(), dtc);
283
284 if (activeDTCList.end() != activeLocation)
285 {
286 inactiveDTCList.push_back(*activeLocation);
287 activeDTCList.erase(activeLocation);
288 }
289 }
290 else
291 {
292 // Already inactive!
293 retVal = false;
294 }
295 }
296 return retVal;
297 }
298
300 {
301 auto activeLocation = std::find(activeDTCList.begin(), activeDTCList.end(), dtc);
302 bool retVal = false;
303
304 if (activeDTCList.end() != activeLocation)
305 {
306 retVal = true;
307 }
308 return retVal;
309 }
310
312 {
313 bool retVal = false;
314
316 {
318 retVal = true;
319 }
320 return retVal;
321 }
322
324 {
325 bool retVal = false;
326
328 {
330 retVal = true;
331 }
332 return retVal;
333 }
334
336 {
337 bool retVal = false;
338
340 {
342 retVal = true;
343 }
344 return retVal;
345 }
346
347 void DiagnosticProtocol::set_software_id_field(std::uint32_t index, const std::string &value)
348 {
349 if (index >= softwareIdentificationFields.size())
350 {
351 softwareIdentificationFields.resize(index + 1);
352 }
353 else if (("" == value) && (index == softwareIdentificationFields.size()))
354 {
356 }
357 softwareIdentificationFields[index] = value;
358 }
359
360 bool DiagnosticProtocol::suspend_broadcasts(std::uint16_t suspendTime_seconds)
361 {
362 broadcastState = false;
363 lastDM13ReceivedTimestamp = SystemTiming::get_timestamp_ms();
364
366 {
367 customDM13SuspensionTime = suspendTime_seconds;
368 }
369 return send_dm13_announce_suspension(suspendTime_seconds);
370 }
371
373 {
374 return broadcastState;
375 }
376
378 {
379 std::uint8_t retVal = 0;
380
381 switch (flash)
382 {
383 case FlashState::Slow:
384 {
385 retVal = 0x00;
386 }
387 break;
388
389 case FlashState::Fast:
390 {
391 retVal = 0x01;
392 }
393 break;
394
395 default:
396 {
397 retVal = 0x03;
398 }
399 break;
400 }
401 return retVal;
402 }
403
405 {
406 flash = FlashState::Solid;
407 lampOn = false;
408
409 for (auto &dtc : activeDTCList)
410 {
411 switch (targetLamp)
412 {
414 {
415 if (dtc.lampState == LampStatus::AmberWarningLampSolid)
416 {
417 lampOn = true;
418 }
419 else if (dtc.lampState == LampStatus::AmberWarningLampSlowFlash)
420 {
421 lampOn = true;
422 if (flash != FlashState::Fast)
423 {
424 flash = FlashState::Slow;
425 }
426 }
427 else if (dtc.lampState == LampStatus::AmberWarningLampFastFlash)
428 {
429 lampOn = true;
430 flash = FlashState::Fast;
431 }
432 }
433 break;
434
436 {
438 {
439 lampOn = true;
440 }
441 else if (dtc.lampState == LampStatus::MalfunctionIndicatorLampSlowFlash)
442 {
443 lampOn = true;
444 if (flash != FlashState::Fast)
445 {
446 flash = FlashState::Slow;
447 }
448 }
449 else if (dtc.lampState == LampStatus::MalfunctionIndicatorLampFastFlash)
450 {
451 lampOn = true;
452 flash = FlashState::Fast;
453 }
454 }
455 break;
456
458 {
459 if (dtc.lampState == LampStatus::EngineProtectLampSolid)
460 {
461 lampOn = true;
462 }
463 else if (dtc.lampState == LampStatus::EngineProtectLampSlowFlash)
464 {
465 lampOn = true;
466 if (flash != FlashState::Fast)
467 {
468 flash = FlashState::Slow;
469 }
470 }
471 else if (dtc.lampState == LampStatus::EngineProtectLampFastFlash)
472 {
473 lampOn = true;
474 flash = FlashState::Fast;
475 }
476 }
477 break;
478
480 {
481 if (dtc.lampState == LampStatus::RedStopLampSolid)
482 {
483 lampOn = true;
484 }
485 else if (dtc.lampState == LampStatus::RedStopLampSlowFlash)
486 {
487 lampOn = true;
488 if (flash != FlashState::Fast)
489 {
490 flash = FlashState::Slow;
491 }
492 }
493 else if (dtc.lampState == LampStatus::RedStopLampFastFlash)
494 {
495 lampOn = true;
496 flash = FlashState::Fast;
497 }
498 }
499 break;
500
501 default:
502 break;
503 }
504 }
505 }
506
508 {
509 flash = FlashState::Solid;
510 lampOn = false;
511
512 for (auto &dtc : inactiveDTCList)
513 {
514 switch (targetLamp)
515 {
517 {
518 if (dtc.lampState == LampStatus::AmberWarningLampSolid)
519 {
520 lampOn = true;
521 }
522 else if (dtc.lampState == LampStatus::AmberWarningLampSlowFlash)
523 {
524 lampOn = true;
525 if (flash != FlashState::Fast)
526 {
527 flash = FlashState::Slow;
528 }
529 }
530 else if (dtc.lampState == LampStatus::AmberWarningLampFastFlash)
531 {
532 lampOn = true;
533 flash = FlashState::Fast;
534 }
535 }
536 break;
537
539 {
541 {
542 lampOn = true;
543 }
544 else if (dtc.lampState == LampStatus::MalfunctionIndicatorLampSlowFlash)
545 {
546 lampOn = true;
547 if (flash != FlashState::Fast)
548 {
549 flash = FlashState::Slow;
550 }
551 }
552 else if (dtc.lampState == LampStatus::MalfunctionIndicatorLampFastFlash)
553 {
554 lampOn = true;
555 flash = FlashState::Fast;
556 }
557 }
558 break;
559
561 {
562 if (dtc.lampState == LampStatus::EngineProtectLampSolid)
563 {
564 lampOn = true;
565 }
566 else if (dtc.lampState == LampStatus::EngineProtectLampSlowFlash)
567 {
568 lampOn = true;
569 if (flash != FlashState::Fast)
570 {
571 flash = FlashState::Slow;
572 }
573 }
574 else if (dtc.lampState == LampStatus::EngineProtectLampFastFlash)
575 {
576 lampOn = true;
577 flash = FlashState::Fast;
578 }
579 }
580 break;
581
583 {
584 if (dtc.lampState == LampStatus::RedStopLampSolid)
585 {
586 lampOn = true;
587 }
588 else if (dtc.lampState == LampStatus::RedStopLampSlowFlash)
589 {
590 lampOn = true;
591 if (flash != FlashState::Fast)
592 {
593 flash = FlashState::Slow;
594 }
595 }
596 else if (dtc.lampState == LampStatus::RedStopLampFastFlash)
597 {
598 lampOn = true;
599 flash = FlashState::Fast;
600 }
601 }
602 break;
603
604 default:
605 break;
606 }
607 }
608 }
609
610 void DiagnosticProtocol::on_address_violation(std::shared_ptr<InternalControlFunction> affectedControlFunction)
611 {
612 if ((nullptr != affectedControlFunction) &&
613 (!get_j1939_mode()) &&
614 (BROADCAST_CAN_ADDRESS != affectedControlFunction->get_address()) &&
615 (NULL_CAN_ADDRESS != affectedControlFunction->get_address()))
616 {
617 constexpr std::uint32_t ADDRESS_VIOLATION_SPN_BASE = 2000; // Defined in ISO 11783-5 section 4.4.4.3
618
619 set_diagnostic_trouble_code_active(DiagnosticTroubleCode(ADDRESS_VIOLATION_SPN_BASE + affectedControlFunction->get_address(),
621 LampStatus::None),
622 true);
623 }
624 }
625
627 {
628 bool retVal = false;
629
630 if (nullptr != myControlFunction)
631 {
632 std::uint16_t payloadSize = static_cast<std::uint16_t>(activeDTCList.size() * DM_PAYLOAD_BYTES_PER_DTC) + 2; // 2 Bytes (0 and 1) are reserved
633
634 if (payloadSize <= MAX_PAYLOAD_SIZE_BYTES)
635 {
636 std::vector<std::uint8_t> buffer;
637 buffer.resize(payloadSize < CAN_DATA_LENGTH ? CAN_DATA_LENGTH : payloadSize);
638
639 if (get_j1939_mode())
640 {
641 bool tempLampState = false;
642 FlashState tempLampFlashState = FlashState::Solid;
643 get_active_list_lamp_state_and_flash_state(Lamps::ProtectLamp, tempLampFlashState, tempLampState);
644
646 buffer[0] = tempLampState;
647 buffer[1] = convert_flash_state_to_byte(tempLampFlashState);
648
650
652 buffer[0] |= (static_cast<std::uint8_t>(tempLampState) << 2);
653 buffer[1] |= (convert_flash_state_to_byte(tempLampFlashState) << 2);
654
655 get_active_list_lamp_state_and_flash_state(Lamps::RedStopLamp, tempLampFlashState, tempLampState);
656
658 buffer[0] |= (static_cast<std::uint8_t>(tempLampState) << 4);
659 buffer[1] |= (convert_flash_state_to_byte(tempLampFlashState) << 4);
660
662
664 buffer[0] |= (static_cast<std::uint8_t>(tempLampState) << 6);
665 buffer[1] |= (convert_flash_state_to_byte(tempLampFlashState) << 6);
666 }
667 else
668 {
669 // ISO 11783 does not use lamp state or lamp flash bytes
670 buffer[0] = 0xFF;
671 buffer[1] = 0xFF;
672 }
673
674 if (activeDTCList.empty())
675 {
676 buffer[2] = 0x00;
677 buffer[3] = 0x00;
678 buffer[4] = 0x00;
679 buffer[5] = 0x00;
680 buffer[6] = 0xFF;
681 buffer[7] = 0xFF;
682 retVal = CANNetworkManager::CANNetwork.send_can_message(static_cast<std::uint32_t>(CANLibParameterGroupNumber::DiagnosticMessage1),
683 buffer.data(),
686 }
687 else
688 {
689 for (std::size_t i = 0; i < activeDTCList.size(); i++)
690 {
691 buffer[2 + (DM_PAYLOAD_BYTES_PER_DTC * i)] = static_cast<std::uint8_t>(activeDTCList[i].suspectParameterNumber & 0xFF);
692 buffer[3 + (DM_PAYLOAD_BYTES_PER_DTC * i)] = static_cast<std::uint8_t>((activeDTCList[i].suspectParameterNumber >> 8) & 0xFF);
693 buffer[4 + (DM_PAYLOAD_BYTES_PER_DTC * i)] = (static_cast<std::uint8_t>(((activeDTCList[i].suspectParameterNumber >> 16) & 0xFF) << 5) | (static_cast<std::uint8_t>(activeDTCList[i].failureModeIdentifier) & 0x1F));
694 buffer[5 + (DM_PAYLOAD_BYTES_PER_DTC * i)] = (activeDTCList[i].occurrenceCount & 0x7F);
695 }
696
697 if (payloadSize < CAN_DATA_LENGTH)
698 {
699 buffer[6] = 0xFF;
700 buffer[7] = 0xFF;
701 payloadSize = CAN_DATA_LENGTH;
702 }
703
704 retVal = CANNetworkManager::CANNetwork.send_can_message(static_cast<std::uint32_t>(CANLibParameterGroupNumber::DiagnosticMessage1),
705 buffer.data(),
706 payloadSize,
708 }
709 }
710 }
711 return retVal;
712 }
713
715 {
716 bool retVal = false;
717
718 if (nullptr != myControlFunction)
719 {
720 std::uint16_t payloadSize = static_cast<std::uint8_t>(inactiveDTCList.size() * DM_PAYLOAD_BYTES_PER_DTC) + 2; // 2 Bytes (0 and 1) are reserved or used for lamp + flash
721
722 if (payloadSize <= MAX_PAYLOAD_SIZE_BYTES)
723 {
724 std::vector<std::uint8_t> buffer;
725 buffer.resize(payloadSize < CAN_DATA_LENGTH ? CAN_DATA_LENGTH : payloadSize);
726
727 if (get_j1939_mode())
728 {
729 bool tempLampState = false;
730 FlashState tempLampFlashState = FlashState::Solid;
731 get_inactive_list_lamp_state_and_flash_state(Lamps::ProtectLamp, tempLampFlashState, tempLampState);
732
734 buffer[0] = tempLampState;
735 buffer[1] = convert_flash_state_to_byte(tempLampFlashState);
736
738
740 buffer[0] |= (static_cast<std::uint8_t>(tempLampState) << 2);
741 buffer[1] |= (convert_flash_state_to_byte(tempLampFlashState) << 2);
742
743 get_inactive_list_lamp_state_and_flash_state(Lamps::RedStopLamp, tempLampFlashState, tempLampState);
744
746 buffer[0] |= (static_cast<std::uint8_t>(tempLampState) << 4);
747 buffer[1] |= (convert_flash_state_to_byte(tempLampFlashState) << 4);
748
750
752 buffer[0] |= (static_cast<std::uint8_t>(tempLampState) << 6);
753 buffer[1] |= (convert_flash_state_to_byte(tempLampFlashState) << 6);
754 }
755 else
756 {
757 // ISO 11783 does not use lamp state or lamp flash bytes
758 buffer[0] = 0xFF;
759 buffer[1] = 0xFF;
760 }
761
762 if (inactiveDTCList.empty())
763 {
764 buffer[2] = 0x00;
765 buffer[3] = 0x00;
766 buffer[4] = 0x00;
767 buffer[5] = 0x00;
768 buffer[6] = 0xFF;
769 buffer[7] = 0xFF;
770 retVal = CANNetworkManager::CANNetwork.send_can_message(static_cast<std::uint32_t>(CANLibParameterGroupNumber::DiagnosticMessage2),
771 buffer.data(),
774 }
775 else
776 {
777 for (std::size_t i = 0; i < inactiveDTCList.size(); i++)
778 {
779 buffer[2 + (DM_PAYLOAD_BYTES_PER_DTC * i)] = static_cast<std::uint8_t>(inactiveDTCList[i].suspectParameterNumber & 0xFF);
780 buffer[3 + (DM_PAYLOAD_BYTES_PER_DTC * i)] = static_cast<std::uint8_t>((inactiveDTCList[i].suspectParameterNumber >> 8) & 0xFF);
781 buffer[4 + (DM_PAYLOAD_BYTES_PER_DTC * i)] = (static_cast<std::uint8_t>(((inactiveDTCList[i].suspectParameterNumber >> 16) & 0xFF) << 5) | (static_cast<std::uint8_t>(inactiveDTCList[i].failureModeIdentifier) & 0x1F));
782 buffer[5 + (DM_PAYLOAD_BYTES_PER_DTC * i)] = (inactiveDTCList[i].occurrenceCount & 0x7F);
783 }
784
785 if (payloadSize < CAN_DATA_LENGTH)
786 {
787 buffer[6] = 0xFF;
788 buffer[7] = 0xFF;
789 payloadSize = CAN_DATA_LENGTH;
790 }
791
792 retVal = CANNetworkManager::CANNetwork.send_can_message(static_cast<std::uint32_t>(CANLibParameterGroupNumber::DiagnosticMessage2),
793 buffer.data(),
794 payloadSize,
796 }
797 }
798 }
799 return retVal;
800 }
801
803 {
804 bool retVal = false;
805
806 if (nullptr != myControlFunction)
807 {
809 constexpr std::uint8_t SUPPORTED_DIAGNOSTIC_PROTOCOLS_BITFIELD = 0x01;
810 std::array<std::uint8_t, CAN_DATA_LENGTH> buffer;
811
812 buffer.fill(0xFF); // Reserved bytes
813 buffer[0] = SUPPORTED_DIAGNOSTIC_PROTOCOLS_BITFIELD;
814 retVal = CANNetworkManager::CANNetwork.send_can_message(static_cast<std::uint32_t>(CANLibParameterGroupNumber::DiagnosticProtocolIdentification),
815 buffer.data(),
818 }
819 return retVal;
820 }
821
822 bool DiagnosticProtocol::send_dm13_announce_suspension(std::uint16_t suspendTime_seconds) const
823 {
824 const std::array<std::uint8_t, CAN_DATA_LENGTH> buffer = {
825 0xFF,
826 0xFF,
827 0xFF,
828 0xFF,
829 static_cast<std::uint8_t>(suspendTime_seconds & 0xFF),
830 static_cast<std::uint8_t>(suspendTime_seconds >> 8),
831 0xFF,
832 0xFF
833 };
834 return CANNetworkManager::CANNetwork.send_can_message(static_cast<std::uint32_t>(CANLibParameterGroupNumber::DiagnosticMessage13),
835 buffer.data(),
836 buffer.size(),
838 }
839
841 {
842 std::string ecuIdString = "";
843 const std::size_t maxComponent = get_j1939_mode() ? static_cast<std::size_t>(ECUIdentificationFields::HardwareID) : static_cast<std::size_t>(ECUIdentificationFields::NumberOfFields);
844
845 for (std::size_t i = 0; i < maxComponent; i++)
846 {
847 ecuIdString.append(ecuIdentificationFields.at(i));
848 }
849
850 std::vector<std::uint8_t> buffer(ecuIdString.begin(), ecuIdString.end());
851 return CANNetworkManager::CANNetwork.send_can_message(static_cast<std::uint32_t>(CANLibParameterGroupNumber::ECUIdentificationInformation),
852 buffer.data(),
853 buffer.size(),
855 }
856
858 {
859 std::string productIdString = productIdentificationCode + "*" + productIdentificationBrand + "*" + productIdentificationModel + "*";
860 std::vector<std::uint8_t> buffer(productIdString.begin(), productIdString.end());
861
862 return CANNetworkManager::CANNetwork.send_can_message(static_cast<std::uint32_t>(CANLibParameterGroupNumber::ProductIdentification),
863 buffer.data(),
864 buffer.size(),
866 }
867
869 {
870 bool retVal = false;
871
872 if (!softwareIdentificationFields.empty())
873 {
874 std::string softIDString = "";
875
876 std::for_each(softwareIdentificationFields.begin(),
878 [&softIDString](const std::string &field) {
879 softIDString.append(field);
880 softIDString.append("*");
881 });
882
883 std::vector<std::uint8_t> buffer(softIDString.begin(), softIDString.end());
884 retVal = CANNetworkManager::CANNetwork.send_can_message(static_cast<std::uint32_t>(CANLibParameterGroupNumber::SoftwareIdentification),
885 buffer.data(),
886 buffer.size(),
888 }
889 return retVal;
890 }
891
893 {
894 bool retVal = false;
895
896 if (!dm22ResponseQueue.empty())
897 {
898 std::size_t numberOfMessage = dm22ResponseQueue.size();
899
900 for (std::size_t i = 0; i < numberOfMessage; i++)
901 {
902 std::array<std::uint8_t, CAN_DATA_LENGTH> buffer;
903 DM22Data currentMessageData = dm22ResponseQueue.back();
904
905 if (currentMessageData.nack)
906 {
907 if (currentMessageData.clearActive)
908 {
909 buffer[0] = static_cast<std::uint8_t>(DM22ControlByte::NegativeAcknowledgeOfActiveDTCClear);
910 buffer[1] = currentMessageData.nackIndicator;
911 }
912 else
913 {
914 buffer[0] = static_cast<std::uint8_t>(DM22ControlByte::NegativeAcknowledgeOfPreviouslyActiveDTCClear);
915 buffer[1] = currentMessageData.nackIndicator;
916 }
917 }
918 else
919 {
920 if (currentMessageData.clearActive)
921 {
922 buffer[0] = static_cast<std::uint8_t>(DM22ControlByte::PositiveAcknowledgeOfActiveDTCClear);
923 buffer[1] = 0xFF;
924 }
925 else
926 {
927 buffer[0] = static_cast<std::uint8_t>(DM22ControlByte::PositiveAcknowledgeOfPreviouslyActiveDTCClear);
928 buffer[1] = 0xFF;
929 }
930 }
931
932 buffer[2] = 0xFF;
933 buffer[3] = 0xFF;
934 buffer[4] = 0xFF;
935 buffer[5] = static_cast<std::uint8_t>(currentMessageData.suspectParameterNumber & 0xFF);
936 buffer[6] = static_cast<std::uint8_t>((currentMessageData.suspectParameterNumber >> 8) & 0xFF);
937 buffer[7] = static_cast<std::uint8_t>(((currentMessageData.suspectParameterNumber >> 16) << 5) & 0xE0);
938 buffer[7] |= (currentMessageData.failureModeIdentifier & 0x1F);
939
940 retVal = CANNetworkManager::CANNetwork.send_can_message(static_cast<std::uint32_t>(CANLibParameterGroupNumber::DiagnosticMessage22),
941 buffer.data(),
942 buffer.size(),
944 currentMessageData.destination);
945 if (retVal)
946 {
947 dm22ResponseQueue.pop_back();
948 }
949 }
950 }
951 return retVal;
952 }
953
955 {
956 if (((nullptr == message.get_destination_control_function()) &&
959 {
960 switch (message.get_identifier().get_parameter_group_number())
961 {
962 case static_cast<std::uint32_t>(CANLibParameterGroupNumber::DiagnosticMessage13):
963 {
964 if (parse_j1939_network_states(message))
965 {
966 lastDM13ReceivedTimestamp = SystemTiming::get_timestamp_ms();
967 }
968 }
969 break;
970
971 case static_cast<std::uint32_t>(CANLibParameterGroupNumber::DiagnosticMessage22):
972 {
973 if (CAN_DATA_LENGTH == message.get_data_length())
974 {
975 const auto &messageData = message.get_data();
976
977 DM22Data tempDM22Data;
978 bool wasDTCCleared = false;
979
980 tempDM22Data.suspectParameterNumber = messageData.at(5);
981 tempDM22Data.suspectParameterNumber |= (messageData.at(6) << 8);
982 tempDM22Data.suspectParameterNumber |= (((messageData.at(7) & 0xE0) >> 5) << 16);
983 tempDM22Data.failureModeIdentifier = (messageData.at(7) & 0x1F);
984 tempDM22Data.destination = message.get_source_control_function();
985 tempDM22Data.nackIndicator = 0;
986 tempDM22Data.clearActive = false;
987
988 switch (messageData.at(0))
989 {
990 case static_cast<std::uint8_t>(DM22ControlByte::RequestToClearActiveDTC):
991 {
992 tempDM22Data.clearActive = true;
993
994 for (auto dtc = activeDTCList.begin(); dtc != activeDTCList.end(); dtc++)
995 {
996 if ((tempDM22Data.suspectParameterNumber == dtc->suspectParameterNumber) &&
997 (tempDM22Data.failureModeIdentifier == static_cast<std::uint8_t>(dtc->failureModeIdentifier)))
998 {
999 inactiveDTCList.push_back(*dtc);
1000 activeDTCList.erase(dtc);
1001 wasDTCCleared = true;
1002 tempDM22Data.nack = false;
1003
1004 dm22ResponseQueue.push_back(tempDM22Data);
1005 txFlags.set_flag(static_cast<std::uint32_t>(TransmitFlags::DM22));
1006 break;
1007 }
1008 }
1009
1010 if (!wasDTCCleared)
1011 {
1012 tempDM22Data.nack = true;
1013
1014 // Since we didn't find the DTC in the active list, we check the inactive to determine the proper NACK reason
1015 for (const auto &dtc : inactiveDTCList)
1016 {
1017 if ((tempDM22Data.suspectParameterNumber == dtc.suspectParameterNumber) &&
1018 (tempDM22Data.failureModeIdentifier == static_cast<std::uint8_t>(dtc.failureModeIdentifier)))
1019 {
1020 // The DTC was active, but is inactive now, so we NACK with the proper reason
1021 tempDM22Data.nackIndicator = static_cast<std::uint8_t>(DM22NegativeAcknowledgeIndicator::DTCNoLongerActive);
1022 break;
1023 }
1024 }
1025
1026 if (0 == tempDM22Data.nackIndicator)
1027 {
1028 // DTC is in neither list. NACK with the reason that we don't know anything about it
1029 tempDM22Data.nackIndicator = static_cast<std::uint8_t>(DM22NegativeAcknowledgeIndicator::UnknownOrDoesNotExist);
1030 }
1031 dm22ResponseQueue.push_back(tempDM22Data);
1032 txFlags.set_flag(static_cast<std::uint32_t>(TransmitFlags::DM22));
1033 }
1034 }
1035 break;
1036
1037 case static_cast<std::uint8_t>(DM22ControlByte::RequestToClearPreviouslyActiveDTC):
1038 {
1039 for (auto dtc = inactiveDTCList.begin(); dtc != inactiveDTCList.end(); dtc++)
1040 {
1041 if ((tempDM22Data.suspectParameterNumber == dtc->suspectParameterNumber) &&
1042 (tempDM22Data.failureModeIdentifier == static_cast<std::uint8_t>(dtc->failureModeIdentifier)))
1043 {
1044 inactiveDTCList.erase(dtc);
1045 wasDTCCleared = true;
1046 tempDM22Data.nack = false;
1047
1048 dm22ResponseQueue.push_back(tempDM22Data);
1049 txFlags.set_flag(static_cast<std::uint32_t>(TransmitFlags::DM22));
1050 break;
1051 }
1052 }
1053
1054 if (!wasDTCCleared)
1055 {
1056 tempDM22Data.nack = true;
1057
1058 // Since we didn't find the DTC in the inactive list, we check the active to determine the proper NACK reason
1059 for (const auto &dtc : activeDTCList)
1060 {
1061 if ((tempDM22Data.suspectParameterNumber == dtc.suspectParameterNumber) &&
1062 (tempDM22Data.failureModeIdentifier == static_cast<std::uint8_t>(dtc.failureModeIdentifier)))
1063 {
1064 // The DTC was inactive, but is active now, so we NACK with the proper reason
1065 tempDM22Data.nackIndicator = static_cast<std::uint8_t>(DM22NegativeAcknowledgeIndicator::DTCNoLongerPreviouslyActive);
1066 break;
1067 }
1068 }
1069
1070 if (0 == tempDM22Data.nackIndicator)
1071 {
1072 // DTC is in neither list. NACK with the reason that we don't know anything about it
1073 tempDM22Data.nackIndicator = static_cast<std::uint8_t>(DM22NegativeAcknowledgeIndicator::UnknownOrDoesNotExist);
1074 }
1075 dm22ResponseQueue.push_back(tempDM22Data);
1076 txFlags.set_flag(static_cast<std::uint32_t>(TransmitFlags::DM22));
1077 }
1078 }
1079 break;
1080
1081 default:
1082 break;
1083 }
1084 }
1085 }
1086 break;
1087
1088 default:
1089 break;
1090 }
1091 }
1092 }
1093
1094 void DiagnosticProtocol::process_message(const CANMessage &message, void *parent)
1095 {
1096 if (nullptr != parent)
1097 {
1098 reinterpret_cast<DiagnosticProtocol *>(parent)->process_message(message);
1099 }
1100 }
1101
1103 {
1104 bool retVal = false;
1105
1106 if ((CAN_DATA_LENGTH == message.get_data_length()) &&
1107 (static_cast<std::uint32_t>(CANLibParameterGroupNumber::DiagnosticMessage13) == message.get_identifier().get_parameter_group_number()))
1108 {
1109 const auto &messageData = message.get_data();
1110
1111 auto command = static_cast<StopStartCommand>((messageData[0] & (DM13_NETWORK_BITMASK << (DM13_BITS_PER_NETWORK * static_cast<std::uint8_t>(networkType)))) >> (DM13_BITS_PER_NETWORK * static_cast<std::uint8_t>(networkType)));
1112 switch (command)
1113 {
1115 {
1116 broadcastState = false;
1117
1118 switch (static_cast<SuspendSignalState>(messageData.at(3) & 0x0F))
1119 {
1122 {
1123 std::uint16_t suspensionTime = message.get_uint16_at(3);
1124
1125 if (suspensionTime < MAX_DM13_CUSTOM_SUSPEND_TIME_MS)
1126 {
1127 customDM13SuspensionTime = suspensionTime;
1128 }
1129 else
1130 {
1132 }
1133 }
1134 break;
1135
1138 {
1140 }
1141 break;
1142
1143 default:
1144 break;
1145 }
1146 }
1147 break;
1148
1150 broadcastState = true;
1151 break;
1152
1153 default:
1154 break;
1155 }
1156
1157 // Check current data link
1158 command = static_cast<StopStartCommand>((messageData[0] & (DM13_NETWORK_BITMASK << (DM13_BITS_PER_NETWORK * static_cast<std::uint8_t>(NetworkType::CurrentDataLink)))) >> (DM13_BITS_PER_NETWORK * static_cast<std::uint8_t>(NetworkType::CurrentDataLink)));
1159 switch (command)
1160 {
1162 {
1163 broadcastState = false;
1164
1165 switch (static_cast<SuspendSignalState>(messageData.at(3) & 0x0F))
1166 {
1169 {
1170 std::uint16_t suspensionTime = message.get_uint16_at(3);
1171
1172 if (suspensionTime < MAX_DM13_CUSTOM_SUSPEND_TIME_MS)
1173 {
1174 customDM13SuspensionTime = suspensionTime;
1175 }
1176 else
1177 {
1179 }
1180 }
1181 break;
1182
1185 {
1187 }
1188 break;
1189
1190 default:
1191 break;
1192 }
1193 }
1194 break;
1195
1197 {
1198 broadcastState = true;
1200 }
1201 break;
1202
1203 default:
1204 break;
1205 }
1206 retVal = true;
1207 }
1208 return retVal;
1209 }
1210
1211 bool DiagnosticProtocol::process_parameter_group_number_request(std::uint32_t parameterGroupNumber,
1212 std::shared_ptr<ControlFunction> requestingControlFunction,
1213 bool &acknowledge,
1214 AcknowledgementType &acknowledgementType)
1215 {
1216 bool retVal = false;
1217 acknowledge = false;
1218 acknowledgementType = AcknowledgementType::Negative;
1219
1220 if (nullptr != requestingControlFunction)
1221 {
1222 switch (parameterGroupNumber)
1223 {
1224 case static_cast<std::uint32_t>(CANLibParameterGroupNumber::DiagnosticMessage1):
1225 {
1226 txFlags.set_flag(static_cast<std::uint32_t>(TransmitFlags::DM1));
1227 retVal = true;
1228 }
1229 break;
1230
1231 case static_cast<std::uint32_t>(CANLibParameterGroupNumber::DiagnosticMessage2):
1232 {
1233 txFlags.set_flag(static_cast<std::uint32_t>(TransmitFlags::DM2));
1234 retVal = true;
1235 }
1236 break;
1237
1238 case static_cast<std::uint32_t>(CANLibParameterGroupNumber::DiagnosticMessage3):
1239 {
1241 acknowledge = true;
1242 acknowledgementType = AcknowledgementType::Positive;
1243 retVal = true;
1244 }
1245 break;
1246
1247 case static_cast<std::uint32_t>(CANLibParameterGroupNumber::DiagnosticMessage11):
1248 {
1250 acknowledge = true;
1251 acknowledgementType = AcknowledgementType::Positive;
1252 retVal = true;
1253 }
1254 break;
1255
1256 case static_cast<std::uint32_t>(CANLibParameterGroupNumber::ProductIdentification):
1257 {
1258 txFlags.set_flag(static_cast<std::uint32_t>(TransmitFlags::ProductIdentification));
1259 retVal = true;
1260 }
1261 break;
1262
1263 case static_cast<std::uint32_t>(CANLibParameterGroupNumber::DiagnosticProtocolIdentification):
1264 {
1265 txFlags.set_flag(static_cast<std::uint32_t>(TransmitFlags::DiagnosticProtocolID));
1266 retVal = true;
1267 }
1268 break;
1269
1270 case static_cast<std::uint32_t>(CANLibParameterGroupNumber::SoftwareIdentification):
1271 {
1272 txFlags.set_flag(static_cast<std::uint32_t>(TransmitFlags::SoftwareIdentification));
1273 retVal = true;
1274 }
1275 break;
1276
1277 case static_cast<std::uint32_t>(CANLibParameterGroupNumber::ECUIdentificationInformation):
1278 {
1279 txFlags.set_flag(static_cast<std::uint32_t>(TransmitFlags::ECUIdentification));
1280 retVal = true;
1281 }
1282 break;
1283
1284 default:
1285 {
1286 // This PGN request is not handled by the diagnostic protocol
1287 }
1288 break;
1289 }
1290 }
1291 return retVal;
1292 }
1293
1294 bool DiagnosticProtocol::process_parameter_group_number_request(std::uint32_t parameterGroupNumber,
1295 std::shared_ptr<ControlFunction> requestingControlFunction,
1296 bool &acknowledge,
1297 AcknowledgementType &acknowledgementType,
1298 void *parentPointer)
1299 {
1300 bool retVal = false;
1301
1302 if (nullptr != parentPointer)
1303 {
1304 retVal = reinterpret_cast<DiagnosticProtocol *>(parentPointer)->process_parameter_group_number_request(parameterGroupNumber, requestingControlFunction, acknowledge, acknowledgementType);
1305 }
1306 return retVal;
1307 }
1308
1309 void DiagnosticProtocol::process_flags(std::uint32_t flag, void *parentPointer)
1310 {
1311 if (nullptr != parentPointer)
1312 {
1313 auto *parent = reinterpret_cast<DiagnosticProtocol *>(parentPointer);
1314 bool transmitSuccessful = false;
1315
1316 switch (flag)
1317 {
1318 case static_cast<std::uint32_t>(TransmitFlags::DM1):
1319 {
1320 transmitSuccessful = parent->send_diagnostic_message_1();
1321
1322 if (transmitSuccessful)
1323 {
1324 parent->lastDM1SentTimestamp = SystemTiming::get_timestamp_ms();
1325 }
1326 }
1327 break;
1328
1329 case static_cast<std::uint32_t>(TransmitFlags::DM2):
1330 {
1331 transmitSuccessful = parent->send_diagnostic_message_2();
1332 }
1333 break;
1334
1335 case static_cast<std::uint32_t>(TransmitFlags::DiagnosticProtocolID):
1336 {
1337 transmitSuccessful = parent->send_diagnostic_protocol_identification();
1338 }
1339 break;
1340
1341 case static_cast<std::uint32_t>(TransmitFlags::ProductIdentification):
1342 {
1343 transmitSuccessful = parent->send_product_identification();
1344 }
1345 break;
1346
1347 case static_cast<std::uint32_t>(TransmitFlags::DM22):
1348 {
1349 transmitSuccessful = parent->process_all_dm22_responses();
1350 }
1351 break;
1352
1353 case static_cast<std::uint32_t>(TransmitFlags::ECUIdentification):
1354 {
1355 transmitSuccessful = parent->send_ecu_identification();
1356 }
1357 break;
1358
1359 case static_cast<std::uint32_t>(TransmitFlags::SoftwareIdentification):
1360 {
1361 transmitSuccessful = parent->send_software_identification();
1362 }
1363 break;
1364
1365 default:
1366 break;
1367 }
1368
1369 if (false == transmitSuccessful)
1370 {
1371 parent->txFlags.set_flag(flag);
1372 }
1373 }
1374 }
1375
1376} // namespace isobus
Defines some PGNs that are used in the library or are very common.
The main class that manages the ISOBUS stack including: callbacks, Name to Address management,...
A protocol that handles PGN requests.
A class that acts as a logging sink. The intent is that someone could make their own derived class of...
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 a generic CAN message of arbitrary length.
const std::vector< std::uint8_t > & get_data() const
Gets a reference to the data in the CAN message.
std::uint32_t get_data_length() const
Returns the length of the data in the CAN message.
std::shared_ptr< ControlFunction > get_source_control_function() const
Gets the source control function that the message is from.
std::uint16_t get_uint16_at(const std::uint32_t index, const ByteFormat format=ByteFormat::LittleEndian) const
Get a 16-bit unsigned integer from the buffer at a specific index. A 16-bit unsigned integer can hold...
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.
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 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)
static CANNetworkManager CANNetwork
Static singleton of the one network manager. Use this to access stack functionality.
bool remove_protocol_parameter_group_number_callback(std::uint32_t parameterGroupNumber, CANLibCallback callback, void *parentPointer)
Removes a PGN callback for a protocol class.
bool add_protocol_parameter_group_number_callback(std::uint32_t parameterGroupNumber, CANLibCallback callback, void *parentPointer)
Adds a PGN callback for a protocol class.
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)
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 update()
The diagnostic protocol will call this update function, make sure to call DiagnosticProtocol::update(...
A storage class for describing a complete DTC.
std::uint32_t suspectParameterNumber
This 19-bit number is used to identify the item for which diagnostics are being reported.
DiagnosticTroubleCode()=default
Constructor for a DTC, sets default values at construction time.
LampStatus lampState
The J1939 lamp state for this DTC.
bool operator==(const DiagnosticTroubleCode &obj) const
A useful way to compare DTC objects to each other for equality.
std::uint8_t get_occurrence_count() const
Returns the occurrence count, which will be kept track of by the protocol.
std::uint32_t get_suspect_parameter_number() const
Returns the suspect parameter number.
FailureModeIdentifier get_failure_mode_identifier() const
Returns the failure mode indicator.
FailureModeIdentifier failureModeIdentifier
The FMI defines the type of failure detected in the sub-system identified by an SPN.
Manages the DM1, DM2, and DM3 messages for ISO11783 or J1939.
SuspendSignalState
Enumerates the different suspend signals for DM13.
@ IndefiniteSuspension
Indefinite suspension of all broadcasts.
@ PartialTemporarySuspension
Temporary suspension of some messages.
@ PartialIndefiniteSuspension
Indefinite suspension of some messages.
@ TemporarySuspension
Temporary suspension of all broadcasts.
bool broadcastState
Bitfield for tracking the network broadcast state for DM13.
~DiagnosticProtocol()
The destructor for this protocol.
void process_message(const CANMessage &message)
A generic way for a protocol to process a received message.
FailureModeIdentifier
FMI as defined in ISO11783-12 Annex E.
@ ConditionExists
The condition that is identified by the SPN exists when no applicable FMI exists (any other error)
ProcessingFlags txFlags
An instance of the processing flags to handle retries of some messages.
void get_inactive_list_lamp_state_and_flash_state(Lamps targetLamp, FlashState &flash, bool &lampOn) const
This is a way to find the overall lamp states to report.
std::string productIdentificationBrand
The product identification brand for sending the product identification message.
static void process_flags(std::uint32_t flag, void *parentPointer)
A generic callback for a the class to process flags from the ProcessingFlags
void clear_software_id_fields()
Clears all previously configured software ID fields set with set_software_id_field.
NetworkType networkType
The diagnostic network type that this protocol will use.
bool get_initialized() const
Returns if the protocol has been initialized.
bool set_product_identification_model(const std::string &value)
Sets the product identification model used in the diagnostic protocol "Product Identification" messag...
static constexpr std::uint16_t MAX_PAYLOAD_SIZE_BYTES
DM 1 and 2 are limited to the BAM message max, because ETP does not allow global destinations.
EventCallbackHandle addressViolationEventHandle
Stores the handle from registering for address violation events.
static constexpr std::uint32_t DM_MAX_FREQUENCY_MS
You are technically allowed to send more than this under limited circumstances, but a hard limit save...
bool suspend_broadcasts(std::uint16_t suspendTime_seconds=0xFFFF)
Informs the diagnostic protocol that you are going to suspend broadcasts.
bool send_ecu_identification() const
Sends the ECU ID message.
NetworkType
Enumerates the different networks in the DM13.
bool send_dm13_announce_suspension(std::uint16_t suspendTime_seconds) const
Sends the DM13 to alert network devices of impending suspended broadcasts.
bool send_diagnostic_protocol_identification() const
Sends a message that identifies which diagnostic protocols are supported.
StopStartCommand
Enumerates the commands in the DM13.
std::vector< std::string > ecuIdentificationFields
Stores the ECU ID fields so we can transmit them when ECU ID's PGN is requested.
FlashState
Enumerates lamp flash states in J1939.
std::uint16_t customDM13SuspensionTime
If using a non-standard DM13 suspension time, this tracks that duration in milliseconds.
void update()
Updates the diagnostic protocol.
DiagnosticProtocol(std::shared_ptr< InternalControlFunction > internalControlFunction, NetworkType networkType=NetworkType::ProprietaryNetwork1)
The constructor for this protocol.
bool set_diagnostic_trouble_code_active(const DiagnosticTroubleCode &dtc, bool active)
Adds a DTC to the active list, or removes one from the active list.
std::shared_ptr< InternalControlFunction > myControlFunction
The internal control function that this protocol will send from.
LampStatus
The DTC lamp status as defined in J1939-73. Not used when in ISO11783 mode.
@ EngineProtectLampSolid
This lamp is used to relay trouble code information that is reporting a problem with a vehicle system...
@ EngineProtectLampSlowFlash
This lamp is used to relay trouble code information that is reporting a problem with a vehicle system...
@ EngineProtectLampFastFlash
This lamp is used to relay trouble code information that is reporting a problem with a vehicle system...
@ AmberWarningLampSlowFlash
This lamp is used to relay trouble code information that is reporting a problem with the vehicle syst...
@ AmberWarningLampFastFlash
This lamp is used to relay trouble code information that is reporting a problem with the vehicle syst...
@ AmberWarningLampSolid
This lamp is used to relay trouble code information that is reporting a problem with the vehicle syst...
@ MalfunctionIndicatorLampSolid
A lamp used to relay only emissions-related trouble code information.
@ RedStopLampSolid
This lamp is used to relay trouble code information that is of a severe-enough condition that it warr...
@ MalfunctionIndicatorLampSlowFlash
A lamp used to relay only emissions-related trouble code information.
@ MalfunctionIndicatorLampFastFlash
A lamp used to relay only emissions-related trouble code information.
@ RedStopLampFastFlash
This lamp is used to relay trouble code information that is of a severe-enough condition that it warr...
@ RedStopLampSlowFlash
This lamp is used to relay trouble code information that is of a severe-enough condition that it warr...
Lamps
Lists the different lamps in J1939-73.
@ ProtectLamp
The engine protect lamp.
std::uint32_t lastDM13ReceivedTimestamp
A timestamp in milliseconds when we last got a DM13 message.
void clear_inactive_diagnostic_trouble_codes()
Clears the list of inactive DTCs and clears occurrence counts.
@ NegativeAcknowledgeOfPreviouslyActiveDTCClear
NACK for clearing a previously active DTC.
@ RequestToClearPreviouslyActiveDTC
Clear a previously active DTC.
@ PositiveAcknowledgeOfPreviouslyActiveDTCClear
ACK for clearing a previously active DTC.
@ PositiveAcknowledgeOfActiveDTCClear
ACK clearing an active DTC.
@ NegativeAcknowledgeOfActiveDTCClear
NACK clearing an active DTC.
bool set_product_identification_brand(const std::string &value)
Sets the product identification brand used in the diagnostic protocol "Product Identification" messag...
static constexpr std::uint32_t DM13_TIMEOUT_MS
The timeout in 5.7.13 after which nodes shall revert back to the normal broadcast state.
@ DTCNoLongerActive
DTC is inactive, not active, but active was requested to be cleared.
@ DTCNoLongerPreviouslyActive
The DTC in in the active list but it was requested to clear from inactive list.
@ UnknownOrDoesNotExist
The DTC is unknown or does not exist.
std::string productIdentificationModel
The product identification model name for sending the product identification message.
std::vector< std::string > softwareIdentificationFields
Stores the Software ID fields so we can transmit them when the PGN is requested.
static constexpr std::uint8_t PRODUCT_IDENTIFICATION_MAX_STRING_LENGTH
The max string length allowed in the fields of product ID, as defined in ISO 11783-12.
ControlFunctionFunctionalities ControlFunctionFunctionalitiesMessageInterface
Use this interface to configure your CF's functionalities. This info will be reported to any ECU that...
std::string productIdentificationCode
The product identification code for sending the product identification message.
bool set_product_identification_code(const std::string &value)
Sets the product ID code used in the diagnostic protocol "Product Identification" message (PGN 0xFC8D...
void terminate()
The protocol's terminate function.
bool get_j1939_mode() const
Returns true if the protocol is in J1939 mode instead of ISO11783 mode, false if using ISO11783 mode.
std::uint8_t convert_flash_state_to_byte(FlashState flash) const
A utility function to get the CAN representation of a FlashState.
std::vector< DM22Data > dm22ResponseQueue
Maintaining a list of DM22 responses we need to send to allow for retrying in case of Tx failures.
bool get_diagnostic_trouble_code_active(const DiagnosticTroubleCode &dtc)
Returns if a DTC is active.
bool initialize()
The protocol's initializer function.
bool send_diagnostic_message_2() const
Sends a DM2 encoded CAN message.
bool j1939Mode
Tells the protocol to operate according to J1939 instead of ISO11783.
void set_software_id_field(std::uint32_t index, const std::string &value)
Adds an ascii string to this internal control function's software ID.
bool get_broadcast_state() const
Gets the current broadcast state for the connected network type.
void set_ecu_id_field(ECUIdentificationFields field, const std::string &value)
Sets one of the ECU identification strings for the ECU ID message.
static constexpr std::uint8_t DM13_BITS_PER_NETWORK
Number of bits for the network SPNs.
TransmitFlags
A set of transmit flags to manage sending DM1, DM2, and protocol ID.
@ DM1
A flag to manage sending the DM1 message.
@ ProductIdentification
A flag to manage sending the product identification message.
@ DM2
A flag to manage sending the DM2 message.
@ SoftwareIdentification
A flag to manage sending the software ID message.
@ ECUIdentification
A flag to manage sending the ECU ID message.
@ DiagnosticProtocolID
A flag to manage sending the Diagnostic protocol ID message.
@ DM22
Process queued up DM22 responses.
@ NumberOfFlags
The number of flags in the enum.
std::vector< DiagnosticTroubleCode > inactiveDTCList
Keeps track of all the previously active DTCs.
void clear_active_diagnostic_trouble_codes()
Clears the list of active DTCs and makes them all inactive.
void set_j1939_mode(bool value)
Enables the protocol to run in J1939 mode instead of ISO11783 mode.
bool parse_j1939_network_states(const CANMessage &message)
Parses out the DM13 J1939 network states from a CAN message.
static constexpr std::uint8_t DM_PAYLOAD_BYTES_PER_DTC
The number of payload bytes per DTC that gets encoded into the messages.
void get_active_list_lamp_state_and_flash_state(Lamps targetLamp, FlashState &flash, bool &lampOn) const
This is a way to find the overall lamp states to report.
void on_address_violation(std::shared_ptr< InternalControlFunction > affectedControlFunction)
A callback function used to consume address violation events and activate a DTC as required in ISO117...
bool send_product_identification() const
Sends the product identification message (PGN 0xFC8D)
bool send_diagnostic_message_1() const
Sends a DM1 encoded CAN message.
std::uint32_t lastDM1SentTimestamp
A timestamp in milliseconds of the last time a DM1 was sent.
ECUIdentificationFields
Enumerates the different fields in the ECU identification message.
@ NumberOfFields
The number of fields currently defined in the ISO standard.
@ HardwareID
ISO 11783 only, This parameter is used to associate the hardware version of an ECU connected to the I...
bool initialized
Stores if the interface has been initialized.
std::vector< DiagnosticTroubleCode > activeDTCList
Keeps track of all the active DTCs.
bool process_parameter_group_number_request(std::uint32_t parameterGroupNumber, std::shared_ptr< ControlFunction > requestingControlFunction, bool &acknowledge, AcknowledgementType &acknowledgementType)
Handles PGN requests for the diagnostic protocol.
bool process_all_dm22_responses()
Processes any DM22 responses from the queue.
bool send_software_identification() const
Sends the software ID message.
static constexpr std::uint16_t MAX_DM13_CUSTOM_SUSPEND_TIME_MS
The max valid value for a DM13 suspension time in milliseconds.
A protocol that handles the ISO 11783-12 Diagnostic Protocol and some J1939 DMs.
This namespace encompasses all of the ISO11783 stack's functionality to reduce global namespace pollu...
constexpr std::uint8_t NULL_CAN_ADDRESS
The NULL CAN address defined by J1939 and ISO11783.
constexpr std::uint8_t CAN_DATA_LENGTH
The length of a classical CAN frame.
constexpr std::uint8_t BROADCAST_CAN_ADDRESS
The global/broadcast CAN address.
AcknowledgementType
The types of acknowledgement that can be sent in the Ack PGN.
@ Positive
"ACK" Indicates that the request was completed
@ Negative
"NACK" Indicates the request was not completed or we do not support the PGN
A structure to hold data about DM22 responses we need to send.
std::uint8_t nackIndicator
The NACK reason, if applicable.
std::uint32_t suspectParameterNumber
SPN of the DTC for the DM22.
bool nack
true if we are sending a NACK instead of PACK. Determines if we use nackIndicator
bool clearActive
true if the DM22 was for an active DTC, false for previously active
std::shared_ptr< ControlFunction > destination
Destination for the DM22 message.
std::uint8_t failureModeIdentifier
FMI of the DTC for the DM22.