AgIsoStack++
A control-function-focused implementation of the major ISOBUS and J1939 protocols
Loading...
Searching...
No Matches
can_transport_protocol.cpp
Go to the documentation of this file.
1//================================================================================================
10//================================================================================================
11
13
18#include "isobus/utility/system_timing.hpp"
19#include "isobus/utility/to_string.hpp"
20
21#include <algorithm>
22#include <memory>
23
24namespace isobus
25{
26 TransportProtocolManager::TransportProtocolSession::TransportProtocolSession(Direction direction, std::unique_ptr<CANMessageData> data, std::uint32_t parameterGroupNumber, std::uint16_t totalMessageSize, std::uint8_t clearToSendPacketMax, std::shared_ptr<ControlFunction> source, std::shared_ptr<ControlFunction> destination, TransmitCompleteCallback sessionCompleteCallback, void *parentPointer) :
27 TransportProtocolSessionBase(direction, std::move(data), parameterGroupNumber, totalMessageSize, source, destination, sessionCompleteCallback, parentPointer),
28 clearToSendPacketCountMax(clearToSendPacketMax)
29 {
30 }
31
36
38 {
39 // We know that this session can only be used to transfer 1785 bytes of data, so we can safely cast to a uint16_t
40 return static_cast<std::uint16_t>(TransportProtocolSessionBase::get_message_length());
41 }
42
44 {
45 return (nullptr == get_destination());
46 }
47
49 {
50 uint32_t transferred = get_last_packet_number() * PROTOCOL_BYTES_PER_FRAME;
51 if (transferred > get_message_length())
52 {
53 transferred = get_message_length();
54 }
55 return transferred;
56 }
57
59 {
60 state = value;
61 update_timestamp();
62 }
63
65 {
66 std::uint8_t packetsSinceCTS = get_last_packet_number() - lastAcknowledgedPacketNumber;
67 return clearToSendPacketCount - packetsSinceCTS;
68 }
69
71 {
72 clearToSendPacketCount = value;
73 update_timestamp();
74 }
75
77 {
78 return clearToSendPacketCount;
79 }
80
82 {
83 return clearToSendPacketCountMax;
84 }
85
87 {
88 return lastSequenceNumber;
89 }
90
92 {
93 return lastSequenceNumber;
94 }
95
97 {
98 lastSequenceNumber = value;
99 update_timestamp();
100 }
101
103 {
104 lastAcknowledgedPacketNumber = value;
105 update_timestamp();
106 }
107
109 {
110 return get_total_number_of_packets() - get_last_packet_number();
111 }
112
114 {
115 auto totalNumberOfPackets = static_cast<std::uint8_t>(get_message_length() / PROTOCOL_BYTES_PER_FRAME);
116 if ((get_message_length() % PROTOCOL_BYTES_PER_FRAME) > 0)
117 {
118 totalNumberOfPackets++;
119 }
120 return totalNumberOfPackets;
121 }
122
131
132 void TransportProtocolManager::process_broadcast_announce_message(const std::shared_ptr<ControlFunction> source,
133 std::uint32_t parameterGroupNumber,
134 std::uint16_t totalMessageSize,
135 std::uint8_t totalNumberOfPackets)
136 {
137 // The standard defines that we may not send aborts for messages with a global destination, we can only ignore them if we need to
139 {
140 // TODO: consider using maximum memory instead of maximum number of sessions
141 LOG_WARNING("[TP]: Ignoring Broadcast Announcement Message (BAM) for 0x%05X, configured maximum number of sessions reached.",
142 parameterGroupNumber);
143 }
144 else if (totalMessageSize > MAX_PROTOCOL_DATA_LENGTH)
145 {
146 LOG_WARNING("[TP]: Ignoring Broadcast Announcement Message (BAM) for 0x%05X, message size (%hu) is greater than the maximum (%hu).",
147 parameterGroupNumber,
148 totalMessageSize,
150 }
151 else
152 {
153 auto oldSession = get_session(source, nullptr);
154 if (nullptr != oldSession)
155 {
156 LOG_WARNING("[TP]: Received Broadcast Announcement Message (BAM) while a session already existed for this source (%hu), overwriting for 0x%05X...",
157 source->get_address(),
158 parameterGroupNumber);
159 close_session(oldSession, false);
160 }
161
162 auto newSession = std::make_shared<TransportProtocolSession>(TransportProtocolSession::Direction::Receive,
163 std::unique_ptr<CANMessageData>(new CANMessageDataVector(totalMessageSize)),
164 parameterGroupNumber,
165 totalMessageSize,
166 0xFF, // Arbitrary - unused for broadcast
167 source,
168 nullptr, // Global destination
169 nullptr, // No callback
170 nullptr);
171
172 if (newSession->get_total_number_of_packets() != totalNumberOfPackets)
173 {
174 LOG_WARNING("[TP]: Received Broadcast Announcement Message (BAM) for 0x%05X with a bad number of packets, aborting...", parameterGroupNumber);
175 }
176 else
177 {
179 activeSessions.push_back(newSession);
180 update_state_machine(newSession);
181 LOG_DEBUG("[TP]: New rx broadcast message session for 0x%05X. Source: %hu", parameterGroupNumber, source->get_address());
182 }
183 }
184 }
185
186 void TransportProtocolManager::process_request_to_send(const std::shared_ptr<ControlFunction> source,
187 const std::shared_ptr<ControlFunction> destination,
188 std::uint32_t parameterGroupNumber,
189 std::uint16_t totalMessageSize,
190 std::uint8_t totalNumberOfPackets,
191 std::uint8_t clearToSendPacketMax)
192 {
194 {
195 // TODO: consider using maximum memory instead of maximum number of sessions
196 LOG_WARNING("[TP]: Replying with abort to Request To Send (RTS) for 0x%05X, configured maximum number of sessions reached.", parameterGroupNumber);
197 send_abort(std::static_pointer_cast<InternalControlFunction>(destination), source, parameterGroupNumber, ConnectionAbortReason::AlreadyInCMSession);
198 }
199 else
200 {
201 auto oldSession = get_session(source, destination);
202 if (nullptr != oldSession)
203 {
204 if (oldSession->get_parameter_group_number() != parameterGroupNumber)
205 {
206 LOG_ERROR("[TP]: Received Request To Send (RTS) while a session already existed for this source and destination, aborting for 0x%05X...",
207 parameterGroupNumber);
209 }
210 else
211 {
212 LOG_WARNING("[TP]: Received Request To Send (RTS) while a session already existed for this source and destination and parameterGroupNumber, overwriting for 0x%05X...",
213 parameterGroupNumber);
214 close_session(oldSession, false);
215 }
216 }
217
218 auto data = std::unique_ptr<CANMessageData>(new CANMessageDataVector(totalMessageSize));
219
220 if (clearToSendPacketMax > configuration->get_number_of_packets_per_cts_message())
221 {
222 LOG_DEBUG("[TP]: Received Request To Send (RTS) with a CTS packet count of %hu, which is greater than the configured maximum of %hu, using the configured maximum instead.",
223 clearToSendPacketMax,
226 }
227
228 auto newSession = std::make_shared<TransportProtocolSession>(TransportProtocolSession::Direction::Receive,
229 std::unique_ptr<CANMessageData>(new CANMessageDataVector(totalMessageSize)),
230 parameterGroupNumber,
231 totalMessageSize,
232 clearToSendPacketMax,
233 source,
234 destination,
235 nullptr, // No callback
236 nullptr);
237
238 if (newSession->get_total_number_of_packets() != totalNumberOfPackets)
239 {
240 LOG_ERROR("[TP]: Received Request To Send (RTS) for 0x%05X with a bad number of packets, aborting...", parameterGroupNumber);
242 }
243 else
244 {
245 newSession->set_state(StateMachineState::SendClearToSend);
246 activeSessions.push_back(newSession);
247 LOG_DEBUG("[TP]: New rx session for 0x%05X. Source: %hu, destination: %hu", parameterGroupNumber, source->get_address(), destination->get_address());
248 update_state_machine(newSession);
249 }
250 }
251 }
252
253 void TransportProtocolManager::process_clear_to_send(const std::shared_ptr<ControlFunction> source,
254 const std::shared_ptr<ControlFunction> destination,
255 std::uint32_t parameterGroupNumber,
256 std::uint8_t packetsToBeSent,
257 std::uint8_t nextPacketNumber)
258 {
259 auto session = get_session(destination, source);
260 if (nullptr != session)
261 {
262 if (session->get_parameter_group_number() != parameterGroupNumber)
263 {
264 LOG_ERROR("[TP]: Received a Clear To Send (CTS) message for 0x%05X while a session already existed for this source and destination, sending abort for both...", parameterGroupNumber);
266 send_abort(std::static_pointer_cast<InternalControlFunction>(destination), source, parameterGroupNumber, ConnectionAbortReason::ClearToSendReceivedWhileTransferInProgress);
267 }
268 else if (nextPacketNumber > session->get_total_number_of_packets())
269 {
270 LOG_ERROR("[TP]: Received a Clear To Send (CTS) message for 0x%05X with a bad sequence number, aborting...", parameterGroupNumber);
272 }
273 else if (StateMachineState::WaitForClearToSend != session->state)
274 {
275 // The session exists, but we're not in the right state to receive a CTS, so we must abort
276 LOG_WARNING("[TP]: Received a Clear To Send (CTS) message for 0x%05X, but not expecting one, aborting session.", parameterGroupNumber);
278 }
279 else
280 {
281 session->set_acknowledged_packet_number(nextPacketNumber - 1);
282 session->set_cts_number_of_packets(packetsToBeSent);
283
284 // If 0 was sent as the packet number, they want us to wait.
285 // Just sit here in this state until we get a non-zero packet count
286 if (0 != packetsToBeSent)
287 {
289 }
290 }
291 }
292 else
293 {
294 // We got a CTS but no session exists, by the standard we must ignore it
295 LOG_WARNING("[TP]: Received Clear To Send (CTS) for 0x%05X while no session existed for this source and destination, ignoring...", parameterGroupNumber);
296 }
297 }
298
299 void TransportProtocolManager::process_end_of_session_acknowledgement(const std::shared_ptr<ControlFunction> source,
300 const std::shared_ptr<ControlFunction> destination,
301 std::uint32_t parameterGroupNumber)
302 {
303 auto session = get_session(destination, source);
304 if (nullptr != session)
305 {
307 {
308 session->state = StateMachineState::None;
309 close_session(session, true);
310 LOG_DEBUG("[TP]: Completed tx session for 0x%05X from %hu", parameterGroupNumber, source->get_address());
311 }
312 else
313 {
314 // The session exists, but we're not in the right state to receive an EOM, by the standard we must ignore it
315 LOG_WARNING("[TP]: Received an End Of Message Acknowledgement message for 0x%05X, but not expecting one, ignoring.", parameterGroupNumber);
316 }
317 }
318 else
319 {
320 LOG_WARNING("[TP]: Received End Of Message Acknowledgement for 0x%05X while no session existed for this source and destination, ignoring.", parameterGroupNumber);
321 }
322 }
323
324 void TransportProtocolManager::process_abort(const std::shared_ptr<ControlFunction> source,
325 const std::shared_ptr<ControlFunction> destination,
326 std::uint32_t parameterGroupNumber,
328 {
329 bool foundSession = false;
330
331 auto session = get_session(source, destination);
332 if ((nullptr != session) && (session->get_parameter_group_number() == parameterGroupNumber))
333 {
334 foundSession = true;
335 LOG_ERROR("[TP]: Received an abort (reason=%hu) for an rx session for parameterGroupNumber 0x%05X",
336 static_cast<std::uint8_t>(reason),
337 parameterGroupNumber);
338 close_session(session, false);
339 }
340 session = get_session(destination, source);
341 if ((nullptr != session) && (session->get_parameter_group_number() == parameterGroupNumber))
342 {
343 foundSession = true;
344 LOG_ERROR("[TP]: Received an abort (reason=%hu) for a tx session for parameterGroupNumber 0x%05X",
345 static_cast<std::uint8_t>(reason),
346 parameterGroupNumber);
347 close_session(session, false);
348 }
349
350 if (!foundSession)
351 {
352 LOG_WARNING("[TP]: Received an abort (reason=%hu) with no matching session for parameterGroupNumber 0x%05X",
353 static_cast<std::uint8_t>(reason),
354 parameterGroupNumber);
355 }
356 }
357
359 {
360 if (CAN_DATA_LENGTH != message.get_data_length())
361 {
362 LOG_WARNING("[TP]: Received a Connection Management message of invalid length %hu", message.get_data_length());
363 return;
364 }
365
366 const auto parameterGroupNumber = message.get_uint24_at(5);
367
368 switch (message.get_uint8_at(0))
369 {
371 {
372 if (message.is_broadcast())
373 {
374 const auto totalMessageSize = message.get_uint16_at(1);
375 const auto totalNumberOfPackets = message.get_uint8_at(3);
377 parameterGroupNumber,
378 totalMessageSize,
379 totalNumberOfPackets);
380 }
381 else
382 {
383 LOG_WARNING("[TP]: Received a Broadcast Announcement Message (BAM) with a non-global destination, ignoring");
384 }
385 }
386 break;
387
389 {
390 if (message.is_broadcast())
391 {
392 LOG_WARNING("[TP]: Received a Request to Send (RTS) message with a global destination, ignoring");
393 }
394 else
395 {
396 const auto totalMessageSize = message.get_uint16_at(1);
397 const auto totalNumberOfPackets = message.get_uint8_at(3);
398 const auto clearToSendPacketMax = message.get_uint8_at(4);
401 parameterGroupNumber,
402 totalMessageSize,
403 totalNumberOfPackets,
404 clearToSendPacketMax);
405 }
406 }
407 break;
408
410 {
411 if (message.is_broadcast())
412 {
413 LOG_WARNING("[TP]: Received a Clear to Send (CTS) message with a global destination, ignoring");
414 }
415 else
416 {
417 const auto packetsToBeSent = message.get_uint8_at(1);
418 const auto nextPacketNumber = message.get_uint8_at(2);
421 parameterGroupNumber,
422 packetsToBeSent,
423 nextPacketNumber);
424 }
425 }
426 break;
427
429 {
430 if (message.is_broadcast())
431 {
432 LOG_WARNING("[TP]: Received an End of Message Acknowledge message with a global destination, ignoring");
433 }
434 else
435 {
438 parameterGroupNumber);
439 }
440 }
441 break;
442
444 {
445 if (message.is_broadcast())
446 {
447 LOG_WARNING("[TP]: Received an Abort message with a global destination, ignoring");
448 }
449 else
450 {
451 const auto reason = static_cast<ConnectionAbortReason>(message.get_uint8_at(1));
454 parameterGroupNumber,
455 reason);
456 }
457 }
458 break;
459
460 default:
461 {
462 LOG_WARNING("[TP]: Bad Mux in Transport Protocol Connection Management message");
463 }
464 break;
465 }
466 }
467
469 {
470 if (CAN_DATA_LENGTH != message.get_data_length())
471 {
472 LOG_WARNING("[TP]: Received a Data Transfer message of invalid length %hu", message.get_data_length());
473 return;
474 }
475
476 auto source = message.get_source_control_function();
477 auto destination = message.is_broadcast() ? nullptr : message.get_destination_control_function();
478
479 auto sequenceNumber = message.get_uint8_at(SEQUENCE_NUMBER_DATA_INDEX);
480
481 auto session = get_session(source, destination);
482 if (nullptr != session)
483 {
485 {
486 LOG_WARNING("[TP]: Received a Data Transfer message from %hu while not expecting one, sending abort", source->get_address());
488 }
489 else if (sequenceNumber == session->get_last_sequence_number())
490 {
491 LOG_ERROR("[TP]: Aborting rx session for 0x%05X due to duplicate sequence number", session->get_parameter_group_number());
493 }
494 else if (sequenceNumber == (session->get_last_sequence_number() + 1))
495 {
496 // Convert data type to a vector to allow for manipulation
497 auto &data = static_cast<CANMessageDataVector &>(session->get_data());
498
499 // Correct sequence number, copy the data
500 for (std::uint8_t i = 0; i < PROTOCOL_BYTES_PER_FRAME; i++)
501 {
502 std::uint32_t currentDataIndex = (PROTOCOL_BYTES_PER_FRAME * session->get_last_packet_number()) + i;
503 if (currentDataIndex < session->get_message_length())
504 {
505 data.set_byte(currentDataIndex, message.get_uint8_at(1 + i));
506 }
507 else
508 {
509 // Reached the end of the message, no need to copy any more data
510 break;
511 }
512 }
513
514 session->set_last_sequency_number(sequenceNumber);
515 if (session->get_number_of_remaining_packets() == 0)
516 {
517 // Send End of Message Acknowledgement for sessions with specific destination only
518 if (!message.is_broadcast())
519 {
521 }
522
523 // Construct the completed message
525 session->get_parameter_group_number(),
527 session->is_broadcast() ? CANIdentifier::GLOBAL_ADDRESS : destination->get_address(),
528 source->get_address());
529 CANMessage completedMessage(CANMessage::Type::Receive,
530 identifier,
531 std::move(data),
532 source,
533 destination,
534 0);
535
536 canMessageReceivedCallback(completedMessage);
537 close_session(session, true);
538 LOG_DEBUG("[TP]: Completed rx session for 0x%05X from %hu", session->get_parameter_group_number(), source->get_address());
539 }
540 else if (session->get_cts_number_of_packets_remaining() == 0)
541 {
542 send_clear_to_send(session);
543 }
544 }
545 else
546 {
547 LOG_ERROR("[TP]: Aborting rx session for 0x%05X due to bad sequence number", session->get_parameter_group_number());
549 }
550 }
551 else if (!message.is_broadcast())
552 {
553 LOG_WARNING("[TP]: Received a Data Transfer message from %hu with no matching session, ignoring...", source->get_address());
554 }
555 }
556
558 {
559 // TODO: Allow sniffing of messages to all addresses, not just the ones we normally listen to (#297)
560 if (message.has_valid_source_control_function() && (message.is_destination_our_device() || message.is_broadcast()))
561 {
562 switch (message.get_identifier().get_parameter_group_number())
563 {
564 case static_cast<std::uint32_t>(CANLibParameterGroupNumber::TransportProtocolConnectionManagement):
565 {
567 }
568 break;
569
570 case static_cast<std::uint32_t>(CANLibParameterGroupNumber::TransportProtocolDataTransfer):
571 {
573 }
574 break;
575
576 default:
577 break;
578 }
579 }
580 }
581
582 bool TransportProtocolManager::protocol_transmit_message(std::uint32_t parameterGroupNumber,
583 std::unique_ptr<CANMessageData> &data,
584 std::shared_ptr<ControlFunction> source,
585 std::shared_ptr<ControlFunction> destination,
586 TransmitCompleteCallback sessionCompleteCallback,
587 void *parentPointer)
588 {
589 // Return false early if we can't send the message
590 if ((nullptr == data) || (data->size() <= CAN_DATA_LENGTH) || (data->size() > MAX_PROTOCOL_DATA_LENGTH))
591 {
592 // Invalid message length
593 return false;
594 }
595 else if ((nullptr == source) || (!source->get_address_valid()) || has_session(source, destination))
596 {
597 return false;
598 }
599
600 // We can handle this message! If we only have a view of the data, let's clone the data,
601 // so we don't have to worry about it being deleted.
602 data = data->copy_if_not_owned(std::move(data));
603 auto dataLength = static_cast<std::uint16_t>(data->size());
604
605 auto session = std::make_shared<TransportProtocolSession>(TransportProtocolSession::Direction::Transmit,
606 std::move(data),
607 parameterGroupNumber,
608 dataLength,
610 source,
611 destination,
612 sessionCompleteCallback,
613 parentPointer);
614
615 if (session->is_broadcast())
616 {
617 // Broadcast message
619 LOG_DEBUG("[TP]: New broadcast tx session for 0x%05X. Source: %hu",
620 parameterGroupNumber,
621 source->get_address());
622 }
623 else
624 {
625 // Destination specific message
626 session->set_state(StateMachineState::SendRequestToSend);
627 LOG_DEBUG("[TP]: New tx session for 0x%05X. Source: %hu, destination: %hu",
628 parameterGroupNumber,
629 source->get_address(),
630 destination->get_address());
631 }
632 activeSessions.push_back(session);
633 update_state_machine(session);
634 return true;
635 }
636
638 {
639 // We use a fancy for loop here to allow us to remove sessions from the list while iterating
640 for (std::size_t i = activeSessions.size(); i > 0; i--)
641 {
642 auto session = activeSessions.at(i - 1);
643 if (!session->get_source()->get_address_valid())
644 {
645 LOG_WARNING("[TP]: Closing active session as the source control function is no longer valid");
646 close_session(session, false);
647 }
648 else if (!session->is_broadcast() && !session->get_destination()->get_address_valid())
649 {
650 LOG_WARNING("[TP]: Closing active session as the destination control function is no longer valid");
651 close_session(session, false);
652 }
653 else if (StateMachineState::None != session->state)
654 {
655 update_state_machine(session);
656 }
657 }
658 }
659
660 void TransportProtocolManager::send_data_transfer_packets(std::shared_ptr<TransportProtocolSession> &session)
661 {
662 std::array<std::uint8_t, CAN_DATA_LENGTH> buffer;
663 std::uint8_t framesToSend = session->get_cts_number_of_packets_remaining();
664 if (session->is_broadcast())
665 {
666 framesToSend = 1;
667 }
669 {
671 }
672
673 // Try and send packets
674 for (std::uint8_t i = 0; i < framesToSend; i++)
675 {
676 buffer[0] = session->get_last_sequence_number() + 1;
677
678 std::uint16_t dataOffset = session->get_last_packet_number() * PROTOCOL_BYTES_PER_FRAME;
679 for (std::uint8_t j = 0; j < PROTOCOL_BYTES_PER_FRAME; j++)
680 {
681 std::uint16_t index = dataOffset + j;
682 if (index < session->get_message_length())
683 {
684 buffer[1 + j] = session->get_data().get_byte(index);
685 }
686 else
687 {
688 buffer[1 + j] = 0xFF;
689 }
690 }
691
692 if (sendCANFrameCallback(static_cast<std::uint32_t>(CANLibParameterGroupNumber::TransportProtocolDataTransfer),
693 CANDataSpan(buffer.data(), buffer.size()),
694 std::static_pointer_cast<InternalControlFunction>(session->get_source()),
695 session->get_destination(),
697 {
698 session->set_last_sequency_number(session->get_last_sequence_number() + 1);
699 }
700 else
701 {
702 // Process more next time protocol is updated
703 break;
704 }
705 }
706
707 if (session->get_number_of_remaining_packets() == 0)
708 {
709 if (session->is_broadcast())
710 {
711 LOG_DEBUG("[TP]: Completed broadcast tx session for 0x%05X", session->get_parameter_group_number());
712 close_session(session, true);
713 }
714 else
715 {
717 }
718 }
719 else if ((session->get_cts_number_of_packets_remaining() == 0) && !session->is_broadcast())
720 {
721 session->set_state(StateMachineState::WaitForClearToSend);
722 }
723 }
724
725 void TransportProtocolManager::update_state_machine(std::shared_ptr<TransportProtocolSession> &session)
726 {
727 switch (session->state)
728 {
730 break;
731
733 {
735 {
737 }
738 }
739 break;
740
742 {
743 if (send_request_to_send(session))
744 {
745 session->set_state(StateMachineState::WaitForClearToSend);
746 }
747 }
748 break;
749
751 {
752 if (session->get_time_since_last_update() > T2_T3_TIMEOUT_MS)
753 {
754 LOG_ERROR("[TP]: Timeout tx session for 0x%05X (expected CTS)", session->get_parameter_group_number());
755 if (session->get_cts_number_of_packets() > 0)
756 {
757 // A connection is only considered established if we've received at least one CTS before
758 // And we can only abort a connection if it's considered established
760 }
761 else
762 {
763 close_session(session, false);
764 }
765 }
766 }
767 break;
768
770 {
771 if (send_clear_to_send(session))
772 {
774 }
775 }
776 break;
777
779 {
780 if (session->is_broadcast() && (session->get_time_since_last_update() < configuration->get_minimum_time_between_transport_protocol_bam_frames()))
781 {
782 // Need to wait before sending the next data frame of the broadcast session
783 }
784 else
785 {
787 }
788 }
789 break;
790
792 {
793 if (session->is_broadcast())
794 {
795 // Broadcast message timeout check
796 if (session->get_time_since_last_update() > T1_TIMEOUT_MS)
797 {
798 LOG_WARNING("[TP]: Broadcast rx session timeout");
799 close_session(session, false);
800 }
801 }
802 else if (session->get_cts_number_of_packets_remaining() == session->get_cts_number_of_packets())
803 {
804 // Waiting to receive the first data frame after CTS
805 if (session->get_time_since_last_update() > T2_T3_TIMEOUT_MS)
806 {
807 LOG_ERROR("[TP]: Timeout for destination-specific rx session (expected first data frame)");
809 }
810 }
811 else
812 {
813 // Waiting on sequential data frames
814 if (session->get_time_since_last_update() > T1_TIMEOUT_MS)
815 {
816 LOG_ERROR("[TP]: Timeout for destination-specific rx session (expected sequential data frame)");
818 }
819 }
820 }
821 break;
822
824 {
825 if (session->get_time_since_last_update() > T2_T3_TIMEOUT_MS)
826 {
827 LOG_ERROR("[TP]: Timeout tx session for 0x%05X (expected EOMA)", session->get_parameter_group_number());
829 }
830 }
831 break;
832 }
833 }
834
835 bool TransportProtocolManager::abort_session(std::shared_ptr<TransportProtocolSession> &session, ConnectionAbortReason reason)
836 {
837 bool retVal = false;
838 std::shared_ptr<InternalControlFunction> myControlFunction;
839 std::shared_ptr<ControlFunction> partnerControlFunction;
840 if (TransportProtocolSession::Direction::Transmit == session->get_direction())
841 {
842 myControlFunction = std::static_pointer_cast<InternalControlFunction>(session->get_source());
843 partnerControlFunction = session->get_destination();
844 }
845 else
846 {
847 myControlFunction = std::static_pointer_cast<InternalControlFunction>(session->get_destination());
848 partnerControlFunction = session->get_source();
849 }
850
851 if ((nullptr != myControlFunction) && (nullptr != partnerControlFunction))
852 {
853 retVal = send_abort(myControlFunction, partnerControlFunction, session->get_parameter_group_number(), reason);
854 }
855 close_session(session, false);
856 return retVal;
857 }
858
859 bool TransportProtocolManager::send_abort(std::shared_ptr<InternalControlFunction> sender,
860 std::shared_ptr<ControlFunction> receiver,
861 std::uint32_t parameterGroupNumber,
862 ConnectionAbortReason reason) const
863 {
864 const std::array<std::uint8_t, CAN_DATA_LENGTH> buffer{
866 static_cast<std::uint8_t>(reason),
867 0xFF,
868 0xFF,
869 0xFF,
870 static_cast<std::uint8_t>(parameterGroupNumber & 0xFF),
871 static_cast<std::uint8_t>((parameterGroupNumber >> 8) & 0xFF),
872 static_cast<std::uint8_t>((parameterGroupNumber >> 16) & 0xFF)
873 };
874 return sendCANFrameCallback(static_cast<std::uint32_t>(CANLibParameterGroupNumber::TransportProtocolConnectionManagement),
875 CANDataSpan(buffer.data(), buffer.size()),
876 sender,
877 receiver,
879 }
880
881 void TransportProtocolManager::close_session(std::shared_ptr<TransportProtocolSession> &session, bool successful)
882 {
883 session->complete(successful);
884
885 auto sessionLocation = std::find(activeSessions.begin(), activeSessions.end(), session);
886 if (activeSessions.end() != sessionLocation)
887 {
888 activeSessions.erase(sessionLocation);
889 LOG_DEBUG("[TP]: Session Closed");
890 }
891 }
892
893 bool TransportProtocolManager::send_broadcast_announce_message(std::shared_ptr<TransportProtocolSession> &session) const
894 {
895 const std::array<std::uint8_t, CAN_DATA_LENGTH> buffer{
897 static_cast<std::uint8_t>(session->get_message_length() & 0xFF),
898 static_cast<std::uint8_t>((session->get_message_length() >> 8) & 0xFF),
899 session->get_total_number_of_packets(),
900 0xFF,
901 static_cast<std::uint8_t>(session->get_parameter_group_number() & 0xFF),
902 static_cast<std::uint8_t>((session->get_parameter_group_number() >> 8) & 0xFF),
903 static_cast<std::uint8_t>((session->get_parameter_group_number() >> 16) & 0xFF)
904 };
905 return sendCANFrameCallback(static_cast<std::uint32_t>(CANLibParameterGroupNumber::TransportProtocolConnectionManagement),
906 CANDataSpan(buffer.data(), buffer.size()),
907 std::static_pointer_cast<InternalControlFunction>(session->get_source()),
908 nullptr,
910 }
911
912 bool TransportProtocolManager::send_request_to_send(std::shared_ptr<TransportProtocolSession> &session) const
913 {
914 const std::array<std::uint8_t, CAN_DATA_LENGTH> buffer{
916 static_cast<std::uint8_t>(session->get_message_length() & 0xFF),
917 static_cast<std::uint8_t>((session->get_message_length() >> 8) & 0xFF),
918 session->get_total_number_of_packets(),
919 session->get_rts_number_of_packet_limit(),
920 static_cast<std::uint8_t>(session->get_parameter_group_number() & 0xFF),
921 static_cast<std::uint8_t>((session->get_parameter_group_number() >> 8) & 0xFF),
922 static_cast<std::uint8_t>((session->get_parameter_group_number() >> 16) & 0xFF)
923 };
924 return sendCANFrameCallback(static_cast<std::uint32_t>(CANLibParameterGroupNumber::TransportProtocolConnectionManagement),
925 CANDataSpan(buffer.data(), buffer.size()),
926 std::static_pointer_cast<InternalControlFunction>(session->get_source()),
927 session->get_destination(),
929 }
930
931 bool TransportProtocolManager::send_clear_to_send(std::shared_ptr<TransportProtocolSession> &session) const
932 {
933 bool retVal = false;
934
935 std::uint8_t packetsThisSegment = session->get_number_of_remaining_packets();
936 if (packetsThisSegment > session->get_rts_number_of_packet_limit())
937 {
938 packetsThisSegment = session->get_rts_number_of_packet_limit();
939 }
940 else if (packetsThisSegment > 16)
941 {
943 packetsThisSegment = 16;
944 }
945
946 const std::array<std::uint8_t, CAN_DATA_LENGTH> buffer{
948 packetsThisSegment,
949 static_cast<std::uint8_t>(session->get_last_packet_number() + 1),
950 0xFF,
951 0xFF,
952 static_cast<std::uint8_t>(session->get_parameter_group_number() & 0xFF),
953 static_cast<std::uint8_t>((session->get_parameter_group_number() >> 8) & 0xFF),
954 static_cast<std::uint8_t>((session->get_parameter_group_number() >> 16) & 0xFF)
955 };
956 retVal = sendCANFrameCallback(static_cast<std::uint32_t>(CANLibParameterGroupNumber::TransportProtocolConnectionManagement),
957 CANDataSpan(buffer.data(), buffer.size()),
958 std::static_pointer_cast<InternalControlFunction>(session->get_destination()), // Since we're the receiving side, we are the destination of the session
959 session->get_source(),
961 if (retVal)
962 {
963 session->set_cts_number_of_packets(packetsThisSegment);
964 session->set_acknowledged_packet_number(session->get_last_packet_number());
965 }
966 return retVal;
967 }
968
969 bool TransportProtocolManager::send_end_of_session_acknowledgement(std::shared_ptr<TransportProtocolSession> &session) const
970 {
971 std::uint32_t messageLength = session->get_message_length();
972 std::uint32_t parameterGroupNumber = session->get_parameter_group_number();
973
974 const std::array<std::uint8_t, CAN_DATA_LENGTH> buffer{
976 static_cast<std::uint8_t>(messageLength & 0xFF),
977 static_cast<std::uint8_t>((messageLength >> 8) & 0xFF),
978 session->get_total_number_of_packets(),
979 0xFF,
980 static_cast<std::uint8_t>(parameterGroupNumber & 0xFF),
981 static_cast<std::uint8_t>((parameterGroupNumber >> 8) & 0xFF),
982 static_cast<std::uint8_t>((parameterGroupNumber >> 16) & 0xFF),
983 };
984
985 return sendCANFrameCallback(static_cast<std::uint32_t>(CANLibParameterGroupNumber::TransportProtocolConnectionManagement),
986 CANDataSpan(buffer.data(), buffer.size()),
987 std::static_pointer_cast<InternalControlFunction>(session->get_destination()), // Since we're the receiving side, we are the destination of the session
988
989 session->get_source(),
991 }
992
993 bool TransportProtocolManager::has_session(std::shared_ptr<ControlFunction> source, std::shared_ptr<ControlFunction> destination)
994 {
995 return std::any_of(activeSessions.begin(), activeSessions.end(), [&](const std::shared_ptr<TransportProtocolManager::TransportProtocolSession> &session) {
996 return session->matches(source, destination);
997 });
998 }
999
1000 std::shared_ptr<TransportProtocolManager::TransportProtocolSession> TransportProtocolManager::get_session(std::shared_ptr<ControlFunction> source,
1001 std::shared_ptr<ControlFunction> destination)
1002 {
1003 auto result = std::find_if(activeSessions.begin(), activeSessions.end(), [&](const std::shared_ptr<TransportProtocolManager::TransportProtocolSession> &session) {
1004 return session->matches(source, destination);
1005 });
1006 return (activeSessions.end() != result) ? (*result) : nullptr;
1007 }
1008
1009 const std::vector<std::shared_ptr<TransportProtocolManager::TransportProtocolSession>> &TransportProtocolManager::get_sessions() const
1010 {
1011 return activeSessions;
1012 }
1013}
Defines some PGNs that are used in the library or are very common.
A representation of an ISOBUS ECU that we can send from. Use this class when defining your own contro...
An abstraction of a CAN message, could be > 8 data bytes.
A class that acts as a logging sink. The intent is that someone could make their own derived class of...
A protocol that handles the ISO11783/J1939 transport protocol. It handles both the broadcast version ...
A utility class that allows easy interpretation of a 32 bit CAN identifier.
@ PriorityLowest7
The lowest priority.
@ PriorityDefault6
The default priority.
std::uint32_t get_parameter_group_number() const
Returns the PGN encoded in the identifier.
static constexpr std::uint8_t GLOBAL_ADDRESS
The broadcast CAN address.
@ Extended
Frame is a modern 29 bit ID CAN frame.
A class that represents data of a CAN message by holding a vector of bytes.
void set_byte(std::size_t index, std::uint8_t value)
Set the byte at the given index.
A class that represents a generic CAN message of arbitrary length.
@ Receive
Message is being received.
std::uint32_t get_data_length() const
Returns the length of the data in the CAN message.
bool has_valid_source_control_function() const
Returns whether the message is sent by a device that claimed its address on the bus.
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...
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.
bool is_broadcast() const
Returns whether the message is sent as a broadcast message / to all devices on the bus.
bool is_destination_our_device() const
Returns whether the message is destined for our device on the bus.
A class that defines stack-wide configuration data. You can set the values in there to suit your spec...
std::uint8_t get_max_number_of_network_manager_protocol_frames_per_update() const
Returns the max number of data frames the stack will send from each transport layer protocol,...
std::uint8_t get_number_of_packets_per_cts_message() const
Get the the number of packets per CTS packet for TP sessions.
std::uint32_t get_max_number_transport_protocol_sessions() const
Returns the max number of concurrent TP sessions.
std::uint32_t get_minimum_time_between_transport_protocol_bam_frames() const
Returns the minimum time to wait between sending BAM frames.
void set_acknowledged_packet_number(std::uint8_t value)
Set the last acknowledged packet number by the receiver.
TransportProtocolSession(TransportProtocolSessionBase::Direction direction, std::unique_ptr< CANMessageData > data, std::uint32_t parameterGroupNumber, std::uint16_t totalMessageSize, std::uint8_t clearToSendPacketMax, std::shared_ptr< ControlFunction > source, std::shared_ptr< ControlFunction > destination, TransmitCompleteCallback sessionCompleteCallback, void *parentPointer)
The constructor for a session, for advanced use only. In most cases, you should use the CANNetworkMan...
std::uint8_t get_cts_number_of_packets_remaining() const
Get the number of packets to be sent in response to the current CTS.
void set_state(StateMachineState value)
Set the state of the session.
StateMachineState get_state() const
Get the state of the session.
std::uint32_t get_total_bytes_transferred() const override
Get the number of bytes that have been sent or received in this session.
std::uint8_t get_cts_number_of_packets() const
Get the number of packets that can be sent in response to the current CTS.
std::uint8_t get_rts_number_of_packet_limit() const
Get the maximum number of packets that can be sent per CTS as indicated by the RTS message.
std::uint16_t get_message_length() const
Get the total number of bytes that will be sent or received in this session.
void set_cts_number_of_packets(std::uint8_t value)
Set the number of packets to be sent in response to the curent CTS.
std::uint8_t get_last_sequence_number() const
Get the last sequence number that was processed.
bool is_broadcast() const
Get whether or not this session is a broadcast session (BAM)
std::uint8_t get_number_of_remaining_packets() const
Get the number of packets that remain to be sent or received in this session.
std::uint8_t get_total_number_of_packets() const
Get the total number of packets that will be sent or received in this session.
std::uint8_t get_last_packet_number() const
Get the last packet number that was processed.
void set_last_sequency_number(std::uint8_t value)
Set the last sequence number that will be processed.
void process_connection_management_message(const CANMessage &message)
Processes a connection management message.
static constexpr std::uint32_t REQUEST_TO_SEND_MULTIPLEXOR
(16) TP.CM_RTS Multiplexor
static constexpr std::uint32_t END_OF_MESSAGE_ACKNOWLEDGE_MULTIPLEXOR
(19) TP.CM_EOM_ACK Multiplexor
bool send_broadcast_announce_message(std::shared_ptr< TransportProtocolSession > &session) const
Sends the "broadcast announce" message.
bool send_end_of_session_acknowledgement(std::shared_ptr< TransportProtocolSession > &session) const
Sends the "end of message acknowledgement" message for the provided session.
bool protocol_transmit_message(std::uint32_t parameterGroupNumber, std::unique_ptr< CANMessageData > &data, std::shared_ptr< ControlFunction > source, std::shared_ptr< ControlFunction > destination, TransmitCompleteCallback sessionCompleteCallback, void *parentPointer)
The network manager calls this to see if the protocol can accept a long CAN message for processing.
static constexpr std::uint32_t MAX_PROTOCOL_DATA_LENGTH
The max number of bytes that this protocol can transfer.
void process_clear_to_send(const std::shared_ptr< ControlFunction > source, const std::shared_ptr< ControlFunction > destination, std::uint32_t parameterGroupNumber, std::uint8_t packetsToBeSent, std::uint8_t nextPacketNumber)
Processes the Clear To Send (CTS) message.
void process_abort(const std::shared_ptr< ControlFunction > source, const std::shared_ptr< ControlFunction > destination, std::uint32_t parameterGroupNumber, TransportProtocolManager::ConnectionAbortReason reason)
Processes an abort message in the CAN transport protocol.
const CANMessageFrameCallback sendCANFrameCallback
A callback for sending a CAN frame.
static constexpr std::uint32_t BROADCAST_ANNOUNCE_MESSAGE_MULTIPLEXOR
(32) TP.BAM Multiplexor
TransportProtocolManager(const CANMessageFrameCallback &sendCANFrameCallback, const CANMessageCallback &canMessageReceivedCallback, const CANNetworkConfiguration *configuration)
The constructor for the TransportProtocolManager, for advanced use only. In most cases,...
ConnectionAbortReason
A list of all defined abort reasons in ISO11783.
@ DuplicateSequenceNumber
Re-received a sequence number we've already processed.
@ BadSequenceNumber
Incorrect sequence number was received and cannot be recovered.
@ AlreadyInCMSession
We are already in a connection mode session and can't support another.
@ ClearToSendReceivedWhileTransferInProgress
A CTS was received while already processing the last CTS.
@ AnyOtherError
Any reason not defined in the standard.
@ UnexpectedDataTransferPacketReceived
A data packet was received outside the proper state.
bool send_request_to_send(std::shared_ptr< TransportProtocolSession > &session) const
Sends the "request to send" message as part of initiating a transmit.
static constexpr std::uint16_t T1_TIMEOUT_MS
The t1 timeout as defined by the standard.
std::shared_ptr< TransportProtocolSession > get_session(std::shared_ptr< ControlFunction > source, std::shared_ptr< ControlFunction > destination)
Gets a TP session from the passed in source and destination combination.
const std::vector< std::shared_ptr< TransportProtocolSession > > & get_sessions() const
Gets all the active transport protocol sessions that are currently active.
static constexpr std::uint32_t CLEAR_TO_SEND_MULTIPLEXOR
(17) TP.CM_CTS Multiplexor
const CANMessageCallback canMessageReceivedCallback
A callback for when a complete CAN message is received using the TP protocol.
bool abort_session(std::shared_ptr< TransportProtocolSession > &session, ConnectionAbortReason reason)
Aborts the session with the specified abort reason. Sends a CAN message.
std::vector< std::shared_ptr< TransportProtocolSession > > activeSessions
A list of all active TP sessions.
void process_request_to_send(const std::shared_ptr< ControlFunction > source, const std::shared_ptr< ControlFunction > destination, std::uint32_t parameterGroupNumber, std::uint16_t totalMessageSize, std::uint8_t totalNumberOfPackets, std::uint8_t clearToSendPacketMax)
Processes a request to send a message over the CAN transport protocol.
void process_end_of_session_acknowledgement(const std::shared_ptr< ControlFunction > source, const std::shared_ptr< ControlFunction > destination, std::uint32_t parameterGroupNumber)
Processes the end of session acknowledgement.
void close_session(std::shared_ptr< TransportProtocolSession > &session, bool successful)
Gracefully closes a session to prepare for a new session.
void send_data_transfer_packets(std::shared_ptr< TransportProtocolSession > &session)
Sends data transfer packets for the specified TransportProtocolSession.
bool has_session(std::shared_ptr< ControlFunction > source, std::shared_ptr< ControlFunction > destination)
Checks if the source and destination control function have an active session/connection.
static constexpr std::uint8_t SEQUENCE_NUMBER_DATA_INDEX
The index of the sequence number in a frame.
bool send_abort(std::shared_ptr< InternalControlFunction > sender, std::shared_ptr< ControlFunction > receiver, std::uint32_t parameterGroupNumber, ConnectionAbortReason reason) const
Send an abort with no corresponding session with the specified abort reason. Sends a CAN message.
static constexpr std::uint8_t PROTOCOL_BYTES_PER_FRAME
The number of payload bytes per frame minus overhead of sequence number.
void update_state_machine(std::shared_ptr< TransportProtocolSession > &session)
Update the state machine for the passed in session.
static constexpr std::uint16_t T2_T3_TIMEOUT_MS
The t2/t3 timeouts as defined by the standard.
void process_message(const CANMessage &message)
A generic way for a protocol to process a received message.
bool send_clear_to_send(std::shared_ptr< TransportProtocolSession > &session) const
Sends the "clear to send" message.
StateMachineState
The states that a TP session could be in. Used for the internal state machine.
@ SendBroadcastAnnounce
We are sending the broadcast announce message (BAM)
@ WaitForEndOfMessageAcknowledge
We are waiting for an end of message acknowledgement.
@ SendClearToSend
We are sending clear to send message.
@ SendRequestToSend
We are sending the request to send message.
@ WaitForDataTransferPacket
We are waiting for data transfer packets.
@ WaitForClearToSend
We are waiting for a clear to send message.
@ None
Protocol session is not in progress.
@ SendDataTransferPackets
A Tx data session is in progress.
void update()
Updates all sessions managed by this protocol manager instance.
void process_data_transfer_message(const CANMessage &message)
Processes a data transfer message.
const CANNetworkConfiguration * configuration
The configuration to use for this protocol.
static constexpr std::uint32_t CONNECTION_ABORT_MULTIPLEXOR
(255) Abort multiplexor
void process_broadcast_announce_message(const std::shared_ptr< ControlFunction > source, std::uint32_t parameterGroupNumber, std::uint16_t totalMessageSize, std::uint8_t totalNumberOfPackets)
Processes a broadcast announce message.
An object to keep track of session information internally.
std::uint32_t get_message_length() const
Get the total number of bytes that will be sent or received in this session.
Direction
Enumerates the possible session directions.
@ Transmit
We are transmitting a message.
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.
std::function< bool(std::uint32_t parameterGroupNumber, CANDataSpan data, std::shared_ptr< InternalControlFunction > sourceControlFunction, std::shared_ptr< ControlFunction > destinationControlFunction, CANIdentifier::CANPriority priority)> CANMessageFrameCallback
A callback for communicating CAN message frames.
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.
std::function< void(const CANMessage &message)> CANMessageCallback
A callback for communicating CAN messages.