Diag-Client-Lib
dm_conversation.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 /* includes */
9 
11 
12 #include "src/common/logger.h"
15 
16 namespace diag {
17 namespace client {
18 namespace conversation {
19 
24  public:
33  DmConversation &dm_conversion)
34  : uds_transport::ConversionHandler{handler_id},
35  dm_conversation_{dm_conversion} {}
36 
40  DmConversationHandler(const DmConversationHandler &other) noexcept = delete;
41  DmConversationHandler &operator=(const DmConversationHandler &other) noexcept = delete;
42 
46  DmConversationHandler(DmConversationHandler &&other) noexcept = delete;
48 
52  ~DmConversationHandler() override = default;
53 
79  std::pair<::uds_transport::UdsTransportProtocolMgr::IndicationResult, ::uds_transport::UdsMessagePtr> IndicateMessage(
81  ::uds_transport::UdsMessage::TargetAddressType type, ::uds_transport::ChannelID channel_id, std::size_t size,
82  ::uds_transport::Priority priority, ::uds_transport::ProtocolKind protocol_kind,
83  core_type::Span<std::uint8_t> payload_info) const noexcept override {
84  return (dm_conversation_.IndicateMessage(source_addr, target_addr, type, channel_id, size, priority, protocol_kind,
85  payload_info));
86  }
87 
94  void HandleMessage(::uds_transport::UdsMessagePtr message) const noexcept override {
95  dm_conversation_.HandleMessage(std::move(message));
96  }
97 
98  private:
103 };
104 
105 DmConversation::DmConversation(std::string_view conversion_name, DMConversationType &conversion_identifier)
106  : Conversation{},
107  activity_status_{ActivityStatusType::kInactive},
108  active_session_{SessionControlType::kDefaultSession},
109  active_security_level_{SecurityLevelType::kLocked},
110  rx_buffer_size_{conversion_identifier.rx_buffer_size},
111  p2_client_max_{conversion_identifier.p2_client_max},
112  p2_star_client_max_{conversion_identifier.p2_star_client_max},
113  source_address_{conversion_identifier.source_address},
114  target_address_{},
115  conversation_name_{conversion_name},
116  dm_conversion_handler_{std::make_unique<DmConversationHandler>(conversion_identifier.handler_id, *this)} {
117  (void) (active_session_);
118  (void) (active_security_level_);
119 }
120 
122 
123 void DmConversation::Startup() noexcept {
124  // initialize the connection
125  connection_ptr_->Initialize();
126  // start the connection
127  connection_ptr_->Start();
128  // Change the state to Active
130  logger::DiagClientLogger::GetDiagClientLogger().GetLogger().LogInfo(__FILE__, __LINE__, __func__,
131  [&](std::stringstream &msg) {
132  msg << "'" << conversation_name_ << "'"
133  << "-> "
134  << "Startup completed";
135  });
136 }
137 
138 void DmConversation::Shutdown() noexcept {
139  // shutdown connection
140  connection_ptr_->Stop();
141  // Change the state to InActive
143  logger::DiagClientLogger::GetDiagClientLogger().GetLogger().LogInfo(__FILE__, __LINE__, __func__,
144  [&](std::stringstream &msg) {
145  msg << "'" << conversation_name_ << "'"
146  << "-> "
147  << "Shutdown completed";
148  });
149 }
150 
152  IpAddress host_ip_addr) noexcept {
153  // create an uds message just to get the port number
154  // source address required for Routing Activation
155  uds_transport::ByteVector payload{}; // empty payload
156  // Send Connect request to doip layer
158  connection_ptr_->ConnectToHost(std::make_unique<diag::client::uds_message::DmUdsMessage>(
159  source_address_, target_address, host_ip_addr, payload)))};
160  remote_address_ = host_ip_addr;
161  target_address_ = target_address;
163  logger::DiagClientLogger::GetDiagClientLogger().GetLogger().LogInfo(
164  __FILE__, __LINE__, __func__, [this](std::stringstream &msg) {
165  msg << "'" << conversation_name_ << "'"
166  << "-> "
167  << "Successfully connected to Server with IP <" << remote_address_ << ">"
168  << " and LA= 0x" << std::hex << target_address_;
169  });
170  } else {
171  logger::DiagClientLogger::GetDiagClientLogger().GetLogger().LogError(
172  __FILE__, __LINE__, __func__, [this](std::stringstream &msg) {
173  msg << "'" << conversation_name_ << "'"
174  << "-> "
175  << "Failed connecting to Server with IP <" << remote_address_ << ">"
176  << " and LA= 0x" << std::hex << target_address_;
177  });
178  }
179  return connection_result;
180 }
181 
184  // Check if already connected before disconnecting
185  if (connection_ptr_->IsConnectToHost()) {
186  // Send disconnect request to doip layer
187  ret_val = static_cast<DiagClientConversation::DisconnectResult>(connection_ptr_->DisconnectFromHost());
189  logger::DiagClientLogger::GetDiagClientLogger().GetLogger().LogInfo(
190  __FILE__, __LINE__, __func__, [this](std::stringstream &msg) {
191  msg << "'" << conversation_name_ << "'"
192  << "-> "
193  << "Successfully disconnected from Server with IP <" << remote_address_ << ">"
194  << " and LA= 0x" << std::hex << target_address_;
195  });
196  } else {
197  logger::DiagClientLogger::GetDiagClientLogger().GetLogger().LogWarn(
198  __FILE__, __LINE__, __func__, [&](std::stringstream &msg) {
199  msg << "'" << conversation_name_ << "'"
200  << "-> "
201  << "Failed to disconnect from Server with IP <" << remote_address_ << ">";
202  });
203  }
204  } else {
206  }
207  return ret_val;
208 }
209 
211  uds_message::UdsRequestMessageConstPtr message) noexcept {
215  if (message) {
216  // fill the data
217  uds_transport::ByteVector payload{message->GetPayload()};
218  // Initiate Sending of diagnostic request
220  connection_ptr_->Transmit(std::make_unique<diag::client::uds_message::DmUdsMessage>(
221  source_address_, target_address_, message->GetHostIpAddress(), payload))};
223  // Diagnostic Request Sent successful
224  logger::DiagClientLogger::GetDiagClientLogger().GetLogger().LogInfo(
225  __FILE__, __LINE__, __func__, [&](std::stringstream &msg) {
226  msg << "'" << conversation_name_ << "'"
227  << "-> "
228  << "Diagnostic Request Sent & Positive Ack received";
229  });
230  conversation_state_.GetConversationStateContext().TransitionTo(ConversationState::kDiagWaitForRes);
231  // Wait P6Max / P2ClientMax
232  sync_timer_.WaitForTimeout(
233  [this, &result]() {
235  conversation_state_.GetConversationStateContext().TransitionTo(ConversationState::kIdle);
236  logger::DiagClientLogger::GetDiagClientLogger().GetLogger().LogInfo(
237  __FILE__, __LINE__, "", [&](std::stringstream &msg) {
238  msg << "'" << conversation_name_ << "'"
239  << "-> "
240  << "Diagnostic Response P2 Timeout happened after " << p2_client_max_ << " milliseconds";
241  });
242  },
243  [this]() {
244  // pending or pos/neg response
245  if (conversation_state_.GetConversationStateContext().GetActiveState().GetState() ==
246  ConversationState::kDiagRecvdFinalRes) {
247  // pos/neg response received
248  } else if (conversation_state_.GetConversationStateContext().GetActiveState().GetState() ==
249  ConversationState::kDiagRecvdPendingRes) {
250  // first pending received
251  conversation_state_.GetConversationStateContext().TransitionTo(ConversationState::kDiagStartP2StarTimer);
252  }
253  },
254  std::chrono::milliseconds{p2_client_max_});
255 
256  // Wait until final response or timeout
257  while (conversation_state_.GetConversationStateContext().GetActiveState().GetState() !=
258  ConversationState::kIdle) {
259  // Check the active state
260  switch (conversation_state_.GetConversationStateContext().GetActiveState().GetState()) {
261  case ConversationState::kDiagRecvdPendingRes:
262  conversation_state_.GetConversationStateContext().TransitionTo(ConversationState::kDiagStartP2StarTimer);
263  break;
264  case ConversationState::kDiagRecvdFinalRes:
265  // do nothing
266  break;
267  case ConversationState::kDiagStartP2StarTimer:
268  // wait P6Star/ P2 star client time
269  sync_timer_.WaitForTimeout(
270  [this, &result]() {
271  logger::DiagClientLogger::GetDiagClientLogger().GetLogger().LogInfo(
272  __FILE__, __LINE__, "", [&](std::stringstream &msg) {
273  msg << "'" << conversation_name_ << "'"
274  << "-> "
275  << "Diagnostic Response P2 Star Timeout happened after " << p2_star_client_max_
276  << " milliseconds";
277  ;
278  });
280  conversation_state_.GetConversationStateContext().TransitionTo(ConversationState::kIdle);
281  },
282  [this]() {
283  // pending or pos/neg response
284  if (conversation_state_.GetConversationStateContext().GetActiveState().GetState() ==
285  ConversationState::kDiagRecvdFinalRes) {
286  // pos/neg response received
287  } else if (conversation_state_.GetConversationStateContext().GetActiveState().GetState() ==
288  ConversationState::kDiagRecvdPendingRes) {
289  // pending received again
290  conversation_state_.GetConversationStateContext().TransitionTo(
291  ConversationState::kDiagStartP2StarTimer);
292  }
293  },
294  std::chrono::milliseconds{p2_star_client_max_});
295  break;
296  case ConversationState::kDiagSuccess:
297  // change state to idle, form the uds response and return
298  result.EmplaceValue(std::make_unique<diag::client::uds_message::DmUdsResponse>(payload_rx_buffer_));
299  conversation_state_.GetConversationStateContext().TransitionTo(ConversationState::kIdle);
300  break;
301  default:
302  // nothing
303  break;
304  }
305  }
306  } else {
307  // failure
308  result.EmplaceError(ConvertResponseType(transmission_result));
309  }
310  } else {
312  logger::DiagClientLogger::GetDiagClientLogger().GetLogger().LogWarn(__FILE__, __LINE__, "",
313  [&](std::stringstream &msg) {
314  msg << "'" << conversation_name_ << "'"
315  << "-> "
316  << "Diagnostic Request message is empty";
317  });
318  }
319  return result;
320 }
321 
322 void DmConversation::RegisterConnection(std::unique_ptr<uds_transport::Connection> connection) noexcept {
323  connection_ptr_ = std::move(connection);
324 }
325 
327  return *dm_conversion_handler_;
328 }
329 
330 std::pair<uds_transport::UdsTransportProtocolMgr::IndicationResult, uds_transport::UdsMessagePtr>
334  core_type::Span<std::uint8_t> payload_info) noexcept {
335  std::pair<uds_transport::UdsTransportProtocolMgr::IndicationResult, uds_transport::UdsMessagePtr> ret_val{
337  // Verify the payload received :-
338  if (!payload_info.empty()) {
339  // Check for size, else kIndicationOverflow
340  if (size <= rx_buffer_size_) {
341  // Check for pending response
342  // payload = 0x7F XX 0x78
343  if (payload_info[0U] == 0x7F && payload_info[2U] == 0x78) {
344  logger::DiagClientLogger::GetDiagClientLogger().GetLogger().LogInfo(
345  __FILE__, __LINE__, "", [&](std::stringstream &msg) {
346  msg << "'" << conversation_name_ << "'"
347  << "-> "
348  << "Diagnostic pending response received in Conversation";
349  });
351  conversation_state_.GetConversationStateContext().TransitionTo(ConversationState::kDiagRecvdPendingRes);
352  } else {
353  logger::DiagClientLogger::GetDiagClientLogger().GetLogger().LogDebug(
354  __FILE__, __LINE__, "", [this](std::stringstream &msg) {
355  msg << "'" << conversation_name_ << "'"
356  << "-> "
357  << "Diagnostic final response received in Conversation";
358  });
359  // positive or negative response, provide valid buffer
360  // resize the global rx buffer
361  payload_rx_buffer_.resize(size);
363  ret_val.second = std::make_unique<diag::client::uds_message::DmUdsMessage>(source_address_, target_address_, "",
364  payload_rx_buffer_);
365  conversation_state_.GetConversationStateContext().TransitionTo(ConversationState::kDiagRecvdFinalRes);
366  }
367  sync_timer_.CancelWait();
368  } else {
369  logger::DiagClientLogger::GetDiagClientLogger().GetLogger().LogError(
370  __FILE__, __LINE__, "", [&](std::stringstream &msg) {
371  msg << "'" << conversation_name_ << "'"
372  << "-> "
373  << "Diagnostic Conversation Error Indication Overflow";
374  });
376  }
377  } else {
378  logger::DiagClientLogger::GetDiagClientLogger().GetLogger().LogError(
379  __FILE__, __LINE__, "", [&](std::stringstream &msg) {
380  msg << "'" << conversation_name_ << "'"
381  << "-> "
382  << "Diagnostic Conversation Rx Payload size 0 received";
383  });
384  }
385  return ret_val;
386 }
387 
389  if (message != nullptr) {
390  conversation_state_.GetConversationStateContext().TransitionTo(ConversationState::kDiagSuccess);
391  }
392 }
393 
397  switch (result_type) {
400  break;
403  break;
406  break;
409  break;
410  default:
412  break;
413  }
414  return ret_result;
415 }
416 
417 } // namespace conversation
418 } // namespace client
419 } // namespace diag
Class type to contains a value (of type ValueType), or an error (of type ErrorType)
Definition: result.h:29
static Result FromError(const E &e) noexcept
Build a new Result from the specified error (given as lvalue)
Definition: result.h:83
Interface for diag client conversation.
Definition: conversation.h:24
DiagClientConversation::IpAddress IpAddress
Type alias for Ip address.
Definition: conversation.h:29
DiagError
Definitions of Diagnostics Request Response results.
Class to manage reception from transport protocol handler to dm connection handler.
~DmConversationHandler() override=default
Destructs an instance of DmConversationHandler.
DmConversationHandler(::uds_transport::conversion_manager::ConversionHandlerID handler_id, DmConversation &dm_conversion)
Constructs an instance of DmConversationHandler.
DmConversationHandler & operator=(const DmConversationHandler &other) noexcept=delete
DmConversation & dm_conversation_
Store the reference of dm conversation.
DmConversationHandler & operator=(DmConversationHandler &&other) noexcept=delete
void HandleMessage(::uds_transport::UdsMessagePtr message) const noexcept override
Function to Hands over a valid received Uds message.
DmConversationHandler(DmConversationHandler &&other) noexcept=delete
Deleted move assignment and move constructor.
std::pair<::uds_transport::UdsTransportProtocolMgr::IndicationResult, ::uds_transport::UdsMessagePtr > IndicateMessage(::uds_transport::UdsMessage::Address source_addr, ::uds_transport::UdsMessage::Address target_addr, ::uds_transport::UdsMessage::TargetAddressType type, ::uds_transport::ChannelID channel_id, std::size_t size, ::uds_transport::Priority priority, ::uds_transport::ProtocolKind protocol_kind, core_type::Span< std::uint8_t > payload_info) const noexcept override
Function to indicate a start of reception of message.
DmConversationHandler(const DmConversationHandler &other) noexcept=delete
Deleted copy assignment and copy constructor.
Class to establish connection with Diagnostic Server.
std::pair<::uds_transport::UdsTransportProtocolMgr::IndicationResult, ::uds_transport::UdsMessagePtr > IndicateMessage(::uds_transport::UdsMessage::Address source_addr, ::uds_transport::UdsMessage::Address target_addr, ::uds_transport::UdsMessage::TargetAddressType type, ::uds_transport::ChannelID channel_id, std::size_t size, ::uds_transport::Priority priority, ::uds_transport::ProtocolKind protocol_kind, core_type::Span< std::uint8_t > payload_info) noexcept override
Function to indicate a start of reception of message.
void Startup() noexcept override
Function to start the DmConversation.
~DmConversation() override
Destructs an instance of DmConversation.
DisconnectResult DisconnectFromDiagServer() noexcept override
Function to disconnect from Diagnostic Server.
std::unique_ptr<::uds_transport::ConversionHandler > dm_conversion_handler_
Store the dm conversation handler.
DmConversation(std::string_view conversion_name, DMConversationType &conversion_identifier)
Constructs an instance of DmConversation.
void HandleMessage(::uds_transport::UdsMessagePtr message) noexcept override
Function to Hands over a valid received Uds message.
void RegisterConnection(std::unique_ptr<::uds_transport::Connection > connection) noexcept override
Function to register the conversation to underlying transport protocol handler.
ConnectResult ConnectToDiagServer(std::uint16_t target_address, IpAddress host_ip_addr) noexcept override
Function to connect to Diagnostic Server.
SecurityLevelType
Definitions of active security level.
::uds_transport::ConversionHandler & GetConversationHandler() noexcept override
Function to get the conversation handler from conversation object.
Result< uds_message::UdsResponseMessagePtr, DiagError > SendDiagnosticRequest(uds_message::UdsRequestMessageConstPtr message) noexcept override
Function to send Diagnostic Request and get Diagnostic Response.
ActivityStatusType activity_status_
Store the conversation activity status.
SessionControlType active_session_
Store the active diagnostic session.
std::uint16_t target_address_
Store the logical target address of remote server.
ActivityStatusType
Definitions of current activity status.
std::string remote_address_
Store the remote IP address of remote server.
SecurityLevelType active_security_level_
Store the active diagnostic security level.
void Shutdown() noexcept override
Function to shutdown the DmConversation.
std::unique_ptr<::uds_transport::Connection > connection_ptr_
Store the underlying transport protocol connection object.
std::string conversation_name_
Store the conversation name.
SessionControlType
Definitions of active diagnostic session.
static DiagClientConversation::DiagError ConvertResponseType(::uds_transport::UdsTransportProtocolMgr::TransmissionResult result_type)
Helper function to convert response type.
static auto GetDiagClientLogger() noexcept -> DiagClientLogger &
Get the diag client logger instance.
Definition: logger.h:32
Class to manage reception from transport protocol handler to connection handler.
ConversionHandler(conversion_manager::ConversionHandlerID handler_id)
Constructs an instance of ConversionHandler.
std::unique_ptr< const UdsMessage > UdsRequestMessageConstPtr
Type alias of unique_ptr for constant UdsRequestMessage.
std::string_view ProtocolKind
std::unique_ptr< UdsMessage > UdsMessagePtr
Definition: uds_message.h:71
uint32_t ChannelID
std::uint8_t Priority
std::vector< std::uint8_t > ByteVector
Structure containing DM conversation type.