AgIsoStack++
A control-function-focused implementation of the major ISOBUS and J1939 protocols
Loading...
Searching...
No Matches
can_extended_transport_protocol.cpp
Go to the documentation of this file.
1//================================================================================================
9//================================================================================================
10
12
17#include "isobus/utility/system_timing.hpp"
18#include "isobus/utility/to_string.hpp"
19
20#include <algorithm>
21#include <memory>
22
23namespace isobus
24{
29
31 {
32 state = value;
33 update_timestamp();
34 }
35
37 {
38 uint32_t transferred = get_last_packet_number() * PROTOCOL_BYTES_PER_FRAME;
39 if (transferred > get_message_length())
40 {
41 transferred = get_message_length();
42 }
43 return transferred;
44 }
45
47 {
48 auto packetsSinceDPO = static_cast<std::uint8_t>(get_last_packet_number() - lastAcknowledgedPacketNumber);
49 return dataPacketOffsetPacketCount - packetsSinceDPO;
50 }
51
53 {
54 dataPacketOffsetPacketCount = value;
55 update_timestamp();
56 }
57
59 {
60 return dataPacketOffsetPacketCount;
61 }
62
64 {
65 return clearToSendPacketCountLimit;
66 }
67
69 {
70 clearToSendPacketCountLimit = value;
71 update_timestamp();
72 }
73
78
80 {
81 return lastSequenceNumber + sequenceNumberOffset;
82 }
83
85 {
86 sequenceNumberOffset = value;
87 update_timestamp();
88 }
89
91 {
92 lastSequenceNumber = value;
93 update_timestamp();
94 }
95
97 {
98 lastAcknowledgedPacketNumber = value;
99 update_timestamp();
100 }
101
103 {
104 return lastAcknowledgedPacketNumber;
105 }
106
108 {
109 return get_total_number_of_packets() - get_last_packet_number();
110 }
111
113 {
114 auto totalNumberOfPackets = get_message_length() / PROTOCOL_BYTES_PER_FRAME;
115 if ((get_message_length() % PROTOCOL_BYTES_PER_FRAME) > 0)
116 {
117 totalNumberOfPackets++;
118 }
119 return totalNumberOfPackets;
120 }
121
130
131 void ExtendedTransportProtocolManager::process_request_to_send(const std::shared_ptr<ControlFunction> source,
132 const std::shared_ptr<ControlFunction> destination,
133 std::uint32_t parameterGroupNumber,
134 std::uint32_t totalMessageSize)
135 {
137 {
138 // TODO: consider using maximum memory instead of maximum number of sessions
139 LOG_WARNING("[ETP]: Replying with abort to Request To Send (RTS) for 0x%05X, configured maximum number of sessions reached.",
140 parameterGroupNumber);
141 send_abort(std::static_pointer_cast<InternalControlFunction>(destination), source, parameterGroupNumber, ConnectionAbortReason::AlreadyInCMSession);
142 }
143 else
144 {
145 auto oldSession = get_session(source, destination);
146 if (nullptr != oldSession)
147 {
148 if (oldSession->get_parameter_group_number() != parameterGroupNumber)
149 {
150 LOG_ERROR("[ETP]: Received Request To Send (RTS) while a session already existed for this source and destination, aborting for 0x%05X...",
151 parameterGroupNumber);
153 }
154 else
155 {
156 LOG_WARNING("[ETP]: Received Request To Send (RTS) while a session already existed for this source and destination and parameterGroupNumber, overwriting for 0x%05X...",
157 parameterGroupNumber);
158 close_session(oldSession, false);
159 }
160 }
161
162 auto newSession = std::make_shared<ExtendedTransportProtocolSession>(ExtendedTransportProtocolSession::Direction::Receive,
163 std::unique_ptr<CANMessageData>(new CANMessageDataVector(totalMessageSize)),
164 parameterGroupNumber,
165 totalMessageSize,
166 source,
167 destination,
168 nullptr, // No callback
169 nullptr);
170
171 // Request the maximum number of packets per DPO via the CTS message
172 newSession->set_cts_number_of_packet_limit(configuration->get_number_of_packets_per_dpo_message());
173
174 newSession->set_state(StateMachineState::SendClearToSend);
175 activeSessions.push_back(newSession);
176 LOG_DEBUG("[ETP]: New rx session for 0x%05X. Source: %hu, destination: %hu", parameterGroupNumber, source->get_address(), destination->get_address());
177 update_state_machine(newSession);
178 }
179 }
180
181 void ExtendedTransportProtocolManager::process_clear_to_send(const std::shared_ptr<ControlFunction> source,
182 const std::shared_ptr<ControlFunction> destination,
183 std::uint32_t parameterGroupNumber,
184 std::uint8_t packetsToBeSent,
185 std::uint32_t nextPacketNumber)
186 {
187 auto session = get_session(destination, source);
188 if (nullptr != session)
189 {
190 if (session->get_parameter_group_number() != parameterGroupNumber)
191 {
192 LOG_ERROR("[ETP]: 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);
194 send_abort(std::static_pointer_cast<InternalControlFunction>(destination), source, parameterGroupNumber, ConnectionAbortReason::ClearToSendReceivedWhileTransferInProgress);
195 }
196 else if (nextPacketNumber > session->get_total_number_of_packets())
197 {
198 LOG_ERROR("[ETP]: Received a Clear To Send (CTS) message for 0x%05X with a bad sequence number, aborting...", parameterGroupNumber);
200 }
201 else if (StateMachineState::WaitForClearToSend != session->state)
202 {
203 // The session exists, but we're not in the right state to receive a CTS, so we must abort
204 LOG_WARNING("[ETP]: Received a Clear To Send (CTS) message for 0x%05X, but not expecting one, aborting session->", parameterGroupNumber);
206 }
207 else
208 {
209 session->set_acknowledged_packet_number(nextPacketNumber - 1);
210 session->set_cts_number_of_packet_limit(packetsToBeSent);
211
212 // If 0 was sent as the packet number, they want us to wait.
213 // Just sit here in this state until we get a non-zero packet count
214 if (0 != packetsToBeSent)
215 {
216 session->set_state(StateMachineState::SendDataPacketOffset);
217 }
218 }
219 }
220 else
221 {
222 // We got a CTS but no session exists, by the standard we must ignore it
223 LOG_WARNING("[ETP]: Received Clear To Send (CTS) for 0x%05X while no session existed for this source and destination, ignoring...", parameterGroupNumber);
224 }
225 }
226
227 void ExtendedTransportProtocolManager::process_data_packet_offset(const std::shared_ptr<ControlFunction> source,
228 const std::shared_ptr<ControlFunction> destination,
229 std::uint32_t parameterGroupNumber,
230 std::uint8_t numberOfPackets,
231 std::uint32_t packetOffset)
232 {
233 auto session = get_session(source, destination);
234 if (nullptr != session)
235 {
236 if (session->get_parameter_group_number() != parameterGroupNumber)
237 {
238 LOG_ERROR("[ETP]: Received a Data Packet Offset message for 0x%05X while a session already existed for this source and destination with a different PGN, sending abort for both...", parameterGroupNumber);
240 send_abort(std::static_pointer_cast<InternalControlFunction>(destination), source, parameterGroupNumber, ConnectionAbortReason::UnexpectedDataPacketOffsetPGN);
241 }
242 else if (StateMachineState::WaitForDataPacketOffset != session->state)
243 {
244 // The session exists, but we're not in the right state to receive a DPO, so we must abort
245 LOG_WARNING("[ETP]: Received a Data Packet Offset message for 0x%05X, but not expecting one, aborting session->", parameterGroupNumber);
246 abort_session(session, ConnectionAbortReason::UnexpectedDataPacketOffsetReceived);
247 }
248 else if (numberOfPackets > session->get_cts_number_of_packet_limit())
249 {
250 LOG_ERROR("[ETP]: Received a Data Packet Offset message for 0x%05X with an higher number of packets than our CTS, aborting...", parameterGroupNumber);
252 }
253 else if (packetOffset != session->get_last_acknowledged_packet_number())
254 {
255 LOG_ERROR("[ETP]: Received a Data Packet Offset message for 0x%05X with a bad sequence number, aborting...", parameterGroupNumber);
257 }
258 else
259 {
260 session->set_dpo_number_of_packets(numberOfPackets);
261 session->set_sequence_number_offset(packetOffset);
262 session->set_last_sequency_number(0);
263
264 // If 0 was sent as the packet number, they want us to wait.
265 // Just sit here in this state until we get a non-zero packet count
266 if (0 != numberOfPackets)
267 {
269 }
270 }
271 }
272 else
273 {
274 // We got a CTS but no session exists, by the standard we must ignore it
275 LOG_WARNING("[ETP]: Received Data Packet Offset for 0x%05X while no session existed for this source and destination, ignoring...", parameterGroupNumber);
276 }
277 }
278
279 void ExtendedTransportProtocolManager::process_end_of_session_acknowledgement(const std::shared_ptr<ControlFunction> source,
280 const std::shared_ptr<ControlFunction> destination,
281 std::uint32_t parameterGroupNumber,
282 std::uint32_t numberOfBytesTransferred)
283 {
284 auto session = get_session(destination, source);
285 if (nullptr != session)
286 {
288 {
289 session->state = StateMachineState::None;
290 bool successful = (numberOfBytesTransferred == session->get_message_length());
291 close_session(session, successful);
292 LOG_DEBUG("[ETP]: Completed tx session for 0x%05X from %hu", parameterGroupNumber, source->get_address());
293 }
294 else
295 {
296 // The session exists, but we're not in the right state to receive an EOM, by the standard we must ignore it
297 LOG_WARNING("[ETP]: Received an End Of Message Acknowledgement message for 0x%05X, but not expecting one, ignoring.", parameterGroupNumber);
298 }
299 }
300 else
301 {
302 LOG_WARNING("[ETP]: Received End Of Message Acknowledgement for 0x%05X while no session existed for this source and destination, ignoring.", parameterGroupNumber);
303 }
304 }
305
306 void ExtendedTransportProtocolManager::process_abort(const std::shared_ptr<ControlFunction> source,
307 const std::shared_ptr<ControlFunction> destination,
308 std::uint32_t parameterGroupNumber,
310 {
311 bool foundSession = false;
312
313 auto session = get_session(source, destination);
314 if ((nullptr != session) && (session->get_parameter_group_number() == parameterGroupNumber))
315 {
316 foundSession = true;
317 LOG_ERROR("[ETP]: Received an abort (reason=%hu) for an rx session for parameterGroupNumber 0x%05X", static_cast<std::uint8_t>(reason), parameterGroupNumber);
318 close_session(session, false);
319 }
320 session = get_session(destination, source);
321 if ((nullptr != session) && (session->get_parameter_group_number() == parameterGroupNumber))
322 {
323 foundSession = true;
324 LOG_ERROR("[ETP]: Received an abort (reason=%hu) for a tx session for parameterGroupNumber 0x%05X", static_cast<std::uint8_t>(reason), parameterGroupNumber);
325 close_session(session, false);
326 }
327
328 if (!foundSession)
329 {
330 LOG_WARNING("[ETP]: Received an abort (reason=%hu) with no matching session for parameterGroupNumber 0x%05X", static_cast<std::uint8_t>(reason), parameterGroupNumber);
331 }
332 }
333
335 {
336 if (CAN_DATA_LENGTH != message.get_data_length())
337 {
338 LOG_WARNING("[ETP]: Received a Connection Management message of invalid length %hu", message.get_data_length());
339 return;
340 }
341
342 const auto parameterGroupNumber = message.get_uint24_at(5);
343
344 switch (message.get_uint8_at(0))
345 {
347 {
348 const auto totalMessageSize = message.get_uint32_at(1);
351 parameterGroupNumber,
352 totalMessageSize);
353 }
354 break;
355
357 {
358 const auto packetsToBeSent = message.get_uint8_at(1);
359 const auto nextPacketNumber = message.get_uint24_at(2);
362 parameterGroupNumber,
363 packetsToBeSent,
364 nextPacketNumber);
365 }
366 break;
367
369 {
370 const auto numberOfPackets = message.get_uint8_at(1);
371 const auto packetOffset = message.get_uint24_at(2);
374 parameterGroupNumber,
375 numberOfPackets,
376 packetOffset);
377 }
378 break;
379
381 {
382 const auto numberOfBytesTransferred = message.get_uint32_at(1);
385 parameterGroupNumber,
386 numberOfBytesTransferred);
387 }
388 break;
389
391 {
392 const auto reason = static_cast<ConnectionAbortReason>(message.get_uint8_at(1));
395 parameterGroupNumber,
396 reason);
397 }
398 break;
399
400 default:
401 {
402 LOG_WARNING("[ETP]: Bad Mux in Transport Protocol Connection Management message");
403 }
404 break;
405 }
406 }
407
409 {
410 if (CAN_DATA_LENGTH != message.get_data_length())
411 {
412 LOG_WARNING("[ETP]: Received a Data Transfer message of invalid length %hu", message.get_data_length());
413 return;
414 }
415
416 auto source = message.get_source_control_function();
417 auto destination = message.get_destination_control_function();
418
419 auto sequenceNumber = message.get_uint8_at(SEQUENCE_NUMBER_DATA_INDEX);
420
421 auto session = get_session(source, destination);
422 if (nullptr != session)
423 {
425 {
426 LOG_WARNING("[ETP]: Received a Data Transfer message from %hu while not expecting one, sending abort", source->get_address());
428 }
429 else if (sequenceNumber == session->get_last_sequence_number())
430 {
431 LOG_ERROR("[ETP]: Aborting rx session for 0x%05X due to duplicate sequence number", session->get_parameter_group_number());
433 }
434 else if (sequenceNumber == (session->get_last_sequence_number() + 1))
435 {
436 // Convert data type to a vector to allow for manipulation
437 auto &data = static_cast<CANMessageDataVector &>(session->get_data());
438
439 // Correct sequence number, copy the data
440 for (std::uint8_t i = 0; i < PROTOCOL_BYTES_PER_FRAME; i++)
441 {
442 std::uint32_t currentDataIndex = (PROTOCOL_BYTES_PER_FRAME * session->get_last_packet_number()) + i;
443 if (currentDataIndex < session->get_message_length())
444 {
445 data.set_byte(currentDataIndex, message.get_uint8_at(1 + i));
446 }
447 else
448 {
449 // Reached the end of the message, no need to copy any more data
450 break;
451 }
452 }
453
454 session->set_last_sequency_number(sequenceNumber);
455 if (session->get_number_of_remaining_packets() == 0)
456 {
457 // Send End of Message Acknowledgement for sessions with specific destination only
459
460 // Construct the completed message
462 session->get_parameter_group_number(),
464 destination->get_address(),
465 source->get_address());
466 CANMessage completedMessage(CANMessage::Type::Receive,
467 identifier,
468 std::move(data),
469 source,
470 destination,
471 0);
472
473 canMessageReceivedCallback(completedMessage);
474 close_session(session, true);
475 LOG_DEBUG("[ETP]: Completed rx session for 0x%05X from %hu", session->get_parameter_group_number(), source->get_address());
476 }
477 else if (session->get_dpo_number_of_packets_remaining() == 0)
478 {
479 session->set_state(StateMachineState::SendClearToSend);
480 update_state_machine(session);
481 }
482 }
483 else
484 {
485 LOG_ERROR("[ETP]: Aborting rx session for 0x%05X due to bad sequence number", session->get_parameter_group_number());
487 }
488 }
489 }
490
492 {
493 // TODO: Allow sniffing of messages to all addresses, not just the ones we normally listen to (#297)
495 {
496 switch (message.get_identifier().get_parameter_group_number())
497 {
498 case static_cast<std::uint32_t>(CANLibParameterGroupNumber::ExtendedTransportProtocolConnectionManagement):
499 {
501 }
502 break;
503
504 case static_cast<std::uint32_t>(CANLibParameterGroupNumber::ExtendedTransportProtocolDataTransfer):
505 {
507 }
508 break;
509
510 default:
511 break;
512 }
513 }
514 }
515
516 bool ExtendedTransportProtocolManager::protocol_transmit_message(std::uint32_t parameterGroupNumber,
517 std::unique_ptr<CANMessageData> &data,
518 std::shared_ptr<ControlFunction> source,
519 std::shared_ptr<ControlFunction> destination,
520 TransmitCompleteCallback sessionCompleteCallback,
521 void *parentPointer)
522 {
523 // Return false early if we can't send the message
524 if ((nullptr == data) || (data->size() <= 1785) || (data->size() > MAX_PROTOCOL_DATA_LENGTH))
525 {
526 // Invalid message length
527 return false;
528 }
529 else if ((nullptr == source) || (!source->get_address_valid()) ||
530 (nullptr == destination) || (!destination->get_address_valid()) ||
531 has_session(source, destination))
532 {
533 // Invalid source/destination or already have a session for this source and destination
534 return false;
535 }
536
537 // We can handle this message! If we only have a view of the data, let's clone the data,
538 // so we don't have to worry about it being deleted.
539 data = data->copy_if_not_owned(std::move(data));
540 auto dataLength = static_cast<std::uint32_t>(data->size());
541
542 auto session = std::make_shared<ExtendedTransportProtocolSession>(ExtendedTransportProtocolSession::Direction::Transmit,
543 std::move(data),
544 parameterGroupNumber,
545 dataLength,
546 source,
547 destination,
548 sessionCompleteCallback,
549 parentPointer);
550 session->set_state(StateMachineState::SendRequestToSend);
551 LOG_DEBUG("[ETP]: New tx session for 0x%05X. Source: %hu, destination: %hu",
552 parameterGroupNumber,
553 source->get_address(),
554 destination->get_address());
555
556 activeSessions.push_back(session);
557 update_state_machine(session);
558 return true;
559 }
560
562 {
563 // We use a fancy for loop here to allow us to remove sessions from the list while iterating
564 for (std::size_t i = activeSessions.size(); i > 0; i--)
565 {
566 auto session = activeSessions.at(i - 1);
567 if (!session->get_source()->get_address_valid())
568 {
569 LOG_WARNING("[ETP]: Closing active session as the source control function is no longer valid");
570 close_session(session, false);
571 }
572 else if (!session->get_destination()->get_address_valid())
573 {
574 LOG_WARNING("[ETP]: Closing active session as the destination control function is no longer valid");
575 close_session(session, false);
576 }
577 else if (StateMachineState::None != session->state)
578 {
579 update_state_machine(session);
580 }
581 }
582 }
583
584 void ExtendedTransportProtocolManager::send_data_transfer_packets(std::shared_ptr<ExtendedTransportProtocolSession> &session) const
585 {
586 std::array<std::uint8_t, CAN_DATA_LENGTH> buffer;
587 std::uint8_t framesToSend = session->get_dpo_number_of_packets_remaining();
589 {
591 }
592
593 // Try and send packets
594 for (std::uint8_t i = 0; i < framesToSend; i++)
595 {
596 buffer[0] = session->get_last_sequence_number() + 1;
597
598 std::uint32_t dataOffset = session->get_last_packet_number() * PROTOCOL_BYTES_PER_FRAME;
599 for (std::uint8_t j = 0; j < PROTOCOL_BYTES_PER_FRAME; j++)
600 {
601 std::uint32_t index = dataOffset + j;
602 if (index < session->get_message_length())
603 {
604 buffer[1 + j] = session->get_data().get_byte(index);
605 }
606 else
607 {
608 buffer[1 + j] = 0xFF;
609 }
610 }
611
612 if (sendCANFrameCallback(static_cast<std::uint32_t>(CANLibParameterGroupNumber::ExtendedTransportProtocolDataTransfer),
613 CANDataSpan(buffer.data(), buffer.size()),
614 std::static_pointer_cast<InternalControlFunction>(session->get_source()),
615 session->get_destination(),
617 {
618 session->set_last_sequency_number(session->get_last_sequence_number() + 1);
619 }
620 else
621 {
622 // Process more next time protocol is updated
623 break;
624 }
625 }
626
627 if (session->get_number_of_remaining_packets() == 0)
628 {
630 }
631 else if (session->get_dpo_number_of_packets_remaining() == 0)
632 {
633 session->set_state(StateMachineState::WaitForClearToSend);
634 }
635 }
636
637 void ExtendedTransportProtocolManager::update_state_machine(std::shared_ptr<ExtendedTransportProtocolSession> &session)
638 {
639 switch (session->state)
640 {
642 break;
643
645 {
646 if (send_request_to_send(session))
647 {
648 session->set_state(StateMachineState::WaitForClearToSend);
649 }
650 }
651 break;
652
654 {
655 if (session->get_time_since_last_update() > T2_T3_TIMEOUT_MS)
656 {
657 LOG_ERROR("[ETP]: Timeout tx session for 0x%05X (expected CTS)", session->get_parameter_group_number());
658 if (session->get_cts_number_of_packet_limit() > 0)
659 {
660 // A connection is only considered established if we've received at least one CTS before
661 // And we can only abort a connection if it's considered established
663 }
664 else
665 {
666 close_session(session, false);
667 }
668 }
669 }
670 break;
671
673 {
674 if (send_clear_to_send(session))
675 {
677 }
678 }
679 break;
680
682 {
683 if (session->get_time_since_last_update() > T2_T3_TIMEOUT_MS)
684 {
685 LOG_ERROR("[ETP]: Timeout rx session for 0x%05X (expected DPO)", session->get_parameter_group_number());
687 }
688 }
689 break;
690
692 {
693 if (send_data_packet_offset(session))
694 {
696 }
697 }
698 break;
699
701 {
703 }
704 break;
705
707 {
708 if (session->get_time_since_last_update() > T1_TIMEOUT_MS)
709 {
710 LOG_ERROR("[ETP]: Timeout for destination-specific rx session (expected sequential data frame)");
712 }
713 }
714 break;
715
717 {
718 if (session->get_time_since_last_update() > T2_T3_TIMEOUT_MS)
719 {
720 LOG_ERROR("[ETP]: Timeout tx session for 0x%05X (expected EOMA)", session->get_parameter_group_number());
722 }
723 }
724 break;
725 }
726 }
727
728 bool ExtendedTransportProtocolManager::abort_session(std::shared_ptr<ExtendedTransportProtocolSession> &session, ConnectionAbortReason reason)
729 {
730 bool retVal = false;
731 std::shared_ptr<InternalControlFunction> myControlFunction;
732 std::shared_ptr<ControlFunction> partnerControlFunction;
733 if (ExtendedTransportProtocolSession::Direction::Transmit == session->get_direction())
734 {
735 myControlFunction = std::static_pointer_cast<InternalControlFunction>(session->get_source());
736 partnerControlFunction = session->get_destination();
737 }
738 else
739 {
740 myControlFunction = std::static_pointer_cast<InternalControlFunction>(session->get_destination());
741 partnerControlFunction = session->get_source();
742 }
743
744 if ((nullptr != myControlFunction) && (nullptr != partnerControlFunction))
745 {
746 retVal = send_abort(myControlFunction, partnerControlFunction, session->get_parameter_group_number(), reason);
747 }
748 close_session(session, false);
749 return retVal;
750 }
751
752 bool ExtendedTransportProtocolManager::send_abort(std::shared_ptr<InternalControlFunction> sender,
753 std::shared_ptr<ControlFunction> receiver,
754 std::uint32_t parameterGroupNumber,
755 ConnectionAbortReason reason) const
756 {
757 const std::array<std::uint8_t, CAN_DATA_LENGTH> buffer{
759 static_cast<std::uint8_t>(reason),
760 0xFF,
761 0xFF,
762 0xFF,
763 static_cast<std::uint8_t>(parameterGroupNumber & 0xFF),
764 static_cast<std::uint8_t>((parameterGroupNumber >> 8) & 0xFF),
765 static_cast<std::uint8_t>((parameterGroupNumber >> 16) & 0xFF)
766 };
767 return sendCANFrameCallback(static_cast<std::uint32_t>(CANLibParameterGroupNumber::ExtendedTransportProtocolConnectionManagement),
768 CANDataSpan(buffer.data(), buffer.size()),
769 sender,
770 receiver,
772 }
773
774 void ExtendedTransportProtocolManager::close_session(std::shared_ptr<ExtendedTransportProtocolSession> &session, bool successful)
775 {
776 session->complete(successful);
777 auto sessionLocation = std::find(activeSessions.begin(), activeSessions.end(), session);
778 if (activeSessions.end() != sessionLocation)
779 {
780 activeSessions.erase(sessionLocation);
781 LOG_DEBUG("[ETP]: Session Closed");
782 }
783 }
784
785 bool ExtendedTransportProtocolManager::send_request_to_send(std::shared_ptr<ExtendedTransportProtocolSession> &session) const
786 {
787 const std::array<std::uint8_t, CAN_DATA_LENGTH> buffer{
789 static_cast<std::uint8_t>(session->get_message_length() & 0xFF),
790 static_cast<std::uint8_t>((session->get_message_length() >> 8) & 0xFF),
791 static_cast<std::uint8_t>((session->get_message_length() >> 16) & 0xFF),
792 static_cast<std::uint8_t>((session->get_message_length() >> 24) & 0xFF),
793 static_cast<std::uint8_t>(session->get_parameter_group_number() & 0xFF),
794 static_cast<std::uint8_t>((session->get_parameter_group_number() >> 8) & 0xFF),
795 static_cast<std::uint8_t>((session->get_parameter_group_number() >> 16) & 0xFF)
796 };
797 return sendCANFrameCallback(static_cast<std::uint32_t>(CANLibParameterGroupNumber::ExtendedTransportProtocolConnectionManagement),
798 CANDataSpan(buffer.data(), buffer.size()),
799 std::static_pointer_cast<InternalControlFunction>(session->get_source()),
800 session->get_destination(),
802 }
803
804 bool ExtendedTransportProtocolManager::send_clear_to_send(std::shared_ptr<ExtendedTransportProtocolSession> &session) const
805 {
806 std::uint32_t nextPacketNumber = session->get_last_packet_number() + 1;
807 std::uint8_t packetLimit = session->get_cts_number_of_packet_limit();
808
809 if (packetLimit > session->get_number_of_remaining_packets())
810 {
811 packetLimit = static_cast<std::uint8_t>(session->get_number_of_remaining_packets());
812 }
813
814 const std::array<std::uint8_t, CAN_DATA_LENGTH> buffer{
816 packetLimit,
817 static_cast<std::uint8_t>(nextPacketNumber & 0xFF),
818 static_cast<std::uint8_t>((nextPacketNumber >> 8) & 0xFF),
819 static_cast<std::uint8_t>((nextPacketNumber >> 16) & 0xFF),
820 static_cast<std::uint8_t>(session->get_parameter_group_number() & 0xFF),
821 static_cast<std::uint8_t>((session->get_parameter_group_number() >> 8) & 0xFF),
822 static_cast<std::uint8_t>((session->get_parameter_group_number() >> 16) & 0xFF)
823 };
824 bool retVal = sendCANFrameCallback(static_cast<std::uint32_t>(CANLibParameterGroupNumber::ExtendedTransportProtocolConnectionManagement),
825 CANDataSpan(buffer.data(), buffer.size()),
826 std::static_pointer_cast<InternalControlFunction>(session->get_destination()), // Since we're the receiving side, we are the destination of the session
827 session->get_source(),
829 if (retVal)
830 {
831 session->set_acknowledged_packet_number(session->get_last_packet_number());
832 }
833 return retVal;
834 }
835
836 bool isobus::ExtendedTransportProtocolManager::send_data_packet_offset(std::shared_ptr<ExtendedTransportProtocolSession> &session) const
837 {
838 std::uint8_t packetsThisSegment = 0xFF;
839 if (packetsThisSegment > session->get_number_of_remaining_packets())
840 {
841 packetsThisSegment = static_cast<std::uint8_t>(session->get_number_of_remaining_packets());
842 }
843
844 if (packetsThisSegment > session->get_cts_number_of_packet_limit())
845 {
846 packetsThisSegment = session->get_cts_number_of_packet_limit();
847 }
848 if (packetsThisSegment > configuration->get_number_of_packets_per_dpo_message())
849 {
850 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.",
851 packetsThisSegment,
852 configuration->get_number_of_packets_per_dpo_message());
853 packetsThisSegment = configuration->get_number_of_packets_per_dpo_message();
854 }
855
856 const std::array<std::uint8_t, CAN_DATA_LENGTH> buffer{
857 DATA_PACKET_OFFSET_MULTIPLXOR,
858 packetsThisSegment,
859 static_cast<std::uint8_t>(session->get_last_packet_number()),
860 static_cast<std::uint8_t>((session->get_last_packet_number() >> 8) & 0xFF),
861 static_cast<std::uint8_t>((session->get_last_packet_number() >> 16) & 0xFF),
862 static_cast<std::uint8_t>(session->get_parameter_group_number() & 0xFF),
863 static_cast<std::uint8_t>((session->get_parameter_group_number() >> 8) & 0xFF),
864 static_cast<std::uint8_t>((session->get_parameter_group_number() >> 16) & 0xFF)
865 };
866 bool retVal = sendCANFrameCallback(static_cast<std::uint32_t>(CANLibParameterGroupNumber::ExtendedTransportProtocolConnectionManagement),
867 CANDataSpan(buffer.data(), buffer.size()),
868 std::static_pointer_cast<InternalControlFunction>(session->get_source()),
869 session->get_destination(),
871 if (retVal)
872 {
873 session->set_dpo_number_of_packets(packetsThisSegment);
874 session->set_sequence_number_offset(session->get_last_packet_number());
875 session->set_last_sequency_number(0);
876 }
877 return retVal;
878 }
879
880 bool ExtendedTransportProtocolManager::send_end_of_session_acknowledgement(std::shared_ptr<ExtendedTransportProtocolSession> &session) const
881 {
882 std::uint32_t messageLength = session->get_message_length();
883 std::uint32_t parameterGroupNumber = session->get_parameter_group_number();
884
885 const std::array<std::uint8_t, CAN_DATA_LENGTH> buffer{
887 static_cast<std::uint8_t>(messageLength & 0xFF),
888 static_cast<std::uint8_t>((messageLength >> 8) & 0xFF),
889 static_cast<std::uint8_t>((messageLength >> 16) & 0xFF),
890 static_cast<std::uint8_t>((messageLength >> 24) & 0xFF),
891 static_cast<std::uint8_t>(parameterGroupNumber & 0xFF),
892 static_cast<std::uint8_t>((parameterGroupNumber >> 8) & 0xFF),
893 static_cast<std::uint8_t>((parameterGroupNumber >> 16) & 0xFF),
894 };
895
896 return sendCANFrameCallback(static_cast<std::uint32_t>(CANLibParameterGroupNumber::ExtendedTransportProtocolConnectionManagement),
897 CANDataSpan(buffer.data(), buffer.size()),
898 std::static_pointer_cast<InternalControlFunction>(session->get_destination()), // Since we're the receiving side, we are the destination of the session
899 session->get_source(),
901 }
902
903 bool ExtendedTransportProtocolManager::has_session(std::shared_ptr<ControlFunction> source, std::shared_ptr<ControlFunction> destination)
904 {
905 return std::any_of(activeSessions.begin(), activeSessions.end(), [&](const std::shared_ptr<ExtendedTransportProtocolSession> &session) {
906 return session->matches(source, destination);
907 });
908 }
909
910 std::shared_ptr<ExtendedTransportProtocolManager::ExtendedTransportProtocolSession> ExtendedTransportProtocolManager::get_session(std::shared_ptr<ControlFunction> source,
911 std::shared_ptr<ControlFunction> destination)
912 {
913 auto result = std::find_if(activeSessions.begin(), activeSessions.end(), [&](const std::shared_ptr<ExtendedTransportProtocolSession> &session) {
914 return session->matches(source, destination);
915 });
916 return (activeSessions.end() != result) ? (*result) : nullptr;
917 }
918
919 const std::vector<std::shared_ptr<ExtendedTransportProtocolManager::ExtendedTransportProtocolSession>> &ExtendedTransportProtocolManager::get_sessions() const
920 {
921 return activeSessions;
922 }
923}
A protocol class that handles the ISO11783 extended transport protocol. Designed for destination spec...
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 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.
@ 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...
CANIdentifier get_identifier() const
Returns the identifier of the message.
std::uint32_t get_uint32_at(const std::uint32_t index, const ByteFormat format=ByteFormat::LittleEndian) const
Get a 32-bit unsigned integer from the buffer at a specific index. A 32-bit unsigned integer can hold...
std::shared_ptr< ControlFunction > get_destination_control_function() const
Gets the destination control function that the message is to.
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::uint32_t get_max_number_transport_protocol_sessions() const
Returns the max number of concurrent TP sessions.
std::uint8_t get_number_of_packets_per_dpo_message() const
Returns the max number of data frames the stack will use when in an ETP session, between EDPO phases....
std::uint32_t get_last_acknowledged_packet_number() const
Get the last acknowledged packet number by the receiver.
std::uint32_t get_last_packet_number() const
Get the last packet number that was processed.
std::uint8_t get_dpo_number_of_packets_remaining() const
Get the number of packets to be sent with the current DPO.
std::uint8_t get_last_sequence_number() const
Get the last sequence number that was processed.
void set_last_sequency_number(std::uint8_t value)
Set the last sequence number that has be processed.
std::uint8_t get_cts_number_of_packet_limit() const
Get the maximum number of packets that can be sent per DPO as indicated by the CTS message.
void set_cts_number_of_packet_limit(std::uint8_t value)
Set the maximum number of packets that can be sent per DPO as indicated by the CTS message.
void set_dpo_number_of_packets(std::uint8_t value)
Set the number of packets to be sent with the current DPO.
void set_sequence_number_offset(std::uint32_t value)
Set the last packet number that was processed.
std::uint32_t get_total_number_of_packets() const
Get the total number of packets that will be sent or received in this session.
void set_acknowledged_packet_number(std::uint32_t value)
Set the last acknowledged packet number by the receiver.
std::uint32_t get_number_of_remaining_packets() const
Get the number of packets that remain to be sent or received in this 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_dpo_number_of_packets() const
Get the number of packets that will be sent with the current DPO.
static constexpr std::uint16_t T1_TIMEOUT_MS
The t1 timeout as defined by the standard.
void process_message(const CANMessage &message)
A generic way for a protocol to process a received message.
bool send_request_to_send(std::shared_ptr< ExtendedTransportProtocolSession > &session) const
Sends the "request to send" message as part of initiating a transmit.
bool abort_session(std::shared_ptr< ExtendedTransportProtocolSession > &session, ConnectionAbortReason reason)
Aborts the 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 process_abort(const std::shared_ptr< ControlFunction > source, const std::shared_ptr< ControlFunction > destination, std::uint32_t parameterGroupNumber, ExtendedTransportProtocolManager::ConnectionAbortReason reason)
Processes an abort message in the CAN transport protocol.
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::uint32_t nextPacketNumber)
Processes the Clear To Send (CTS) message.
void send_data_transfer_packets(std::shared_ptr< ExtendedTransportProtocolSession > &session) const
Sends data transfer packets for the specified ExtendedTransportProtocolSession.
const CANMessageFrameCallback sendCANFrameCallback
A callback for sending a CAN frame.
void process_data_packet_offset(const std::shared_ptr< ControlFunction > source, const std::shared_ptr< ControlFunction > destination, std::uint32_t parameterGroupNumber, std::uint8_t numberOfPackets, std::uint32_t packetOffset)
Processes the Data Packet Offset (DPO) message.
bool send_end_of_session_acknowledgement(std::shared_ptr< ExtendedTransportProtocolSession > &session) const
Sends the "end of message acknowledgement" message for the provided session.
std::shared_ptr< ExtendedTransportProtocolSession > get_session(std::shared_ptr< ControlFunction > source, std::shared_ptr< ControlFunction > destination)
Gets a ETP session from the passed in source and destination and PGN combination.
const std::vector< std::shared_ptr< ExtendedTransportProtocolSession > > & get_sessions() const
Gets all the active transport protocol sessions that are currently active.
ExtendedTransportProtocolManager(const CANMessageFrameCallback &sendCANFrameCallback, const CANMessageCallback &canMessageReceivedCallback, const CANNetworkConfiguration *configuration)
The constructor for the ExtendedTransportProtocolManager, for advanced use only. In most cases,...
static constexpr std::uint32_t CLEAR_TO_SEND_MULTIPLEXOR
(21) ETP.CM_CTS Multiplexor
std::vector< std::shared_ptr< ExtendedTransportProtocolSession > > activeSessions
A list of all active ETP sessions.
void update()
Updates all sessions managed by this protocol manager instance.
void process_request_to_send(const std::shared_ptr< ControlFunction > source, const std::shared_ptr< ControlFunction > destination, std::uint32_t parameterGroupNumber, std::uint32_t totalMessageSize)
Processes a request to send a message over the CAN transport protocol.
const CANNetworkConfiguration * configuration
The configuration to use for this protocol.
static constexpr std::uint32_t END_OF_MESSAGE_ACKNOWLEDGE_MULTIPLEXOR
(23) TP.CM_EOMA Multiplexor
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.
void update_state_machine(std::shared_ptr< ExtendedTransportProtocolSession > &session)
Update the state machine for the passed in session.
static constexpr std::uint8_t SEQUENCE_NUMBER_DATA_INDEX
The index of the sequence number in a frame.
const CANMessageCallback canMessageReceivedCallback
A callback for when a complete CAN message is received using the ETP protocol.
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 CONNECTION_ABORT_MULTIPLEXOR
(255) Abort multiplexor
void close_session(std::shared_ptr< ExtendedTransportProtocolSession > &session, bool successful)
Gracefully closes a session to prepare for a new session.
void process_end_of_session_acknowledgement(const std::shared_ptr< ControlFunction > source, const std::shared_ptr< ControlFunction > destination, std::uint32_t parameterGroupNumber, std::uint32_t numberOfBytesTransferred)
Processes the end of session acknowledgement.
static constexpr std::uint32_t MAX_PROTOCOL_DATA_LENGTH
The max number of bytes that this protocol can transfer.
static constexpr std::uint16_t T2_T3_TIMEOUT_MS
The t2/t3 timeouts as defined by the standard.
void process_data_transfer_message(const CANMessage &message)
Processes a data transfer message.
bool send_data_packet_offset(std::shared_ptr< ExtendedTransportProtocolSession > &session) const
Sends the "data packet offset" message for the provided session.
bool send_clear_to_send(std::shared_ptr< ExtendedTransportProtocolSession > &session) const
Sends the "clear to send" message.
static constexpr std::uint32_t DATA_PACKET_OFFSET_MULTIPLXOR
(22) ETP.CM_DPO Multiplexor
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.
void process_connection_management_message(const CANMessage &message)
Processes a connection management message.
StateMachineState
The states that a ETP session could be in. Used for the internal state machine.
@ 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.
@ SendDataPacketOffset
We are sending a data packet offset message.
@ WaitForDataPacketOffset
We are waiting for a data packet offset message.
@ SendDataTransferPackets
A Tx data session is in progress.
static constexpr std::uint32_t REQUEST_TO_SEND_MULTIPLEXOR
(20) ETP.CM_RTS Multiplexor
ConnectionAbortReason
A list of all defined abort reasons in ISO11783.
@ DataPacketOffsetExceedsClearToSend
Received a number of packets in EDPO greater than CTS.
@ 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.
@ NumberOfClearToSendPacketsExceedsMessage
Received a CTS with a number of packets greater than the message.
@ UnexpectedDataPacketOffsetPGN
Received a data packet offset with an unexpected PGN.
@ UnexpectedDataTransferPacketReceived
A data packet was received outside the proper state.
@ BadDataPacketOffset
Received a data packet offset that is incorrect.
@ 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.