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 kDoipDiagMessage{0x8001};
29 constexpr std::uint16_t kDoipDiagMessagePosAck{0x8002};
30 constexpr std::uint16_t kDoipDiagMessageNegAck{0x8003};
31 
35 constexpr std::uint8_t kDoipDiagnosticMessageNegAckCodeInvalidSa{0x02};
36 constexpr std::uint8_t kDoipDiagnosticMessageNegAckCodeUnknownTa{0x03};
38 constexpr std::uint8_t kDoipDiagnosticMessageNegAckCodeOutOfMemory{0x05};
41 constexpr std::uint8_t kDoipDiagnosticMessageNegAckCodeTpError{0x08};
42 
46 constexpr std::uint8_t kDoipDiagnosticMessagePosAckCodeConfirm{0x00};
47 
51 constexpr std::uint8_t kDoipDiagMessageReqResMinLen = 4U; // considering SA and TA
52 constexpr std::uint8_t kDoipDiagMessageAckResMinLen = 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:
89 
93  void Start() override {}
94 
98  void Stop() override {}
99 };
100 
104 class kSendDiagnosticReqFailed final : public utility::state::State<DiagnosticMessageState> {
105  public:
112  : State<DiagnosticMessageState>(state) {}
113 
117  void Start() override {}
118 
122  void Stop() override {}
123 };
124 
128 class kWaitForDiagnosticAck final : public utility::state::State<DiagnosticMessageState> {
129  public:
136  : State<DiagnosticMessageState>(state) {}
137 
141  void Start() override {}
142 
146  void Stop() override {}
147 };
148 
152 class kDiagnosticPositiveAckRecvd final : public utility::state::State<DiagnosticMessageState> {
153  public:
160  : State<DiagnosticMessageState>(state) {}
161 
165  void Start() override {}
166 
170  void Stop() override {}
171 };
172 
176 class kDiagnosticNegativeAckRecvd final : public utility::state::State<DiagnosticMessageState> {
177  public:
184  : State<DiagnosticMessageState>(state) {}
185 
189  void Start() override {}
190 
194  void Stop() override {}
195 };
196 
200 class kWaitForDiagnosticResponse final : public utility::state::State<DiagnosticMessageState> {
201  public:
208  : State<DiagnosticMessageState>(state) {}
209 
213  void Start() override {}
214 
218  void Stop() override {}
219 };
220 
224 struct DiagAckType {
225  std::uint8_t ack_type_;
226 };
227 
231 std::ostream &operator<<(std::ostream &msg, DiagAckType diag_ack_type) {
232  switch (diag_ack_type.ack_type_) {
234  msg << "invalid source address.";
235  break;
237  msg << "unknown target address.";
238  break;
240  msg << "diagnostic message too large.";
241  break;
243  msg << "server out of memory.";
244  break;
246  msg << "target unreachable.";
247  break;
249  msg << "unknown network.";
250  break;
252  msg << "transport protocol error.";
253  break;
254  default:
255  msg << "unknown reason.";
256  break;
257  }
258  return msg;
259 }
260 
268 auto CreateDoipGenericHeader(std::uint16_t payload_type, std::uint32_t payload_len) noexcept
269  -> std::vector<std::uint8_t> {
270  std::vector<std::uint8_t> output_buffer{};
271  output_buffer.emplace_back(kDoip_ProtocolVersion);
272  output_buffer.emplace_back(~(static_cast<std::uint8_t>(kDoip_ProtocolVersion)));
273  output_buffer.emplace_back(static_cast<std::uint8_t>((payload_type & 0xFF00) >> 8));
274  output_buffer.emplace_back(static_cast<std::uint8_t>(payload_type & 0x00FF));
275  output_buffer.emplace_back(static_cast<std::uint8_t>((payload_len & 0xFF000000) >> 24));
276  output_buffer.emplace_back(static_cast<std::uint8_t>((payload_len & 0x00FF0000) >> 16));
277  output_buffer.emplace_back(static_cast<std::uint8_t>((payload_len & 0x0000FF00) >> 8));
278  output_buffer.emplace_back(static_cast<std::uint8_t>(payload_len & 0x000000FF));
279  return output_buffer;
280 }
281 
282 } // namespace
283 
288  public:
293 
298 
307  DoipTcpChannel &channel)
308  : tcp_socket_handler_{tcp_socket_handler},
309  channel_{channel},
310  state_context_{},
311  sync_timer_{} {
312  // create and add state for Diagnostic State
313  // kIdle
314  state_context_.AddState(DiagnosticMessageState::kIdle,
315  std::make_unique<kIdle>(DiagnosticMessageState::kIdle));
316  // kSendDiagnosticReqFailed
317  state_context_.AddState(DiagnosticMessageState::kSendDiagnosticReqFailed,
318  std::make_unique<kSendDiagnosticReqFailed>(
319  DiagnosticMessageState::kSendDiagnosticReqFailed));
320  // kWaitForDiagnosticAck
322  DiagnosticMessageState::kWaitForDiagnosticAck,
323  std::make_unique<kWaitForDiagnosticAck>(DiagnosticMessageState::kWaitForDiagnosticAck));
324  // kDiagnosticPositiveAckRecvd
325  state_context_.AddState(DiagnosticMessageState::kDiagnosticPositiveAckRecvd,
326  std::make_unique<kDiagnosticPositiveAckRecvd>(
327  DiagnosticMessageState::kDiagnosticPositiveAckRecvd));
328  // kDiagnosticNegativeAckRecvd
329  state_context_.AddState(DiagnosticMessageState::kDiagnosticNegativeAckRecvd,
330  std::make_unique<kDiagnosticNegativeAckRecvd>(
331  DiagnosticMessageState::kDiagnosticNegativeAckRecvd));
332  // kWaitForDiagnosticResponse
333  state_context_.AddState(DiagnosticMessageState::kWaitForDiagnosticResponse,
334  std::make_unique<kWaitForDiagnosticResponse>(
335  DiagnosticMessageState::kWaitForDiagnosticResponse));
336  // Transit to idle state
337  state_context_.TransitionTo(DiagnosticMessageState::kIdle);
338  }
339 
343  void Start() {}
344 
349  void Stop() {
351  state_context_.TransitionTo(DiagnosticMessageState::kIdle);
352  }
353 
358  void Reset() { Stop(); }
359 
365 
370  auto GetSocketHandler() noexcept -> sockets::TcpSocketHandler & { return tcp_socket_handler_; }
371 
376  auto GetDoipChannel() noexcept -> DoipTcpChannel & { return channel_; }
377 
382  auto GetSyncTimer() noexcept -> SyncTimer & { return sync_timer_; }
383 
384  private:
389 
394 
399 
404 };
405 
407  DoipTcpChannel &channel)
408  : handler_impl_{std::make_unique<DiagnosticMessageHandlerImpl>(tcp_socket_handler, channel)} {}
409 
411 
413 
415 
417 
419  DoipMessage &doip_payload) noexcept -> void {
420  DiagnosticMessageState final_state{DiagnosticMessageState::kDiagnosticNegativeAckRecvd};
421  if (handler_impl_->GetStateContext().GetActiveState().GetState() ==
422  DiagnosticMessageState::kWaitForDiagnosticAck) {
423  // get the ack code
424  DiagAckType const diag_ack_type{doip_payload.GetPayload()[0u]};
425  if (doip_payload.GetPayloadType() == kDoipDiagMessagePosAck) {
426  if (diag_ack_type.ack_type_ == kDoipDiagnosticMessagePosAckCodeConfirm) {
427  final_state = DiagnosticMessageState::kDiagnosticPositiveAckRecvd;
428  logger::DoipClientLogger::GetDiagClientLogger().GetLogger().LogInfo(
429  FILE_NAME, __LINE__, __func__, [&doip_payload](std::stringstream &msg) {
430  msg << "Diagnostic message positively acknowledged from remote "
431  "server "
432  << " (0x" << std::hex << doip_payload.GetServerAddress() << ")";
433  });
434  } else {
435  // do nothing
436  }
437  } else if (doip_payload.GetPayloadType() == kDoipDiagMessageNegAck) {
438  logger::DoipClientLogger::GetDiagClientLogger().GetLogger().LogWarn(
439  FILE_NAME, __LINE__, __func__, [&diag_ack_type](std::stringstream &msg) {
440  msg << "Diagnostic request denied due to " << diag_ack_type;
441  });
442  } else {
443  // do nothing
444  }
445  handler_impl_->GetStateContext().TransitionTo(final_state);
446  handler_impl_->GetSyncTimer().CancelWait();
447  } else {
448  /* ignore */
449  }
450 }
451 
453  DoipMessage &doip_payload) noexcept -> void {
454  if (handler_impl_->GetStateContext().GetActiveState().GetState() ==
455  DiagnosticMessageState::kWaitForDiagnosticResponse) {
456  // Indicate upper layer about incoming data
459  ret_val{handler_impl_->GetDoipChannel().IndicateMessage(
460  doip_payload.GetServerAddress(), doip_payload.GetClientAddress(),
462  doip_payload.GetPayload().size(), 0u, "DoIPTcp", doip_payload.GetPayload())};
463  if (ret_val.first ==
465  // keep channel alive since pending request received, do not change channel state
466  } else {
467  // Check result and udsMessagePtr
468  if ((ret_val.first ==
470  (ret_val.second != nullptr)) {
471  // copy to application buffer
472  (void) std::copy(doip_payload.GetPayload().begin(), doip_payload.GetPayload().end(),
473  ret_val.second->GetPayload().begin());
474  handler_impl_->GetDoipChannel().HandleMessage(std::move(ret_val.second));
475  } else {
476  logger::DoipClientLogger::GetDiagClientLogger().GetLogger().LogVerbose(
477  FILE_NAME, __LINE__, __func__, [](std::stringstream &msg) {
478  msg << "Diagnostic message response ignored due to unknown error";
479  });
480  }
481  handler_impl_->GetStateContext().TransitionTo(DiagnosticMessageState::kIdle);
482  }
483  } else {
484  // ignore
485  logger::DoipClientLogger::GetDiagClientLogger().GetLogger().LogVerbose(
486  FILE_NAME, __LINE__, __func__, [this](std::stringstream &msg) {
487  msg << "Diagnostic message response ignored due to channel in state: "
488  << static_cast<int>(handler_impl_->GetStateContext().GetActiveState().GetState());
489  });
490  }
491 }
492 
494  uds_transport::UdsMessageConstPtr diagnostic_request) noexcept
498  if (handler_impl_->GetStateContext().GetActiveState().GetState() ==
499  DiagnosticMessageState::kIdle) {
500  if (SendDiagnosticRequest(std::move(diagnostic_request)) ==
502  handler_impl_->GetStateContext().TransitionTo(DiagnosticMessageState::kWaitForDiagnosticAck);
503  handler_impl_->GetSyncTimer().WaitForTimeout(
504  [this, &result]() {
505  result =
507  handler_impl_->GetStateContext().TransitionTo(DiagnosticMessageState::kIdle);
508  logger::DoipClientLogger::GetDiagClientLogger().GetLogger().LogError(
509  FILE_NAME, __LINE__, "", [](std::stringstream &msg) {
510  msg << "Diagnostic Message Ack Request timed out, no "
511  "response received in: "
512  << kDoIPDiagnosticAckTimeout << " seconds";
513  });
514  },
515  [this, &result]() {
516  if (handler_impl_->GetStateContext().GetActiveState().GetState() ==
517  DiagnosticMessageState::kDiagnosticPositiveAckRecvd) {
518  handler_impl_->GetStateContext().TransitionTo(
519  DiagnosticMessageState::kWaitForDiagnosticResponse);
520  // success
522  logger::DoipClientLogger::GetDiagClientLogger().GetLogger().LogInfo(
523  FILE_NAME, __LINE__, "", [](std::stringstream &msg) {
524  msg << "Diagnostic Message Positive Ack received";
525  });
526  } else {
527  // failed with neg acknowledgement from server
530  handler_impl_->GetStateContext().TransitionTo(DiagnosticMessageState::kIdle);
531  logger::DoipClientLogger::GetDiagClientLogger().GetLogger().LogInfo(
532  FILE_NAME, __LINE__, "", [](std::stringstream &msg) {
533  msg << "Diagnostic Message Transmission Failed Neg Ack "
534  "Received";
535  });
536  }
537  },
538  std::chrono::milliseconds{kDoIPDiagnosticAckTimeout});
539  } else {
540  // Failed, do nothing
541  handler_impl_->GetStateContext().TransitionTo(DiagnosticMessageState::kIdle);
542  logger::DoipClientLogger::GetDiagClientLogger().GetLogger().LogError(
543  FILE_NAME, __LINE__, "",
544  [](std::stringstream &msg) { msg << "Diagnostic Request Message Transmission Failed"; });
545  }
546  } else {
547  // channel not in idle state
549  logger::DoipClientLogger::GetDiagClientLogger().GetLogger().LogVerbose(
550  FILE_NAME, __LINE__, "", [](std::stringstream &msg) {
551  msg << "Diagnostic Message Transmission already in progress";
552  });
553  }
554  return result;
555 }
556 
558  uds_transport::UdsMessageConstPtr diagnostic_request) noexcept
562  constexpr std::uint8_t kSourceAddressSize{4u};
563  // Create header
564  std::uint32_t const total_diagnostic_response_length{static_cast<uint32_t>(
565  kDoipDiagMessageReqResMinLen + diagnostic_request->GetPayload().size())};
566  TcpMessage::BufferType compose_diag_req{
567  CreateDoipGenericHeader(kDoipDiagMessage, total_diagnostic_response_length)};
568  compose_diag_req.reserve(kDoipheadrSize + total_diagnostic_response_length);
569 
570  // Add source address
571  compose_diag_req.emplace_back(
572  static_cast<std::uint8_t>((diagnostic_request->GetSa() & 0xFF00) >> 8u));
573  compose_diag_req.emplace_back(static_cast<std::uint8_t>(diagnostic_request->GetSa() & 0x00FF));
574  // Add target address
575  compose_diag_req.emplace_back(
576  static_cast<std::uint8_t>((diagnostic_request->GetTa() & 0xFF00) >> 8u));
577  compose_diag_req.emplace_back(static_cast<std::uint8_t>(diagnostic_request->GetTa() & 0x00FF));
578  // Copy data bytes
579  compose_diag_req.insert(compose_diag_req.begin() + kDoipheadrSize + kSourceAddressSize,
580  diagnostic_request->GetPayload().begin(),
581  diagnostic_request->GetPayload().end());
582  TcpMessagePtr doip_diag_req{std::make_unique<TcpMessage>(diagnostic_request->GetHostIpAddress(),
583  diagnostic_request->GetHostPortNumber(),
584  std::move(compose_diag_req))};
585  // Initiate transmission
586  if (handler_impl_->GetSocketHandler().Transmit(std::move(doip_diag_req))) {
588  }
589  return ret_val;
590 }
591 
592 } // namespace tcp_channel
593 } // namespace channel
594 } // 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.
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.
sockets::TcpSocketHandler::MessagePtr TcpMessagePtr
Type alias for Tcp message pointer.
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
Handler class to manage different socket of various client (Udp / Tcp)
void TransitionTo(EnumState state)
Definition: state.h:70
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:91
auto IsTimerActive()
Function to query if timer is running.
Definition: sync_timer.h:83
#define FILE_NAME
Definition: file_path.h:14
constexpr std::uint8_t kDoipDiagnosticMessagePosAckCodeConfirm
Diagnostic Message positive acknowledgement code.
constexpr std::uint8_t kDoipDiagMessageReqResMinLen
Diagnostic Message request/response lengths.
std::ostream & operator<<(std::ostream &msg, DiagAckType diag_ack_type)
Function to stream the diagnostic acknowledgement type.
auto CreateDoipGenericHeader(std::uint16_t payload_type, std::uint32_t payload_len) noexcept -> std::vector< std::uint8_t >
Function to create doip generic header.
constexpr std::uint32_t kDoIPDiagnosticAckTimeout
The timeout specifies the maximum time that the test equipment waits for a confirmation ACK or NACK f...
constexpr std::uint8_t kDoipDiagnosticMessageNegAckCodeInvalidSa
Diagnostic Message negative acknowledgement code.
SocketHandler< boost_support::client::tcp::TcpClient > TcpSocketHandler
Type alias of Tcp socket handler.
constexpr std::uint8_t kDoipheadrSize
constexpr std::uint8_t kDoip_ProtocolVersion
std::unique_ptr< const UdsMessage > UdsMessageConstPtr
Definition: uds_message.h:69
std::unique_ptr< UdsMessage > UdsMessagePtr
Definition: uds_message.h:71
State
Definitions of different connection state.
Definition: tls_client.cpp:56