Diag-Client-Lib
doip_diagnostic_message_handler.cpp
Go to the documentation of this file.
1 /* Diagnostic Client library
2 * Copyright (C) 2024 Avijit Dey
3 *
4 * This Source Code Form is subject to the terms of the Mozilla Public
5 * License, v. 2.0. If a copy of the MPL was not distributed with this
6 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
7 */
8 
10 
11 #include <algorithm>
12 #include <utility>
13 
16 #include "common/logger.h"
17 #include "utility/state.h"
18 #include "utility/sync_timer.h"
19 
20 namespace doip_client {
21 namespace channel {
22 namespace tcp_channel {
23 namespace {
24 
28 constexpr std::uint16_t kDoip_DiagMessage_Type{0x8001};
29 constexpr std::uint16_t kDoip_DiagMessagePosAck_Type{0x8002};
30 constexpr std::uint16_t kDoip_DiagMessageNegAck_Type{0x8003};
31 
35 constexpr std::uint8_t kDoip_DiagnosticMessage_NegAckCode_InvalidSA{0x02};
36 constexpr std::uint8_t kDoip_DiagnosticMessage_NegAckCode_UnknownTA{0x03};
41 constexpr std::uint8_t kDoip_DiagnosticMessage_NegAckCode_TPError{0x08};
42 
46 constexpr std::uint8_t kDoip_DiagnosticMessage_PosAckCode_Confirm{0x00};
47 
51 constexpr std::uint8_t kDoip_DiagMessage_ReqResMinLen = 4U; // considering SA and TA
52 constexpr std::uint8_t kDoip_DiagMessageAck_ResMinLen = 5U; // considering SA, TA, Ack code
53 
54 /* Description: This timeout specifies the maximum time that
55  the test equipment waits for a confirmation ACK or NACK
56  from the DoIP entity after the last byte of a DoIP Diagnostic
57  request message has been sent
58 */
63 constexpr std::uint32_t kDoIPDiagnosticAckTimeout{2000u}; // 2 sec
64 
68 enum class DiagnosticMessageState : std::uint8_t {
69  kIdle = 0U,
76 };
77 
81 class kIdle final : public utility::state::State<DiagnosticMessageState> {
82  public:
88  explicit kIdle(DiagnosticMessageState state) : State<DiagnosticMessageState>(state) {}
89 
93  void Start() override {}
94 
98  void Stop() override {}
99 };
100 
104 class kSendDiagnosticReqFailed final : public utility::state::State<DiagnosticMessageState> {
105  public:
112 
116  void Start() override {}
117 
121  void Stop() override {}
122 };
123 
127 class kWaitForDiagnosticAck final : public utility::state::State<DiagnosticMessageState> {
128  public:
135 
139  void Start() override {}
140 
144  void Stop() override {}
145 };
146 
150 class kDiagnosticPositiveAckRecvd final : public utility::state::State<DiagnosticMessageState> {
151  public:
158 
162  void Start() override {}
163 
167  void Stop() override {}
168 };
169 
173 class kDiagnosticNegativeAckRecvd final : public utility::state::State<DiagnosticMessageState> {
174  public:
181 
185  void Start() override {}
186 
190  void Stop() override {}
191 };
192 
196 class kWaitForDiagnosticResponse final : public utility::state::State<DiagnosticMessageState> {
197  public:
204 
208  void Start() override {}
209 
213  void Stop() override {}
214 };
215 
216 } // namespace
217 
221 struct DiagAckType {
222  std::uint8_t ack_type_;
223 };
224 
228 std::ostream &operator<<(std::ostream &msg, DiagAckType diag_ack_type) {
229  switch (diag_ack_type.ack_type_) {
231  msg << "invalid source address.";
232  break;
234  msg << "unknown target address.";
235  break;
237  msg << "diagnostic message too large.";
238  break;
240  msg << "server out of memory.";
241  break;
243  msg << "target unreachable.";
244  break;
246  msg << "unknown network.";
247  break;
249  msg << "transport protocol error.";
250  break;
251  default:
252  msg << "unknown reason.";
253  break;
254  }
255  return msg;
256 }
257 
262  public:
267 
272 
281  : tcp_socket_handler_{tcp_socket_handler},
282  channel_{channel},
283  state_context_{},
284  sync_timer_{} {
285  // create and add state for Diagnostic State
286  // kIdle
287  state_context_.AddState(DiagnosticMessageState::kIdle, std::make_unique<kIdle>(DiagnosticMessageState::kIdle));
288  // kSendDiagnosticReqFailed
290  DiagnosticMessageState::kSendDiagnosticReqFailed,
291  std::make_unique<kSendDiagnosticReqFailed>(DiagnosticMessageState::kSendDiagnosticReqFailed));
292  // kWaitForDiagnosticAck
293  state_context_.AddState(DiagnosticMessageState::kWaitForDiagnosticAck,
294  std::make_unique<kWaitForDiagnosticAck>(DiagnosticMessageState::kWaitForDiagnosticAck));
295  // kDiagnosticPositiveAckRecvd
297  DiagnosticMessageState::kDiagnosticPositiveAckRecvd,
298  std::make_unique<kDiagnosticPositiveAckRecvd>(DiagnosticMessageState::kDiagnosticPositiveAckRecvd));
299  // kDiagnosticNegativeAckRecvd
301  DiagnosticMessageState::kDiagnosticNegativeAckRecvd,
302  std::make_unique<kDiagnosticNegativeAckRecvd>(DiagnosticMessageState::kDiagnosticNegativeAckRecvd));
303  // kWaitForDiagnosticResponse
305  DiagnosticMessageState::kWaitForDiagnosticResponse,
306  std::make_unique<kWaitForDiagnosticResponse>(DiagnosticMessageState::kWaitForDiagnosticResponse));
307  // Transit to idle state
308  state_context_.TransitionTo(DiagnosticMessageState::kIdle);
309  }
310 
314  void Start() {}
315 
320  void Stop() {
322  state_context_.TransitionTo(DiagnosticMessageState::kIdle);
323  }
324 
329  void Reset() { Stop(); }
330 
336 
341  auto GetSocketHandler() noexcept -> sockets::TcpSocketHandler & { return tcp_socket_handler_; }
342 
347  auto GetDoipChannel() noexcept -> DoipTcpChannel & { return channel_; }
348 
353  auto GetSyncTimer() noexcept -> SyncTimer & { return sync_timer_; }
354 
355  private:
360 
365 
370 
375 };
376 
378  DoipTcpChannel &channel)
379  : handler_impl_{std::make_unique<DiagnosticMessageHandlerImpl>(tcp_socket_handler, channel)} {}
380 
382 
384 
386 
388 
390  DiagnosticMessageState final_state{DiagnosticMessageState::kDiagnosticNegativeAckRecvd};
391  if (handler_impl_->GetStateContext().GetActiveState().GetState() == DiagnosticMessageState::kWaitForDiagnosticAck) {
392  // get the ack code
393  DiagAckType const diag_ack_type{doip_payload.GetPayload()[0u]};
394  if (doip_payload.GetPayloadType() == kDoip_DiagMessagePosAck_Type) {
395  if (diag_ack_type.ack_type_ == kDoip_DiagnosticMessage_PosAckCode_Confirm) {
396  final_state = DiagnosticMessageState::kDiagnosticPositiveAckRecvd;
397  logger::DoipClientLogger::GetDiagClientLogger().GetLogger().LogInfo(
398  __FILE__, __LINE__, __func__, [&doip_payload](std::stringstream &msg) {
399  msg << "Diagnostic message positively acknowledged from remote server "
400  << " (0x" << std::hex << doip_payload.GetServerAddress() << ")";
401  });
402  } else {
403  // do nothing
404  }
405  } else if (doip_payload.GetPayloadType() == kDoip_DiagMessageNegAck_Type) {
406  logger::DoipClientLogger::GetDiagClientLogger().GetLogger().LogWarn(
407  __FILE__, __LINE__, __func__,
408  [&diag_ack_type](std::stringstream &msg) { msg << "Diagnostic request denied due to " << diag_ack_type; });
409  } else {
410  // do nothing
411  }
412  handler_impl_->GetStateContext().TransitionTo(final_state);
413  handler_impl_->GetSyncTimer().CancelWait();
414  } else {
415  /* ignore */
416  }
417 }
418 
420  if (handler_impl_->GetStateContext().GetActiveState().GetState() ==
421  DiagnosticMessageState::kWaitForDiagnosticResponse) {
422  // Indicate upper layer about incoming data
423  std::pair<uds_transport::UdsTransportProtocolMgr::IndicationResult, uds_transport::UdsMessagePtr> ret_val{
424  handler_impl_->GetDoipChannel().IndicateMessage(
425  doip_payload.GetServerAddress(), doip_payload.GetClientAddress(),
426  uds_transport::UdsMessage::TargetAddressType::kPhysical, 0U, doip_payload.GetPayload().size(), 0u,
427  "DoIPTcp", doip_payload.GetPayload())};
429  // keep channel alive since pending request received, do not change channel state
430  } else {
431  // Check result and udsMessagePtr
433  (ret_val.second != nullptr)) {
434  // copy to application buffer
435  (void) std::copy(doip_payload.GetPayload().begin(), doip_payload.GetPayload().end(),
436  ret_val.second->GetPayload().begin());
437  handler_impl_->GetDoipChannel().HandleMessage(std::move(ret_val.second));
438  } else {
439  logger::DoipClientLogger::GetDiagClientLogger().GetLogger().LogVerbose(
440  __FILE__, __LINE__, __func__,
441  [](std::stringstream &msg) { msg << "Diagnostic message response ignored due to unknown error"; });
442  }
443  handler_impl_->GetStateContext().TransitionTo(DiagnosticMessageState::kIdle);
444  }
445  } else {
446  // ignore
447  logger::DoipClientLogger::GetDiagClientLogger().GetLogger().LogVerbose(
448  __FILE__, __LINE__, __func__, [this](std::stringstream &msg) {
449  msg << "Diagnostic message response ignored due to channel in state: "
450  << static_cast<int>(handler_impl_->GetStateContext().GetActiveState().GetState());
451  });
452  }
453 }
454 
459  if (handler_impl_->GetStateContext().GetActiveState().GetState() == DiagnosticMessageState::kIdle) {
460  if (SendDiagnosticRequest(std::move(diagnostic_request)) ==
462  handler_impl_->GetStateContext().TransitionTo(DiagnosticMessageState::kWaitForDiagnosticAck);
463  handler_impl_->GetSyncTimer().WaitForTimeout(
464  [this, &result]() {
466  handler_impl_->GetStateContext().TransitionTo(DiagnosticMessageState::kIdle);
467  logger::DoipClientLogger::GetDiagClientLogger().GetLogger().LogError(
468  __FILE__, __LINE__, "", [](std::stringstream &msg) {
469  msg << "Diagnostic Message Ack Request timed out, no response received in: "
470  << kDoIPDiagnosticAckTimeout << "seconds";
471  });
472  },
473  [this, &result]() {
474  if (handler_impl_->GetStateContext().GetActiveState().GetState() ==
475  DiagnosticMessageState::kDiagnosticPositiveAckRecvd) {
476  handler_impl_->GetStateContext().TransitionTo(DiagnosticMessageState::kWaitForDiagnosticResponse);
477  // success
479  logger::DoipClientLogger::GetDiagClientLogger().GetLogger().LogInfo(
480  __FILE__, __LINE__, "",
481  [](std::stringstream &msg) { msg << "Diagnostic Message Positive Ack received"; });
482  } else {
483  // failed with neg acknowledgement from server
485  handler_impl_->GetStateContext().TransitionTo(DiagnosticMessageState::kIdle);
486  logger::DoipClientLogger::GetDiagClientLogger().GetLogger().LogInfo(
487  __FILE__, __LINE__, "",
488  [](std::stringstream &msg) { msg << "Diagnostic Message Transmission Failed Neg Ack Received"; });
489  }
490  },
491  std::chrono::milliseconds{kDoIPDiagnosticAckTimeout});
492  } else {
493  // Failed, do nothing
494  handler_impl_->GetStateContext().TransitionTo(DiagnosticMessageState::kIdle);
495  logger::DoipClientLogger::GetDiagClientLogger().GetLogger().LogError(
496  __FILE__, __LINE__, "",
497  [](std::stringstream &msg) { msg << "Diagnostic Request Message Transmission Failed"; });
498  }
499  } else {
500  // channel not in idle state
502  logger::DoipClientLogger::GetDiagClientLogger().GetLogger().LogVerbose(
503  __FILE__, __LINE__, "",
504  [](std::stringstream &msg) { msg << "Diagnostic Message Transmission already in progress"; });
505  }
506  return result;
507 }
508 
513  constexpr std::uint8_t kDoipheadrSize{8u};
514  constexpr std::uint8_t kSourceAddressSize{4u};
515  TcpMessagePtr doip_diag_req = std::make_unique<TcpMessage>();
516  // reserve bytes in vector
517  doip_diag_req->GetTxBuffer().reserve(kDoipheadrSize + kDoip_DiagMessage_ReqResMinLen +
518  diagnostic_request->GetPayload().size());
519  // create header
521  doip_diag_req->GetTxBuffer(), kDoip_DiagMessage_Type,
522  kDoip_DiagMessage_ReqResMinLen + static_cast<std::uint32_t>(diagnostic_request->GetPayload().size()));
523  // Add source address
524  doip_diag_req->GetTxBuffer().emplace_back(static_cast<std::uint8_t>((diagnostic_request->GetSa() & 0xFF00) >> 8u));
525  doip_diag_req->GetTxBuffer().emplace_back(static_cast<std::uint8_t>(diagnostic_request->GetSa() & 0x00FF));
526  // Add target address
527  doip_diag_req->GetTxBuffer().emplace_back(static_cast<std::uint8_t>((diagnostic_request->GetSa() & 0xFF00) >> 8u));
528  doip_diag_req->GetTxBuffer().emplace_back(static_cast<std::uint8_t>(diagnostic_request->GetSa() & 0x00FF));
529  // Copy data bytes
530  doip_diag_req->GetTxBuffer().insert(doip_diag_req->GetTxBuffer().begin() + kDoipheadrSize + kSourceAddressSize,
531  diagnostic_request->GetPayload().begin(), diagnostic_request->GetPayload().end());
532 
533  // Initiate transmission
534  if (handler_impl_->GetSocketHandler().Transmit(std::move(doip_diag_req))) {
536  }
537  return ret_val;
538 }
539 
540 void DiagnosticMessageHandler::CreateDoipGenericHeader(std::vector<std::uint8_t> &doip_header_buffer,
541  std::uint16_t payload_type, std::uint32_t payload_len) {
542  doip_header_buffer.emplace_back(kDoip_ProtocolVersion);
543  doip_header_buffer.emplace_back(~(static_cast<std::uint8_t>(kDoip_ProtocolVersion)));
544  doip_header_buffer.emplace_back(static_cast<std::uint8_t>((payload_type & 0xFF00) >> 8));
545  doip_header_buffer.emplace_back(static_cast<std::uint8_t>(payload_type & 0x00FF));
546  doip_header_buffer.emplace_back(static_cast<std::uint8_t>((payload_len & 0xFF000000) >> 24));
547  doip_header_buffer.emplace_back(static_cast<std::uint8_t>((payload_len & 0x00FF0000) >> 16));
548  doip_header_buffer.emplace_back(static_cast<std::uint8_t>((payload_len & 0x0000FF00) >> 8));
549  doip_header_buffer.emplace_back(static_cast<std::uint8_t>(payload_len & 0x000000FF));
550 }
551 
552 } // namespace tcp_channel
553 } // namespace channel
554 } // namespace doip_client
Immutable class to store received doip message.
Definition: doip_message.h:21
DiagnosticMessageHandlerImpl(sockets::TcpSocketHandler &tcp_socket_handler, DoipTcpChannel &channel)
Constructs an instance of DiagnosticMessageHandlerImpl.
auto GetSocketHandler() noexcept -> sockets::TcpSocketHandler &
Function to get the socket handler.
auto GetStateContext() noexcept -> DiagnosticMessageStateContext &
Function to get the Diagnostic Message State context.
~DiagnosticMessageHandler()
Destruct an instance of DiagnosticMessageHandler.
auto HandleDiagnosticRequest(uds_transport::UdsMessageConstPtr diagnostic_request) noexcept -> uds_transport::UdsTransportProtocolMgr::TransmissionResult
Function to handle sending of diagnostic request.
DiagnosticMessageHandler(sockets::TcpSocketHandler &tcp_socket_handler, DoipTcpChannel &channel)
Constructs an instance of DiagnosticMessageHandler.
sockets::TcpSocketHandler::TcpMessagePtr TcpMessagePtr
Type alias for Tcp message pointer.
void ProcessDoIPDiagnosticAckMessageResponse(DoipMessage &doip_payload) noexcept
Function to process received diagnostic acknowledgement from server.
auto SendDiagnosticRequest(uds_transport::UdsMessageConstPtr diagnostic_request) noexcept -> uds_transport::UdsTransportProtocolMgr::TransmissionResult
Function to send diagnostic request.
static void CreateDoipGenericHeader(std::vector< std::uint8_t > &doip_header, std::uint16_t payload_type, std::uint32_t payload_len)
Function to create doip generic header.
std::unique_ptr< DiagnosticMessageHandlerImpl > handler_impl_
Stores the Handler implementation.
void ProcessDoIPDiagnosticMessageResponse(DoipMessage &doip_payload) noexcept
Function to process received diagnostic positive/negative response from server.
Class to manage a tcp channel as per DoIP protocol.
static auto GetDiagClientLogger() noexcept -> DoipClientLogger &
Definition: logger.h:20
Class used to create a tcp socket for handling transmission and reception of tcp message from driver.
void TransitionTo(EnumState state)
Definition: state.h:69
void AddState(EnumState state, std::unique_ptr< State< EnumState >> state_ptr)
Definition: state.h:58
void CancelWait()
Function to cancel the synchronous wait.
Definition: sync_timer.h:90
auto IsTimerActive()
Function to query if timer is running.
Definition: sync_timer.h:82
constexpr std::uint8_t kDoip_DiagMessage_ReqResMinLen
Diagnostic Message request/response lengths.
constexpr std::uint8_t kDoip_DiagnosticMessage_NegAckCode_InvalidSA
Diagnostic Message negative acknowledgement code.
constexpr std::uint8_t kDoip_DiagnosticMessage_PosAckCode_Confirm
Diagnostic Message positive acknowledgement code.
constexpr std::uint32_t kDoIPDiagnosticAckTimeout
The timeout specifies the maximum time that the test equipment waits for a confirmation ACK or NACK f...
std::ostream & operator<<(std::ostream &msg, DiagAckType diag_ack_type)
Function to stream the diagnostic acknowledgement type.
void CreateDoipGenericHeader(std::vector< std::uint8_t > &doip_header_buffer, std::uint16_t payload_type, std::uint32_t payload_len)
Create the doip generic header.
constexpr std::uint8_t kDoipheadrSize
constexpr std::uint8_t kDoip_ProtocolVersion
std::unique_ptr< const UdsMessage > UdsMessageConstPtr
Definition: uds_message.h:69