From 07cf4f654b8300b02d66a85e095a224db99a2935 Mon Sep 17 00:00:00 2001 From: tompzf <141007871+tompzf@users.noreply.github.com> Date: Thu, 2 Apr 2026 17:37:00 +0200 Subject: [PATCH] tunnel component & update vehicle abstraction example (#8) --- .gitignore | 3 +- examples/CMakeLists.txt | 1 + examples/auto_headlamp_example/CMakeLists.txt | 3 +- .../autoheadlight_app/autoheadlight_app.cpp | 2 +- .../autoheadlight_console.cpp | 8 +- .../autoheadlight_app/autoheadlight_console.h | 7 +- .../autoheadlight_simulate.h | 2 +- .../autoheadlight_app/signal_names.h | 35 - examples/door_demo_example/CMakeLists.txt | 2 - .../door_demo_example/door_app/console.cpp | 10 +- .../door_app/door_application.cpp | 2 +- .../door_app/door_extern_application.cpp | 2 +- .../door_app/include/console.h | 7 +- .../door_app/include/door_application.h | 2 +- .../include/door_extern_application.h | 3 +- .../door_app/include/signal_names.h | 33 - examples/open_trunk_example/CMakeLists.txt | 7 +- .../open_trunk_app/console.cpp | 36 +- .../open_trunk_app/console.h | 8 +- .../open_trunk_app/signal_names.h | 35 - .../open_trunk_app/trunk_application.cpp | 2 +- .../open_trunk_app/trunk_application.h | 3 +- examples/system_demo_example/CMakeLists.txt | 8 +- .../example_app/console.cpp | 2 +- .../system_demo_example/example_app/console.h | 2 +- .../system_demo_example/example_app/control.h | 2 +- .../example_app/signal_names.h | 35 - .../CMakeLists.txt | 123 + ...om_simulation_vehicle_abstraction_kmh.toml | 9 + ...com_simulation_vehicle_abstraction_ms.toml | 9 + .../data_dispatch_vehicle_abstraction.toml | 8 + .../config/data_link_vehicle_abstraction.toml | 6 + .../config/task_timer_vehicle.toml | 6 + .../vehicle_abstraction_basic_service.toml | 12 + .../vehicle_abstraction_device_kmh.toml | 6 + .../config/vehicle_abstraction_device_ms.toml | 8 + .../vehicle_abstraction_app/console.cpp | 316 +++ .../vehicle_abstraction_app/console.h | 200 ++ .../vehicle_abstraction_application.cpp | 103 + .../vehicle_abstraction_application.h | 60 + .../vehicle_abstraction_example.cpp | 47 + .../vehicle_abstraction_example.csv | 4 + .../vehicle_abstraction_example.dbc | 115 + .../vehicle_abstraction_example_ms.csv | 3 + ...hicle_abstraction_example_receiver_kmh.asc | 2357 +++++++++++++++++ ...ehicle_abstraction_example_receiver_ms.asc | 1505 +++++++++++ export/interfaces/ipc.idl | 33 +- export/support/component_impl.h | 4 +- sdv_executables/sdv_iso/main.cpp | 28 +- .../sdv_vss_util/vss_bs_codingrx.cpp | 6 +- .../sdv_vss_util/vss_rx_templates.h | 4 +- .../sdv_vss_util/vss_tx_templates.h | 4 +- .../sdv_vss_util/vss_vd_codingrx.cpp | 12 +- sdv_services/CMakeLists.txt | 4 +- sdv_services/core/iso_monitor.h | 2 +- sdv_services/ipc_com/com_channel.cpp | 28 +- sdv_services/ipc_com/com_channel.h | 12 +- sdv_services/ipc_shared_mem/connection.cpp | 198 +- sdv_services/ipc_shared_mem/connection.h | 32 +- sdv_services/ipc_shared_mem/watchdog.cpp | 2 +- sdv_services/uds_unix_sockets/CMakeLists.txt | 9 +- sdv_services/uds_unix_sockets/connection.cpp | 100 +- sdv_services/uds_unix_sockets/connection.h | 24 +- sdv_services/uds_unix_tunnel/CMakeLists.txt | 33 + sdv_services/uds_unix_tunnel/channel_mgnt.cpp | 178 ++ sdv_services/uds_unix_tunnel/channel_mgnt.h | 140 + sdv_services/uds_unix_tunnel/connection.cpp | 230 ++ sdv_services/uds_unix_tunnel/connection.h | 191 ++ sdv_services/uds_win_sockets/CMakeLists.txt | 22 +- sdv_services/uds_win_sockets/channel_mgnt.cpp | 20 +- sdv_services/uds_win_sockets/channel_mgnt.h | 23 +- sdv_services/uds_win_sockets/connection.cpp | 178 +- sdv_services/uds_win_sockets/connection.h | 56 +- sdv_services/uds_win_tunnel/CMakeLists.txt | 40 + sdv_services/uds_win_tunnel/channel_mgnt.cpp | 414 +++ sdv_services/uds_win_tunnel/channel_mgnt.h | 106 + sdv_services/uds_win_tunnel/connection.cpp | 226 ++ sdv_services/uds_win_tunnel/connection.h | 168 ++ tests/CMakeLists.txt | 6 +- tests/unit_tests/ipc_com/ipc_com.cpp | 8 +- tests/unit_tests/shared_mem/app_connect.cpp | 26 +- .../shared_mem/shared_mem_connect.cpp | 164 +- .../shared_mem_large_data_tests.cpp | 58 +- tests/unit_tests/unix_sockets/CMakeLists.txt | 19 +- .../unix_sockets/uds_connect_tests.cpp | 156 +- tests/unit_tests/unix_tunnel/CMakeLists.txt | 107 + .../unix_tunnel_channel_mgnt_tests.cpp | 240 ++ .../unix_tunnel/unix_tunnel_connect_tests.cpp | 376 +++ tests/unit_tests/win_sockets/CMakeLists.txt | 5 +- .../win_sockets/win_connect_tests.cpp | 53 +- tests/unit_tests/win_tunnel/CMakeLists.txt | 134 + .../win_tunnel_channel_mgnt_tests.cpp | 442 ++++ .../win_tunnel/win_tunnel_connect_tests.cpp | 454 ++++ .../win_tunnel_negative_edge_tests.cpp | 164 ++ 94 files changed, 9268 insertions(+), 830 deletions(-) delete mode 100644 examples/auto_headlamp_example/autoheadlight_app/signal_names.h delete mode 100644 examples/door_demo_example/door_app/include/signal_names.h delete mode 100644 examples/open_trunk_example/open_trunk_app/signal_names.h delete mode 100644 examples/system_demo_example/example_app/signal_names.h create mode 100644 examples/vehicle_abstraction_example/CMakeLists.txt create mode 100644 examples/vehicle_abstraction_example/config/can_com_simulation_vehicle_abstraction_kmh.toml create mode 100644 examples/vehicle_abstraction_example/config/can_com_simulation_vehicle_abstraction_ms.toml create mode 100644 examples/vehicle_abstraction_example/config/data_dispatch_vehicle_abstraction.toml create mode 100644 examples/vehicle_abstraction_example/config/data_link_vehicle_abstraction.toml create mode 100644 examples/vehicle_abstraction_example/config/task_timer_vehicle.toml create mode 100644 examples/vehicle_abstraction_example/config/vehicle_abstraction_basic_service.toml create mode 100644 examples/vehicle_abstraction_example/config/vehicle_abstraction_device_kmh.toml create mode 100644 examples/vehicle_abstraction_example/config/vehicle_abstraction_device_ms.toml create mode 100644 examples/vehicle_abstraction_example/vehicle_abstraction_app/console.cpp create mode 100644 examples/vehicle_abstraction_example/vehicle_abstraction_app/console.h create mode 100644 examples/vehicle_abstraction_example/vehicle_abstraction_app/vehicle_abstraction_application.cpp create mode 100644 examples/vehicle_abstraction_example/vehicle_abstraction_app/vehicle_abstraction_application.h create mode 100644 examples/vehicle_abstraction_example/vehicle_abstraction_app/vehicle_abstraction_example.cpp create mode 100644 examples/vehicle_abstraction_example/vehicle_abstraction_example.csv create mode 100644 examples/vehicle_abstraction_example/vehicle_abstraction_example.dbc create mode 100644 examples/vehicle_abstraction_example/vehicle_abstraction_example_ms.csv create mode 100644 examples/vehicle_abstraction_example/vehicle_abstraction_example_receiver_kmh.asc create mode 100644 examples/vehicle_abstraction_example/vehicle_abstraction_example_receiver_ms.asc create mode 100644 sdv_services/uds_unix_tunnel/CMakeLists.txt create mode 100644 sdv_services/uds_unix_tunnel/channel_mgnt.cpp create mode 100644 sdv_services/uds_unix_tunnel/channel_mgnt.h create mode 100644 sdv_services/uds_unix_tunnel/connection.cpp create mode 100644 sdv_services/uds_unix_tunnel/connection.h create mode 100644 sdv_services/uds_win_tunnel/CMakeLists.txt create mode 100644 sdv_services/uds_win_tunnel/channel_mgnt.cpp create mode 100644 sdv_services/uds_win_tunnel/channel_mgnt.h create mode 100644 sdv_services/uds_win_tunnel/connection.cpp create mode 100644 sdv_services/uds_win_tunnel/connection.h create mode 100644 tests/unit_tests/unix_tunnel/CMakeLists.txt create mode 100644 tests/unit_tests/unix_tunnel/unix_tunnel_channel_mgnt_tests.cpp create mode 100644 tests/unit_tests/unix_tunnel/unix_tunnel_connect_tests.cpp create mode 100644 tests/unit_tests/win_tunnel/CMakeLists.txt create mode 100644 tests/unit_tests/win_tunnel/win_tunnel_channel_mgnt_tests.cpp create mode 100644 tests/unit_tests/win_tunnel/win_tunnel_connect_tests.cpp create mode 100644 tests/unit_tests/win_tunnel/win_tunnel_negative_edge_tests.cpp diff --git a/.gitignore b/.gitignore index bac52e6..418c05a 100644 --- a/.gitignore +++ b/.gitignore @@ -19,7 +19,7 @@ DoxygenWarning.txt packages/ windows_vapi/.vs -#idl & dbc +#idl & dbc & vss export/interfaces/*.h export/interfaces/ps/ export/interfaces/serdes/ @@ -27,6 +27,7 @@ export/interfaces/serdes/ */*/*/generated/ */*/generated/ */generated/ +*/*/generated2/* #CMake intermediate files and folders diff --git a/examples/CMakeLists.txt b/examples/CMakeLists.txt index 69aee23..b28f54f 100644 --- a/examples/CMakeLists.txt +++ b/examples/CMakeLists.txt @@ -118,3 +118,4 @@ add_subdirectory(system_demo_example) add_subdirectory(door_demo_example) add_subdirectory(auto_headlamp_example) add_subdirectory(open_trunk_example) +add_subdirectory(vehicle_abstraction_example) diff --git a/examples/auto_headlamp_example/CMakeLists.txt b/examples/auto_headlamp_example/CMakeLists.txt index 4dd0dbd..c2ceab1 100644 --- a/examples/auto_headlamp_example/CMakeLists.txt +++ b/examples/auto_headlamp_example/CMakeLists.txt @@ -65,7 +65,7 @@ execute_process(COMMAND "${SDV_IDL_COMPILER}" "${PROJECT_SOURCE_DIR}/autoheadlig # REMARK: Proxy/stub and vehicle device and basic service code was generated during the configuration phase of CMake. Following # below are the build steps to build the components that were generated. -message("Include: auto headlight proxy/stub for vehicle devices and basic services") +message("Include: auto headlight proxy/stub for basic services") include_directories(${CMAKE_CURRENT_LIST_DIR}/generated/vss_files) add_subdirectory(generated/vss_files/ps) @@ -107,7 +107,6 @@ add_executable(auto_headlight_app autoheadlight_app/autoheadlight_simulate.cpp autoheadlight_app/autoheadlight_console.h autoheadlight_app/autoheadlight_console.cpp - autoheadlight_app/signal_names.h ) if (CMAKE_CXX_COMPILER_ID STREQUAL "GNU") diff --git a/examples/auto_headlamp_example/autoheadlight_app/autoheadlight_app.cpp b/examples/auto_headlamp_example/autoheadlight_app/autoheadlight_app.cpp index d8fdff9..8b31c36 100644 --- a/examples/auto_headlamp_example/autoheadlight_app/autoheadlight_app.cpp +++ b/examples/auto_headlamp_example/autoheadlight_app/autoheadlight_app.cpp @@ -12,7 +12,7 @@ #include #include #include -#include "signal_names.h" +#include "../generated/vss_files/signal_identifier.h" #include "autoheadlight_simulate.h" #include "autoheadlight_console.h" diff --git a/examples/auto_headlamp_example/autoheadlight_app/autoheadlight_console.cpp b/examples/auto_headlamp_example/autoheadlight_app/autoheadlight_console.cpp index d305d9c..40a2191 100644 --- a/examples/auto_headlamp_example/autoheadlight_app/autoheadlight_console.cpp +++ b/examples/auto_headlamp_example/autoheadlight_app/autoheadlight_console.cpp @@ -108,14 +108,14 @@ void CConsole::PrintHeader() bool CConsole::PrepareDataConsumers() { // Vehicle Device - auto pVDCurrentLatitudeSvc = sdv::core::GetObject("Vehicle.Position.CurrentLatitude_Device").GetInterface(); + auto pVDCurrentLatitudeSvc = sdv::core::GetObject("Vehicle.Position.CurrentLatitude_Device").GetInterface(); if (!pVDCurrentLatitudeSvc) { SDV_LOG_ERROR("Could not get interface 'IVSS_GetVDCurrentLatitude': [CAutoHeadlightService]"); return false; } - auto pVDCurrentLongitudeSvc = sdv::core::GetObject("Vehicle.Position.CurrentLongitude_Device").GetInterface(); + auto pVDCurrentLongitudeSvc = sdv::core::GetObject("Vehicle.Position.CurrentLongitude_Device").GetInterface(); if (!pVDCurrentLongitudeSvc) { SDV_LOG_ERROR("Could not get interface 'IVSS_GetVDCurrentLongitude': [CAutoHeadlightService]"); @@ -212,11 +212,11 @@ void CConsole::ResetSignals() SetCursorPos(g_sCursor); // Vehicle Device - auto pVDCurrentLatitudeSvc = sdv::core::GetObject("Vehicle.Position.CurrentLatitude_Device").GetInterface(); + auto pVDCurrentLatitudeSvc = sdv::core::GetObject("Vehicle.Position.CurrentLatitude_Device").GetInterface(); if (pVDCurrentLatitudeSvc) pVDCurrentLatitudeSvc->UnregisterCurrentLatitudeEvent(static_cast (this)); - auto pVDCurrentLongitudeSvc = sdv::core::GetObject("Vehicle.Position.CurrentLongitude_Device").GetInterface(); + auto pVDCurrentLongitudeSvc = sdv::core::GetObject("Vehicle.Position.CurrentLongitude_Device").GetInterface(); if (pVDCurrentLongitudeSvc) pVDCurrentLongitudeSvc->UnregisterCurrentLongitudeEvent(static_cast (this)); diff --git a/examples/auto_headlamp_example/autoheadlight_app/autoheadlight_console.h b/examples/auto_headlamp_example/autoheadlight_app/autoheadlight_console.h index 2afc01c..ab13c36 100644 --- a/examples/auto_headlamp_example/autoheadlight_app/autoheadlight_console.h +++ b/examples/auto_headlamp_example/autoheadlight_app/autoheadlight_console.h @@ -19,7 +19,7 @@ #include #include #include -#include "signal_names.h" +#include "../generated/vss_files/signal_identifier.h" #include #include "vss_vehiclepositioncurrentlatitude_vd_rx.h" #include "vss_vehiclepositioncurrentlongitude_vd_rx.h" @@ -187,7 +187,7 @@ private: * @param[in] sPos The location to print the value at. * @param[in] rssName Reference to the value. * @param[in] tValue The value. - * @param[in] rssStatus Status, becuse we have signals of type bool + * @param[in] rssStatus Status, because we have signals of type bool */ template void PrintValue(SConsolePos sPos, const std::string& rssName, TValue tValue, const std::string& rssStatus); @@ -204,7 +204,6 @@ private: std::thread m_threadReadTxSignals; ///< Simulation datalink thread. bool m_bThreadStarted = false; ///< Set when initialized. std::atomic_bool m_bRunning = false; ///< When set, the application is running. - mutable std::mutex m_mPrintToConsole; ///< Mutex to print complete message sdv::core::CSignal m_signalCurrentLatitude; ///< Signal Current latitude sdv::core::CSignal m_signalCurrentLongitude; ///< Signal Current longitude @@ -244,7 +243,7 @@ inline void CConsole::PrintValue(SConsolePos sPos, const std::string& rssName, T std::string(nValueNameLen - std::min(rssName.size(), static_cast(nValueNameLen - 1)) - 1, '.') << " " << std::fixed << std::setprecision(6) << tValue << " " << rssUnits << " "; - std::lock_guard lock(m_mPrintToConsole); + std::lock_guard lock(m_mtxPrintToConsole); SetCursorPos(sPos); std::cout << sstreamValueText.str(); } diff --git a/examples/auto_headlamp_example/autoheadlight_app/autoheadlight_simulate.h b/examples/auto_headlamp_example/autoheadlight_app/autoheadlight_simulate.h index e5ef69a..495835b 100644 --- a/examples/auto_headlamp_example/autoheadlight_app/autoheadlight_simulate.h +++ b/examples/auto_headlamp_example/autoheadlight_app/autoheadlight_simulate.h @@ -15,7 +15,7 @@ #include #include #include -#include "signal_names.h" +#include "../generated/vss_files/signal_identifier.h" // VSS interfaces - located in ../generated/vss_files/include #include "vss_vehiclepositioncurrentlatitude_bs_rx.h" diff --git a/examples/auto_headlamp_example/autoheadlight_app/signal_names.h b/examples/auto_headlamp_example/autoheadlight_app/signal_names.h deleted file mode 100644 index 153b2d5..0000000 --- a/examples/auto_headlamp_example/autoheadlight_app/signal_names.h +++ /dev/null @@ -1,35 +0,0 @@ - /******************************************************************************** - * Copyright (c) 2025-2026 ZF Friedrichshafen AG - * - * This program and the accompanying materials are made available under the - * terms of the Apache License Version 2.0 which is available at - * https://www.apache.org/licenses/LICENSE-2.0 - * - * SPDX-License-Identifier: Apache-2.0 - ********************************************************************************/ - - /** - * namespace for the signal names - * in case /generated/vss_files/signal_identifier.h - * exists, use the file, otherwise define the namespace - */ - -#ifndef SIGNAL_NAMES_H -#define SIGNAL_NAMES_H - -#ifdef __has_include -#if __has_include("../generated/vss_files/signal_identifier.h") -#include "../generated/vss_files/signal_identifier.h" -#else -namespace headlight -{ - // Data Dispatch Service signal names to dbc variable names C-type RX/TX vss name space - static std::string dsFCurrentLatitude = "CAN_Input.Current_Latitude" ; ///< float RX Vehicle.Position.CurrentLatitude - static std::string dsFCurrentLongitude = "CAN_Input.Current_Longitude" ; ///< float RX Vehicle.Position.CurrentLongitude - static std::string dsBHeadlightLowBeam = "CAN_Output.HeadLight_LowBeam"; ///< bool TX Vehicle.Body.Light.Front.Lowbeam -} // headlight -#endif -#endif - -#endif // ! defined SIGNAL_NAMES_H - diff --git a/examples/door_demo_example/CMakeLists.txt b/examples/door_demo_example/CMakeLists.txt index cbe207d..54e8a98 100644 --- a/examples/door_demo_example/CMakeLists.txt +++ b/examples/door_demo_example/CMakeLists.txt @@ -132,7 +132,6 @@ add_executable(door_demo_example "door_app/door_application.cpp" "door_app/include/console.h" "door_app/console.cpp" - "door_app/include/signal_names.h" "door_service/lock_doors_thread.h") if (CMAKE_CXX_COMPILER_ID STREQUAL "GNU") @@ -156,7 +155,6 @@ add_executable(door_external_app "door_app/door_extern_application.cpp" "door_app/include/console.h" "door_app/console.cpp" - "door_app/include/signal_names.h" "door_service/lock_doors_thread.h") if (CMAKE_CXX_COMPILER_ID STREQUAL "GNU") diff --git a/examples/door_demo_example/door_app/console.cpp b/examples/door_demo_example/door_app/console.cpp index 63b12a3..acd9dea 100644 --- a/examples/door_demo_example/door_app/console.cpp +++ b/examples/door_demo_example/door_app/console.cpp @@ -144,7 +144,7 @@ bool CConsole::PrepareDataConsumers() auto basicServiceL1 = sdv::core::GetObject("Vehicle.Body.Door.Axle._01.Left_Service").GetInterface(); if (!basicServiceL1) { - SDV_LOG_ERROR("Could not get interface 'LeftService::IVSS_IsOpen': [CConsole]"); + SDV_LOG_ERROR("Could not get interface 'LeftService::IVSS_ReadIsOpen': [CConsole]"); return false; } else @@ -207,7 +207,7 @@ void CConsole::ResetSignals() // Registrate for the vehicle device & basic service of the front left door. Front left door mzust exist, the others are optional auto vehicleDevice = - sdv::core::GetObject("Vehicle.Body.Door.Axle._01.Left_Device").GetInterface < vss::Vehicle::Body::Door::Axle::_01 ::LeftDevice::IVSS_IsOpen > (); + sdv::core::GetObject("Vehicle.Body.Door.Axle._01.Left_Device").GetInterface < vss::Vehicle::Body::Door::Axle::_01 ::LeftDevice::IVSS_ReadIsOpen > (); if (vehicleDevice) vehicleDevice->UnregisterIsOpenEvent(dynamic_cast (this)); @@ -227,7 +227,7 @@ void CConsole::ResetSignals() if (basicServiceR2) basicServiceR2->UnregisterOnSignalChangeOfRightDoorIsOpen02(dynamic_cast (this)); - // Unregister the data link signalss + // Unregister the data link signals if (m_SignalFrontLeftDoorIsOpen) m_SignalFrontLeftDoorIsOpen.Reset(); if (m_SignalFrontRightDoorIsOpen) @@ -338,10 +338,10 @@ bool CConsole::PrepareDataConsumersForStandAlone() } // Registrate for the vehicle device & basic service of the front left door. Front left door mzust exist, the others are optional - auto vehicleDevice = sdv::core::GetObject("Vehicle.Body.Door.Axle._01.Left_Device").GetInterface(); + auto vehicleDevice = sdv::core::GetObject("Vehicle.Body.Door.Axle._01.Left_Device").GetInterface(); if (!vehicleDevice) { - SDV_LOG_ERROR("Could not get interface 'LeftDevice::IVSS_IsOpen': [CConsole]"); + SDV_LOG_ERROR("Could not get interface 'LeftDevice::IVSS_ReadIsOpen': [CConsole]"); return false; } vehicleDevice->RegisterIsOpenEvent(dynamic_cast (this)); diff --git a/examples/door_demo_example/door_app/door_application.cpp b/examples/door_demo_example/door_app/door_application.cpp index 8eece56..1f0ee6a 100644 --- a/examples/door_demo_example/door_app/door_application.cpp +++ b/examples/door_demo_example/door_app/door_application.cpp @@ -9,7 +9,7 @@ ********************************************************************************/ #include "../door_app/include/door_application.h" -#include "../door_app/include/signal_names.h" +#include "../generated/vss_files/signal_identifier.h" #ifdef _WIN32 #include // Needed for _kbhit diff --git a/examples/door_demo_example/door_app/door_extern_application.cpp b/examples/door_demo_example/door_app/door_extern_application.cpp index 22f96ca..9339fc7 100644 --- a/examples/door_demo_example/door_app/door_extern_application.cpp +++ b/examples/door_demo_example/door_app/door_extern_application.cpp @@ -9,7 +9,7 @@ ********************************************************************************/ #include "../door_app/include/door_extern_application.h" -#include "../door_app/include/signal_names.h" +#include "../generated/vss_files/signal_identifier.h" #ifdef _WIN32 #include // Needed for _kbhit diff --git a/examples/door_demo_example/door_app/include/console.h b/examples/door_demo_example/door_app/include/console.h index 0f55358..b88e257 100644 --- a/examples/door_demo_example/door_app/include/console.h +++ b/examples/door_demo_example/door_app/include/console.h @@ -19,7 +19,7 @@ #include #include #include -#include "signal_names.h" +#include "../../generated/vss_files/signal_identifier.h" #include #include @@ -202,7 +202,7 @@ private: * @param[in] sPos The location to print the value at. * @param[in] rssName Reference to the value. * @param[in] tValue The value. - * @param[in] rssStatus Status, becuse we have signals of type bool + * @param[in] rssStatus Status, because we have signals of type bool */ template void PrintValue(SConsolePos sPos, const std::string& rssName, TValue tValue, const std::string& rssStatus); @@ -219,7 +219,6 @@ private: bool m_bThreadStarted = false; ///< Set when initialized. std::atomic_bool m_bRunning = false; ///< When set, the application is running. bool m_isExternalApp = false; ///< True when we have an external application - mutable std::mutex m_mPrintToConsole; ///< Mutex to print complete message std::thread m_threadReadTxSignals; ///< Simulation datalink thread. @@ -269,7 +268,7 @@ inline void CConsole::PrintValue(SConsolePos sPos, const std::string& rssName, T " " << std::fixed << std::setprecision(2) << tValue << " " << rssUnits << std::string(nEndNameLen - std::min(endName.size(), static_cast(nEndNameLen - 1)) - 1, ' '); - std::lock_guard lock(m_mPrintToConsole); + std::lock_guard lock(m_mtxPrintToConsole); SetCursorPos(sPos); std::cout << sstreamValueText.str(); } diff --git a/examples/door_demo_example/door_app/include/door_application.h b/examples/door_demo_example/door_app/include/door_application.h index e660329..122cf7b 100644 --- a/examples/door_demo_example/door_app/include/door_application.h +++ b/examples/door_demo_example/door_app/include/door_application.h @@ -64,7 +64,7 @@ private: bool IsSDVFrameworkEnvironmentSet(); /** - * @brief Loac config file and register vehicle device and basic service. + * @brief Load config file and register vehicle device and basic service. * @remarks It is expected that each config file has the complete door: * vehicle device & basic service for input and output * @param[in] inputMsg message string to be printed on console in case of success and failure diff --git a/examples/door_demo_example/door_app/include/door_extern_application.h b/examples/door_demo_example/door_app/include/door_extern_application.h index af33be3..11c40d2 100644 --- a/examples/door_demo_example/door_app/include/door_extern_application.h +++ b/examples/door_demo_example/door_app/include/door_extern_application.h @@ -21,12 +21,11 @@ public: /** * @brief Start and initialize the application control and load vehicle devices and - * basic services depending on the numerb of doors + * basic services depending on the number of doors * @return Return true on success otherwise false */ bool Initialize(); - /** * @brief Run loop as long as user input does not exit * Allow user to open/close each door. diff --git a/examples/door_demo_example/door_app/include/signal_names.h b/examples/door_demo_example/door_app/include/signal_names.h deleted file mode 100644 index d01b038..0000000 --- a/examples/door_demo_example/door_app/include/signal_names.h +++ /dev/null @@ -1,33 +0,0 @@ - /******************************************************************************** - * Copyright (c) 2025-2026 ZF Friedrichshafen AG - * - * This program and the accompanying materials are made available under the - * terms of the Apache License Version 2.0 which is available at - * https://www.apache.org/licenses/LICENSE-2.0 - * - * SPDX-License-Identifier: Apache-2.0 - ********************************************************************************/ - -/** - * namespace for the signal names - * in case /interfaces/signal_identifier.h - * exists, use the file, otherwise define the namespace - */ -#ifndef SIGNAL_NAMES_H -#define SIGNAL_NAMES_H - -namespace doors -{ - // Data Dispatch Service signal names to dbc variable names C-type RX/TX vss name space - static std::string dsLeftDoorIsOpen01 = "CAN_Input_L1.Door01LeftIsOpen"; ///< bool RX Vehicle.Body.Door.Axle._01.Left - static std::string dsRightDoorIsOpen01 = "CAN_Input_R1.Door01RightIsOpen"; ///< bool RX Vehicle.Body.Door.Axle._01.Right - static std::string dsLeftDoorIsOpen02 = "CAN_Input_L2.Door02LeftIsOpen"; ///< bool RX Vehicle.Body.Door.Axle._02.Left - static std::string dsRightDoorIsOpen02 = "CAN_Input_R2.Door02RightIsOpen"; ///< bool RX Vehicle.Body.Door.Axle._02.Right - static std::string dsLeftLatch01 = "CAN_Output.LockDoor01Left"; ///< bool TX Vehicle.Body.Door.Axle._01.Left - static std::string dsRightLatch01 = "CAN_Output.LockDoor01Right"; ///< bool TX Vehicle.Body.Door.Axle._01.Right - static std::string dsLeftLatch02 = "CAN_Output.LockDoor02Left"; ///< bool TX Vehicle.Body.Door.Axle._02.Left - static std::string dsRightLatch02 = "CAN_Output.LockDoor02Right"; ///< bool TX Vehicle.Body.Door.Axle._02.Right - -} // doors - -#endif // SIGNAL_NAMES_H diff --git a/examples/open_trunk_example/CMakeLists.txt b/examples/open_trunk_example/CMakeLists.txt index 646eafd..4d0334d 100644 --- a/examples/open_trunk_example/CMakeLists.txt +++ b/examples/open_trunk_example/CMakeLists.txt @@ -30,7 +30,7 @@ include_directories(${SDV_FRAMEWORK_DEV_INCLUDE}) # services are generated during the configuration phase of CMake. This is necessary, since CMakeFiles.txt files are generated and # they have to be available during the configuration phase of CMake to be taken into the build process. Requisite for the code # generation during the configuration time of CMake is the availability of the tools to do the generation. Hence the tools cannot be -# created during the build process, which is executed after the configuraiton process. +# created during the build process, which is executed after the configuration process. # Execute sdv_vss_util to create IDL files for devices and basic services. message("Create interface code for devices and basic services of open trunk example.") @@ -46,7 +46,7 @@ execute_process(COMMAND "${SDV_IDL_COMPILER}" "${PROJECT_SOURCE_DIR}/generated/v -# We need proxies and stubs for basic services to give access to the interfaces for complex services ans applications: --ps_lib_namedemo_proxystub +# We need proxies and stubs for basic services to give access to the interfaces for complex services and applications: --ps_lib_namedemo_proxystub message("Compiling vss_vehiclebodytrunk_bs_tx.idl") execute_process(COMMAND "${SDV_IDL_COMPILER}" "${PROJECT_SOURCE_DIR}/generated/vss_files/vss_vehiclebodytrunk_bs_tx.idl" "-O${PROJECT_SOURCE_DIR}/generated/vss_files/" "-I${SDV_FRAMEWORK_DEV_INCLUDE}" -Igenerated/vss_files/ --ps_lib_nametrunk_proxystub) message("Compiling vss_vehiclespeed_bs_rx.idl") @@ -78,7 +78,7 @@ add_subdirectory(generated/can_dl) # REMARK: Proxy/stub and vehicle device and basic service code was generated during the configuration phase of CMake. Following # below are the build steps to build the components that were generated. -message("Include: trunk proxy/stub for vehicle devices and basic services") +message("Include: trunk proxy/stub for basic services") include_directories(${CMAKE_CURRENT_LIST_DIR}/generated/vss_files) add_subdirectory(generated/vss_files/ps) @@ -116,7 +116,6 @@ add_executable(open_trunk_example open_trunk_app/trunk_application.h open_trunk_app/console.cpp open_trunk_app/console.h - open_trunk_app/signal_names.h ) if (CMAKE_CXX_COMPILER_ID STREQUAL "GNU") diff --git a/examples/open_trunk_example/open_trunk_app/console.cpp b/examples/open_trunk_example/open_trunk_app/console.cpp index 62ec835..46fff6e 100644 --- a/examples/open_trunk_example/open_trunk_app/console.cpp +++ b/examples/open_trunk_example/open_trunk_app/console.cpp @@ -34,8 +34,8 @@ const CConsole::SConsolePos g_sComment3{ 24, 1 }; const CConsole::SConsolePos g_sComment4{ 25, 1 }; const CConsole::SConsolePos g_sComment5{ 26, 1 }; const CConsole::SConsolePos g_sSeparator5{ 28, 1 }; -const CConsole::SConsolePos g_sComplexServcie1{ 30, 1 }; -const CConsole::SConsolePos g_sComplexServcie2{ 31, 1 }; +const CConsole::SConsolePos g_sComplexService1{ 30, 1 }; +const CConsole::SConsolePos g_sComplexService2{ 31, 1 }; const CConsole::SConsolePos g_sSeparator6{ 33, 1 }; const CConsole::SConsolePos g_sControlDescription{ 35, 1 }; const CConsole::SConsolePos g_sCursor{ 36, 1 }; @@ -111,12 +111,12 @@ void CConsole::PrintHeader(uint32_t uiInstance) PrintText(g_sSeparator4, "============================================================================"); PrintText(g_sComment1, "The complex service which checks the speed of the vehicle can be seen"); PrintText(g_sComment2, "as an ASIL A/B component and will block the call from QM."); - PrintText(g_sComment3, "The extern apllication can be seen as a QM function."); + PrintText(g_sComment3, "The extern application can be seen as a QM function."); PrintText(g_sComment4, "If this example would run in a mixed critical environment the connection"); PrintText(g_sComment5, "from QM to basic service interface would be forbidden."); PrintText(g_sSeparator5, "============================================================================"); - PrintText(g_sComplexServcie1, "Basic Service Interface not available."); - PrintText(g_sComplexServcie2, "Complex Service Interface not available."); + PrintText(g_sComplexService1, "Basic Service Interface not available."); + PrintText(g_sComplexService2, "Complex Service Interface not available."); PrintText(g_sSeparator6, "----------------------------------------------------------------------------"); PrintText(g_sControlDescription, "Press 'X' to quit; 'C' to clear screen, '1' or '2' to open trunk "); } @@ -130,7 +130,7 @@ bool CConsole::PrepareDataConsumers() PrintValue(g_sDataLinkSpeed, "Vehicle Speed RX", m_SpeedDL, "m/s"); // Registrate for the vehicle device & basic service of the speed. - auto vehicleDevice = sdv::core::GetObject("Vehicle.Speed_Device").GetInterface(); + auto vehicleDevice = sdv::core::GetObject("Vehicle.Speed_Device").GetInterface(); if (vehicleDevice) { PrintValue(g_sVehicleDeviceSpeed, "Vehicle Speed RX", m_SpeedVD, "m/s"); @@ -147,15 +147,15 @@ bool CConsole::PrepareDataConsumers() // Request the basic service for opening the trunk. m_pTrunkSvc = sdv::core::GetObject("Vehicle.Body.Trunk_Service").GetInterface(); if (m_pTrunkSvc) - PrintText(g_sComplexServcie1, "Basic Service available"); + PrintText(g_sComplexService1, "Basic Service available"); else - PrintText(g_sComplexServcie1, "Basic Service NOT available"); + PrintText(g_sComplexService1, "Basic Service NOT available"); m_pTrunkComplexService = sdv::core::GetObject("Open Trunk Service").GetInterface(); if (m_pTrunkComplexService) - PrintText(g_sComplexServcie2, "Complex Service available"); + PrintText(g_sComplexService2, "Complex Service available"); else - PrintText(g_sComplexServcie2, "Complex Service NOT available"); + PrintText(g_sComplexService2, "Complex Service NOT available"); return true; } @@ -165,7 +165,7 @@ void CConsole::ResetSignals() // Set the cursor position at the end SetCursorPos(g_sCursor); - auto vehicleDevice = sdv::core::GetObject("Vehicle.Speed_Device").GetInterface(); + auto vehicleDevice = sdv::core::GetObject("Vehicle.Speed_Device").GetInterface(); if (vehicleDevice) vehicleDevice->UnregisterSpeedEvent(dynamic_cast (this)); @@ -173,7 +173,7 @@ void CConsole::ResetSignals() if (basicService) basicService->UnregisterOnSignalChangeOfVehicleSpeed(dynamic_cast (this)); - // Unregister the data link signalss + // Unregister the data link signals if (m_SignalSpeed) m_SignalSpeed.Reset(); } @@ -291,25 +291,25 @@ void CConsole::RunUntilBreak() { case 'c': case 'C': - PrintText(g_sComplexServcie1, " "); - PrintText(g_sComplexServcie2, " "); + PrintText(g_sComplexService1, " "); + PrintText(g_sComplexService2, " "); break; case '1': if (m_pTrunkSvc) { if (m_pTrunkSvc->SetOpen(true)) - PrintText(g_sComplexServcie1, "Open trunk via basic service - will not be available in Mixed-Critical mode!"); + PrintText(g_sComplexService1, "Open trunk via basic service - will not be available in Mixed-Critical mode!"); else - PrintText(g_sComplexServcie1, "Open trunk via basic service failed."); + PrintText(g_sComplexService1, "Open trunk via basic service failed."); } break; case '2': if (m_pTrunkComplexService) { if (m_pTrunkComplexService->PopTrunk()) - PrintText(g_sComplexServcie2, "Safety open trunk via complex service."); + PrintText(g_sComplexService2, "Safety open trunk via complex service."); else - PrintText(g_sComplexServcie2, "Safety open trunk via complex service failed, car is moving"); + PrintText(g_sComplexService2, "Safety open trunk via complex service failed, car is moving"); } break; case 'x': diff --git a/examples/open_trunk_example/open_trunk_app/console.h b/examples/open_trunk_example/open_trunk_app/console.h index f9d4567..fb8cc04 100644 --- a/examples/open_trunk_example/open_trunk_app/console.h +++ b/examples/open_trunk_example/open_trunk_app/console.h @@ -18,7 +18,8 @@ #include #include #include -#include "signal_names.h" +#include "../generated/vss_files/signal_identifier.h" + #include #include "../generated/vss_files/vss_vehiclebodytrunk_bs_tx.h" #include "../generated/vss_files/vss_vehiclespeed_bs_rx.h" @@ -139,7 +140,7 @@ private: * @param[in] sPos The location to print the value at. * @param[in] rssName Reference to the value. * @param[in] tValue The value. - * @param[in] rssStatus Status, becuse we have signals of type bool + * @param[in] rssStatus Status, because we have signals of type bool */ template void PrintValue(SConsolePos sPos, const std::string& rssName, TValue tValue, const std::string& rssStatus); @@ -154,7 +155,6 @@ private: mutable std::mutex m_mtxPrintToConsole; ///< Mutex to print complete message bool m_bRunning = false; ///< When set, the application is running. - mutable std::mutex m_mPrintToConsole; ///< Mutex to print complete message sdv::core::CSignal m_SignalSpeed; ///< Speed float m_SpeedDL = 0.0; ///< Speed Data Link @@ -186,7 +186,7 @@ inline void CConsole::PrintValue(SConsolePos sPos, const std::string& rssName, T std::string(nValueNameLen - std::min(rssName.size(), static_cast(nValueNameLen - 1)) - 1, '.') << " " << std::fixed << std::setprecision(2) << tValue << " " << rssUnits << " "; - std::lock_guard lock(m_mPrintToConsole); + std::lock_guard lock(m_mtxPrintToConsole); SetCursorPos(sPos); std::cout << sstreamValueText.str(); } diff --git a/examples/open_trunk_example/open_trunk_app/signal_names.h b/examples/open_trunk_example/open_trunk_app/signal_names.h deleted file mode 100644 index d74c1de..0000000 --- a/examples/open_trunk_example/open_trunk_app/signal_names.h +++ /dev/null @@ -1,35 +0,0 @@ - /******************************************************************************** - * Copyright (c) 2025-2026 ZF Friedrichshafen AG - * - * This program and the accompanying materials are made available under the - * terms of the Apache License Version 2.0 which is available at - * https://www.apache.org/licenses/LICENSE-2.0 - * - * SPDX-License-Identifier: Apache-2.0 - ********************************************************************************/ - -/** - * namespace for the signal names - * in case /interfaces/signal_identifier.h - * exists, use the file, otherwise define the namespace - */ -#ifndef SIGNAL_NAMES_H -#define SIGNAL_NAMES_H - -#ifdef __has_include -#if __has_include("../interfaces/signal_identifier.h") -#include "../interfaces/signal_identifier.h" -#else - -namespace trunk -{ - // Data Dispatch Service signal names to dbc variable names C-type RX/TX vss name space - static std::string dsVehicleSpeed = "CAN_Input.Speed"; ///< bool RX Vehicle.Speed - static std::string dsTrunk = "CAN_Output.OpenTrunk"; ///< bool TX Vehicle.Body.Trunk -} // trunk - -#endif -#endif - -#endif // SIGNAL_NAMES_H - diff --git a/examples/open_trunk_example/open_trunk_app/trunk_application.cpp b/examples/open_trunk_example/open_trunk_app/trunk_application.cpp index bbfee95..8e1bee3 100644 --- a/examples/open_trunk_example/open_trunk_app/trunk_application.cpp +++ b/examples/open_trunk_example/open_trunk_app/trunk_application.cpp @@ -9,7 +9,7 @@ ********************************************************************************/ #include "trunk_application.h" -#include "signal_names.h" +#include "../generated/vss_files/signal_identifier.h" #ifdef _WIN32 #include // Needed for _kbhit diff --git a/examples/open_trunk_example/open_trunk_app/trunk_application.h b/examples/open_trunk_example/open_trunk_app/trunk_application.h index 6e77103..66fef65 100644 --- a/examples/open_trunk_example/open_trunk_app/trunk_application.h +++ b/examples/open_trunk_example/open_trunk_app/trunk_application.h @@ -11,7 +11,6 @@ #include #include #include -#include "vss_vehiclespeed_bs_rx.h" /** @@ -47,7 +46,7 @@ private: bool IsSDVFrameworkEnvironmentSet(); /** - * @brief Loac config file and register vehicle device and basic service. + * @brief Load config file and register vehicle device and basic service. * @param[in] inputMsg message string to be printed on console in case of success and failure * @param[in] configFileName config toml file name * @return Return true on success otherwise false diff --git a/examples/system_demo_example/CMakeLists.txt b/examples/system_demo_example/CMakeLists.txt index d4adf7e..d52f193 100644 --- a/examples/system_demo_example/CMakeLists.txt +++ b/examples/system_demo_example/CMakeLists.txt @@ -30,7 +30,7 @@ include_directories(${SDV_FRAMEWORK_DEV_INCLUDE}) # services are generated during the configuration phase of CMake. This is necessary, since CMakeFiles.txt files are generated and # they have to be available during the configuration phase of CMake to be taken into the build process. Requisite for the code # generation during the configuration time of CMake is the availability of the tools to do the generation. Hence the tools cannot be -# created during the build process, which is executed after the configuraiton process. +# created during the build process, which is executed after the configuration process. # Execute sdv_vss_util to create IDL files for devices and basic services. message("Create interface code for devices and basic services of demo example.") @@ -46,7 +46,7 @@ execute_process(COMMAND "${SDV_IDL_COMPILER}" "${PROJECT_SOURCE_DIR}/generated/v message("Compiling vss_vehiclespeed_vd_rx.idl") execute_process(COMMAND "${SDV_IDL_COMPILER}" "${PROJECT_SOURCE_DIR}/generated/vss_files/vss_vehiclespeed_vd_rx.idl" "-O${PROJECT_SOURCE_DIR}/generated/vss_files/" "-I${SDV_FRAMEWORK_DEV_INCLUDE}" -Igenerated/vss_files/ --no_ps) -# We need proxies and stubs for basic services to give access to the interfaces for complex services ans applications: --ps_lib_namedemo_proxystub +# We need proxies and stubs for basic services to give access to the interfaces for complex services and applications: --ps_lib_namedemo_proxystub message("Compiling vss_vehiclechassisrearaxlerowwheel_bs_tx.idl") execute_process(COMMAND "${SDV_IDL_COMPILER}" "${PROJECT_SOURCE_DIR}/generated/vss_files/vss_vehiclechassisrearaxlerowwheel_bs_tx.idl" "-O${PROJECT_SOURCE_DIR}/generated/vss_files/" "-I${SDV_FRAMEWORK_DEV_INCLUDE}" -Igenerated/vss_files/ --ps_lib_namedemo_proxystub) message("Compiling vss_vehiclechassissteeringwheelangle_bs_rx.idl") @@ -81,7 +81,7 @@ add_subdirectory(generated/can_dl) # REMARK: Proxy/stub and vehicle device and basic service code was generated during the configuration phase of CMake. Following # below are the build steps to build the components that were generated. -message("Include: demo proxy/stub for vehicle devices and basic services") +message("Include: demo proxy/stub for basic services") include_directories(${CMAKE_CURRENT_LIST_DIR}/generated/vss_files) add_subdirectory(generated/vss_files/ps) @@ -120,7 +120,6 @@ set_target_properties(demo_complex_service PROPERTIES SUFFIX ".sdv") # Define the executable add_executable(system_extern_example example_app/system_extern_example.cpp - example_app/signal_names.h ) if (CMAKE_CXX_COMPILER_ID STREQUAL "GNU") @@ -145,7 +144,6 @@ add_executable(system_demo_example example_app/control.cpp example_app/console.h example_app/console.cpp - example_app/signal_names.h ) if (CMAKE_CXX_COMPILER_ID STREQUAL "GNU") diff --git a/examples/system_demo_example/example_app/console.cpp b/examples/system_demo_example/example_app/console.cpp index 3a163e5..f9b588e 100644 --- a/examples/system_demo_example/example_app/console.cpp +++ b/examples/system_demo_example/example_app/console.cpp @@ -253,7 +253,7 @@ void CConsole::RunUntilBreak() // Set the cursor position at the end SetCursorPos(g_sCursor); - // Unregister the data link signalss + // Unregister the data link signals if (m_signalSteeringWheel) m_signalSteeringWheel.Reset(); if (m_signalSpeed) m_signalSpeed.Reset(); if (m_signalRearAxleAngle) m_signalRearAxleAngle.Reset(); diff --git a/examples/system_demo_example/example_app/console.h b/examples/system_demo_example/example_app/console.h index 28ccf45..d481000 100644 --- a/examples/system_demo_example/example_app/console.h +++ b/examples/system_demo_example/example_app/console.h @@ -18,7 +18,7 @@ #include #include #include -#include "signal_names.h" +#include "../generated/vss_files/signal_identifier.h" #ifdef __unix__ #include // Needed for tcgetattr and fcntl diff --git a/examples/system_demo_example/example_app/control.h b/examples/system_demo_example/example_app/control.h index b97d435..c6d8e7e 100644 --- a/examples/system_demo_example/example_app/control.h +++ b/examples/system_demo_example/example_app/control.h @@ -29,7 +29,7 @@ #endif #endif -#include "signal_names.h" +#include "../generated/vss_files/signal_identifier.h" /** * @brief Utility handler for example demos diff --git a/examples/system_demo_example/example_app/signal_names.h b/examples/system_demo_example/example_app/signal_names.h deleted file mode 100644 index 3000ebe..0000000 --- a/examples/system_demo_example/example_app/signal_names.h +++ /dev/null @@ -1,35 +0,0 @@ - /******************************************************************************** - * Copyright (c) 2025-2026 ZF Friedrichshafen AG - * - * This program and the accompanying materials are made available under the - * terms of the Apache License Version 2.0 which is available at - * https://www.apache.org/licenses/LICENSE-2.0 - * - * SPDX-License-Identifier: Apache-2.0 - ********************************************************************************/ - -/** - * namespace for the signal names - * in case /generated/vss_files/signal_identifier.h - * exists, use the file, otherwise define the namespace - */ - -#ifndef SIGNAL_NAMES_H -#define SIGNAL_NAMES_H - -#ifdef __has_include -#if __has_include("../generated/vss_files/signal_identifier.h") -#include "../generated/vss_files/signal_identifier.h" -#else -namespace demo -{ - static std::string dsWheelAngle = "CAN_Input.SteeringWheel"; ///< float RX Vehicle.Chassis.SteeringWheel.Angle - static std::string dsVehicleSpeed = "CAN_Input.Speed"; ///< float RX Vehicle.Speed - static std::string dsAxleAngle = "CAN_Output.RearAngle"; ///< float TX Vehicle.Chassis.RearAxle.Row.Wheel - static std::string dsLiveCounter = "CAN_Output.IsActiveCounter"; ///< uint8_t TX Vehicle.Software.Application.IsActiveCounter -} // demo -#endif -#endif - -#endif // ! defined SIGNAL_NAMES_H - diff --git a/examples/vehicle_abstraction_example/CMakeLists.txt b/examples/vehicle_abstraction_example/CMakeLists.txt new file mode 100644 index 0000000..51096de --- /dev/null +++ b/examples/vehicle_abstraction_example/CMakeLists.txt @@ -0,0 +1,123 @@ +#******************************************************************************* +# Copyright (c) 2025-2026 ZF Friedrichshafen AG +# +# This program and the accompanying materials are made available under the +# terms of the Apache License Version 2.0 which is available at +# https://www.apache.org/licenses/LICENSE-2.0 +# +# SPDX-License-Identifier: Apache-2.0 +#******************************************************************************* + +project(SystemDemoExample) + +# Use new policy for project version settings and default warning level +cmake_policy(SET CMP0048 NEW) # requires CMake 3.14 +cmake_policy(SET CMP0092 NEW) # requires CMake 3.15 + +set(CMAKE_CXX_STANDARD 17) + +# Libary symbols are hidden by default +set(CMAKE_CXX_VISIBILITY_PRESET hidden) + +# Include directory to the core framework +include_directories(${SDV_FRAMEWORK_DEV_INCLUDE}) + +###################################################################################################################################################################### +# preparation +###################################################################################################################################################################### + +# REMARK: The code generation for the proxy/stub, interface definitions and serialization, the vehicle devices and the basic +# services are generated during the configuration phase of CMake. This is necessary, since CMakeFiles.txt files are generated and +# they have to be available during the configuration phase of CMake to be taken into the build process. Requisite for the code +# generation during the configuration time of CMake is the availability of the tools to do the generation. Hence the tools cannot be +# created during the build process, which is executed after the configuration process. + +# Execute sdv_vss_util to create IDL files for devices and basic services. +# REMARK: We need 2 cvs definition files because we want to create 2 identical components. +# Both need to have the same input signal as well as the same output interface. Output must be vehicle speed in km/h. +# One of the component has to convert the value from m/s to km/h, the other one can pass the value directly. +message("Create interface code for devices and basic services of vehicle abstraction example.") +execute_process(COMMAND "${SDV_VSS_UTIL}" "${PROJECT_SOURCE_DIR}/vehicle_abstraction_example.csv" "-O${PROJECT_SOURCE_DIR}/generated/" --prefixabstraction --version1.0.0.1 --enable_components) +execute_process(COMMAND "${SDV_VSS_UTIL}" "${PROJECT_SOURCE_DIR}/vehicle_abstraction_example_ms.csv" "-O${PROJECT_SOURCE_DIR}/generated2/" --prefixabstraction --version1.0.0.1 --enable_components) + +# Execute the IDL compiler for the VSS interfaces to digest interface code. Compile with --no_ps as we do not need proxies and stubs as we do not like to expose these interfaces for complex services or applications +message("Compiling vss_vehiclespeed_vd_rx.idl") +execute_process(COMMAND "${SDV_IDL_COMPILER}" "${PROJECT_SOURCE_DIR}/generated/vss_files/vss_vehiclespeed_vd_rx.idl" "-O${PROJECT_SOURCE_DIR}/generated/vss_files/" "-I${SDV_FRAMEWORK_DEV_INCLUDE}" -Igenerated/vss_files/ --no_ps) +message("Compiling vss_vehiclespeed_vd_rx.idl") +execute_process(COMMAND "${SDV_IDL_COMPILER}" "${PROJECT_SOURCE_DIR}/generated2/vss_files/vss_vehiclespeed_vd_rx.idl" "-O${PROJECT_SOURCE_DIR}/generated2/vss_files/" "-I${SDV_FRAMEWORK_DEV_INCLUDE}" -Igenerated2/vss_files/ --no_ps) + + +# We need proxies and stubs for basic services to give access to the interfaces for complex services and applications: --ps_lib_namedemo_proxystub +message("Compiling vss_vehiclespeed_bs_rx.idl") +execute_process(COMMAND "${SDV_IDL_COMPILER}" "${PROJECT_SOURCE_DIR}/generated/vss_files/vss_vehiclespeed_bs_rx.idl" "-O${PROJECT_SOURCE_DIR}/generated/vss_files/" "-I${SDV_FRAMEWORK_DEV_INCLUDE}" -Igenerated/vss_files/ --ps_lib_nameabstraction_proxystub) + + +# Execute sdv_dbc_util to create data link code & FMU code. +message("Create data link for vehicle abstraction example") +execute_process(COMMAND ${SDV_DBC_UTIL} "${PROJECT_SOURCE_DIR}/vehicle_abstraction_example.dbc" "-O${PROJECT_SOURCE_DIR}/generated/" --nodesabstraction --version1.0.0.1 --dl_lib_namecan_dl_abstraction) + +###################################################################################################################################################################### +# data link component +###################################################################################################################################################################### + +# REMARK: CAN data link code was generated during the configuration phase of CMake. Following below is the build step to build the +# component that was generated. + +message("Include: example component can_dl_abstraction") +add_subdirectory(generated/can_dl) + +####################################################################################################################################################################### +## vehicle devices and basic services +####################################################################################################################################################################### + +# REMARK: Proxy/stub and vehicle device and basic service code was generated during the configuration phase of CMake. Following +# below are the build steps to build the components that were generated. +message("Include: vehicle abstraction proxy/stub for basic services") +include_directories(${CMAKE_CURRENT_LIST_DIR}/generated/vss_files) +add_subdirectory(generated/vss_files/ps) + +# REMARK: We create 2 'vd_*' components with identical input and output interface. Therefore we needed 2 csv definition files for their creation. +# The difference is: one passes the input value to output, the other one needs to convert the value from m/s to km/h to match the unit. +add_subdirectory(generated/vss_files/vd_vehiclespeedkmh) +add_subdirectory(generated2/vss_files/vd_vehiclespeedms) + +# REMARK: Because the output interface of the 'vd_*' components is identical (km/h), we need only one bs* component which expects to get a value in km/h. +add_subdirectory(generated/vss_files/bs_vehiclespeed) + +###################################################################################################################################################################### +# vehicle abstraction application +###################################################################################################################################################################### + +# Define the executable +add_executable(vehicle_abstraction_example + vehicle_abstraction_app/vehicle_abstraction_example.cpp + vehicle_abstraction_app/vehicle_abstraction_application.cpp + vehicle_abstraction_app/vehicle_abstraction_application.h + vehicle_abstraction_app/console.cpp + vehicle_abstraction_app/console.h + ) + +if (CMAKE_CXX_COMPILER_ID STREQUAL "GNU") + if (WIN32) + target_link_libraries(vehicle_abstraction_example Ws2_32 Winmm Rpcrt4.lib) + else() + target_link_libraries(vehicle_abstraction_example ${CMAKE_DL_LIBS} rt ${CMAKE_THREAD_LIBS_INIT}) + endif() +else() + target_link_libraries(vehicle_abstraction_example Rpcrt4.lib) +endif() + +# Copy the config files +file (COPY ${PROJECT_SOURCE_DIR}/config/can_com_simulation_vehicle_abstraction_ms.toml DESTINATION ${CMAKE_BINARY_DIR}/bin/config) +file (COPY ${PROJECT_SOURCE_DIR}/config/can_com_simulation_vehicle_abstraction_kmh.toml DESTINATION ${CMAKE_BINARY_DIR}/bin/config) +file (COPY ${PROJECT_SOURCE_DIR}/config/data_dispatch_vehicle_abstraction.toml DESTINATION ${CMAKE_BINARY_DIR}/bin/config) +file (COPY ${PROJECT_SOURCE_DIR}/config/data_link_vehicle_abstraction.toml DESTINATION ${CMAKE_BINARY_DIR}/bin/config) +file (COPY ${PROJECT_SOURCE_DIR}/config/task_timer_vehicle.toml DESTINATION ${CMAKE_BINARY_DIR}/bin/config) +file (COPY ${PROJECT_SOURCE_DIR}/config/vehicle_abstraction_device_kmh.toml DESTINATION ${CMAKE_BINARY_DIR}/bin/config) +file (COPY ${PROJECT_SOURCE_DIR}/config/vehicle_abstraction_device_ms.toml DESTINATION ${CMAKE_BINARY_DIR}/bin/config) +file (COPY ${PROJECT_SOURCE_DIR}/config/vehicle_abstraction_basic_service.toml DESTINATION ${CMAKE_BINARY_DIR}/bin/config) + +# Copy the ASC files used by can_com_sim.sdv which includes the CAN messages. One file includes vehicle speed in m/s, the other file in km/h. +file (COPY ${PROJECT_SOURCE_DIR}/vehicle_abstraction_example_receiver_ms.asc DESTINATION ${CMAKE_BINARY_DIR}/bin) +file (COPY ${PROJECT_SOURCE_DIR}/vehicle_abstraction_example_receiver_kmh.asc DESTINATION ${CMAKE_BINARY_DIR}/bin) + diff --git a/examples/vehicle_abstraction_example/config/can_com_simulation_vehicle_abstraction_kmh.toml b/examples/vehicle_abstraction_example/config/can_com_simulation_vehicle_abstraction_kmh.toml new file mode 100644 index 0000000..1d3445b --- /dev/null +++ b/examples/vehicle_abstraction_example/config/can_com_simulation_vehicle_abstraction_kmh.toml @@ -0,0 +1,9 @@ +[Configuration] +Version = 100 + +[[Component]] +Path = "can_com_sim.sdv" +Class = "CAN_Com_Sim" +[Component.Parameters] +Source="vehicle_abstraction_example_receiver_kmh.asc" +Target="vehicle_abstraction_example_writer_kmh.asc" diff --git a/examples/vehicle_abstraction_example/config/can_com_simulation_vehicle_abstraction_ms.toml b/examples/vehicle_abstraction_example/config/can_com_simulation_vehicle_abstraction_ms.toml new file mode 100644 index 0000000..88b9b14 --- /dev/null +++ b/examples/vehicle_abstraction_example/config/can_com_simulation_vehicle_abstraction_ms.toml @@ -0,0 +1,9 @@ +[Configuration] +Version = 100 + +[[Component]] +Path = "can_com_sim.sdv" +Class = "CAN_Com_Sim" +[Component.Parameters] +Source="vehicle_abstraction_example_receiver_ms.asc" +Target="vehicle_abstraction_example_writer_ms.asc" diff --git a/examples/vehicle_abstraction_example/config/data_dispatch_vehicle_abstraction.toml b/examples/vehicle_abstraction_example/config/data_dispatch_vehicle_abstraction.toml new file mode 100644 index 0000000..ae3efe7 --- /dev/null +++ b/examples/vehicle_abstraction_example/config/data_dispatch_vehicle_abstraction.toml @@ -0,0 +1,8 @@ +[Configuration] +Version = 100 + +[[Component]] +Path = "data_dispatch_service.sdv" +Class = "DataDispatchService" + + diff --git a/examples/vehicle_abstraction_example/config/data_link_vehicle_abstraction.toml b/examples/vehicle_abstraction_example/config/data_link_vehicle_abstraction.toml new file mode 100644 index 0000000..4743bd6 --- /dev/null +++ b/examples/vehicle_abstraction_example/config/data_link_vehicle_abstraction.toml @@ -0,0 +1,6 @@ +[Configuration] +Version = 100 + +[[Component]] +Path = "can_dl_abstraction.sdv" +Class = "CAN_data_link" diff --git a/examples/vehicle_abstraction_example/config/task_timer_vehicle.toml b/examples/vehicle_abstraction_example/config/task_timer_vehicle.toml new file mode 100644 index 0000000..5d960e8 --- /dev/null +++ b/examples/vehicle_abstraction_example/config/task_timer_vehicle.toml @@ -0,0 +1,6 @@ +[Configuration] +Version = 100 + +[[Component]] +Path = "task_timer.sdv" +Class = "TaskTimerService" diff --git a/examples/vehicle_abstraction_example/config/vehicle_abstraction_basic_service.toml b/examples/vehicle_abstraction_example/config/vehicle_abstraction_basic_service.toml new file mode 100644 index 0000000..def2d90 --- /dev/null +++ b/examples/vehicle_abstraction_example/config/vehicle_abstraction_basic_service.toml @@ -0,0 +1,12 @@ +[Configuration] +Version = 100 + + + + +[[Component]] +Path = "abstraction_bs_vehiclespeed_rx.sdv" +Class = "Vehicle.Speed_Service" + + + diff --git a/examples/vehicle_abstraction_example/config/vehicle_abstraction_device_kmh.toml b/examples/vehicle_abstraction_example/config/vehicle_abstraction_device_kmh.toml new file mode 100644 index 0000000..709c93a --- /dev/null +++ b/examples/vehicle_abstraction_example/config/vehicle_abstraction_device_kmh.toml @@ -0,0 +1,6 @@ +[Configuration] +Version = 100 + +[[Component]] +Path = "abstraction_vd_vehiclespeedkmh_rx.sdv" +Class = "Vehicle.Speed_Device" diff --git a/examples/vehicle_abstraction_example/config/vehicle_abstraction_device_ms.toml b/examples/vehicle_abstraction_example/config/vehicle_abstraction_device_ms.toml new file mode 100644 index 0000000..96161a5 --- /dev/null +++ b/examples/vehicle_abstraction_example/config/vehicle_abstraction_device_ms.toml @@ -0,0 +1,8 @@ +[Configuration] +Version = 100 + + + +[[Component]] +Path = "abstraction_vd_vehiclespeedms_rx.sdv" +Class = "Vehicle.Speed_Device" diff --git a/examples/vehicle_abstraction_example/vehicle_abstraction_app/console.cpp b/examples/vehicle_abstraction_example/vehicle_abstraction_app/console.cpp new file mode 100644 index 0000000..5454898 --- /dev/null +++ b/examples/vehicle_abstraction_example/vehicle_abstraction_app/console.cpp @@ -0,0 +1,316 @@ + /******************************************************************************** + * Copyright (c) 2025-2026 ZF Friedrichshafen AG + * + * This program and the accompanying materials are made available under the + * terms of the Apache License Version 2.0 which is available at + * https://www.apache.org/licenses/LICENSE-2.0 + * + * SPDX-License-Identifier: Apache-2.0 + ********************************************************************************/ + +#include "console.h" + +#ifdef _WIN32 +#include // Needed for _kbhit +#else +#include +#endif + +const CConsole::SConsolePos g_sTitle{ 1, 1 }; +const CConsole::SConsolePos g_sSubTitle1{ 2, 1 }; +const CConsole::SConsolePos g_sSubTitle2{3, 1}; +const CConsole::SConsolePos g_sSeparator1{ 5, 1 }; +const CConsole::SConsolePos g_sDataUnit{ 7, 1 }; +const CConsole::SConsolePos g_sDataLinkSpeed{ 8, 1 }; +const CConsole::SConsolePos g_sSeparator2{ 10, 1 }; +const CConsole::SConsolePos g_sDeviceServiceSpeed{ 12, 1 }; +const CConsole::SConsolePos g_sSeparator3{ 14, 1 }; +const CConsole::SConsolePos g_sBasicServiceSpeed{ 16, 1 }; +const CConsole::SConsolePos g_sSeparator4{ 18, 1 }; +const CConsole::SConsolePos g_sComment1{ 20, 1 }; +const CConsole::SConsolePos g_sComment2{ 21, 1 }; +const CConsole::SConsolePos g_sComment3{ 23, 1 }; +const CConsole::SConsolePos g_sComment4{ 24, 1 }; +const CConsole::SConsolePos g_sComment5{ 25, 1 }; +const CConsole::SConsolePos g_sComment6{ 27, 1 }; +const CConsole::SConsolePos g_sComment7{ 28, 1 }; +const CConsole::SConsolePos g_sSeparator5{ 30, 1 }; + +const CConsole::SConsolePos g_sControlDescription{ 32, 1 }; +const CConsole::SConsolePos g_sCursor{ 33, 1 }; + +CConsole::CConsole() +{ +#ifdef _WIN32 + // Enable ANSI escape codes + HANDLE hStdOut = GetStdHandle(STD_OUTPUT_HANDLE); + if (hStdOut != INVALID_HANDLE_VALUE && GetConsoleMode(hStdOut, &m_dwConsoleOutMode)) + SetConsoleMode(hStdOut, m_dwConsoleOutMode | ENABLE_VIRTUAL_TERMINAL_PROCESSING); + HANDLE hStdIn = GetStdHandle(STD_INPUT_HANDLE); + if (hStdIn != INVALID_HANDLE_VALUE && GetConsoleMode(hStdIn, &m_dwConsoleInMode)) + SetConsoleMode(hStdIn, m_dwConsoleInMode & ~(ENABLE_ECHO_INPUT | ENABLE_LINE_INPUT)); +#elif defined __unix__ + // Disable echo + tcgetattr(STDIN_FILENO, &m_sTermAttr); + struct termios sTermAttrTemp = m_sTermAttr; + sTermAttrTemp.c_lflag &= ~(ICANON | ECHO); + tcsetattr(STDIN_FILENO, TCSANOW, &sTermAttrTemp); + m_iFileStatus = fcntl(STDIN_FILENO, F_GETFL, 0); + fcntl(STDIN_FILENO, F_SETFL, m_iFileStatus | O_NONBLOCK); +#else +#error The OS is not supported! +#endif +} + +CConsole::~CConsole() +{ + SetCursorPos(g_sCursor); + +#ifdef _WIN32 + // Return to the stored console mode + HANDLE hStdOut = GetStdHandle(STD_OUTPUT_HANDLE); + if (hStdOut != INVALID_HANDLE_VALUE) + SetConsoleMode(hStdOut, m_dwConsoleOutMode); + HANDLE hStdIn = GetStdHandle(STD_INPUT_HANDLE); + if (hStdIn != INVALID_HANDLE_VALUE) + SetConsoleMode(hStdIn, m_dwConsoleInMode); +#elif defined __unix__ + // Return the previous file status flags. + fcntl(STDIN_FILENO, F_SETFL, m_iFileStatus); + + // Return to previous terminal state + tcsetattr(STDIN_FILENO, TCSANOW, &m_sTermAttr); +#endif +} + +void CConsole::PrintHeader() +{ + // Clear the screen... + std::cout << "\x1b[2J"; + + // Print the titles + PrintText(g_sTitle, "Vehicle Abstraction: "); + PrintText(g_sSubTitle1, "This example demonstrates that the vehicle function implementation is independent of the vehicle itself."); + PrintText(g_sSubTitle2, "CAN bus vehicle 1 => km/h, CAN bus vehicle 2 => m/s."); + PrintText(g_sSeparator1, "===================================================================================================================="); + PrintText(g_sDataUnit, m_DataUnit); + PrintText(g_sDataLinkSpeed, "Data Link not available."); + PrintText(g_sSeparator2, "--------------------------------------------------------------------------------------------------------------------"); + PrintText(g_sDeviceServiceSpeed, "Platform Abstraction Interface not available."); + PrintText(g_sSeparator3, "--------------------------------------------------------------------------------------------------------------------"); + PrintText(g_sBasicServiceSpeed, "Basic Service Interface not available."); + PrintText(g_sSeparator4, "===================================================================================================================="); + PrintText(g_sComment1, "The dispatch service displays the value that is written by the CAN message read from the ASC file."); + PrintText(g_sComment2, "The speed should be increased by steps of 10 km/h."); + PrintText(g_sComment3, "The 'Platform Abstraction' component is responsible for the vehicle abstraction and converts the value if necessary."); + PrintText(g_sComment4, "The example contains 2 components for 'Platform Abstraction' with identical output interfaces."); + PrintText(g_sComment5, "Depending on the input (m/s or km/h) the correct 'Platform Abstraction' component must be loaded."); + PrintText(g_sComment6, "Therefore, only one speed sensor component is required, which always receives the speed in km/h."); + PrintText(g_sComment7, "The vehicle function does not require any logic that depends on the specific vehicle."); + PrintText(g_sSeparator5, "===================================================================================================================="); + PrintText(g_sControlDescription, "Press 'X' to quit;"); +} + +bool CConsole::PrepareDataConsumers() +{ + sdv::core::CDispatchService dispatch; + m_SignalSpeed = dispatch.Subscribe(abstraction::dsVehicleSpeed, [&](sdv::any_t value) { CallbackSpeed(value); }); + if (m_SignalSpeed) + { + auto unit = m_Unit; + unit.append(" [ data dispatch service - input from CAN bus ] "); + PrintValue(g_sDataLinkSpeed, "Vehicle Speed RX", m_SpeedDataLink, unit); + } + + auto deviceServiceSpeed = sdv::core::GetObject("Vehicle.Speed_Device").GetInterface(); + if (deviceServiceSpeed) + { + PrintValue(g_sDeviceServiceSpeed, "Vehicle Speed RX", m_PlatformSpeed, "km/h [ Output of Platform Abstraction, not accessible by application ] "); + deviceServiceSpeed->RegisterSpeedEvent(dynamic_cast (this)); + } + + auto basicServiceSpeed = sdv::core::GetObject("Vehicle.Speed_Service").GetInterface(); + if (basicServiceSpeed) + { + PrintValue(g_sBasicServiceSpeed, "Vehicle Speed RX", m_BasicSpeed, "km/h [ Output of Speed Sensor Service, accessible by application ] "); + basicServiceSpeed->RegisterOnSignalChangeOfVehicleSpeed(dynamic_cast (this)); + } + + return true; +} + +void CConsole::WriteSpeed( float value) +{ + if (m_PlatformSpeed != value) + { + m_PlatformSpeed = value; + PrintValue(g_sDeviceServiceSpeed, "Vehicle Speed RX", m_PlatformSpeed, "km/h [ Output of Platform Abstraction, not accessible by application ] "); + } +} + +void CConsole::SetSpeed( float value) +{ + if (m_BasicSpeed != value) + { + m_BasicSpeed= value; + PrintValue(g_sBasicServiceSpeed, "Vehicle Speed RX", m_BasicSpeed, "km/h [ Output of Speed Sensor Service, accessible by application ] "); + } +} + +void CConsole::ResetSignals() +{ + // Set the cursor position at the end + SetCursorPos(g_sCursor); + + auto deviceServiceSpeed = sdv::core::GetObject("Vehicle.Speed_Devicee").GetInterface(); + if (deviceServiceSpeed) + { + deviceServiceSpeed->UnregisterSpeedEvent(dynamic_cast (this)); + } + + auto basicServiceSpeed = sdv::core::GetObject("Vehicle.Speed_Service").GetInterface(); + if (basicServiceSpeed) + { + basicServiceSpeed->UnregisterOnSignalChangeOfVehicleSpeed(dynamic_cast (this)); + } + + if (m_SignalSpeed) + m_SignalSpeed.Reset(); +} + +void CConsole::CallbackSpeed(sdv::any_t value) +{ + if (m_SpeedDataLink != value.get()) + { + m_SpeedDataLink = value.get(); + + auto unit = m_Unit; + unit.append(" [ data dispatch service - input from CAN bus ] "); + PrintValue(g_sDataLinkSpeed, "Vehicle Speed RX", m_SpeedDataLink, unit); + } +} + + +CConsole::SConsolePos CConsole::GetCursorPos() const +{ + SConsolePos sPos{}; + std::cout << "\033[6n"; + + char buff[128]; + int indx = 0; + for(;;) { + int cc = std::cin.get(); + buff[indx] = (char)cc; + indx++; + if(cc == 'R') { + buff[indx + 1] = '\0'; + break; + } + } + int iRow = 0, iCol = 0; + sscanf(buff, "\x1b[%d;%dR", &iRow, &iCol); + sPos.uiRow = static_cast(iRow); + sPos.uiCol = static_cast(iCol); + fseek(stdin, 0, SEEK_END); + + return sPos; +} + +void CConsole::SetCursorPos(SConsolePos sPos) +{ + std::cout << "\033[" << sPos.uiRow << ";" << sPos.uiCol << "H"; +} + +void CConsole::PrintText(SConsolePos sPos, const std::string& rssText) +{ + auto text = rssText; + while (text.length() < 76) + text.append(" "); + + std::lock_guard lock(m_mtxPrintToConsole); + SetCursorPos(sPos); + std::cout << text; +} + + + +bool CConsole::KeyHit() +{ +#ifdef _WIN32 + return _kbhit(); +#elif __unix__ + int ch = getchar(); + if (ch != EOF) { + ungetc(ch, stdin); + return true; + } + + return false; +#endif +} + +char CConsole::GetChar() +{ +#ifdef _WIN32 + return static_cast(_getch()); +#else + return getchar(); +#endif +} + +void CConsole::RunUntilBreak() +{ + bool bRunning = true; + + while (bRunning) + { + // Check for a key + if (!KeyHit()) + { + std::this_thread::sleep_for(std::chrono::milliseconds(100)); + continue; + } + + // Get a keyboard value (if there is any). + char c = GetChar(); + switch (c) + { + case 'x': + case 'X': + bRunning = false; + break; + default: + break; + } + } +} + +bool CConsole::SelectInputDataUnits() +{ + // Clear the screen and goto top... + std::cout << "\x1b[2J\033[0;0H"; + std::cout << "Click '1' to select km/h or '2' to select m/s"; + + bool bIsKmh = false; + char c = '0'; + while(c != '1' && c != '2') + { + c = GetChar(); + switch (c) + { + case '1': + m_DataUnit = "[ --- Input data in km/h --- ]"; + m_Unit = "km/h"; + bIsKmh = true; + break; + case '2': + m_DataUnit = "[ --- Input data in m/s --- ]"; + m_Unit = "m/s"; + bIsKmh = false; + break; + default: + break; + } + } + return bIsKmh; +} \ No newline at end of file diff --git a/examples/vehicle_abstraction_example/vehicle_abstraction_app/console.h b/examples/vehicle_abstraction_example/vehicle_abstraction_app/console.h new file mode 100644 index 0000000..431ad29 --- /dev/null +++ b/examples/vehicle_abstraction_example/vehicle_abstraction_app/console.h @@ -0,0 +1,200 @@ + /******************************************************************************** + * Copyright (c) 2025-2026 ZF Friedrichshafen AG + * + * This program and the accompanying materials are made available under the + * terms of the Apache License Version 2.0 which is available at + * https://www.apache.org/licenses/LICENSE-2.0 + * + * SPDX-License-Identifier: Apache-2.0 + ********************************************************************************/ + +#ifndef CONSOLE_OUTPUT_H +#define CONSOLE_OUTPUT_H + +#include +#include +#include +#include +#include +#include +#include +#include "../generated/vss_files/signal_identifier.h" +#include + +#include "../generated/vss_files/vss_vehiclespeed_vd_rx.h" +#include "../generated/vss_files/vss_vehiclespeed_bs_rx.h" + +#ifdef __unix__ +#include // Needed for tcgetattr and fcntl +#include +#endif + +/** + * @brief Console operation class. + * @details This class retrieves RX data from the data dispatch service, platform abstraction & basic service of speed on event change. + * Furthermore, it shows TX value by polling the RX signals. + */ + class CConsole : public vss::Vehicle::SpeedDevice::IVSS_WriteSpeed_Event + , public vss::Vehicle::SpeedService::IVSS_SetSpeed_Event +{ +public: + /** + * @brief Screen position structure + */ + struct SConsolePos + { + uint32_t uiRow; ///< Row position (starts at 1) + uint32_t uiCol; ///< Column position (starts at 1) + }; + + /** + * @brief Constructor + */ + CConsole(); + + /** + * @brief Destructor + */ + ~CConsole(); + + /** + * @brief Print the header. + */ + void PrintHeader(); + + /** + * @brief Prepare the data consumers.. + * @return Returns whether the preparation of the data consumers was successful or not. + */ + bool PrepareDataConsumers(); + + /** + * @brief Run loop as long as user input does not exit + */ + void RunUntilBreak(); + + /** + * @brief Let the user decide which unit the input data should have + * @return Return true if inout data should be km/h, otherwise false + */ + bool SelectInputDataUnits(); + + /** + * @brief Write vehicleSpeed signal + * @param[in] value vehicleSpeed + */ + void WriteSpeed( float value) override; + + /** + * @brief Set vehicleSpeed signal + * @param[in] value vehicleSpeed + */ + void SetSpeed( float value) override; + + /** + * @brief For gracefully shutdown all signals need to be reset. + */ + void ResetSignals(); + +private: + + /** + * @brief Callback function for speed. + * @param[in] value The value of the signal to update. + */ + void CallbackSpeed(sdv::any_t value); + + /** + * @brief Key hit check. Windows uses the _kbhit function; POSIX emulates this. + * @return Returns whether a key has been pressed. + */ + bool KeyHit(); + + /** + * @brief Get the character from the keyboard buffer if pressed. + * @return Returns the character from the keyboard buffer. + */ + char GetChar(); + + /** + * @brief Get the cursor position of the console. + * @return The cursor position. + */ + SConsolePos GetCursorPos() const; + + /** + * @brief Set the current cursor position for the console. + * @param[in] sPos Console position to place the current cursor at. + */ + void SetCursorPos(SConsolePos sPos); + + /** + * @brief Print text at a specific location. + * @param[in] sPos The location to print text at. + * @param[in] rssText Reference to the text to print. + */ + void PrintText(SConsolePos sPos, const std::string& rssText); + + /** + * @brief Print a value string at a specific location. + * @tparam TValue Type of value. + * @param[in] sPos The location to print the value at. + * @param[in] rssName Reference to the value. + * @param[in] tValue The value. + * @param[in] rssStatus Status, because we have signals of type bool + */ + template + void PrintValue(SConsolePos sPos, const std::string& rssName, TValue tValue, const std::string& rssStatus); + + /** + * @brief Align string between name and value. + * @param[in] message Reference to the message to align. + * @param[in] desiredLength The desired length or 0 when no length is specified. + * @return The aligned string. + */ + std::string AlignString(const std::string& message, uint32_t desiredLength = 0); + + mutable std::mutex m_mtxPrintToConsole; ///< Mutex to print complete message + bool m_bRunning = false; ///< When set, the application is running. + std::string m_DataUnit = "[ --- Input data in m/s --- ]"; + std::string m_Unit = "m/s"; + + sdv::core::CSignal m_SignalSpeed; ///< Signal to subscribe to the speed signal in dispatch service + + float m_SpeedDataLink = 0.0; ///< Data Link value, either km/h or m/s + float m_PlatformSpeed = 0.0; ///< Generalized Speed + float m_BasicSpeed = 0.0; ///< Generalized Speed + +#ifdef _WIN32 + DWORD m_dwConsoleOutMode = 0u; ///< The console mode before switching on ANSI support. + DWORD m_dwConsoleInMode = 0u; ///< The console mode before switching on ANSI support. +#elif defined __unix__ + struct termios m_sTermAttr{}; ///< The terminal attributes before disabling echo. + int m_iFileStatus = 0; ///< The file status flags for STDIN. +#else +#error The OS is not supported! +#endif + +}; + +template +inline void CConsole::PrintValue(SConsolePos sPos, const std::string& rssName, TValue tValue, const std::string& rssUnits) +{ + const size_t nValueNameLen = 26; + std::stringstream sstreamValueText; + sstreamValueText << rssName << + std::string(nValueNameLen - std::min(rssName.size(), static_cast(nValueNameLen - 1)) - 1, '.') << + " " << std::fixed << std::setprecision(2) << tValue << " " << rssUnits << " "; + + std::lock_guard lock(m_mtxPrintToConsole); + SetCursorPos(sPos); + std::cout << sstreamValueText.str(); +} + +template <> +inline void CConsole::PrintValue(SConsolePos sPos, const std::string& rssName, bool bValue, const std::string& rssStatus) +{; + PrintValue(sPos, rssName, bValue ? "" : "", rssStatus); +} + +#endif // !define CONSOLE_OUTPUT_H diff --git a/examples/vehicle_abstraction_example/vehicle_abstraction_app/vehicle_abstraction_application.cpp b/examples/vehicle_abstraction_example/vehicle_abstraction_app/vehicle_abstraction_application.cpp new file mode 100644 index 0000000..b96546e --- /dev/null +++ b/examples/vehicle_abstraction_example/vehicle_abstraction_app/vehicle_abstraction_application.cpp @@ -0,0 +1,103 @@ + /******************************************************************************** + * Copyright (c) 2025-2026 ZF Friedrichshafen AG + * + * This program and the accompanying materials are made available under the + * terms of the Apache License Version 2.0 which is available at + * https://www.apache.org/licenses/LICENSE-2.0 + * + * SPDX-License-Identifier: Apache-2.0 + ********************************************************************************/ + +#include "vehicle_abstraction_application.h" +#include "../generated/vss_files/signal_identifier.h" + +#ifdef _WIN32 +#include // Needed for _kbhit +#else +#include +#endif + +bool CAbstractionControl::LoadConfigFile(const std::string& inputMsg, const std::string& configFileName) +{ + std::string msg = inputMsg; + if (m_appcontrol.LoadConfig(configFileName) == sdv::core::EConfigProcessResult::successful) + { + msg.append("ok\n"); + std::cout << msg.c_str(); + return true; + } + + msg.append("FAILED.\n"); + std::cout << msg.c_str(); + return false; +} + +bool CAbstractionControl::IsSDVFrameworkEnvironmentSet() +{ + const char* envVariable = std::getenv("SDV_FRAMEWORK_RUNTIME"); + if (envVariable) + { + return true; + } + + return false; +} + +bool CAbstractionControl::Initialize(bool bSimulate) +{ + if (m_bInitialized) + return true; + + m_bSimulate = bSimulate; + if (!IsSDVFrameworkEnvironmentSet()) + { + // if SDV_FRAMEWORK_RUNTIME environment variable is not set we need to set the Framework Runtime directory + m_appcontrol.SetFrameworkRuntimeDirectory("../../bin"); + } + + if (!m_appcontrol.Startup("")) + return false; + + m_appcontrol.SetConfigMode(); + bool bResult = LoadConfigFile("Load dispatch service: ", "data_dispatch_vehicle_abstraction.toml"); + bResult &= LoadConfigFile("Load task_timer_vehicle: ", "task_timer_vehicle.toml"); + + if (bSimulate) + { + bResult &= LoadConfigFile("Load can_com_simulation_vehicle_abstraction_kmh: ", "can_com_simulation_vehicle_abstraction_kmh.toml"); + bResult &= LoadConfigFile("Load data_link_vehicle_abstraction: ", "data_link_vehicle_abstraction.toml"); + bResult &= LoadConfigFile("Load vehicle_abstraction_kmh: ", "vehicle_abstraction_device_kmh.toml"); + } + else + { + bResult &= LoadConfigFile("Load can_com_simulation_vehicle_abstraction_ms: ", "can_com_simulation_vehicle_abstraction_ms.toml"); + bResult &= LoadConfigFile("Load data_link_vehicle_abstraction: ", "data_link_vehicle_abstraction.toml"); + bResult &= LoadConfigFile("Load vehicle_abstraction_ms: ", "vehicle_abstraction_device_ms.toml"); + } + + bResult &= LoadConfigFile("Load vehicle_abstraction_basic_service: ", "vehicle_abstraction_basic_service.toml"); + + if (!bResult) + { + SDV_LOG_ERROR("One or more configurations could not be loaded. Cannot continue."); + return false; + } + + m_bInitialized = true; + + return true; +} + + +void CAbstractionControl::Shutdown() +{ + if (!m_bInitialized) + m_appcontrol.Shutdown(); + m_bInitialized = false; +} + +void CAbstractionControl::SetRunningMode() +{ + m_appcontrol.SetRunningMode(); +} + diff --git a/examples/vehicle_abstraction_example/vehicle_abstraction_app/vehicle_abstraction_application.h b/examples/vehicle_abstraction_example/vehicle_abstraction_app/vehicle_abstraction_application.h new file mode 100644 index 0000000..4237e4e --- /dev/null +++ b/examples/vehicle_abstraction_example/vehicle_abstraction_app/vehicle_abstraction_application.h @@ -0,0 +1,60 @@ + /******************************************************************************** + * Copyright (c) 2025-2026 ZF Friedrichshafen AG + * + * This program and the accompanying materials are made available under the + * terms of the Apache License Version 2.0 which is available at + * https://www.apache.org/licenses/LICENSE-2.0 + * + * SPDX-License-Identifier: Apache-2.0 + ********************************************************************************/ + +#include +#include +#include + +/** +* @brief Application Class of the vehicle abstraction example +*/ +class CAbstractionControl +{ +public: + + /** + * @brief Start and initialize the application control and load + * platform abstraction components and basic services + * @param[in] bSimulate If signals should be simulated or CAN input signals should be used + * @return Return true on success otherwise false + */ + bool Initialize(bool bSimulate); + + /** + * @brief After initialization/configuration the system mode needs to be set to running mode + */ + void SetRunningMode(); + + /** + * @brief Shutdown the system. + */ + void Shutdown(); + +private: + + /** + * @brief check if SDV_FRAMEWORK_RUNTIME environment variable exists + * @return Return true if environment variable is found otherwise false + */ + bool IsSDVFrameworkEnvironmentSet(); + + /** + * @brief Load config file and register vehicle device and basic service. + * @param[in] inputMsg message string to be printed on console in case of success and failure + * @param[in] configFileName config toml file name + * @return Return true on success otherwise false + */ + bool LoadConfigFile(const std::string& inputMsg, const std::string& configFileName); + + sdv::app::CAppControl m_appcontrol; ///< App-control of SDV V-API. + bool m_bInitialized = false; ///< Set when initialized. + + bool m_bSimulate = false; +}; diff --git a/examples/vehicle_abstraction_example/vehicle_abstraction_app/vehicle_abstraction_example.cpp b/examples/vehicle_abstraction_example/vehicle_abstraction_app/vehicle_abstraction_example.cpp new file mode 100644 index 0000000..8981ff0 --- /dev/null +++ b/examples/vehicle_abstraction_example/vehicle_abstraction_app/vehicle_abstraction_example.cpp @@ -0,0 +1,47 @@ + /******************************************************************************** + * Copyright (c) 2025-2026 ZF Friedrichshafen AG + * + * This program and the accompanying materials are made available under the + * terms of the Apache License Version 2.0 which is available at + * https://www.apache.org/licenses/LICENSE-2.0 + * + * SPDX-License-Identifier: Apache-2.0 + ********************************************************************************/ + +#include +#include // for std::strtol +#include +#include +#include +#include +#include "vehicle_abstraction_application.h" +#include "console.h" + + +#if defined(_WIN32) && defined(_UNICODE) +extern "C" int wmain([[maybe_unused]] int argc, [[maybe_unused]] wchar_t* argv[]) +{ +#else +extern "C" int main([[maybe_unused]] int argc, [[maybe_unused]] char* argv[]) +{ +#endif + CConsole visual_obj; + CAbstractionControl appobj; + if (!appobj.Initialize(visual_obj.SelectInputDataUnits())) + { + std::cout << "ERROR: Failed to initialize application control." << std::endl; + return 0; + } + + visual_obj.PrintHeader(); + visual_obj.PrepareDataConsumers(); + + appobj.SetRunningMode(); + + visual_obj.RunUntilBreak(); + + visual_obj.ResetSignals(); + + appobj.Shutdown(); + return 0; +} diff --git a/examples/vehicle_abstraction_example/vehicle_abstraction_example.csv b/examples/vehicle_abstraction_example/vehicle_abstraction_example.csv new file mode 100644 index 0000000..1e17e97 --- /dev/null +++ b/examples/vehicle_abstraction_example/vehicle_abstraction_example.csv @@ -0,0 +1,4 @@ +;Class name;Function name;Signal name;vss;Signal direction;type;DBC CAN name includes CAN message name +;;;;;;; +VD;VehicleSpeedKmh;Speed;vehicleSpeed;Vehicle.Speed;RX;float;CAN_Input.Speed_General +BS;VehicleSpeed;Speed;vehicleSpeed;Vehicle.Speed;RX;float;Vehicle.Speed diff --git a/examples/vehicle_abstraction_example/vehicle_abstraction_example.dbc b/examples/vehicle_abstraction_example/vehicle_abstraction_example.dbc new file mode 100644 index 0000000..34e3cd4 --- /dev/null +++ b/examples/vehicle_abstraction_example/vehicle_abstraction_example.dbc @@ -0,0 +1,115 @@ +VERSION "PrivateCAN" + + +NS_ : + NS_DESC_ + CM_ + BA_DEF_ + BA_ + VAL_ + CAT_DEF_ + CAT_ + FILTER + BA_DEF_DEF_ + EV_DATA_ + ENVVAR_DATA_ + SGTYPE_ + SGTYPE_VAL_ + BA_DEF_SGTYPE_ + BA_SGTYPE_ + SIG_TYPE_REF_ + VAL_TABLE_ + SIG_GROUP_ + SIG_VALTYPE_ + SIGTYPE_VALTYPE_ + BO_TX_BU_ + BA_DEF_REL_ + BA_REL_ + BA_DEF_DEF_REL_ + BU_SG_REL_ + BU_EV_REL_ + BU_BO_REL_ + SG_MUL_VAL_ + +BS_: + +BU_: abstraction +VAL_TABLE_ Fault_Codes 27 "UKWN" 26 "VEHSPDMAX_EXDD" 25 "STS_ALIVE" 24 "STEER_NOT_E2E_MODE" 23 "OTA_SPD" 22 "OTA_TIMER_DOWNLOAD_FAILED" 21 "OTA_MAX_TIME" 20 "CUBIXAD_STEERSTREQ_NOTACTV" 19 "CUBIXAD_DRVSTREQ_NOTACTV" 18 "SFTYDRV_INTV" 17 "LSDC_ALIVE" 16 "CUBIXAD_ALIVE" 15 "IBC_MAB_NO_PRIO" 14 "IBC_NOT_RDY" 13 "IBC_ALIVE" 12 "LSDC_GEAR" 11 "LSDC_SPD" 10 "LSDC_ACCL" 9 "IBC_NOT_MAB_MOD" 8 "GOLDBOX_ALIVE" 7 "CUBIXAD_GEAR" 6 "CUBIXAD_SPD_TESTTRACK" 5 "DRVREQCHG" 4 "RDY_TIMER" 3 "SFTY_CDN_FAILED" 2 "ACTVNCHK_SPD" 1 "ACTVNCHK_TIMR" 0 "NONE" ; +VAL_TABLE_ TestMapID 6 "E_TESTMAPID_UNDEFINED" 5 "E_TESTMAPID_TEST_DRIVE" 4 "E_TESTMAPID_AD_AREA" 3 "E_TESTMAPID_STUTT_ARENA" 2 "E_TESTMAPID_ZF_LASTMILE" 1 "E_TESTMAPID_ZF_TESTTRACK_2" 0 "E_TESTMAPID_NONE" ; +VAL_TABLE_ CtrlReqStates 7 "CtrlSts3b_RESERVED_4" 6 "CtrlSts3b_RESERVED_3" 5 "CtrlSts3b_RESERVED_2" 4 "CtrlSts3b_RESERVED_1" 3 "CtrlSts3b_ERROR" 2 "CtrlSts3b_CONTROL_REQUESTED" 1 "CtrlSts3b_CONTROL_NOT_REQUESTED" 0 "CtrlSts3b_INIT" ; +VAL_TABLE_ SteerActrReSts 7 "Diag" 6 "Inactive" 5 "Ramping" 4 "Yellow" 3 "Red" 2 "Normal" 1 "Pending" 0 "Initialisation" ; +VAL_TABLE_ SwtPark1 1 "SwtParkActv" 0 "SwtParkNotActv" ; +VAL_TABLE_ PE_State 2 "ERROR" 1 "INIT" 0 "NO_ERROR" ; +VAL_TABLE_ SSM_Req 7 "HMS_TAKEOVER" 6 "RESERVED" 5 "RELESE_VIA_RAMP" 4 "DRIVEOFF" 3 "HOLD_STANDBY" 2 "PARK" 1 "HOLD" 0 "NO_REQUEST" ; +VAL_TABLE_ IBC_StandStillMode 12 "SSM_ERROR" 11 "SSM_INIT" 10 "SSM_DRIVEOFF_STANDBY_ACTIVE" 9 "SSM_HOLD_STANDBY_ACTIVE" 8 "SSM_HILL_SLIPPOFF_DETECTED" 7 "SSM_RELEASE_REQ_FROM_DRIVER" 6 "SSM_RELEASE_REQ_ACTIVE" 5 "SSM_DRIVEOFF_ACTIVE" 4 "SSM_PARK_RETAINED_ACTIVE" 3 "SSM_PARK_ACTIVE" 2 "SSM_PARK_REQUESTED" 1 "SSM_HOLD_ACTIVE" 0 "SSM_NO_ACTIVE_FUNCTION" ; +VAL_TABLE_ AppTgtStDrv 3 "ACTIVE" 2 "READY" 1 "RESERVED" 0 "NOT_ACTIVE" ; +VAL_TABLE_ IBC_Status 4 "IBC_MAB_ERR_COMM" 3 "IBC_MAB_NO_PRIO" 2 "IBC_IN_MAB_MODE" 1 "IBC_READY" 0 "IBC_NOT_READY_FAILED" ; +VAL_TABLE_ GearLvrIndcn 7 "GearLvrIndcn2_Undefd" 6 "GearLvrIndcn2_Resd2" 5 "GearLvrIndcn2_Resd1" 4 "GearLvrIndcn2_ManModeIndcn" 3 "GearLvrIndcn2_DrvIndcn" 2 "GearLvrIndcn2_NeutIndcn" 1 "GearLvrIndcn2_RvsIndcn" 0 "GearLvrIndcn2_ParkIndcn" ; +VAL_TABLE_ LvlgAdjReq 7 "LvlgAdjReq_Resd2" 6 "LvlgAdjReq_Resd1" 5 "LvlgAdjReq_Ll2" 4 "LvlgAdjReq_Ll1" 3 "LvlgAdjReq_Nrh" 2 "LvlgAdjReq_Hl1" 1 "LvlgAdjReq_Hl2" 0 "LvlgAdjReq_Ukwn" ; +VAL_TABLE_ DrvModReq 15 "Err" 14 "Rock" 13 "Mud" 12 "Sand" 11 "Snow" 10 "Power" 9 "Hybrid" 8 "Pure_EV" 7 "Race" 6 "Adaptive" 5 "Offroad_CrossTerrain" 4 "Individual" 3 "Dynamic_Sport" 2 "Comfort_Normal" 1 "ECO" 0 "Undefd" ; +VAL_TABLE_ MAB_Info_Message 4 "DRV_GEARLVR_TO_P" 3 "DRV_P_TO_D" 2 "LSDC_DI_NOT_PSBL" 1 "LSDC_ENA_NOT_POSSIBLE" 0 "NONE" ; +VAL_TABLE_ MAB_OvrdTool_Sts 11 "HACKATHON" 10 "OTA" 9 "INIT" 8 "FINSHD" 7 "FLT" 6 "CUBIX_AD" 5 "SAVE_THE_SPOILER" 4 "LSDC" 3 "RDY" 2 "ACTVN_CHK" 1 "NO_MANIPULATION" 0 "NONE" ; +VAL_TABLE_ HMI_Drvr_Req 9 "FCT_DEACTVN_REQ" 8 "FCT_ACTVN_OTA_CFMD" 7 "FCT_ACTVN_OTA_REQ" 6 "FCT_ACTVN_SAVETHESPOILER_CFMD" 5 "FCT_ACTVN_SAVETHESPOILER_REQ" 4 "FCT_ACTVN_LSDC_CFMD" 3 "FCT_ACTVN_LSDC_REQ" 2 "FCT_ACTVN_CUBIXAD_CFMD" 1 "FCT_ACTVN_CUBIXAD_REQ" 0 "FCT_ACTVN_NONE" ; +VAL_TABLE_ Info_Message 4 "DRV_GEARLVR_TO_P" 3 "DRV_P_TO_D" 2 "LSDC_DI_NOT_PSBL" 1 "LSDC_ENA_NOT_POSSIBLE" 0 "NONE" ; +VAL_TABLE_ HMI_Fct_Req 8 "FCT_DEACTVN_REQ" 7 "FCT_ACTVN_OTA_REQ" 6 "FCT_ACTVN_SAVETHESPOILER_CFMD" 5 "FCT_ACTVN_SAVETHESPOILER_REQ" 4 "FCT_ACTVN_LSDC_CFMD" 3 "FCT_ACTVN_LSDC_REQ" 2 "FCT_ACTVN_AI4MTN_CFMD" 1 "FCT_ACTVN_AI4MTN_REQ" 0 "FCT_ACTVN_NONE" ; +VAL_TABLE_ SOVD_states 2 "SOVD_SHOWCASE_ACTIVE" 1 "SOVD_SHOWCASE_DEACTIVE" 0 "SOVD_NONE" ; +VAL_TABLE_ OTA_states 7 "OTA_DOWNLOAD_FAILED" 6 "OTA_INSTALL_FAILED" 5 "OTA_INSTALL_FINISHED" 4 "OTA_INSTALL_START" 3 "OTA_DOWNLOAD_START" 2 "OTA_SCHEDULED" 1 "OTA_STANDBY" 0 "OTA_NONE" ; + + +BO_ 1 CAN_Input: 8 Vector__XXX + SG_ Speed_General : 7|15@0+ (0.00391,0) [0|128.11897] "km/h" abstraction + + + +CM_ SG_ 1 Speed_General "Vehicle speed used in a example to demonstrate vehicel abstraction. One data is in m/s, the otherdata is km/h"; +BA_DEF_ "Baudrate" INT 1000 1000000; +BA_DEF_ "BusType" STRING ; +BA_DEF_ "DBName" STRING ; +BA_DEF_ "ProtocolType" STRING ; +BA_DEF_ BU_ "NmAsrNode" ENUM "No","Yes"; +BA_DEF_ BU_ "NmAsrNodeIdentifier" INT 0 255; +BA_DEF_ BO_ "GenMsgCycleTime" INT 0 65536; +BA_DEF_ BO_ "GenMsgCycleTimeFast" FLOAT 0 300000; +BA_DEF_ BO_ "GenMsgDelayTime" INT 0 65536; +BA_DEF_ BO_ "GenMsgNrOfRepetition" INT 0 100000; +BA_DEF_ BO_ "GenMsgSendType" ENUM "cyclic","spontaneous","not-used","not-used","not-used","cyclicAndSpontaneous","not-used","cyclicIfActive","NoMsgSendType"; +BA_DEF_ BO_ "GenMsgStartDelayTime" INT 0 65536; +BA_DEF_ SG_ "GenSigSendType" ENUM "Cyclic","OnWrite","OnWriteWithRepetition","OnChange","OnChangeWithRepetition","IfActive","IfActiveWithRepetition","NoSigSendType"; +BA_DEF_ SG_ "GenSigStartValue" HEX 0 80000000; +BA_DEF_ BO_ "GenMsgILSupport" ENUM "No","Yes"; +BA_DEF_ BO_ "NmAsrMessage" ENUM "No","Yes"; +BA_DEF_ "NmAsrBaseAddress" HEX 0 536870911; +BA_DEF_ "NmAsrMessageCount" INT 0 255; +BA_DEF_ BU_ "NodeLayerModules" STRING ; +BA_DEF_ BU_ "ILused" ENUM "No","Yes"; +BA_DEF_ SG_ "GenSigFuncType" ENUM "NoFunction","n/a","n/a","n/a","n/a","n/a","n/a","n/a","n/a","n/a","n/a","n/a","n/a","n/a","n/a","CHK","CNTR","n/a","n/a","n/a","CNTR_AR_01","CRC_AR_01_BOTH","CRC_AR_01_ALT","CRC_AR_01_LOW","CRC_AR_01_NIBBLE","CNTR_AR_04","CRC_AR_04A","CNTR_AR_05","CRC_AR_05"; +BA_DEF_ SG_ "GenSigDataID" STRING ; +BA_DEF_ SG_ "SigGroup" STRING ; +BA_DEF_DEF_ "Baudrate" 1000; +BA_DEF_DEF_ "BusType" ""; +BA_DEF_DEF_ "DBName" ""; +BA_DEF_DEF_ "ProtocolType" ""; +BA_DEF_DEF_ "NmAsrNode" "No"; +BA_DEF_DEF_ "NmAsrNodeIdentifier" 0; +BA_DEF_DEF_ "GenMsgCycleTime" 0; +BA_DEF_DEF_ "GenMsgCycleTimeFast" 0; +BA_DEF_DEF_ "GenMsgDelayTime" 0; +BA_DEF_DEF_ "GenMsgNrOfRepetition" 0; +BA_DEF_DEF_ "GenMsgSendType" "NoMsgSendType"; +BA_DEF_DEF_ "GenMsgStartDelayTime" 0; +BA_DEF_DEF_ "GenSigSendType" "NoSigSendType"; +BA_DEF_DEF_ "GenSigStartValue" 0; +BA_DEF_DEF_ "GenMsgILSupport" "Yes"; +BA_DEF_DEF_ "NmAsrMessage" "No"; +BA_DEF_DEF_ "NmAsrBaseAddress" 1280; +BA_DEF_DEF_ "NmAsrMessageCount" 64; +BA_DEF_DEF_ "NodeLayerModules" "CANoeILNLSPA.dll"; +BA_DEF_DEF_ "ILused" "Yes"; +BA_DEF_DEF_ "GenSigFuncType" "NoFunction"; +BA_DEF_DEF_ "GenSigDataID" ""; +BA_DEF_DEF_ "SigGroup" ""; +BA_ "ProtocolType" "CAN"; +BA_ "BusType" "CAN"; +BA_ "Baudrate" 500000; +BA_ "DBName" "PrivateCAN"; + diff --git a/examples/vehicle_abstraction_example/vehicle_abstraction_example_ms.csv b/examples/vehicle_abstraction_example/vehicle_abstraction_example_ms.csv new file mode 100644 index 0000000..02406f5 --- /dev/null +++ b/examples/vehicle_abstraction_example/vehicle_abstraction_example_ms.csv @@ -0,0 +1,3 @@ +;Class name;Function name;Signal name;vss;Signal direction;type;DBC CAN name includes CAN message name; +;;;;;;;; +VD;VehicleSpeedMs;Speed;vehicleSpeed;Vehicle.Speed;RX;float;CAN_Input.Speed_General;"float vehicleSpeed = value.get() * 3.6f;" diff --git a/examples/vehicle_abstraction_example/vehicle_abstraction_example_receiver_kmh.asc b/examples/vehicle_abstraction_example/vehicle_abstraction_example_receiver_kmh.asc new file mode 100644 index 0000000..e99328e --- /dev/null +++ b/examples/vehicle_abstraction_example/vehicle_abstraction_example_receiver_kmh.asc @@ -0,0 +1,2357 @@ +date Sat Mar 28 14:51:33 2026 +base hex timestamps absolute +Begin TriggerBlock Sat Mar 28 14:51:33 2026 + 0.000000 Start of measurement + 0.057220 1 1 Tx d 8 13 FC 00 00 00 00 00 00 + 0.067241 1 1 Tx d 8 13 FC 00 00 00 00 00 00 + 0.077232 1 1 Tx d 8 13 FC 00 00 00 00 00 00 + 0.087260 1 1 Tx d 8 13 FC 00 00 00 00 00 00 + 0.097231 1 1 Tx d 8 13 FC 00 00 00 00 00 00 + 0.107295 1 1 Tx d 8 13 FC 00 00 00 00 00 00 + 0.117266 1 1 Tx d 8 13 FC 00 00 00 00 00 00 + 0.126720 1 1 Tx d 8 13 FC 00 00 00 00 00 00 + 0.136840 1 1 Tx d 8 13 FC 00 00 00 00 00 00 + 0.146851 1 1 Tx d 8 13 FC 00 00 00 00 00 00 + 0.156762 1 1 Tx d 8 13 FC 00 00 00 00 00 00 + 0.166736 1 1 Tx d 8 13 FC 00 00 00 00 00 00 + 0.176931 1 1 Tx d 8 13 FC 00 00 00 00 00 00 + 0.186824 1 1 Tx d 8 13 FC 00 00 00 00 00 00 + 0.196865 1 1 Tx d 8 13 FC 00 00 00 00 00 00 + 0.206888 1 1 Tx d 8 13 FC 00 00 00 00 00 00 + 0.216946 1 1 Tx d 8 13 FC 00 00 00 00 00 00 + 0.227655 1 1 Tx d 8 13 FC 00 00 00 00 00 00 + 0.237781 1 1 Tx d 8 13 FC 00 00 00 00 00 00 + 0.247714 1 1 Tx d 8 13 FC 00 00 00 00 00 00 + 0.257206 1 1 Tx d 8 13 FC 00 00 00 00 00 00 + 0.266848 1 1 Tx d 8 13 FC 00 00 00 00 00 00 + 0.277852 1 1 Tx d 8 13 FC 00 00 00 00 00 00 + 0.286851 1 1 Tx d 8 13 FC 00 00 00 00 00 00 + 0.297193 1 1 Tx d 8 13 FC 00 00 00 00 00 00 + 0.307680 1 1 Tx d 8 13 FC 00 00 00 00 00 00 + 0.316922 1 1 Tx d 8 13 FC 00 00 00 00 00 00 + 0.327415 1 1 Tx d 8 13 FC 00 00 00 00 00 00 + 0.336871 1 1 Tx d 8 13 FC 00 00 00 00 00 00 + 0.346865 1 1 Tx d 8 13 FC 00 00 00 00 00 00 + 0.356867 1 1 Tx d 8 13 FC 00 00 00 00 00 00 + 0.366858 1 1 Tx d 8 13 FC 00 00 00 00 00 00 + 0.376864 1 1 Tx d 8 13 FC 00 00 00 00 00 00 + 0.386879 1 1 Tx d 8 13 FC 00 00 00 00 00 00 + 0.396929 1 1 Tx d 8 13 FC 00 00 00 00 00 00 + 0.406931 1 1 Tx d 8 13 FC 00 00 00 00 00 00 + 0.417127 1 1 Tx d 8 13 FC 00 00 00 00 00 00 + 0.426711 1 1 Tx d 8 13 FC 00 00 00 00 00 00 + 0.436725 1 1 Tx d 8 13 FC 00 00 00 00 00 00 + 0.447668 1 1 Tx d 8 13 FC 00 00 00 00 00 00 + 0.457668 1 1 Tx d 8 13 FC 00 00 00 00 00 00 + 0.467668 1 1 Tx d 8 13 FC 00 00 00 00 00 00 + 0.476717 1 1 Tx d 8 13 FC 00 00 00 00 00 00 + 0.486727 1 1 Tx d 8 13 FC 00 00 00 00 00 00 + 0.496717 1 1 Tx d 8 13 FC 00 00 00 00 00 00 + 0.506719 1 1 Tx d 8 13 FC 00 00 00 00 00 00 + 0.517037 1 1 Tx d 8 13 FC 00 00 00 00 00 00 + 0.526832 1 1 Tx d 8 13 FC 00 00 00 00 00 00 + 0.536857 1 1 Tx d 8 13 FC 00 00 00 00 00 00 + 0.546934 1 1 Tx d 8 13 FC 00 00 00 00 00 00 + 0.556951 1 1 Tx d 8 13 FC 00 00 00 00 00 00 + 0.566829 1 1 Tx d 8 13 FC 00 00 00 00 00 00 + 0.576917 1 1 Tx d 8 13 FC 00 00 00 00 00 00 + 0.586922 1 1 Tx d 8 13 FC 00 00 00 00 00 00 + 0.596922 1 1 Tx d 8 13 FC 00 00 00 00 00 00 + 0.606922 1 1 Tx d 8 13 FC 00 00 00 00 00 00 + 0.617022 1 1 Tx d 8 13 FC 00 00 00 00 00 00 + 0.626730 1 1 Tx d 8 13 FC 00 00 00 00 00 00 + 0.636737 1 1 Tx d 8 13 FC 00 00 00 00 00 00 + 0.646722 1 1 Tx d 8 13 FC 00 00 00 00 00 00 + 0.656728 1 1 Tx d 8 13 FC 00 00 00 00 00 00 + 0.666734 1 1 Tx d 8 13 FC 00 00 00 00 00 00 + 0.677776 1 1 Tx d 8 13 FC 00 00 00 00 00 00 + 0.686719 1 1 Tx d 8 13 FC 00 00 00 00 00 00 + 0.696718 1 1 Tx d 8 13 FC 00 00 00 00 00 00 + 0.707182 1 1 Tx d 8 13 FC 00 00 00 00 00 00 + 0.717166 1 1 Tx d 8 13 FC 00 00 00 00 00 00 + 0.727702 1 1 Tx d 8 13 FC 00 00 00 00 00 00 + 0.736765 1 1 Tx d 8 13 FC 00 00 00 00 00 00 + 0.747777 1 1 Tx d 8 13 FC 00 00 00 00 00 00 + 0.757687 1 1 Tx d 8 13 FC 00 00 00 00 00 00 + 0.767702 1 1 Tx d 8 13 FC 00 00 00 00 00 00 + 0.777718 1 1 Tx d 8 13 FC 00 00 00 00 00 00 + 0.786785 1 1 Tx d 8 13 FC 00 00 00 00 00 00 + 0.796762 1 1 Tx d 8 13 FC 00 00 00 00 00 00 + 0.806774 1 1 Tx d 8 13 FC 00 00 00 00 00 00 + 0.816882 1 1 Tx d 8 13 FC 00 00 00 00 00 00 + 0.827547 1 1 Tx d 8 13 FC 00 00 00 00 00 00 + 0.837546 1 1 Tx d 8 13 FC 00 00 00 00 00 00 + 0.848189 1 1 Tx d 8 13 FC 00 00 00 00 00 00 + 0.857059 1 1 Tx d 8 13 FC 00 00 00 00 00 00 + 0.867051 1 1 Tx d 8 13 FC 00 00 00 00 00 00 + 0.877051 1 1 Tx d 8 13 FC 00 00 00 00 00 00 + 0.887044 1 1 Tx d 8 13 FC 00 00 00 00 00 00 + 0.897039 1 1 Tx d 8 13 FC 00 00 00 00 00 00 + 0.907143 1 1 Tx d 8 13 FC 00 00 00 00 00 00 + 0.917148 1 1 Tx d 8 13 FC 00 00 00 00 00 00 + 0.926826 1 1 Tx d 8 13 FC 00 00 00 00 00 00 + 0.936743 1 1 Tx d 8 13 FC 00 00 00 00 00 00 + 0.946757 1 1 Tx d 8 13 FC 00 00 00 00 00 00 + 0.956776 1 1 Tx d 8 13 FC 00 00 00 00 00 00 + 0.966757 1 1 Tx d 8 13 FC 00 00 00 00 00 00 + 0.976800 1 1 Tx d 8 13 FC 00 00 00 00 00 00 + 0.986747 1 1 Tx d 8 13 FC 00 00 00 00 00 00 + 0.996746 1 1 Tx d 8 13 FC 00 00 00 00 00 00 + 1.006779 1 1 Tx d 8 13 FC 00 00 00 00 00 00 + 1.016756 1 1 Tx d 8 13 FC 00 00 00 00 00 00 + 1.027756 1 1 Tx d 8 13 FC 00 00 00 00 00 00 + 1.037625 1 1 Tx d 8 13 FC 00 00 00 00 00 00 + 1.047710 1 1 Tx d 8 13 FC 00 00 00 00 00 00 + 1.057730 1 1 Tx d 8 13 FC 00 00 00 00 00 00 + 1.067636 1 1 Tx d 8 13 FC 00 00 00 00 00 00 + 1.077000 1 1 Tx d 8 13 FC 00 00 00 00 00 00 + 1.087396 1 1 Tx d 8 13 FC 00 00 00 00 00 00 + 1.097505 1 1 Tx d 8 13 FC 00 00 00 00 00 00 + 1.107112 1 1 Tx d 8 13 FC 00 00 00 00 00 00 + 1.117165 1 1 Tx d 8 13 FC 00 00 00 00 00 00 + 1.127458 1 1 Tx d 8 13 FC 00 00 00 00 00 00 + 1.137495 1 1 Tx d 8 13 FC 00 00 00 00 00 00 + 1.147502 1 1 Tx d 8 13 FC 00 00 00 00 00 00 + 1.157568 1 1 Tx d 8 13 FC 00 00 00 00 00 00 + 1.167069 1 1 Tx d 8 13 FC 00 00 00 00 00 00 + 1.177022 1 1 Tx d 8 13 FC 00 00 00 00 00 00 + 1.187197 1 1 Tx d 8 13 FC 00 00 00 00 00 00 + 1.197198 1 1 Tx d 8 13 FC 00 00 00 00 00 00 + 1.207102 1 1 Tx d 8 13 FC 00 00 00 00 00 00 + 1.217121 1 1 Tx d 8 13 FC 00 00 00 00 00 00 + 1.227667 1 1 Tx d 8 13 FC 00 00 00 00 00 00 + 1.237672 1 1 Tx d 8 13 FC 00 00 00 00 00 00 + 1.247678 1 1 Tx d 8 13 FC 00 00 00 00 00 00 + 1.257662 1 1 Tx d 8 13 FC 00 00 00 00 00 00 + 1.267642 1 1 Tx d 8 13 FC 00 00 00 00 00 00 + 1.277635 1 1 Tx d 8 13 FC 00 00 00 00 00 00 + 1.287672 1 1 Tx d 8 13 FC 00 00 00 00 00 00 + 1.297707 1 1 Tx d 8 13 FC 00 00 00 00 00 00 + 1.306743 1 1 Tx d 8 13 FC 00 00 00 00 00 00 + 1.316849 1 1 Tx d 8 13 FC 00 00 00 00 00 00 + 1.327350 1 1 Tx d 8 13 FC 00 00 00 00 00 00 + 1.337336 1 1 Tx d 8 13 FC 00 00 00 00 00 00 + 1.347339 1 1 Tx d 8 13 FC 00 00 00 00 00 00 + 1.357084 1 1 Tx d 8 13 FC 00 00 00 00 00 00 + 1.367058 1 1 Tx d 8 13 FC 00 00 00 00 00 00 + 1.377082 1 1 Tx d 8 13 FC 00 00 00 00 00 00 + 1.387085 1 1 Tx d 8 13 FC 00 00 00 00 00 00 + 1.397088 1 1 Tx d 8 13 FC 00 00 00 00 00 00 + 1.407119 1 1 Tx d 8 13 FC 00 00 00 00 00 00 + 1.417186 1 1 Tx d 8 13 FC 00 00 00 00 00 00 + 1.426777 1 1 Tx d 8 13 FC 00 00 00 00 00 00 + 1.436871 1 1 Tx d 8 13 FC 00 00 00 00 00 00 + 1.446865 1 1 Tx d 8 13 FC 00 00 00 00 00 00 + 1.456872 1 1 Tx d 8 13 FC 00 00 00 00 00 00 + 1.466876 1 1 Tx d 8 13 FC 00 00 00 00 00 00 + 1.476869 1 1 Tx d 8 13 FC 00 00 00 00 00 00 + 1.486876 1 1 Tx d 8 13 FC 00 00 00 00 00 00 + 1.496863 1 1 Tx d 8 13 FC 00 00 00 00 00 00 + 1.506857 1 1 Tx d 8 13 FC 00 00 00 00 00 00 + 1.516982 1 1 Tx d 8 13 FC 00 00 00 00 00 00 + 1.527628 1 1 Tx d 8 13 FC 00 00 00 00 00 00 + 1.537627 1 1 Tx d 8 13 FC 00 00 00 00 00 00 + 1.548073 1 1 Tx d 8 13 FC 00 00 00 00 00 00 + 1.557120 1 1 Tx d 8 13 FC 00 00 00 00 00 00 + 1.567134 1 1 Tx d 8 13 FC 00 00 00 00 00 00 + 1.577127 1 1 Tx d 8 13 FC 00 00 00 00 00 00 + 1.587083 1 1 Tx d 8 13 FC 00 00 00 00 00 00 + 1.597131 1 1 Tx d 8 13 FC 00 00 00 00 00 00 + 1.607132 1 1 Tx d 8 13 FC 00 00 00 00 00 00 + 1.617228 1 1 Tx d 8 13 FC 00 00 00 00 00 00 + 1.626933 1 1 Tx d 8 13 FC 00 00 00 00 00 00 + 1.636920 1 1 Tx d 8 27 F6 00 00 00 00 00 00 + 1.646909 1 1 Tx d 8 27 F6 00 00 00 00 00 00 + 1.656910 1 1 Tx d 8 27 F6 00 00 00 00 00 00 + 1.666911 1 1 Tx d 8 27 F6 00 00 00 00 00 00 + 1.676907 1 1 Tx d 8 27 F6 00 00 00 00 00 00 + 1.686988 1 1 Tx d 8 27 F6 00 00 00 00 00 00 + 1.696994 1 1 Tx d 8 27 F6 00 00 00 00 00 00 + 1.706819 1 1 Tx d 8 27 F6 00 00 00 00 00 00 + 1.717053 1 1 Tx d 8 27 F6 00 00 00 00 00 00 + 1.727302 1 1 Tx d 8 27 F6 00 00 00 00 00 00 + 1.737284 1 1 Tx d 8 27 F6 00 00 00 00 00 00 + 1.747181 1 1 Tx d 8 27 F6 00 00 00 00 00 00 + 1.757180 1 1 Tx d 8 27 F6 00 00 00 00 00 00 + 1.767224 1 1 Tx d 8 27 F6 00 00 00 00 00 00 + 1.777223 1 1 Tx d 8 27 F6 00 00 00 00 00 00 + 1.787224 1 1 Tx d 8 27 F6 00 00 00 00 00 00 + 1.797276 1 1 Tx d 8 27 F6 00 00 00 00 00 00 + 1.807282 1 1 Tx d 8 27 F6 00 00 00 00 00 00 + 1.817285 1 1 Tx d 8 27 F6 00 00 00 00 00 00 + 1.827785 1 1 Tx d 8 27 F6 00 00 00 00 00 00 + 1.837740 1 1 Tx d 8 27 F6 00 00 00 00 00 00 + 1.847688 1 1 Tx d 8 27 F6 00 00 00 00 00 00 + 1.857776 1 1 Tx d 8 27 F6 00 00 00 00 00 00 + 1.867730 1 1 Tx d 8 27 F6 00 00 00 00 00 00 + 1.877742 1 1 Tx d 8 27 F6 00 00 00 00 00 00 + 1.886775 1 1 Tx d 8 27 F6 00 00 00 00 00 00 + 1.896776 1 1 Tx d 8 27 F6 00 00 00 00 00 00 + 1.906780 1 1 Tx d 8 27 F6 00 00 00 00 00 00 + 1.916942 1 1 Tx d 8 27 F6 00 00 00 00 00 00 + 1.927664 1 1 Tx d 8 27 F6 00 00 00 00 00 00 + 1.937567 1 1 Tx d 8 27 F6 00 00 00 00 00 00 + 1.946824 1 1 Tx d 8 27 F6 00 00 00 00 00 00 + 1.956981 1 1 Tx d 8 27 F6 00 00 00 00 00 00 + 1.966821 1 1 Tx d 8 27 F6 00 00 00 00 00 00 + 1.977181 1 1 Tx d 8 27 F6 00 00 00 00 00 00 + 1.987422 1 1 Tx d 8 27 F6 00 00 00 00 00 00 + 1.997415 1 1 Tx d 8 27 F6 00 00 00 00 00 00 + 2.007362 1 1 Tx d 8 27 F6 00 00 00 00 00 00 + 2.017438 1 1 Tx d 8 27 F6 00 00 00 00 00 00 + 2.026941 1 1 Tx d 8 27 F6 00 00 00 00 00 00 + 2.037032 1 1 Tx d 8 27 F6 00 00 00 00 00 00 + 2.047038 1 1 Tx d 8 27 F6 00 00 00 00 00 00 + 2.057036 1 1 Tx d 8 27 F6 00 00 00 00 00 00 + 2.067032 1 1 Tx d 8 27 F6 00 00 00 00 00 00 + 2.077134 1 1 Tx d 8 27 F6 00 00 00 00 00 00 + 2.087143 1 1 Tx d 8 27 F6 00 00 00 00 00 00 + 2.097035 1 1 Tx d 8 27 F6 00 00 00 00 00 00 + 2.107031 1 1 Tx d 8 27 F6 00 00 00 00 00 00 + 2.117130 1 1 Tx d 8 27 F6 00 00 00 00 00 00 + 2.127142 1 1 Tx d 8 27 F6 00 00 00 00 00 00 + 2.136813 1 1 Tx d 8 27 F6 00 00 00 00 00 00 + 2.146832 1 1 Tx d 8 27 F6 00 00 00 00 00 00 + 2.156791 1 1 Tx d 8 27 F6 00 00 00 00 00 00 + 2.166761 1 1 Tx d 8 27 F6 00 00 00 00 00 00 + 2.177249 1 1 Tx d 8 27 F6 00 00 00 00 00 00 + 2.187249 1 1 Tx d 8 27 F6 00 00 00 00 00 00 + 2.197317 1 1 Tx d 8 27 F6 00 00 00 00 00 00 + 2.207316 1 1 Tx d 8 27 F6 00 00 00 00 00 00 + 2.217415 1 1 Tx d 8 27 F6 00 00 00 00 00 00 + 2.227414 1 1 Tx d 8 27 F6 00 00 00 00 00 00 + 2.237098 1 1 Tx d 8 27 F6 00 00 00 00 00 00 + 2.247108 1 1 Tx d 8 27 F6 00 00 00 00 00 00 + 2.257116 1 1 Tx d 8 27 F6 00 00 00 00 00 00 + 2.267099 1 1 Tx d 8 27 F6 00 00 00 00 00 00 + 2.277040 1 1 Tx d 8 27 F6 00 00 00 00 00 00 + 2.287093 1 1 Tx d 8 27 F6 00 00 00 00 00 00 + 2.297094 1 1 Tx d 8 27 F6 00 00 00 00 00 00 + 2.307615 1 1 Tx d 8 27 F6 00 00 00 00 00 00 + 2.316739 1 1 Tx d 8 27 F6 00 00 00 00 00 00 + 2.326740 1 1 Tx d 8 27 F6 00 00 00 00 00 00 + 2.337428 1 1 Tx d 8 27 F6 00 00 00 00 00 00 + 2.347434 1 1 Tx d 8 27 F6 00 00 00 00 00 00 + 2.357440 1 1 Tx d 8 27 F6 00 00 00 00 00 00 + 2.367447 1 1 Tx d 8 27 F6 00 00 00 00 00 00 + 2.377437 1 1 Tx d 8 27 F6 00 00 00 00 00 00 + 2.387383 1 1 Tx d 8 27 F6 00 00 00 00 00 00 + 2.397431 1 1 Tx d 8 27 F6 00 00 00 00 00 00 + 2.407436 1 1 Tx d 8 27 F6 00 00 00 00 00 00 + 2.417348 1 1 Tx d 8 27 F6 00 00 00 00 00 00 + 2.427918 1 1 Tx d 8 27 F6 00 00 00 00 00 00 + 2.436964 1 1 Tx d 8 27 F6 00 00 00 00 00 00 + 2.446972 1 1 Tx d 8 27 F6 00 00 00 00 00 00 + 2.456959 1 1 Tx d 8 27 F6 00 00 00 00 00 00 + 2.466962 1 1 Tx d 8 27 F6 00 00 00 00 00 00 + 2.476984 1 1 Tx d 8 27 F6 00 00 00 00 00 00 + 2.486970 1 1 Tx d 8 27 F6 00 00 00 00 00 00 + 2.497242 1 1 Tx d 8 27 F6 00 00 00 00 00 00 + 2.507291 1 1 Tx d 8 27 F6 00 00 00 00 00 00 + 2.517390 1 1 Tx d 8 27 F6 00 00 00 00 00 00 + 2.527934 1 1 Tx d 8 27 F6 00 00 00 00 00 00 + 2.536929 1 1 Tx d 8 27 F6 00 00 00 00 00 00 + 2.547040 1 1 Tx d 8 27 F6 00 00 00 00 00 00 + 2.557027 1 1 Tx d 8 27 F6 00 00 00 00 00 00 + 2.567019 1 1 Tx d 8 27 F6 00 00 00 00 00 00 + 2.577017 1 1 Tx d 8 27 F6 00 00 00 00 00 00 + 2.587027 1 1 Tx d 8 27 F6 00 00 00 00 00 00 + 2.597029 1 1 Tx d 8 27 F6 00 00 00 00 00 00 + 2.608146 1 1 Tx d 8 27 F6 00 00 00 00 00 00 + 2.617444 1 1 Tx d 8 27 F6 00 00 00 00 00 00 + 2.627957 1 1 Tx d 8 27 F6 00 00 00 00 00 00 + 2.636972 1 1 Tx d 8 27 F6 00 00 00 00 00 00 + 2.647036 1 1 Tx d 8 27 F6 00 00 00 00 00 00 + 2.657038 1 1 Tx d 8 27 F6 00 00 00 00 00 00 + 2.666946 1 1 Tx d 8 27 F6 00 00 00 00 00 00 + 2.676970 1 1 Tx d 8 27 F6 00 00 00 00 00 00 + 2.687042 1 1 Tx d 8 27 F6 00 00 00 00 00 00 + 2.697000 1 1 Tx d 8 27 F6 00 00 00 00 00 00 + 2.707051 1 1 Tx d 8 27 F6 00 00 00 00 00 00 + 2.717137 1 1 Tx d 8 27 F6 00 00 00 00 00 00 + 2.727152 1 1 Tx d 8 27 F6 00 00 00 00 00 00 + 2.736821 1 1 Tx d 8 27 F6 00 00 00 00 00 00 + 2.746958 1 1 Tx d 8 27 F6 00 00 00 00 00 00 + 2.756814 1 1 Tx d 8 27 F6 00 00 00 00 00 00 + 2.766734 1 1 Tx d 8 27 F6 00 00 00 00 00 00 + 2.776763 1 1 Tx d 8 27 F6 00 00 00 00 00 00 + 2.786751 1 1 Tx d 8 27 F6 00 00 00 00 00 00 + 2.796820 1 1 Tx d 8 27 F6 00 00 00 00 00 00 + 2.806821 1 1 Tx d 8 27 F6 00 00 00 00 00 00 + 2.816915 1 1 Tx d 8 27 F6 00 00 00 00 00 00 + 2.826917 1 1 Tx d 8 27 F6 00 00 00 00 00 00 + 2.837603 1 1 Tx d 8 27 F6 00 00 00 00 00 00 + 2.847602 1 1 Tx d 8 27 F6 00 00 00 00 00 00 + 2.857600 1 1 Tx d 8 27 F6 00 00 00 00 00 00 + 2.867600 1 1 Tx d 8 27 F6 00 00 00 00 00 00 + 2.877603 1 1 Tx d 8 27 F6 00 00 00 00 00 00 + 2.887600 1 1 Tx d 8 27 F6 00 00 00 00 00 00 + 2.897598 1 1 Tx d 8 27 F6 00 00 00 00 00 00 + 2.907598 1 1 Tx d 8 27 F6 00 00 00 00 00 00 + 2.916764 1 1 Tx d 8 27 F6 00 00 00 00 00 00 + 2.927657 1 1 Tx d 8 27 F6 00 00 00 00 00 00 + 2.937396 1 1 Tx d 8 27 F6 00 00 00 00 00 00 + 2.947396 1 1 Tx d 8 27 F6 00 00 00 00 00 00 + 2.957488 1 1 Tx d 8 27 F6 00 00 00 00 00 00 + 2.967332 1 1 Tx d 8 27 F6 00 00 00 00 00 00 + 2.977343 1 1 Tx d 8 27 F6 00 00 00 00 00 00 + 2.987345 1 1 Tx d 8 27 F6 00 00 00 00 00 00 + 2.997402 1 1 Tx d 8 27 F6 00 00 00 00 00 00 + 3.007390 1 1 Tx d 8 27 F6 00 00 00 00 00 00 + 3.017496 1 1 Tx d 8 27 F6 00 00 00 00 00 00 + 3.027487 1 1 Tx d 8 27 F6 00 00 00 00 00 00 + 3.037196 1 1 Tx d 8 27 F6 00 00 00 00 00 00 + 3.047143 1 1 Tx d 8 27 F6 00 00 00 00 00 00 + 3.057155 1 1 Tx d 8 27 F6 00 00 00 00 00 00 + 3.067177 1 1 Tx d 8 27 F6 00 00 00 00 00 00 + 3.077176 1 1 Tx d 8 27 F6 00 00 00 00 00 00 + 3.087175 1 1 Tx d 8 27 F6 00 00 00 00 00 00 + 3.097187 1 1 Tx d 8 27 F6 00 00 00 00 00 00 + 3.107253 1 1 Tx d 8 27 F6 00 00 00 00 00 00 + 3.117289 1 1 Tx d 8 27 F6 00 00 00 00 00 00 + 3.127347 1 1 Tx d 8 27 F6 00 00 00 00 00 00 + 3.136925 1 1 Tx d 8 27 F6 00 00 00 00 00 00 + 3.146928 1 1 Tx d 8 27 F6 00 00 00 00 00 00 + 3.156925 1 1 Tx d 8 27 F6 00 00 00 00 00 00 + 3.167013 1 1 Tx d 8 27 F6 00 00 00 00 00 00 + 3.176925 1 1 Tx d 8 27 F6 00 00 00 00 00 00 + 3.186925 1 1 Tx d 8 27 F6 00 00 00 00 00 00 + 3.197018 1 1 Tx d 8 27 F6 00 00 00 00 00 00 + 3.206920 1 1 Tx d 8 27 F6 00 00 00 00 00 00 + 3.216922 1 1 Tx d 8 27 F6 00 00 00 00 00 00 + 3.226964 1 1 Tx d 8 27 F6 00 00 00 00 00 00 + 3.237521 1 1 Tx d 8 3B F2 00 00 00 00 00 00 + 3.247594 1 1 Tx d 8 3B F2 00 00 00 00 00 00 + 3.257420 1 1 Tx d 8 3B F2 00 00 00 00 00 00 + 3.267424 1 1 Tx d 8 3B F2 00 00 00 00 00 00 + 3.277598 1 1 Tx d 8 3B F2 00 00 00 00 00 00 + 3.287490 1 1 Tx d 8 3B F2 00 00 00 00 00 00 + 3.297478 1 1 Tx d 8 3B F2 00 00 00 00 00 00 + 3.307447 1 1 Tx d 8 3B F2 00 00 00 00 00 00 + 3.317500 1 1 Tx d 8 3B F2 00 00 00 00 00 00 + 3.327507 1 1 Tx d 8 3B F2 00 00 00 00 00 00 + 3.337001 1 1 Tx d 8 3B F2 00 00 00 00 00 00 + 3.347015 1 1 Tx d 8 3B F2 00 00 00 00 00 00 + 3.357038 1 1 Tx d 8 3B F2 00 00 00 00 00 00 + 3.367026 1 1 Tx d 8 3B F2 00 00 00 00 00 00 + 3.377074 1 1 Tx d 8 3B F2 00 00 00 00 00 00 + 3.387560 1 1 Tx d 8 3B F2 00 00 00 00 00 00 + 3.397567 1 1 Tx d 8 3B F2 00 00 00 00 00 00 + 3.407559 1 1 Tx d 8 3B F2 00 00 00 00 00 00 + 3.417510 1 1 Tx d 8 3B F2 00 00 00 00 00 00 + 3.427565 1 1 Tx d 8 3B F2 00 00 00 00 00 00 + 3.437069 1 1 Tx d 8 3B F2 00 00 00 00 00 00 + 3.447071 1 1 Tx d 8 3B F2 00 00 00 00 00 00 + 3.457072 1 1 Tx d 8 3B F2 00 00 00 00 00 00 + 3.467082 1 1 Tx d 8 3B F2 00 00 00 00 00 00 + 3.477070 1 1 Tx d 8 3B F2 00 00 00 00 00 00 + 3.487072 1 1 Tx d 8 3B F2 00 00 00 00 00 00 + 3.497092 1 1 Tx d 8 3B F2 00 00 00 00 00 00 + 3.507172 1 1 Tx d 8 3B F2 00 00 00 00 00 00 + 3.517177 1 1 Tx d 8 3B F2 00 00 00 00 00 00 + 3.527166 1 1 Tx d 8 3B F2 00 00 00 00 00 00 + 3.536856 1 1 Tx d 8 3B F2 00 00 00 00 00 00 + 3.546864 1 1 Tx d 8 3B F2 00 00 00 00 00 00 + 3.556871 1 1 Tx d 8 3B F2 00 00 00 00 00 00 + 3.566866 1 1 Tx d 8 3B F2 00 00 00 00 00 00 + 3.576966 1 1 Tx d 8 3B F2 00 00 00 00 00 00 + 3.586888 1 1 Tx d 8 3B F2 00 00 00 00 00 00 + 3.596865 1 1 Tx d 8 3B F2 00 00 00 00 00 00 + 3.606866 1 1 Tx d 8 3B F2 00 00 00 00 00 00 + 3.616951 1 1 Tx d 8 3B F2 00 00 00 00 00 00 + 3.626944 1 1 Tx d 8 3B F2 00 00 00 00 00 00 + 3.637136 1 1 Tx d 8 3B F2 00 00 00 00 00 00 + 3.647150 1 1 Tx d 8 3B F2 00 00 00 00 00 00 + 3.657145 1 1 Tx d 8 3B F2 00 00 00 00 00 00 + 3.667165 1 1 Tx d 8 3B F2 00 00 00 00 00 00 + 3.677138 1 1 Tx d 8 3B F2 00 00 00 00 00 00 + 3.687145 1 1 Tx d 8 3B F2 00 00 00 00 00 00 + 3.697144 1 1 Tx d 8 3B F2 00 00 00 00 00 00 + 3.707161 1 1 Tx d 8 3B F2 00 00 00 00 00 00 + 3.717218 1 1 Tx d 8 3B F2 00 00 00 00 00 00 + 3.727202 1 1 Tx d 8 3B F2 00 00 00 00 00 00 + 3.737791 1 1 Tx d 8 3B F2 00 00 00 00 00 00 + 3.746769 1 1 Tx d 8 3B F2 00 00 00 00 00 00 + 3.756759 1 1 Tx d 8 3B F2 00 00 00 00 00 00 + 3.766763 1 1 Tx d 8 3B F2 00 00 00 00 00 00 + 3.776852 1 1 Tx d 8 3B F2 00 00 00 00 00 00 + 3.786763 1 1 Tx d 8 3B F2 00 00 00 00 00 00 + 3.796757 1 1 Tx d 8 3B F2 00 00 00 00 00 00 + 3.807734 1 1 Tx d 8 3B F2 00 00 00 00 00 00 + 3.816872 1 1 Tx d 8 3B F2 00 00 00 00 00 00 + 3.826856 1 1 Tx d 8 3B F2 00 00 00 00 00 00 + 3.837494 1 1 Tx d 8 3B F2 00 00 00 00 00 00 + 3.847538 1 1 Tx d 8 3B F2 00 00 00 00 00 00 + 3.857626 1 1 Tx d 8 3B F2 00 00 00 00 00 00 + 3.867638 1 1 Tx d 8 3B F2 00 00 00 00 00 00 + 3.877532 1 1 Tx d 8 3B F2 00 00 00 00 00 00 + 3.887489 1 1 Tx d 8 3B F2 00 00 00 00 00 00 + 3.897539 1 1 Tx d 8 3B F2 00 00 00 00 00 00 + 3.907530 1 1 Tx d 8 3B F2 00 00 00 00 00 00 + 3.917638 1 1 Tx d 8 3B F2 00 00 00 00 00 00 + 3.927634 1 1 Tx d 8 3B F2 00 00 00 00 00 00 + 3.937332 1 1 Tx d 8 3B F2 00 00 00 00 00 00 + 3.947329 1 1 Tx d 8 3B F2 00 00 00 00 00 00 + 3.957332 1 1 Tx d 8 3B F2 00 00 00 00 00 00 + 3.967863 1 1 Tx d 8 3B F2 00 00 00 00 00 00 + 3.977646 1 1 Tx d 8 3B F2 00 00 00 00 00 00 + 3.987683 1 1 Tx d 8 3B F2 00 00 00 00 00 00 + 3.997778 1 1 Tx d 8 3B F2 00 00 00 00 00 00 + 4.007740 1 1 Tx d 8 3B F2 00 00 00 00 00 00 + 4.017677 1 1 Tx d 8 3B F2 00 00 00 00 00 00 + 4.027698 1 1 Tx d 8 3B F2 00 00 00 00 00 00 + 4.037182 1 1 Tx d 8 3B F2 00 00 00 00 00 00 + 4.047251 1 1 Tx d 8 3B F2 00 00 00 00 00 00 + 4.057191 1 1 Tx d 8 3B F2 00 00 00 00 00 00 + 4.067236 1 1 Tx d 8 3B F2 00 00 00 00 00 00 + 4.077244 1 1 Tx d 8 3B F2 00 00 00 00 00 00 + 4.087249 1 1 Tx d 8 3B F2 00 00 00 00 00 00 + 4.097250 1 1 Tx d 8 3B F2 00 00 00 00 00 00 + 4.107249 1 1 Tx d 8 3B F2 00 00 00 00 00 00 + 4.117256 1 1 Tx d 8 3B F2 00 00 00 00 00 00 + 4.127244 1 1 Tx d 8 3B F2 00 00 00 00 00 00 + 4.137750 1 1 Tx d 8 3B F2 00 00 00 00 00 00 + 4.147748 1 1 Tx d 8 3B F2 00 00 00 00 00 00 + 4.157766 1 1 Tx d 8 3B F2 00 00 00 00 00 00 + 4.167754 1 1 Tx d 8 3B F2 00 00 00 00 00 00 + 4.177789 1 1 Tx d 8 3B F2 00 00 00 00 00 00 + 4.187701 1 1 Tx d 8 3B F2 00 00 00 00 00 00 + 4.197695 1 1 Tx d 8 3B F2 00 00 00 00 00 00 + 4.207749 1 1 Tx d 8 3B F2 00 00 00 00 00 00 + 4.217758 1 1 Tx d 8 3B F2 00 00 00 00 00 00 + 4.227743 1 1 Tx d 8 3B F2 00 00 00 00 00 00 + 4.237262 1 1 Tx d 8 3B F2 00 00 00 00 00 00 + 4.247265 1 1 Tx d 8 3B F2 00 00 00 00 00 00 + 4.257261 1 1 Tx d 8 3B F2 00 00 00 00 00 00 + 4.267262 1 1 Tx d 8 3B F2 00 00 00 00 00 00 + 4.277261 1 1 Tx d 8 3B F2 00 00 00 00 00 00 + 4.287254 1 1 Tx d 8 3B F2 00 00 00 00 00 00 + 4.297265 1 1 Tx d 8 3B F2 00 00 00 00 00 00 + 4.307262 1 1 Tx d 8 3B F2 00 00 00 00 00 00 + 4.317264 1 1 Tx d 8 3B F2 00 00 00 00 00 00 + 4.327230 1 1 Tx d 8 3B F2 00 00 00 00 00 00 + 4.337555 1 1 Tx d 8 3B F2 00 00 00 00 00 00 + 4.347559 1 1 Tx d 8 3B F2 00 00 00 00 00 00 + 4.357595 1 1 Tx d 8 3B F2 00 00 00 00 00 00 + 4.367598 1 1 Tx d 8 3B F2 00 00 00 00 00 00 + 4.377618 1 1 Tx d 8 3B F2 00 00 00 00 00 00 + 4.387637 1 1 Tx d 8 3B F2 00 00 00 00 00 00 + 4.397663 1 1 Tx d 8 3B F2 00 00 00 00 00 00 + 4.407699 1 1 Tx d 8 3B F2 00 00 00 00 00 00 + 4.417570 1 1 Tx d 8 3B F2 00 00 00 00 00 00 + 4.427606 1 1 Tx d 8 3B F2 00 00 00 00 00 00 + 4.437108 1 1 Tx d 8 3B F2 00 00 00 00 00 00 + 4.447109 1 1 Tx d 8 3B F2 00 00 00 00 00 00 + 4.457098 1 1 Tx d 8 3B F2 00 00 00 00 00 00 + 4.467206 1 1 Tx d 8 3B F2 00 00 00 00 00 00 + 4.477105 1 1 Tx d 8 3B F2 00 00 00 00 00 00 + 4.487098 1 1 Tx d 8 3B F2 00 00 00 00 00 00 + 4.497147 1 1 Tx d 8 3B F2 00 00 00 00 00 00 + 4.507161 1 1 Tx d 8 3B F2 00 00 00 00 00 00 + 4.517168 1 1 Tx d 8 3B F2 00 00 00 00 00 00 + 4.527169 1 1 Tx d 8 3B F2 00 00 00 00 00 00 + 4.537667 1 1 Tx d 8 3B F2 00 00 00 00 00 00 + 4.547672 1 1 Tx d 8 3B F2 00 00 00 00 00 00 + 4.557667 1 1 Tx d 8 3B F2 00 00 00 00 00 00 + 4.567623 1 1 Tx d 8 3B F2 00 00 00 00 00 00 + 4.577686 1 1 Tx d 8 3B F2 00 00 00 00 00 00 + 4.587669 1 1 Tx d 8 3B F2 00 00 00 00 00 00 + 4.598480 1 1 Tx d 8 3B F2 00 00 00 00 00 00 + 4.607226 1 1 Tx d 8 3B F2 00 00 00 00 00 00 + 4.617329 1 1 Tx d 8 3B F2 00 00 00 00 00 00 + 4.627429 1 1 Tx d 8 3B F2 00 00 00 00 00 00 + 4.637005 1 1 Tx d 8 3B F2 00 00 00 00 00 00 + 4.647007 1 1 Tx d 8 3B F2 00 00 00 00 00 00 + 4.656914 1 1 Tx d 8 3B F2 00 00 00 00 00 00 + 4.667003 1 1 Tx d 8 3B F2 00 00 00 00 00 00 + 4.677002 1 1 Tx d 8 3B F2 00 00 00 00 00 00 + 4.687167 1 1 Tx d 8 3B F2 00 00 00 00 00 00 + 4.697006 1 1 Tx d 8 3B F2 00 00 00 00 00 00 + 4.707000 1 1 Tx d 8 3B F2 00 00 00 00 00 00 + 4.717099 1 1 Tx d 8 3B F2 00 00 00 00 00 00 + 4.727109 1 1 Tx d 8 3B F2 00 00 00 00 00 00 + 4.736791 1 1 Tx d 8 3B F2 00 00 00 00 00 00 + 4.746788 1 1 Tx d 8 3B F2 00 00 00 00 00 00 + 4.756797 1 1 Tx d 8 3B F2 00 00 00 00 00 00 + 4.767741 1 1 Tx d 8 3B F2 00 00 00 00 00 00 + 4.776793 1 1 Tx d 8 3B F2 00 00 00 00 00 00 + 4.787702 1 1 Tx d 8 3B F2 00 00 00 00 00 00 + 4.797699 1 1 Tx d 8 4F EC 00 00 00 00 00 00 + 4.806708 1 1 Tx d 8 4F EC 00 00 00 00 00 00 + 4.816738 1 1 Tx d 8 4F EC 00 00 00 00 00 00 + 4.826745 1 1 Tx d 8 4F EC 00 00 00 00 00 00 + 4.837060 1 1 Tx d 8 4F EC 00 00 00 00 00 00 + 4.846991 1 1 Tx d 8 4F EC 00 00 00 00 00 00 + 4.856989 1 1 Tx d 8 4F EC 00 00 00 00 00 00 + 4.866989 1 1 Tx d 8 4F EC 00 00 00 00 00 00 + 4.877434 1 1 Tx d 8 4F EC 00 00 00 00 00 00 + 4.887485 1 1 Tx d 8 4F EC 00 00 00 00 00 00 + 4.897483 1 1 Tx d 8 4F EC 00 00 00 00 00 00 + 4.907499 1 1 Tx d 8 4F EC 00 00 00 00 00 00 + 4.917584 1 1 Tx d 8 4F EC 00 00 00 00 00 00 + 4.927584 1 1 Tx d 8 4F EC 00 00 00 00 00 00 + 4.937271 1 1 Tx d 8 4F EC 00 00 00 00 00 00 + 4.947274 1 1 Tx d 8 4F EC 00 00 00 00 00 00 + 4.957277 1 1 Tx d 8 4F EC 00 00 00 00 00 00 + 4.967282 1 1 Tx d 8 4F EC 00 00 00 00 00 00 + 4.977271 1 1 Tx d 8 4F EC 00 00 00 00 00 00 + 4.987250 1 1 Tx d 8 4F EC 00 00 00 00 00 00 + 4.997276 1 1 Tx d 8 4F EC 00 00 00 00 00 00 + 5.007274 1 1 Tx d 8 4F EC 00 00 00 00 00 00 + 5.017277 1 1 Tx d 8 4F EC 00 00 00 00 00 00 + 5.027367 1 1 Tx d 8 4F EC 00 00 00 00 00 00 + 5.037060 1 1 Tx d 8 4F EC 00 00 00 00 00 00 + 5.047056 1 1 Tx d 8 4F EC 00 00 00 00 00 00 + 5.057059 1 1 Tx d 8 4F EC 00 00 00 00 00 00 + 5.067056 1 1 Tx d 8 4F EC 00 00 00 00 00 00 + 5.077164 1 1 Tx d 8 4F EC 00 00 00 00 00 00 + 5.087054 1 1 Tx d 8 4F EC 00 00 00 00 00 00 + 5.097080 1 1 Tx d 8 4F EC 00 00 00 00 00 00 + 5.107056 1 1 Tx d 8 4F EC 00 00 00 00 00 00 + 5.117059 1 1 Tx d 8 4F EC 00 00 00 00 00 00 + 5.127173 1 1 Tx d 8 4F EC 00 00 00 00 00 00 + 5.137651 1 1 Tx d 8 4F EC 00 00 00 00 00 00 + 5.147658 1 1 Tx d 8 4F EC 00 00 00 00 00 00 + 5.157651 1 1 Tx d 8 4F EC 00 00 00 00 00 00 + 5.167655 1 1 Tx d 8 4F EC 00 00 00 00 00 00 + 5.177650 1 1 Tx d 8 4F EC 00 00 00 00 00 00 + 5.187652 1 1 Tx d 8 4F EC 00 00 00 00 00 00 + 5.197658 1 1 Tx d 8 4F EC 00 00 00 00 00 00 + 5.207160 1 1 Tx d 8 4F EC 00 00 00 00 00 00 + 5.217165 1 1 Tx d 8 4F EC 00 00 00 00 00 00 + 5.227257 1 1 Tx d 8 4F EC 00 00 00 00 00 00 + 5.236940 1 1 Tx d 8 4F EC 00 00 00 00 00 00 + 5.246949 1 1 Tx d 8 4F EC 00 00 00 00 00 00 + 5.256939 1 1 Tx d 8 4F EC 00 00 00 00 00 00 + 5.266951 1 1 Tx d 8 4F EC 00 00 00 00 00 00 + 5.276940 1 1 Tx d 8 4F EC 00 00 00 00 00 00 + 5.286945 1 1 Tx d 8 4F EC 00 00 00 00 00 00 + 5.296944 1 1 Tx d 8 4F EC 00 00 00 00 00 00 + 5.306949 1 1 Tx d 8 4F EC 00 00 00 00 00 00 + 5.317512 1 1 Tx d 8 4F EC 00 00 00 00 00 00 + 5.327605 1 1 Tx d 8 4F EC 00 00 00 00 00 00 + 5.337046 1 1 Tx d 8 4F EC 00 00 00 00 00 00 + 5.347634 1 1 Tx d 8 4F EC 00 00 00 00 00 00 + 5.357625 1 1 Tx d 8 4F EC 00 00 00 00 00 00 + 5.367664 1 1 Tx d 8 4F EC 00 00 00 00 00 00 + 5.377671 1 1 Tx d 8 4F EC 00 00 00 00 00 00 + 5.387333 1 1 Tx d 8 4F EC 00 00 00 00 00 00 + 5.397197 1 1 Tx d 8 4F EC 00 00 00 00 00 00 + 5.407202 1 1 Tx d 8 4F EC 00 00 00 00 00 00 + 5.417267 1 1 Tx d 8 4F EC 00 00 00 00 00 00 + 5.427508 1 1 Tx d 8 4F EC 00 00 00 00 00 00 + 5.437212 1 1 Tx d 8 4F EC 00 00 00 00 00 00 + 5.447201 1 1 Tx d 8 4F EC 00 00 00 00 00 00 + 5.457206 1 1 Tx d 8 4F EC 00 00 00 00 00 00 + 5.467208 1 1 Tx d 8 4F EC 00 00 00 00 00 00 + 5.477206 1 1 Tx d 8 4F EC 00 00 00 00 00 00 + 5.487201 1 1 Tx d 8 4F EC 00 00 00 00 00 00 + 5.497160 1 1 Tx d 8 4F EC 00 00 00 00 00 00 + 5.507210 1 1 Tx d 8 4F EC 00 00 00 00 00 00 + 5.517212 1 1 Tx d 8 4F EC 00 00 00 00 00 00 + 5.527306 1 1 Tx d 8 4F EC 00 00 00 00 00 00 + 5.537310 1 1 Tx d 8 4F EC 00 00 00 00 00 00 + 5.547314 1 1 Tx d 8 4F EC 00 00 00 00 00 00 + 5.557310 1 1 Tx d 8 4F EC 00 00 00 00 00 00 + 5.567209 1 1 Tx d 8 4F EC 00 00 00 00 00 00 + 5.577200 1 1 Tx d 8 4F EC 00 00 00 00 00 00 + 5.587341 1 1 Tx d 8 4F EC 00 00 00 00 00 00 + 5.597347 1 1 Tx d 8 4F EC 00 00 00 00 00 00 + 5.607306 1 1 Tx d 8 4F EC 00 00 00 00 00 00 + 5.617308 1 1 Tx d 8 4F EC 00 00 00 00 00 00 + 5.627403 1 1 Tx d 8 4F EC 00 00 00 00 00 00 + 5.636923 1 1 Tx d 8 4F EC 00 00 00 00 00 00 + 5.646933 1 1 Tx d 8 4F EC 00 00 00 00 00 00 + 5.656942 1 1 Tx d 8 4F EC 00 00 00 00 00 00 + 5.666925 1 1 Tx d 8 4F EC 00 00 00 00 00 00 + 5.676835 1 1 Tx d 8 4F EC 00 00 00 00 00 00 + 5.686932 1 1 Tx d 8 4F EC 00 00 00 00 00 00 + 5.696921 1 1 Tx d 8 4F EC 00 00 00 00 00 00 + 5.707019 1 1 Tx d 8 4F EC 00 00 00 00 00 00 + 5.716921 1 1 Tx d 8 4F EC 00 00 00 00 00 00 + 5.727024 1 1 Tx d 8 4F EC 00 00 00 00 00 00 + 5.737708 1 1 Tx d 8 4F EC 00 00 00 00 00 00 + 5.747176 1 1 Tx d 8 4F EC 00 00 00 00 00 00 + 5.757428 1 1 Tx d 8 4F EC 00 00 00 00 00 00 + 5.767518 1 1 Tx d 8 4F EC 00 00 00 00 00 00 + 5.777520 1 1 Tx d 8 4F EC 00 00 00 00 00 00 + 5.787508 1 1 Tx d 8 4F EC 00 00 00 00 00 00 + 5.797508 1 1 Tx d 8 4F EC 00 00 00 00 00 00 + 5.807456 1 1 Tx d 8 4F EC 00 00 00 00 00 00 + 5.817454 1 1 Tx d 8 4F EC 00 00 00 00 00 00 + 5.827460 1 1 Tx d 8 4F EC 00 00 00 00 00 00 + 5.837047 1 1 Tx d 8 4F EC 00 00 00 00 00 00 + 5.847145 1 1 Tx d 8 4F EC 00 00 00 00 00 00 + 5.857044 1 1 Tx d 8 4F EC 00 00 00 00 00 00 + 5.866983 1 1 Tx d 8 4F EC 00 00 00 00 00 00 + 5.876843 1 1 Tx d 8 4F EC 00 00 00 00 00 00 + 5.886845 1 1 Tx d 8 4F EC 00 00 00 00 00 00 + 5.896845 1 1 Tx d 8 4F EC 00 00 00 00 00 00 + 5.906845 1 1 Tx d 8 4F EC 00 00 00 00 00 00 + 5.916891 1 1 Tx d 8 4F EC 00 00 00 00 00 00 + 5.927055 1 1 Tx d 8 4F EC 00 00 00 00 00 00 + 5.937494 1 1 Tx d 8 4F EC 00 00 00 00 00 00 + 5.947483 1 1 Tx d 8 4F EC 00 00 00 00 00 00 + 5.957489 1 1 Tx d 8 4F EC 00 00 00 00 00 00 + 5.967479 1 1 Tx d 8 4F EC 00 00 00 00 00 00 + 5.977489 1 1 Tx d 8 4F EC 00 00 00 00 00 00 + 5.987532 1 1 Tx d 8 4F EC 00 00 00 00 00 00 + 5.997636 1 1 Tx d 8 4F EC 00 00 00 00 00 00 + 6.007536 1 1 Tx d 8 4F EC 00 00 00 00 00 00 + 6.017537 1 1 Tx d 8 4F EC 00 00 00 00 00 00 + 6.027625 1 1 Tx d 8 4F EC 00 00 00 00 00 00 + 6.037253 1 1 Tx d 8 4F EC 00 00 00 00 00 00 + 6.047383 1 1 Tx d 8 4F EC 00 00 00 00 00 00 + 6.057377 1 1 Tx d 8 4F EC 00 00 00 00 00 00 + 6.067402 1 1 Tx d 8 4F EC 00 00 00 00 00 00 + 6.077491 1 1 Tx d 8 4F EC 00 00 00 00 00 00 + 6.087386 1 1 Tx d 8 4F EC 00 00 00 00 00 00 + 6.097384 1 1 Tx d 8 4F EC 00 00 00 00 00 00 + 6.107378 1 1 Tx d 8 4F EC 00 00 00 00 00 00 + 6.117344 1 1 Tx d 8 4F EC 00 00 00 00 00 00 + 6.127392 1 1 Tx d 8 4F EC 00 00 00 00 00 00 + 6.137087 1 1 Tx d 8 4F EC 00 00 00 00 00 00 + 6.147085 1 1 Tx d 8 4F EC 00 00 00 00 00 00 + 6.157086 1 1 Tx d 8 4F EC 00 00 00 00 00 00 + 6.167084 1 1 Tx d 8 4F EC 00 00 00 00 00 00 + 6.177097 1 1 Tx d 8 4F EC 00 00 00 00 00 00 + 6.187127 1 1 Tx d 8 4F EC 00 00 00 00 00 00 + 6.197027 1 1 Tx d 8 4F EC 00 00 00 00 00 00 + 6.207019 1 1 Tx d 8 4F EC 00 00 00 00 00 00 + 6.217093 1 1 Tx d 8 4F EC 00 00 00 00 00 00 + 6.227184 1 1 Tx d 8 4F EC 00 00 00 00 00 00 + 6.237387 1 1 Tx d 8 4F EC 00 00 00 00 00 00 + 6.247388 1 1 Tx d 8 4F EC 00 00 00 00 00 00 + 6.257392 1 1 Tx d 8 4F EC 00 00 00 00 00 00 + 6.267384 1 1 Tx d 8 4F EC 00 00 00 00 00 00 + 6.277385 1 1 Tx d 8 4F EC 00 00 00 00 00 00 + 6.287393 1 1 Tx d 8 4F EC 00 00 00 00 00 00 + 6.297384 1 1 Tx d 8 4F EC 00 00 00 00 00 00 + 6.307381 1 1 Tx d 8 4F EC 00 00 00 00 00 00 + 6.317386 1 1 Tx d 8 4F EC 00 00 00 00 00 00 + 6.326846 1 1 Tx d 8 4F EC 00 00 00 00 00 00 + 6.340058 1 1 Tx d 8 4F EC 00 00 00 00 00 00 + 6.346986 1 1 Tx d 8 4F EC 00 00 00 00 00 00 + 6.357732 1 1 Tx d 8 63 E8 00 00 00 00 00 00 + 6.367720 1 1 Tx d 8 63 E8 00 00 00 00 00 00 + 6.376790 1 1 Tx d 8 63 E8 00 00 00 00 00 00 + 6.386784 1 1 Tx d 8 63 E8 00 00 00 00 00 00 + 6.396796 1 1 Tx d 8 63 E8 00 00 00 00 00 00 + 6.406786 1 1 Tx d 8 63 E8 00 00 00 00 00 00 + 6.416779 1 1 Tx d 8 63 E8 00 00 00 00 00 00 + 6.426886 1 1 Tx d 8 63 E8 00 00 00 00 00 00 + 6.437003 1 1 Tx d 8 63 E8 00 00 00 00 00 00 + 6.447097 1 1 Tx d 8 63 E8 00 00 00 00 00 00 + 6.456994 1 1 Tx d 8 63 E8 00 00 00 00 00 00 + 6.466990 1 1 Tx d 8 63 E8 00 00 00 00 00 00 + 6.476991 1 1 Tx d 8 63 E8 00 00 00 00 00 00 + 6.486993 1 1 Tx d 8 63 E8 00 00 00 00 00 00 + 6.496993 1 1 Tx d 8 63 E8 00 00 00 00 00 00 + 6.506954 1 1 Tx d 8 63 E8 00 00 00 00 00 00 + 6.517041 1 1 Tx d 8 63 E8 00 00 00 00 00 00 + 6.527420 1 1 Tx d 8 63 E8 00 00 00 00 00 00 + 6.537178 1 1 Tx d 8 63 E8 00 00 00 00 00 00 + 6.547298 1 1 Tx d 8 63 E8 00 00 00 00 00 00 + 6.557155 1 1 Tx d 8 63 E8 00 00 00 00 00 00 + 6.567156 1 1 Tx d 8 63 E8 00 00 00 00 00 00 + 6.577158 1 1 Tx d 8 63 E8 00 00 00 00 00 00 + 6.587161 1 1 Tx d 8 63 E8 00 00 00 00 00 00 + 6.597153 1 1 Tx d 8 63 E8 00 00 00 00 00 00 + 6.607152 1 1 Tx d 8 63 E8 00 00 00 00 00 00 + 6.617153 1 1 Tx d 8 63 E8 00 00 00 00 00 00 + 6.627254 1 1 Tx d 8 63 E8 00 00 00 00 00 00 + 6.636934 1 1 Tx d 8 63 E8 00 00 00 00 00 00 + 6.646934 1 1 Tx d 8 63 E8 00 00 00 00 00 00 + 6.657443 1 1 Tx d 8 63 E8 00 00 00 00 00 00 + 6.667453 1 1 Tx d 8 63 E8 00 00 00 00 00 00 + 6.677449 1 1 Tx d 8 63 E8 00 00 00 00 00 00 + 6.686930 1 1 Tx d 8 63 E8 00 00 00 00 00 00 + 6.697673 1 1 Tx d 8 63 E8 00 00 00 00 00 00 + 6.707443 1 1 Tx d 8 63 E8 00 00 00 00 00 00 + 6.717537 1 1 Tx d 8 63 E8 00 00 00 00 00 00 + 6.727623 1 1 Tx d 8 63 E8 00 00 00 00 00 00 + 6.737308 1 1 Tx d 8 63 E8 00 00 00 00 00 00 + 6.747308 1 1 Tx d 8 63 E8 00 00 00 00 00 00 + 6.757349 1 1 Tx d 8 63 E8 00 00 00 00 00 00 + 6.767314 1 1 Tx d 8 63 E8 00 00 00 00 00 00 + 6.777305 1 1 Tx d 8 63 E8 00 00 00 00 00 00 + 6.787314 1 1 Tx d 8 63 E8 00 00 00 00 00 00 + 6.797307 1 1 Tx d 8 63 E8 00 00 00 00 00 00 + 6.807509 1 1 Tx d 8 63 E8 00 00 00 00 00 00 + 6.817252 1 1 Tx d 8 63 E8 00 00 00 00 00 00 + 6.827245 1 1 Tx d 8 63 E8 00 00 00 00 00 00 + 6.836739 1 1 Tx d 8 63 E8 00 00 00 00 00 00 + 6.846775 1 1 Tx d 8 63 E8 00 00 00 00 00 00 + 6.856841 1 1 Tx d 8 63 E8 00 00 00 00 00 00 + 6.866786 1 1 Tx d 8 63 E8 00 00 00 00 00 00 + 6.876755 1 1 Tx d 8 63 E8 00 00 00 00 00 00 + 6.886738 1 1 Tx d 8 63 E8 00 00 00 00 00 00 + 6.896776 1 1 Tx d 8 63 E8 00 00 00 00 00 00 + 6.906786 1 1 Tx d 8 63 E8 00 00 00 00 00 00 + 6.916741 1 1 Tx d 8 63 E8 00 00 00 00 00 00 + 6.926799 1 1 Tx d 8 63 E8 00 00 00 00 00 00 + 6.937387 1 1 Tx d 8 63 E8 00 00 00 00 00 00 + 6.947406 1 1 Tx d 8 63 E8 00 00 00 00 00 00 + 6.956963 1 1 Tx d 8 63 E8 00 00 00 00 00 00 + 6.966866 1 1 Tx d 8 63 E8 00 00 00 00 00 00 + 6.977642 1 1 Tx d 8 63 E8 00 00 00 00 00 00 + 6.987767 1 1 Tx d 8 63 E8 00 00 00 00 00 00 + 6.996816 1 1 Tx d 8 63 E8 00 00 00 00 00 00 + 7.006807 1 1 Tx d 8 63 E8 00 00 00 00 00 00 + 7.016806 1 1 Tx d 8 63 E8 00 00 00 00 00 00 + 7.026817 1 1 Tx d 8 63 E8 00 00 00 00 00 00 + 7.037598 1 1 Tx d 8 63 E8 00 00 00 00 00 00 + 7.047589 1 1 Tx d 8 63 E8 00 00 00 00 00 00 + 7.057601 1 1 Tx d 8 63 E8 00 00 00 00 00 00 + 7.067596 1 1 Tx d 8 63 E8 00 00 00 00 00 00 + 7.077589 1 1 Tx d 8 63 E8 00 00 00 00 00 00 + 7.087591 1 1 Tx d 8 63 E8 00 00 00 00 00 00 + 7.096926 1 1 Tx d 8 63 E8 00 00 00 00 00 00 + 7.106927 1 1 Tx d 8 63 E8 00 00 00 00 00 00 + 7.116932 1 1 Tx d 8 63 E8 00 00 00 00 00 00 + 7.127026 1 1 Tx d 8 63 E8 00 00 00 00 00 00 + 7.137701 1 1 Tx d 8 63 E8 00 00 00 00 00 00 + 7.147699 1 1 Tx d 8 63 E8 00 00 00 00 00 00 + 7.163428 1 1 Tx d 8 63 E8 00 00 00 00 00 00 + 7.167226 1 1 Tx d 8 63 E8 00 00 00 00 00 00 + 7.177218 1 1 Tx d 8 63 E8 00 00 00 00 00 00 + 7.187216 1 1 Tx d 8 63 E8 00 00 00 00 00 00 + 7.197217 1 1 Tx d 8 63 E8 00 00 00 00 00 00 + 7.207214 1 1 Tx d 8 63 E8 00 00 00 00 00 00 + 7.217538 1 1 Tx d 8 63 E8 00 00 00 00 00 00 + 7.227534 1 1 Tx d 8 63 E8 00 00 00 00 00 00 + 7.237732 1 1 Tx d 8 63 E8 00 00 00 00 00 00 + 7.247796 1 1 Tx d 8 63 E8 00 00 00 00 00 00 + 7.256757 1 1 Tx d 8 63 E8 00 00 00 00 00 00 + 7.266801 1 1 Tx d 8 63 E8 00 00 00 00 00 00 + 7.276803 1 1 Tx d 8 63 E8 00 00 00 00 00 00 + 7.286840 1 1 Tx d 8 63 E8 00 00 00 00 00 00 + 7.296801 1 1 Tx d 8 63 E8 00 00 00 00 00 00 + 7.306800 1 1 Tx d 8 63 E8 00 00 00 00 00 00 + 7.316843 1 1 Tx d 8 63 E8 00 00 00 00 00 00 + 7.326968 1 1 Tx d 8 63 E8 00 00 00 00 00 00 + 7.337464 1 1 Tx d 8 63 E8 00 00 00 00 00 00 + 7.347468 1 1 Tx d 8 63 E8 00 00 00 00 00 00 + 7.357468 1 1 Tx d 8 63 E8 00 00 00 00 00 00 + 7.367468 1 1 Tx d 8 63 E8 00 00 00 00 00 00 + 7.377464 1 1 Tx d 8 63 E8 00 00 00 00 00 00 + 7.387461 1 1 Tx d 8 63 E8 00 00 00 00 00 00 + 7.397466 1 1 Tx d 8 63 E8 00 00 00 00 00 00 + 7.406965 1 1 Tx d 8 63 E8 00 00 00 00 00 00 + 7.416967 1 1 Tx d 8 63 E8 00 00 00 00 00 00 + 7.426986 1 1 Tx d 8 63 E8 00 00 00 00 00 00 + 7.437477 1 1 Tx d 8 63 E8 00 00 00 00 00 00 + 7.447538 1 1 Tx d 8 63 E8 00 00 00 00 00 00 + 7.457535 1 1 Tx d 8 63 E8 00 00 00 00 00 00 + 7.467532 1 1 Tx d 8 63 E8 00 00 00 00 00 00 + 7.477538 1 1 Tx d 8 63 E8 00 00 00 00 00 00 + 7.487542 1 1 Tx d 8 63 E8 00 00 00 00 00 00 + 7.497559 1 1 Tx d 8 63 E8 00 00 00 00 00 00 + 7.507534 1 1 Tx d 8 63 E8 00 00 00 00 00 00 + 7.517543 1 1 Tx d 8 63 E8 00 00 00 00 00 00 + 7.527631 1 1 Tx d 8 63 E8 00 00 00 00 00 00 + 7.537751 1 1 Tx d 8 63 E8 00 00 00 00 00 00 + 7.546766 1 1 Tx d 8 63 E8 00 00 00 00 00 00 + 7.556752 1 1 Tx d 8 63 E8 00 00 00 00 00 00 + 7.567071 1 1 Tx d 8 63 E8 00 00 00 00 00 00 + 7.577062 1 1 Tx d 8 63 E8 00 00 00 00 00 00 + 7.587059 1 1 Tx d 8 63 E8 00 00 00 00 00 00 + 7.597058 1 1 Tx d 8 63 E8 00 00 00 00 00 00 + 7.607057 1 1 Tx d 8 63 E8 00 00 00 00 00 00 + 7.617058 1 1 Tx d 8 63 E8 00 00 00 00 00 00 + 7.627154 1 1 Tx d 8 63 E8 00 00 00 00 00 00 + 7.636844 1 1 Tx d 8 63 E8 00 00 00 00 00 00 + 7.647287 1 1 Tx d 8 63 E8 00 00 00 00 00 00 + 7.657391 1 1 Tx d 8 63 E8 00 00 00 00 00 00 + 7.667286 1 1 Tx d 8 63 E8 00 00 00 00 00 00 + 7.677284 1 1 Tx d 8 63 E8 00 00 00 00 00 00 + 7.687283 1 1 Tx d 8 63 E8 00 00 00 00 00 00 + 7.697356 1 1 Tx d 8 63 E8 00 00 00 00 00 00 + 7.706799 1 1 Tx d 8 63 E8 00 00 00 00 00 00 + 7.716888 1 1 Tx d 8 63 E8 00 00 00 00 00 00 + 7.726972 1 1 Tx d 8 63 E8 00 00 00 00 00 00 + 7.737512 1 1 Tx d 8 63 E8 00 00 00 00 00 00 + 7.747548 1 1 Tx d 8 63 E8 00 00 00 00 00 00 + 7.756982 1 1 Tx d 8 63 E8 00 00 00 00 00 00 + 7.767079 1 1 Tx d 8 63 E8 00 00 00 00 00 00 + 7.777087 1 1 Tx d 8 63 E8 00 00 00 00 00 00 + 7.787120 1 1 Tx d 8 63 E8 00 00 00 00 00 00 + 7.797079 1 1 Tx d 8 63 E8 00 00 00 00 00 00 + 7.807080 1 1 Tx d 8 63 E8 00 00 00 00 00 00 + 7.817087 1 1 Tx d 8 63 E8 00 00 00 00 00 00 + 7.827178 1 1 Tx d 8 63 E8 00 00 00 00 00 00 + 7.837815 1 1 Tx d 8 63 E8 00 00 00 00 00 00 + 7.846812 1 1 Tx d 8 63 E8 00 00 00 00 00 00 + 7.856829 1 1 Tx d 8 63 E8 00 00 00 00 00 00 + 7.866913 1 1 Tx d 8 63 E8 00 00 00 00 00 00 + 7.876809 1 1 Tx d 8 63 E8 00 00 00 00 00 00 + 7.886761 1 1 Tx d 8 63 E8 00 00 00 00 00 00 + 7.896810 1 1 Tx d 8 63 E8 00 00 00 00 00 00 + 7.906808 1 1 Tx d 8 63 E8 00 00 00 00 00 00 + 7.916806 1 1 Tx d 8 63 E8 00 00 00 00 00 00 + 7.928512 1 1 Tx d 8 77 E2 00 00 00 00 00 00 + 7.936787 1 1 Tx d 8 77 E2 00 00 00 00 00 00 + 7.946875 1 1 Tx d 8 77 E2 00 00 00 00 00 00 + 7.956874 1 1 Tx d 8 77 E2 00 00 00 00 00 00 + 7.966896 1 1 Tx d 8 77 E2 00 00 00 00 00 00 + 7.976821 1 1 Tx d 8 77 E2 00 00 00 00 00 00 + 7.986868 1 1 Tx d 8 77 E2 00 00 00 00 00 00 + 7.996875 1 1 Tx d 8 77 E2 00 00 00 00 00 00 + 8.006775 1 1 Tx d 8 77 E2 00 00 00 00 00 00 + 8.016768 1 1 Tx d 8 77 E2 00 00 00 00 00 00 + 8.026768 1 1 Tx d 8 77 E2 00 00 00 00 00 00 + 8.036887 1 1 Tx d 8 77 E2 00 00 00 00 00 00 + 8.047479 1 1 Tx d 8 77 E2 00 00 00 00 00 00 + 8.057429 1 1 Tx d 8 77 E2 00 00 00 00 00 00 + 8.067345 1 1 Tx d 8 77 E2 00 00 00 00 00 00 + 8.077388 1 1 Tx d 8 77 E2 00 00 00 00 00 00 + 8.087372 1 1 Tx d 8 77 E2 00 00 00 00 00 00 + 8.097361 1 1 Tx d 8 77 E2 00 00 00 00 00 00 + 8.107369 1 1 Tx d 8 77 E2 00 00 00 00 00 00 + 8.117436 1 1 Tx d 8 77 E2 00 00 00 00 00 00 + 8.127518 1 1 Tx d 8 77 E2 00 00 00 00 00 00 + 8.137127 1 1 Tx d 8 77 E2 00 00 00 00 00 00 + 8.147214 1 1 Tx d 8 77 E2 00 00 00 00 00 00 + 8.157220 1 1 Tx d 8 77 E2 00 00 00 00 00 00 + 8.167218 1 1 Tx d 8 77 E2 00 00 00 00 00 00 + 8.177218 1 1 Tx d 8 77 E2 00 00 00 00 00 00 + 8.187699 1 1 Tx d 8 77 E2 00 00 00 00 00 00 + 8.197697 1 1 Tx d 8 77 E2 00 00 00 00 00 00 + 8.207691 1 1 Tx d 8 77 E2 00 00 00 00 00 00 + 8.217699 1 1 Tx d 8 77 E2 00 00 00 00 00 00 + 8.226788 1 1 Tx d 8 77 E2 00 00 00 00 00 00 + 8.237351 1 1 Tx d 8 77 E2 00 00 00 00 00 00 + 8.247474 1 1 Tx d 8 77 E2 00 00 00 00 00 00 + 8.257429 1 1 Tx d 8 77 E2 00 00 00 00 00 00 + 8.267428 1 1 Tx d 8 77 E2 00 00 00 00 00 00 + 8.277442 1 1 Tx d 8 77 E2 00 00 00 00 00 00 + 8.287438 1 1 Tx d 8 77 E2 00 00 00 00 00 00 + 8.296867 1 1 Tx d 8 77 E2 00 00 00 00 00 00 + 8.306867 1 1 Tx d 8 77 E2 00 00 00 00 00 00 + 8.316819 1 1 Tx d 8 77 E2 00 00 00 00 00 00 + 8.326960 1 1 Tx d 8 77 E2 00 00 00 00 00 00 + 8.337556 1 1 Tx d 8 77 E2 00 00 00 00 00 00 + 8.347638 1 1 Tx d 8 77 E2 00 00 00 00 00 00 + 8.362264 1 1 Tx d 8 77 E2 00 00 00 00 00 00 + 8.367639 1 1 Tx d 8 77 E2 00 00 00 00 00 00 + 8.377649 1 1 Tx d 8 77 E2 00 00 00 00 00 00 + 8.387640 1 1 Tx d 8 77 E2 00 00 00 00 00 00 + 8.397645 1 1 Tx d 8 77 E2 00 00 00 00 00 00 + 8.407595 1 1 Tx d 8 77 E2 00 00 00 00 00 00 + 8.417577 1 1 Tx d 8 77 E2 00 00 00 00 00 00 + 8.427583 1 1 Tx d 8 77 E2 00 00 00 00 00 00 + 8.437588 1 1 Tx d 8 77 E2 00 00 00 00 00 00 + 8.447143 1 1 Tx d 8 77 E2 00 00 00 00 00 00 + 8.457151 1 1 Tx d 8 77 E2 00 00 00 00 00 00 + 8.467152 1 1 Tx d 8 77 E2 00 00 00 00 00 00 + 8.477155 1 1 Tx d 8 77 E2 00 00 00 00 00 00 + 8.487160 1 1 Tx d 8 77 E2 00 00 00 00 00 00 + 8.497158 1 1 Tx d 8 77 E2 00 00 00 00 00 00 + 8.507155 1 1 Tx d 8 77 E2 00 00 00 00 00 00 + 8.517553 1 1 Tx d 8 77 E2 00 00 00 00 00 00 + 8.527640 1 1 Tx d 8 77 E2 00 00 00 00 00 00 + 8.537652 1 1 Tx d 8 77 E2 00 00 00 00 00 00 + 8.547333 1 1 Tx d 8 77 E2 00 00 00 00 00 00 + 8.557336 1 1 Tx d 8 77 E2 00 00 00 00 00 00 + 8.567305 1 1 Tx d 8 77 E2 00 00 00 00 00 00 + 8.577343 1 1 Tx d 8 77 E2 00 00 00 00 00 00 + 8.587401 1 1 Tx d 8 77 E2 00 00 00 00 00 00 + 8.597344 1 1 Tx d 8 77 E2 00 00 00 00 00 00 + 8.607349 1 1 Tx d 8 77 E2 00 00 00 00 00 00 + 8.617338 1 1 Tx d 8 77 E2 00 00 00 00 00 00 + 8.627442 1 1 Tx d 8 77 E2 00 00 00 00 00 00 + 8.637967 1 1 Tx d 8 77 E2 00 00 00 00 00 00 + 8.647040 1 1 Tx d 8 77 E2 00 00 00 00 00 00 + 8.656978 1 1 Tx d 8 77 E2 00 00 00 00 00 00 + 8.667040 1 1 Tx d 8 77 E2 00 00 00 00 00 00 + 8.677040 1 1 Tx d 8 77 E2 00 00 00 00 00 00 + 8.687035 1 1 Tx d 8 77 E2 00 00 00 00 00 00 + 8.697047 1 1 Tx d 8 77 E2 00 00 00 00 00 00 + 8.707035 1 1 Tx d 8 77 E2 00 00 00 00 00 00 + 8.716995 1 1 Tx d 8 77 E2 00 00 00 00 00 00 + 8.727059 1 1 Tx d 8 77 E2 00 00 00 00 00 00 + 8.736777 1 1 Tx d 8 77 E2 00 00 00 00 00 00 + 8.747468 1 1 Tx d 8 77 E2 00 00 00 00 00 00 + 8.757359 1 1 Tx d 8 77 E2 00 00 00 00 00 00 + 8.767456 1 1 Tx d 8 77 E2 00 00 00 00 00 00 + 8.777457 1 1 Tx d 8 77 E2 00 00 00 00 00 00 + 8.787459 1 1 Tx d 8 77 E2 00 00 00 00 00 00 + 8.797459 1 1 Tx d 8 77 E2 00 00 00 00 00 00 + 8.807463 1 1 Tx d 8 77 E2 00 00 00 00 00 00 + 8.817456 1 1 Tx d 8 77 E2 00 00 00 00 00 00 + 8.827512 1 1 Tx d 8 77 E2 00 00 00 00 00 00 + 8.837466 1 1 Tx d 8 77 E2 00 00 00 00 00 00 + 8.847473 1 1 Tx d 8 77 E2 00 00 00 00 00 00 + 8.857480 1 1 Tx d 8 77 E2 00 00 00 00 00 00 + 8.867165 1 1 Tx d 8 77 E2 00 00 00 00 00 00 + 8.876822 1 1 Tx d 8 77 E2 00 00 00 00 00 00 + 8.886816 1 1 Tx d 8 77 E2 00 00 00 00 00 00 + 8.896807 1 1 Tx d 8 77 E2 00 00 00 00 00 00 + 8.906841 1 1 Tx d 8 77 E2 00 00 00 00 00 00 + 8.916807 1 1 Tx d 8 77 E2 00 00 00 00 00 00 + 8.926850 1 1 Tx d 8 77 E2 00 00 00 00 00 00 + 8.936835 1 1 Tx d 8 77 E2 00 00 00 00 00 00 + 8.947388 1 1 Tx d 8 77 E2 00 00 00 00 00 00 + 8.957397 1 1 Tx d 8 77 E2 00 00 00 00 00 00 + 8.967396 1 1 Tx d 8 77 E2 00 00 00 00 00 00 + 8.977391 1 1 Tx d 8 77 E2 00 00 00 00 00 00 + 8.987402 1 1 Tx d 8 77 E2 00 00 00 00 00 00 + 8.997391 1 1 Tx d 8 77 E2 00 00 00 00 00 00 + 9.007390 1 1 Tx d 8 77 E2 00 00 00 00 00 00 + 9.017359 1 1 Tx d 8 77 E2 00 00 00 00 00 00 + 9.027487 1 1 Tx d 8 77 E2 00 00 00 00 00 00 + 9.037506 1 1 Tx d 8 77 E2 00 00 00 00 00 00 + 9.047194 1 1 Tx d 8 77 E2 00 00 00 00 00 00 + 9.057200 1 1 Tx d 8 77 E2 00 00 00 00 00 00 + 9.067143 1 1 Tx d 8 77 E2 00 00 00 00 00 00 + 9.077194 1 1 Tx d 8 77 E2 00 00 00 00 00 00 + 9.087194 1 1 Tx d 8 77 E2 00 00 00 00 00 00 + 9.097201 1 1 Tx d 8 77 E2 00 00 00 00 00 00 + 9.107198 1 1 Tx d 8 77 E2 00 00 00 00 00 00 + 9.117197 1 1 Tx d 8 77 E2 00 00 00 00 00 00 + 9.127294 1 1 Tx d 8 77 E2 00 00 00 00 00 00 + 9.137294 1 1 Tx d 8 77 E2 00 00 00 00 00 00 + 9.146974 1 1 Tx d 8 77 E2 00 00 00 00 00 00 + 9.156980 1 1 Tx d 8 77 E2 00 00 00 00 00 00 + 9.166975 1 1 Tx d 8 77 E2 00 00 00 00 00 00 + 9.177497 1 1 Tx d 8 77 E2 00 00 00 00 00 00 + 9.187506 1 1 Tx d 8 77 E2 00 00 00 00 00 00 + 9.197494 1 1 Tx d 8 77 E2 00 00 00 00 00 00 + 9.207500 1 1 Tx d 8 77 E2 00 00 00 00 00 00 + 9.217497 1 1 Tx d 8 77 E2 00 00 00 00 00 00 + 9.227594 1 1 Tx d 8 77 E2 00 00 00 00 00 00 + 9.237596 1 1 Tx d 8 77 E2 00 00 00 00 00 00 + 9.247298 1 1 Tx d 8 77 E2 00 00 00 00 00 00 + 9.257299 1 1 Tx d 8 77 E2 00 00 00 00 00 00 + 9.267299 1 1 Tx d 8 77 E2 00 00 00 00 00 00 + 9.277117 1 1 Tx d 8 77 E2 00 00 00 00 00 00 + 9.287733 1 1 Tx d 8 77 E2 00 00 00 00 00 00 + 9.297737 1 1 Tx d 8 77 E2 00 00 00 00 00 00 + 9.307307 1 1 Tx d 8 77 E2 00 00 00 00 00 00 + 9.317305 1 1 Tx d 8 77 E2 00 00 00 00 00 00 + 9.327306 1 1 Tx d 8 77 E2 00 00 00 00 00 00 + 9.337353 1 1 Tx d 8 77 E2 00 00 00 00 00 00 + 9.346948 1 1 Tx d 8 77 E2 00 00 00 00 00 00 + 9.356934 1 1 Tx d 8 77 E2 00 00 00 00 00 00 + 9.366893 1 1 Tx d 8 77 E2 00 00 00 00 00 00 + 9.376838 1 1 Tx d 8 77 E2 00 00 00 00 00 00 + 9.386869 1 1 Tx d 8 77 E2 00 00 00 00 00 00 + 9.397595 1 1 Tx d 8 77 E2 00 00 00 00 00 00 + 9.407273 1 1 Tx d 8 77 E2 00 00 00 00 00 00 + 9.417208 1 1 Tx d 8 77 E2 00 00 00 00 00 00 + 9.427171 1 1 Tx d 8 77 E2 00 00 00 00 00 00 + 9.437209 1 1 Tx d 8 77 E2 00 00 00 00 00 00 + 9.447743 1 1 Tx d 8 77 E2 00 00 00 00 00 00 + 9.457743 1 1 Tx d 8 77 E2 00 00 00 00 00 00 + 9.466805 1 1 Tx d 8 77 E2 00 00 00 00 00 00 + 9.477798 1 1 Tx d 8 8B DE 00 00 00 00 00 00 + 9.486879 1 1 Tx d 8 8B DE 00 00 00 00 00 00 + 9.496806 1 1 Tx d 8 8B DE 00 00 00 00 00 00 + 9.507300 1 1 Tx d 8 8B DE 00 00 00 00 00 00 + 9.517277 1 1 Tx d 8 8B DE 00 00 00 00 00 00 + 9.527125 1 1 Tx d 8 8B DE 00 00 00 00 00 00 + 9.537228 1 1 Tx d 8 8B DE 00 00 00 00 00 00 + 9.546937 1 1 Tx d 8 8B DE 00 00 00 00 00 00 + 9.556911 1 1 Tx d 8 8B DE 00 00 00 00 00 00 + 9.566911 1 1 Tx d 8 8B DE 00 00 00 00 00 00 + 9.576817 1 1 Tx d 8 8B DE 00 00 00 00 00 00 + 9.586808 1 1 Tx d 8 8B DE 00 00 00 00 00 00 + 9.596825 1 1 Tx d 8 8B DE 00 00 00 00 00 00 + 9.606851 1 1 Tx d 8 8B DE 00 00 00 00 00 00 + 9.616812 1 1 Tx d 8 8B DE 00 00 00 00 00 00 + 9.626890 1 1 Tx d 8 8B DE 00 00 00 00 00 00 + 9.636864 1 1 Tx d 8 8B DE 00 00 00 00 00 00 + 9.647400 1 1 Tx d 8 8B DE 00 00 00 00 00 00 + 9.657406 1 1 Tx d 8 8B DE 00 00 00 00 00 00 + 9.667458 1 1 Tx d 8 8B DE 00 00 00 00 00 00 + 9.677654 1 1 Tx d 8 8B DE 00 00 00 00 00 00 + 9.687648 1 1 Tx d 8 8B DE 00 00 00 00 00 00 + 9.697165 1 1 Tx d 8 8B DE 00 00 00 00 00 00 + 9.707679 1 1 Tx d 8 8B DE 00 00 00 00 00 00 + 9.717683 1 1 Tx d 8 8B DE 00 00 00 00 00 00 + 9.726779 1 1 Tx d 8 8B DE 00 00 00 00 00 00 + 9.736808 1 1 Tx d 8 8B DE 00 00 00 00 00 00 + 9.747508 1 1 Tx d 8 8B DE 00 00 00 00 00 00 + 9.757501 1 1 Tx d 8 8B DE 00 00 00 00 00 00 + 9.767511 1 1 Tx d 8 8B DE 00 00 00 00 00 00 + 9.777510 1 1 Tx d 8 8B DE 00 00 00 00 00 00 + 9.787500 1 1 Tx d 8 8B DE 00 00 00 00 00 00 + 9.797514 1 1 Tx d 8 8B DE 00 00 00 00 00 00 + 9.807496 1 1 Tx d 8 8B DE 00 00 00 00 00 00 + 9.817501 1 1 Tx d 8 8B DE 00 00 00 00 00 00 + 9.827694 1 1 Tx d 8 8B DE 00 00 00 00 00 00 + 9.837605 1 1 Tx d 8 8B DE 00 00 00 00 00 00 + 9.847772 1 1 Tx d 8 8B DE 00 00 00 00 00 00 + 9.856776 1 1 Tx d 8 8B DE 00 00 00 00 00 00 + 9.867028 1 1 Tx d 8 8B DE 00 00 00 00 00 00 + 9.881664 1 1 Tx d 8 8B DE 00 00 00 00 00 00 + 9.887182 1 1 Tx d 8 8B DE 00 00 00 00 00 00 + 9.897166 1 1 Tx d 8 8B DE 00 00 00 00 00 00 + 9.907175 1 1 Tx d 8 8B DE 00 00 00 00 00 00 + 9.917176 1 1 Tx d 8 8B DE 00 00 00 00 00 00 + 9.927267 1 1 Tx d 8 8B DE 00 00 00 00 00 00 + 9.937266 1 1 Tx d 8 8B DE 00 00 00 00 00 00 + 9.946953 1 1 Tx d 8 8B DE 00 00 00 00 00 00 + 9.956863 1 1 Tx d 8 8B DE 00 00 00 00 00 00 + 9.966949 1 1 Tx d 8 8B DE 00 00 00 00 00 00 + 9.976960 1 1 Tx d 8 8B DE 00 00 00 00 00 00 + 9.987220 1 1 Tx d 8 8B DE 00 00 00 00 00 00 + 9.997315 1 1 Tx d 8 8B DE 00 00 00 00 00 00 + 10.007314 1 1 Tx d 8 8B DE 00 00 00 00 00 00 + 10.017321 1 1 Tx d 8 8B DE 00 00 00 00 00 00 + 10.027404 1 1 Tx d 8 8B DE 00 00 00 00 00 00 + 10.037709 1 1 Tx d 8 8B DE 00 00 00 00 00 00 + 10.047278 1 1 Tx d 8 8B DE 00 00 00 00 00 00 + 10.057241 1 1 Tx d 8 8B DE 00 00 00 00 00 00 + 10.067240 1 1 Tx d 8 8B DE 00 00 00 00 00 00 + 10.077253 1 1 Tx d 8 8B DE 00 00 00 00 00 00 + 10.087241 1 1 Tx d 8 8B DE 00 00 00 00 00 00 + 10.097240 1 1 Tx d 8 8B DE 00 00 00 00 00 00 + 10.107242 1 1 Tx d 8 8B DE 00 00 00 00 00 00 + 10.117238 1 1 Tx d 8 8B DE 00 00 00 00 00 00 + 10.127329 1 1 Tx d 8 8B DE 00 00 00 00 00 00 + 10.137331 1 1 Tx d 8 8B DE 00 00 00 00 00 00 + 10.147047 1 1 Tx d 8 8B DE 00 00 00 00 00 00 + 10.157037 1 1 Tx d 8 8B DE 00 00 00 00 00 00 + 10.167016 1 1 Tx d 8 8B DE 00 00 00 00 00 00 + 10.177432 1 1 Tx d 8 8B DE 00 00 00 00 00 00 + 10.187427 1 1 Tx d 8 8B DE 00 00 00 00 00 00 + 10.197427 1 1 Tx d 8 8B DE 00 00 00 00 00 00 + 10.207421 1 1 Tx d 8 8B DE 00 00 00 00 00 00 + 10.217426 1 1 Tx d 8 8B DE 00 00 00 00 00 00 + 10.227521 1 1 Tx d 8 8B DE 00 00 00 00 00 00 + 10.237568 1 1 Tx d 8 8B DE 00 00 00 00 00 00 + 10.247218 1 1 Tx d 8 8B DE 00 00 00 00 00 00 + 10.257220 1 1 Tx d 8 8B DE 00 00 00 00 00 00 + 10.267239 1 1 Tx d 8 8B DE 00 00 00 00 00 00 + 10.277605 1 1 Tx d 8 8B DE 00 00 00 00 00 00 + 10.286909 1 1 Tx d 8 8B DE 00 00 00 00 00 00 + 10.296871 1 1 Tx d 8 8B DE 00 00 00 00 00 00 + 10.306909 1 1 Tx d 8 8B DE 00 00 00 00 00 00 + 10.316912 1 1 Tx d 8 8B DE 00 00 00 00 00 00 + 10.326999 1 1 Tx d 8 8B DE 00 00 00 00 00 00 + 10.337718 1 1 Tx d 8 8B DE 00 00 00 00 00 00 + 10.347416 1 1 Tx d 8 8B DE 00 00 00 00 00 00 + 10.357418 1 1 Tx d 8 8B DE 00 00 00 00 00 00 + 10.367317 1 1 Tx d 8 8B DE 00 00 00 00 00 00 + 10.377314 1 1 Tx d 8 8B DE 00 00 00 00 00 00 + 10.387510 1 1 Tx d 8 8B DE 00 00 00 00 00 00 + 10.397317 1 1 Tx d 8 8B DE 00 00 00 00 00 00 + 10.407429 1 1 Tx d 8 8B DE 00 00 00 00 00 00 + 10.417419 1 1 Tx d 8 8B DE 00 00 00 00 00 00 + 10.427512 1 1 Tx d 8 8B DE 00 00 00 00 00 00 + 10.437513 1 1 Tx d 8 8B DE 00 00 00 00 00 00 + 10.447425 1 1 Tx d 8 8B DE 00 00 00 00 00 00 + 10.457418 1 1 Tx d 8 8B DE 00 00 00 00 00 00 + 10.467427 1 1 Tx d 8 8B DE 00 00 00 00 00 00 + 10.477418 1 1 Tx d 8 8B DE 00 00 00 00 00 00 + 10.487425 1 1 Tx d 8 8B DE 00 00 00 00 00 00 + 10.497424 1 1 Tx d 8 8B DE 00 00 00 00 00 00 + 10.507420 1 1 Tx d 8 8B DE 00 00 00 00 00 00 + 10.517443 1 1 Tx d 8 8B DE 00 00 00 00 00 00 + 10.527045 1 1 Tx d 8 8B DE 00 00 00 00 00 00 + 10.537048 1 1 Tx d 8 8B DE 00 00 00 00 00 00 + 10.547720 1 1 Tx d 8 8B DE 00 00 00 00 00 00 + 10.556964 1 1 Tx d 8 8B DE 00 00 00 00 00 00 + 10.566962 1 1 Tx d 8 8B DE 00 00 00 00 00 00 + 10.576969 1 1 Tx d 8 8B DE 00 00 00 00 00 00 + 10.586879 1 1 Tx d 8 8B DE 00 00 00 00 00 00 + 10.596906 1 1 Tx d 8 8B DE 00 00 00 00 00 00 + 10.606904 1 1 Tx d 8 8B DE 00 00 00 00 00 00 + 10.616963 1 1 Tx d 8 8B DE 00 00 00 00 00 00 + 10.627076 1 1 Tx d 8 8B DE 00 00 00 00 00 00 + 10.637058 1 1 Tx d 8 8B DE 00 00 00 00 00 00 + 10.647659 1 1 Tx d 8 8B DE 00 00 00 00 00 00 + 10.657656 1 1 Tx d 8 8B DE 00 00 00 00 00 00 + 10.667652 1 1 Tx d 8 8B DE 00 00 00 00 00 00 + 10.677656 1 1 Tx d 8 8B DE 00 00 00 00 00 00 + 10.687691 1 1 Tx d 8 8B DE 00 00 00 00 00 00 + 10.697660 1 1 Tx d 8 8B DE 00 00 00 00 00 00 + 10.707657 1 1 Tx d 8 8B DE 00 00 00 00 00 00 + 10.717655 1 1 Tx d 8 8B DE 00 00 00 00 00 00 + 10.727750 1 1 Tx d 8 8B DE 00 00 00 00 00 00 + 10.736750 1 1 Tx d 8 8B DE 00 00 00 00 00 00 + 10.747442 1 1 Tx d 8 8B DE 00 00 00 00 00 00 + 10.757444 1 1 Tx d 8 8B DE 00 00 00 00 00 00 + 10.767399 1 1 Tx d 8 8B DE 00 00 00 00 00 00 + 10.777489 1 1 Tx d 8 8B DE 00 00 00 00 00 00 + 10.786965 1 1 Tx d 8 8B DE 00 00 00 00 00 00 + 10.797669 1 1 Tx d 8 8B DE 00 00 00 00 00 00 + 10.807662 1 1 Tx d 8 8B DE 00 00 00 00 00 00 + 10.817662 1 1 Tx d 8 8B DE 00 00 00 00 00 00 + 10.827824 1 1 Tx d 8 8B DE 00 00 00 00 00 00 + 10.837720 1 1 Tx d 8 8B DE 00 00 00 00 00 00 + 10.847422 1 1 Tx d 8 8B DE 00 00 00 00 00 00 + 10.857353 1 1 Tx d 8 8B DE 00 00 00 00 00 00 + 10.867348 1 1 Tx d 8 8B DE 00 00 00 00 00 00 + 10.877348 1 1 Tx d 8 8B DE 00 00 00 00 00 00 + 10.887348 1 1 Tx d 8 8B DE 00 00 00 00 00 00 + 10.897363 1 1 Tx d 8 8B DE 00 00 00 00 00 00 + 10.907452 1 1 Tx d 8 8B DE 00 00 00 00 00 00 + 10.917416 1 1 Tx d 8 8B DE 00 00 00 00 00 00 + 10.927505 1 1 Tx d 8 8B DE 00 00 00 00 00 00 + 10.937507 1 1 Tx d 8 8B DE 00 00 00 00 00 00 + 10.947214 1 1 Tx d 8 8B DE 00 00 00 00 00 00 + 10.957534 1 1 Tx d 8 8B DE 00 00 00 00 00 00 + 10.967498 1 1 Tx d 8 8B DE 00 00 00 00 00 00 + 10.977494 1 1 Tx d 8 8B DE 00 00 00 00 00 00 + 10.987491 1 1 Tx d 8 8B DE 00 00 00 00 00 00 + 10.997500 1 1 Tx d 8 8B DE 00 00 00 00 00 00 + 11.007505 1 1 Tx d 8 8B DE 00 00 00 00 00 00 + 11.017516 1 1 Tx d 8 8B DE 00 00 00 00 00 00 + 11.026792 1 1 Tx d 8 8B DE 00 00 00 00 00 00 + 11.036793 1 1 Tx d 8 9F D8 00 00 00 00 00 00 + 11.047340 1 1 Tx d 8 9F D8 00 00 00 00 00 00 + 11.057459 1 1 Tx d 8 9F D8 00 00 00 00 00 00 + 11.067360 1 1 Tx d 8 9F D8 00 00 00 00 00 00 + 11.077451 1 1 Tx d 8 9F D8 00 00 00 00 00 00 + 11.087597 1 1 Tx d 8 9F D8 00 00 00 00 00 00 + 11.097581 1 1 Tx d 8 9F D8 00 00 00 00 00 00 + 11.107451 1 1 Tx d 8 9F D8 00 00 00 00 00 00 + 11.117457 1 1 Tx d 8 9F D8 00 00 00 00 00 00 + 11.127354 1 1 Tx d 8 9F D8 00 00 00 00 00 00 + 11.137724 1 1 Tx d 8 9F D8 00 00 00 00 00 00 + 11.147221 1 1 Tx d 8 9F D8 00 00 00 00 00 00 + 11.157228 1 1 Tx d 8 9F D8 00 00 00 00 00 00 + 11.167205 1 1 Tx d 8 9F D8 00 00 00 00 00 00 + 11.177207 1 1 Tx d 8 9F D8 00 00 00 00 00 00 + 11.187211 1 1 Tx d 8 9F D8 00 00 00 00 00 00 + 11.197209 1 1 Tx d 8 9F D8 00 00 00 00 00 00 + 11.207204 1 1 Tx d 8 9F D8 00 00 00 00 00 00 + 11.217207 1 1 Tx d 8 9F D8 00 00 00 00 00 00 + 11.227208 1 1 Tx d 8 9F D8 00 00 00 00 00 00 + 11.237214 1 1 Tx d 8 9F D8 00 00 00 00 00 00 + 11.247718 1 1 Tx d 8 9F D8 00 00 00 00 00 00 + 11.257785 1 1 Tx d 8 9F D8 00 00 00 00 00 00 + 11.267769 1 1 Tx d 8 9F D8 00 00 00 00 00 00 + 11.277772 1 1 Tx d 8 9F D8 00 00 00 00 00 00 + 11.287776 1 1 Tx d 8 9F D8 00 00 00 00 00 00 + 11.297770 1 1 Tx d 8 9F D8 00 00 00 00 00 00 + 11.307771 1 1 Tx d 8 9F D8 00 00 00 00 00 00 + 11.317770 1 1 Tx d 8 9F D8 00 00 00 00 00 00 + 11.327726 1 1 Tx d 8 9F D8 00 00 00 00 00 00 + 11.337771 1 1 Tx d 8 9F D8 00 00 00 00 00 00 + 11.347282 1 1 Tx d 8 9F D8 00 00 00 00 00 00 + 11.357287 1 1 Tx d 8 9F D8 00 00 00 00 00 00 + 11.367283 1 1 Tx d 8 9F D8 00 00 00 00 00 00 + 11.377290 1 1 Tx d 8 9F D8 00 00 00 00 00 00 + 11.387288 1 1 Tx d 8 9F D8 00 00 00 00 00 00 + 11.397191 1 1 Tx d 8 9F D8 00 00 00 00 00 00 + 11.407496 1 1 Tx d 8 9F D8 00 00 00 00 00 00 + 11.417784 1 1 Tx d 8 9F D8 00 00 00 00 00 00 + 11.427890 1 1 Tx d 8 9F D8 00 00 00 00 00 00 + 11.436776 1 1 Tx d 8 9F D8 00 00 00 00 00 00 + 11.447478 1 1 Tx d 8 9F D8 00 00 00 00 00 00 + 11.457477 1 1 Tx d 8 9F D8 00 00 00 00 00 00 + 11.467470 1 1 Tx d 8 9F D8 00 00 00 00 00 00 + 11.477483 1 1 Tx d 8 9F D8 00 00 00 00 00 00 + 11.487478 1 1 Tx d 8 9F D8 00 00 00 00 00 00 + 11.497472 1 1 Tx d 8 9F D8 00 00 00 00 00 00 + 11.507758 1 1 Tx d 8 9F D8 00 00 00 00 00 00 + 11.517750 1 1 Tx d 8 9F D8 00 00 00 00 00 00 + 11.526757 1 1 Tx d 8 9F D8 00 00 00 00 00 00 + 11.536887 1 1 Tx d 8 9F D8 00 00 00 00 00 00 + 11.547525 1 1 Tx d 8 9F D8 00 00 00 00 00 00 + 11.557523 1 1 Tx d 8 9F D8 00 00 00 00 00 00 + 11.567532 1 1 Tx d 8 9F D8 00 00 00 00 00 00 + 11.577527 1 1 Tx d 8 9F D8 00 00 00 00 00 00 + 11.587523 1 1 Tx d 8 9F D8 00 00 00 00 00 00 + 11.597526 1 1 Tx d 8 9F D8 00 00 00 00 00 00 + 11.607525 1 1 Tx d 8 9F D8 00 00 00 00 00 00 + 11.616904 1 1 Tx d 8 9F D8 00 00 00 00 00 00 + 11.626929 1 1 Tx d 8 9F D8 00 00 00 00 00 00 + 11.637020 1 1 Tx d 8 9F D8 00 00 00 00 00 00 + 11.646874 1 1 Tx d 8 9F D8 00 00 00 00 00 00 + 11.656868 1 1 Tx d 8 9F D8 00 00 00 00 00 00 + 11.666872 1 1 Tx d 8 9F D8 00 00 00 00 00 00 + 11.679511 1 1 Tx d 8 9F D8 00 00 00 00 00 00 + 11.687110 1 1 Tx d 8 9F D8 00 00 00 00 00 00 + 11.697100 1 1 Tx d 8 9F D8 00 00 00 00 00 00 + 11.707100 1 1 Tx d 8 9F D8 00 00 00 00 00 00 + 11.717100 1 1 Tx d 8 9F D8 00 00 00 00 00 00 + 11.727099 1 1 Tx d 8 9F D8 00 00 00 00 00 00 + 11.737252 1 1 Tx d 8 9F D8 00 00 00 00 00 00 + 11.746874 1 1 Tx d 8 9F D8 00 00 00 00 00 00 + 11.756874 1 1 Tx d 8 9F D8 00 00 00 00 00 00 + 11.766874 1 1 Tx d 8 9F D8 00 00 00 00 00 00 + 11.777392 1 1 Tx d 8 9F D8 00 00 00 00 00 00 + 11.787388 1 1 Tx d 8 9F D8 00 00 00 00 00 00 + 11.797399 1 1 Tx d 8 9F D8 00 00 00 00 00 00 + 11.807398 1 1 Tx d 8 9F D8 00 00 00 00 00 00 + 11.817401 1 1 Tx d 8 9F D8 00 00 00 00 00 00 + 11.827390 1 1 Tx d 8 9F D8 00 00 00 00 00 00 + 11.837495 1 1 Tx d 8 9F D8 00 00 00 00 00 00 + 11.847174 1 1 Tx d 8 9F D8 00 00 00 00 00 00 + 11.857175 1 1 Tx d 8 9F D8 00 00 00 00 00 00 + 11.867168 1 1 Tx d 8 9F D8 00 00 00 00 00 00 + 11.877188 1 1 Tx d 8 9F D8 00 00 00 00 00 00 + 11.887178 1 1 Tx d 8 9F D8 00 00 00 00 00 00 + 11.897311 1 1 Tx d 8 9F D8 00 00 00 00 00 00 + 11.907309 1 1 Tx d 8 9F D8 00 00 00 00 00 00 + 11.917316 1 1 Tx d 8 9F D8 00 00 00 00 00 00 + 11.927317 1 1 Tx d 8 9F D8 00 00 00 00 00 00 + 11.937417 1 1 Tx d 8 9F D8 00 00 00 00 00 00 + 11.947113 1 1 Tx d 8 9F D8 00 00 00 00 00 00 + 11.957146 1 1 Tx d 8 9F D8 00 00 00 00 00 00 + 11.967112 1 1 Tx d 8 9F D8 00 00 00 00 00 00 + 11.977120 1 1 Tx d 8 9F D8 00 00 00 00 00 00 + 11.987310 1 1 Tx d 8 9F D8 00 00 00 00 00 00 + 11.997108 1 1 Tx d 8 9F D8 00 00 00 00 00 00 + 12.007108 1 1 Tx d 8 9F D8 00 00 00 00 00 00 + 12.017106 1 1 Tx d 8 9F D8 00 00 00 00 00 00 + 12.027103 1 1 Tx d 8 9F D8 00 00 00 00 00 00 + 12.037208 1 1 Tx d 8 9F D8 00 00 00 00 00 00 + 12.046925 1 1 Tx d 8 9F D8 00 00 00 00 00 00 + 12.057288 1 1 Tx d 8 9F D8 00 00 00 00 00 00 + 12.067293 1 1 Tx d 8 9F D8 00 00 00 00 00 00 + 12.077295 1 1 Tx d 8 9F D8 00 00 00 00 00 00 + 12.087287 1 1 Tx d 8 9F D8 00 00 00 00 00 00 + 12.097296 1 1 Tx d 8 9F D8 00 00 00 00 00 00 + 12.107288 1 1 Tx d 8 9F D8 00 00 00 00 00 00 + 12.117294 1 1 Tx d 8 9F D8 00 00 00 00 00 00 + 12.127287 1 1 Tx d 8 9F D8 00 00 00 00 00 00 + 12.137607 1 1 Tx d 8 9F D8 00 00 00 00 00 00 + 12.147016 1 1 Tx d 8 9F D8 00 00 00 00 00 00 + 12.156835 1 1 Tx d 8 9F D8 00 00 00 00 00 00 + 12.167001 1 1 Tx d 8 9F D8 00 00 00 00 00 00 + 12.177004 1 1 Tx d 8 9F D8 00 00 00 00 00 00 + 12.187001 1 1 Tx d 8 9F D8 00 00 00 00 00 00 + 12.197009 1 1 Tx d 8 9F D8 00 00 00 00 00 00 + 12.207039 1 1 Tx d 8 9F D8 00 00 00 00 00 00 + 12.217005 1 1 Tx d 8 9F D8 00 00 00 00 00 00 + 12.227657 1 1 Tx d 8 9F D8 00 00 00 00 00 00 + 12.237011 1 1 Tx d 8 9F D8 00 00 00 00 00 00 + 12.247707 1 1 Tx d 8 9F D8 00 00 00 00 00 00 + 12.257701 1 1 Tx d 8 9F D8 00 00 00 00 00 00 + 12.267699 1 1 Tx d 8 9F D8 00 00 00 00 00 00 + 12.277705 1 1 Tx d 8 9F D8 00 00 00 00 00 00 + 12.288007 1 1 Tx d 8 9F D8 00 00 00 00 00 00 + 12.297145 1 1 Tx d 8 9F D8 00 00 00 00 00 00 + 12.307102 1 1 Tx d 8 9F D8 00 00 00 00 00 00 + 12.317140 1 1 Tx d 8 9F D8 00 00 00 00 00 00 + 12.327102 1 1 Tx d 8 9F D8 00 00 00 00 00 00 + 12.337202 1 1 Tx d 8 9F D8 00 00 00 00 00 00 + 12.346884 1 1 Tx d 8 9F D8 00 00 00 00 00 00 + 12.356799 1 1 Tx d 8 9F D8 00 00 00 00 00 00 + 12.366888 1 1 Tx d 8 9F D8 00 00 00 00 00 00 + 12.376883 1 1 Tx d 8 9F D8 00 00 00 00 00 00 + 12.386882 1 1 Tx d 8 9F D8 00 00 00 00 00 00 + 12.396887 1 1 Tx d 8 9F D8 00 00 00 00 00 00 + 12.406889 1 1 Tx d 8 9F D8 00 00 00 00 00 00 + 12.416791 1 1 Tx d 8 9F D8 00 00 00 00 00 00 + 12.427145 1 1 Tx d 8 9F D8 00 00 00 00 00 00 + 12.437138 1 1 Tx d 8 9F D8 00 00 00 00 00 00 + 12.446936 1 1 Tx d 8 9F D8 00 00 00 00 00 00 + 12.456936 1 1 Tx d 8 9F D8 00 00 00 00 00 00 + 12.466937 1 1 Tx d 8 9F D8 00 00 00 00 00 00 + 12.476938 1 1 Tx d 8 9F D8 00 00 00 00 00 00 + 12.486937 1 1 Tx d 8 9F D8 00 00 00 00 00 00 + 12.496942 1 1 Tx d 8 9F D8 00 00 00 00 00 00 + 12.506939 1 1 Tx d 8 9F D8 00 00 00 00 00 00 + 12.516936 1 1 Tx d 8 9F D8 00 00 00 00 00 00 + 12.526935 1 1 Tx d 8 9F D8 00 00 00 00 00 00 + 12.537039 1 1 Tx d 8 9F D8 00 00 00 00 00 00 + 12.547743 1 1 Tx d 8 9F D8 00 00 00 00 00 00 + 12.557706 1 1 Tx d 8 9F D8 00 00 00 00 00 00 + 12.567729 1 1 Tx d 8 9F D8 00 00 00 00 00 00 + 12.577709 1 1 Tx d 8 9F D8 00 00 00 00 00 00 + 12.587131 1 1 Tx d 8 9F D8 00 00 00 00 00 00 + 12.597124 1 1 Tx d 8 9F D8 00 00 00 00 00 00 + 12.607233 1 1 Tx d 8 9F D8 00 00 00 00 00 00 + 12.617179 1 1 Tx d 8 B3 D4 00 00 00 00 00 00 + 12.627417 1 1 Tx d 8 B3 D4 00 00 00 00 00 00 + 12.637438 1 1 Tx d 8 B3 D4 00 00 00 00 00 00 + 12.646896 1 1 Tx d 8 B3 D4 00 00 00 00 00 00 + 12.656937 1 1 Tx d 8 B3 D4 00 00 00 00 00 00 + 12.666957 1 1 Tx d 8 B3 D4 00 00 00 00 00 00 + 12.676941 1 1 Tx d 8 B3 D4 00 00 00 00 00 00 + 12.686938 1 1 Tx d 8 B3 D4 00 00 00 00 00 00 + 12.696936 1 1 Tx d 8 B3 D4 00 00 00 00 00 00 + 12.707006 1 1 Tx d 8 B3 D4 00 00 00 00 00 00 + 12.716939 1 1 Tx d 8 B3 D4 00 00 00 00 00 00 + 12.726937 1 1 Tx d 8 B3 D4 00 00 00 00 00 00 + 12.736950 1 1 Tx d 8 B3 D4 00 00 00 00 00 00 + 12.747449 1 1 Tx d 8 B3 D4 00 00 00 00 00 00 + 12.757447 1 1 Tx d 8 B3 D4 00 00 00 00 00 00 + 12.772035 1 1 Tx d 8 B3 D4 00 00 00 00 00 00 + 12.777448 1 1 Tx d 8 B3 D4 00 00 00 00 00 00 + 12.787446 1 1 Tx d 8 B3 D4 00 00 00 00 00 00 + 12.797447 1 1 Tx d 8 B3 D4 00 00 00 00 00 00 + 12.807450 1 1 Tx d 8 B3 D4 00 00 00 00 00 00 + 12.817455 1 1 Tx d 8 B3 D4 00 00 00 00 00 00 + 12.827445 1 1 Tx d 8 B3 D4 00 00 00 00 00 00 + 12.837444 1 1 Tx d 8 B3 D4 00 00 00 00 00 00 + 12.846958 1 1 Tx d 8 B3 D4 00 00 00 00 00 00 + 12.856950 1 1 Tx d 8 B3 D4 00 00 00 00 00 00 + 12.866867 1 1 Tx d 8 B3 D4 00 00 00 00 00 00 + 12.876911 1 1 Tx d 8 B3 D4 00 00 00 00 00 00 + 12.886890 1 1 Tx d 8 B3 D4 00 00 00 00 00 00 + 12.896891 1 1 Tx d 8 B3 D4 00 00 00 00 00 00 + 12.906887 1 1 Tx d 8 B3 D4 00 00 00 00 00 00 + 12.916891 1 1 Tx d 8 B3 D4 00 00 00 00 00 00 + 12.926955 1 1 Tx d 8 B3 D4 00 00 00 00 00 00 + 12.936950 1 1 Tx d 8 B3 D4 00 00 00 00 00 00 + 12.947453 1 1 Tx d 8 B3 D4 00 00 00 00 00 00 + 12.957452 1 1 Tx d 8 B3 D4 00 00 00 00 00 00 + 12.967456 1 1 Tx d 8 B3 D4 00 00 00 00 00 00 + 12.977460 1 1 Tx d 8 B3 D4 00 00 00 00 00 00 + 12.987456 1 1 Tx d 8 B3 D4 00 00 00 00 00 00 + 12.997451 1 1 Tx d 8 B3 D4 00 00 00 00 00 00 + 13.007470 1 1 Tx d 8 B3 D4 00 00 00 00 00 00 + 13.017458 1 1 Tx d 8 B3 D4 00 00 00 00 00 00 + 13.027469 1 1 Tx d 8 B3 D4 00 00 00 00 00 00 + 13.037528 1 1 Tx d 8 B3 D4 00 00 00 00 00 00 + 13.047324 1 1 Tx d 8 B3 D4 00 00 00 00 00 00 + 13.057168 1 1 Tx d 8 B3 D4 00 00 00 00 00 00 + 13.067015 1 1 Tx d 8 B3 D4 00 00 00 00 00 00 + 13.077018 1 1 Tx d 8 B3 D4 00 00 00 00 00 00 + 13.087014 1 1 Tx d 8 B3 D4 00 00 00 00 00 00 + 13.097035 1 1 Tx d 8 B3 D4 00 00 00 00 00 00 + 13.107016 1 1 Tx d 8 B3 D4 00 00 00 00 00 00 + 13.117023 1 1 Tx d 8 B3 D4 00 00 00 00 00 00 + 13.127011 1 1 Tx d 8 B3 D4 00 00 00 00 00 00 + 13.137107 1 1 Tx d 8 B3 D4 00 00 00 00 00 00 + 13.146773 1 1 Tx d 8 B3 D4 00 00 00 00 00 00 + 13.156771 1 1 Tx d 8 B3 D4 00 00 00 00 00 00 + 13.166772 1 1 Tx d 8 B3 D4 00 00 00 00 00 00 + 13.176918 1 1 Tx d 8 B3 D4 00 00 00 00 00 00 + 13.187008 1 1 Tx d 8 B3 D4 00 00 00 00 00 00 + 13.197009 1 1 Tx d 8 B3 D4 00 00 00 00 00 00 + 13.207057 1 1 Tx d 8 B3 D4 00 00 00 00 00 00 + 13.217008 1 1 Tx d 8 B3 D4 00 00 00 00 00 00 + 13.227009 1 1 Tx d 8 B3 D4 00 00 00 00 00 00 + 13.237139 1 1 Tx d 8 B3 D4 00 00 00 00 00 00 + 13.246780 1 1 Tx d 8 B3 D4 00 00 00 00 00 00 + 13.256981 1 1 Tx d 8 B3 D4 00 00 00 00 00 00 + 13.267877 1 1 Tx d 8 B3 D4 00 00 00 00 00 00 + 13.276979 1 1 Tx d 8 B3 D4 00 00 00 00 00 00 + 13.286977 1 1 Tx d 8 B3 D4 00 00 00 00 00 00 + 13.296978 1 1 Tx d 8 B3 D4 00 00 00 00 00 00 + 13.306983 1 1 Tx d 8 B3 D4 00 00 00 00 00 00 + 13.316897 1 1 Tx d 8 B3 D4 00 00 00 00 00 00 + 13.326987 1 1 Tx d 8 B3 D4 00 00 00 00 00 00 + 13.336983 1 1 Tx d 8 B3 D4 00 00 00 00 00 00 + 13.347707 1 1 Tx d 8 B3 D4 00 00 00 00 00 00 + 13.357706 1 1 Tx d 8 B3 D4 00 00 00 00 00 00 + 13.367720 1 1 Tx d 8 B3 D4 00 00 00 00 00 00 + 13.377722 1 1 Tx d 8 B3 D4 00 00 00 00 00 00 + 13.387705 1 1 Tx d 8 B3 D4 00 00 00 00 00 00 + 13.397710 1 1 Tx d 8 B3 D4 00 00 00 00 00 00 + 13.407712 1 1 Tx d 8 B3 D4 00 00 00 00 00 00 + 13.417709 1 1 Tx d 8 B3 D4 00 00 00 00 00 00 + 13.427798 1 1 Tx d 8 B3 D4 00 00 00 00 00 00 + 13.436803 1 1 Tx d 8 B3 D4 00 00 00 00 00 00 + 13.447458 1 1 Tx d 8 B3 D4 00 00 00 00 00 00 + 13.457744 1 1 Tx d 8 B3 D4 00 00 00 00 00 00 + 13.467735 1 1 Tx d 8 B3 D4 00 00 00 00 00 00 + 13.476841 1 1 Tx d 8 B3 D4 00 00 00 00 00 00 + 13.487254 1 1 Tx d 8 B3 D4 00 00 00 00 00 00 + 13.497196 1 1 Tx d 8 B3 D4 00 00 00 00 00 00 + 13.506856 1 1 Tx d 8 B3 D4 00 00 00 00 00 00 + 13.517137 1 1 Tx d 8 B3 D4 00 00 00 00 00 00 + 13.527208 1 1 Tx d 8 B3 D4 00 00 00 00 00 00 + 13.537323 1 1 Tx d 8 B3 D4 00 00 00 00 00 00 + 13.546987 1 1 Tx d 8 B3 D4 00 00 00 00 00 00 + 13.556989 1 1 Tx d 8 B3 D4 00 00 00 00 00 00 + 13.566982 1 1 Tx d 8 B3 D4 00 00 00 00 00 00 + 13.576983 1 1 Tx d 8 B3 D4 00 00 00 00 00 00 + 13.586989 1 1 Tx d 8 B3 D4 00 00 00 00 00 00 + 13.596991 1 1 Tx d 8 B3 D4 00 00 00 00 00 00 + 13.607130 1 1 Tx d 8 B3 D4 00 00 00 00 00 00 + 13.616923 1 1 Tx d 8 B3 D4 00 00 00 00 00 00 + 13.626993 1 1 Tx d 8 B3 D4 00 00 00 00 00 00 + 13.636934 1 1 Tx d 8 B3 D4 00 00 00 00 00 00 + 13.647127 1 1 Tx d 8 B3 D4 00 00 00 00 00 00 + 13.657065 1 1 Tx d 8 B3 D4 00 00 00 00 00 00 + 13.667058 1 1 Tx d 8 B3 D4 00 00 00 00 00 00 + 13.677072 1 1 Tx d 8 B3 D4 00 00 00 00 00 00 + 13.687018 1 1 Tx d 8 B3 D4 00 00 00 00 00 00 + 13.697065 1 1 Tx d 8 B3 D4 00 00 00 00 00 00 + 13.707057 1 1 Tx d 8 B3 D4 00 00 00 00 00 00 + 13.717139 1 1 Tx d 8 B3 D4 00 00 00 00 00 00 + 13.727063 1 1 Tx d 8 B3 D4 00 00 00 00 00 00 + 13.737072 1 1 Tx d 8 B3 D4 00 00 00 00 00 00 + 13.747601 1 1 Tx d 8 B3 D4 00 00 00 00 00 00 + 13.757607 1 1 Tx d 8 B3 D4 00 00 00 00 00 00 + 13.767674 1 1 Tx d 8 B3 D4 00 00 00 00 00 00 + 13.777673 1 1 Tx d 8 B3 D4 00 00 00 00 00 00 + 13.787666 1 1 Tx d 8 B3 D4 00 00 00 00 00 00 + 13.797665 1 1 Tx d 8 B3 D4 00 00 00 00 00 00 + 13.807667 1 1 Tx d 8 B3 D4 00 00 00 00 00 00 + 13.817722 1 1 Tx d 8 B3 D4 00 00 00 00 00 00 + 13.827672 1 1 Tx d 8 B3 D4 00 00 00 00 00 00 + 13.837757 1 1 Tx d 8 B3 D4 00 00 00 00 00 00 + 13.847398 1 1 Tx d 8 B3 D4 00 00 00 00 00 00 + 13.857393 1 1 Tx d 8 B3 D4 00 00 00 00 00 00 + 13.867377 1 1 Tx d 8 B3 D4 00 00 00 00 00 00 + 13.877415 1 1 Tx d 8 B3 D4 00 00 00 00 00 00 + 13.887304 1 1 Tx d 8 B3 D4 00 00 00 00 00 00 + 13.897394 1 1 Tx d 8 B3 D4 00 00 00 00 00 00 + 13.907405 1 1 Tx d 8 B3 D4 00 00 00 00 00 00 + 13.917394 1 1 Tx d 8 B3 D4 00 00 00 00 00 00 + 13.927399 1 1 Tx d 8 B3 D4 00 00 00 00 00 00 + 13.937499 1 1 Tx d 8 B3 D4 00 00 00 00 00 00 + 13.946986 1 1 Tx d 8 B3 D4 00 00 00 00 00 00 + 13.957004 1 1 Tx d 8 B3 D4 00 00 00 00 00 00 + 13.966993 1 1 Tx d 8 B3 D4 00 00 00 00 00 00 + 13.976992 1 1 Tx d 8 B3 D4 00 00 00 00 00 00 + 13.986993 1 1 Tx d 8 B3 D4 00 00 00 00 00 00 + 13.996993 1 1 Tx d 8 B3 D4 00 00 00 00 00 00 + 14.006986 1 1 Tx d 8 B3 D4 00 00 00 00 00 00 + 14.016994 1 1 Tx d 8 B3 D4 00 00 00 00 00 00 + 14.027202 1 1 Tx d 8 B3 D4 00 00 00 00 00 00 + 14.037305 1 1 Tx d 8 B3 D4 00 00 00 00 00 00 + 14.046903 1 1 Tx d 8 B3 D4 00 00 00 00 00 00 + 14.056987 1 1 Tx d 8 B3 D4 00 00 00 00 00 00 + 14.066986 1 1 Tx d 8 B3 D4 00 00 00 00 00 00 + 14.076897 1 1 Tx d 8 B3 D4 00 00 00 00 00 00 + 14.086987 1 1 Tx d 8 B3 D4 00 00 00 00 00 00 + 14.096991 1 1 Tx d 8 B3 D4 00 00 00 00 00 00 + 14.106988 1 1 Tx d 8 B3 D4 00 00 00 00 00 00 + 14.117042 1 1 Tx d 8 B3 D4 00 00 00 00 00 00 + 14.126985 1 1 Tx d 8 B3 D4 00 00 00 00 00 00 + 14.136987 1 1 Tx d 8 B3 D4 00 00 00 00 00 00 + 14.146988 1 1 Tx d 8 B3 D4 00 00 00 00 00 00 + 14.157441 1 1 Tx d 8 B3 D4 00 00 00 00 00 00 + 14.167490 1 1 Tx d 8 B3 D4 00 00 00 00 00 00 + 14.177591 1 1 Tx d 8 B3 D4 00 00 00 00 00 00 + 14.187473 1 1 Tx d 8 C7 CE 00 00 00 00 00 00 + 14.197423 1 1 Tx d 8 C7 CE 00 00 00 00 00 00 + 14.207394 1 1 Tx d 8 C7 CE 00 00 00 00 00 00 + 14.217433 1 1 Tx d 8 C7 CE 00 00 00 00 00 00 + 14.227441 1 1 Tx d 8 C7 CE 00 00 00 00 00 00 + 14.237442 1 1 Tx d 8 C7 CE 00 00 00 00 00 00 + 14.247161 1 1 Tx d 8 C7 CE 00 00 00 00 00 00 + 14.257164 1 1 Tx d 8 C7 CE 00 00 00 00 00 00 + 14.267133 1 1 Tx d 8 C7 CE 00 00 00 00 00 00 + 14.277168 1 1 Tx d 8 C7 CE 00 00 00 00 00 00 + 14.287169 1 1 Tx d 8 C7 CE 00 00 00 00 00 00 + 14.297168 1 1 Tx d 8 C7 CE 00 00 00 00 00 00 + 14.307279 1 1 Tx d 8 C7 CE 00 00 00 00 00 00 + 14.317489 1 1 Tx d 8 C7 CE 00 00 00 00 00 00 + 14.327493 1 1 Tx d 8 C7 CE 00 00 00 00 00 00 + 14.337585 1 1 Tx d 8 C7 CE 00 00 00 00 00 00 + 14.347567 1 1 Tx d 8 C7 CE 00 00 00 00 00 00 + 14.357030 1 1 Tx d 8 C7 CE 00 00 00 00 00 00 + 14.367033 1 1 Tx d 8 C7 CE 00 00 00 00 00 00 + 14.377029 1 1 Tx d 8 C7 CE 00 00 00 00 00 00 + 14.386944 1 1 Tx d 8 C7 CE 00 00 00 00 00 00 + 14.396935 1 1 Tx d 8 C7 CE 00 00 00 00 00 00 + 14.406994 1 1 Tx d 8 C7 CE 00 00 00 00 00 00 + 14.417032 1 1 Tx d 8 C7 CE 00 00 00 00 00 00 + 14.427041 1 1 Tx d 8 C7 CE 00 00 00 00 00 00 + 14.436940 1 1 Tx d 8 C7 CE 00 00 00 00 00 00 + 14.447487 1 1 Tx d 8 C7 CE 00 00 00 00 00 00 + 14.457472 1 1 Tx d 8 C7 CE 00 00 00 00 00 00 + 14.467536 1 1 Tx d 8 C7 CE 00 00 00 00 00 00 + 14.477536 1 1 Tx d 8 C7 CE 00 00 00 00 00 00 + 14.487544 1 1 Tx d 8 C7 CE 00 00 00 00 00 00 + 14.497533 1 1 Tx d 8 C7 CE 00 00 00 00 00 00 + 14.507532 1 1 Tx d 8 C7 CE 00 00 00 00 00 00 + 14.517542 1 1 Tx d 8 C7 CE 00 00 00 00 00 00 + 14.527536 1 1 Tx d 8 C7 CE 00 00 00 00 00 00 + 14.537538 1 1 Tx d 8 C7 CE 00 00 00 00 00 00 + 14.547540 1 1 Tx d 8 C7 CE 00 00 00 00 00 00 + 14.557043 1 1 Tx d 8 C7 CE 00 00 00 00 00 00 + 14.567048 1 1 Tx d 8 C7 CE 00 00 00 00 00 00 + 14.577043 1 1 Tx d 8 C7 CE 00 00 00 00 00 00 + 14.587044 1 1 Tx d 8 C7 CE 00 00 00 00 00 00 + 14.597046 1 1 Tx d 8 C7 CE 00 00 00 00 00 00 + 14.607041 1 1 Tx d 8 C7 CE 00 00 00 00 00 00 + 14.617041 1 1 Tx d 8 C7 CE 00 00 00 00 00 00 + 14.627044 1 1 Tx d 8 C7 CE 00 00 00 00 00 00 + 14.637052 1 1 Tx d 8 C7 CE 00 00 00 00 00 00 + 14.647555 1 1 Tx d 8 C7 CE 00 00 00 00 00 00 + 14.657545 1 1 Tx d 8 C7 CE 00 00 00 00 00 00 + 14.667545 1 1 Tx d 8 C7 CE 00 00 00 00 00 00 + 14.677548 1 1 Tx d 8 C7 CE 00 00 00 00 00 00 + 14.687468 1 1 Tx d 8 C7 CE 00 00 00 00 00 00 + 14.697545 1 1 Tx d 8 C7 CE 00 00 00 00 00 00 + 14.707545 1 1 Tx d 8 C7 CE 00 00 00 00 00 00 + 14.717546 1 1 Tx d 8 C7 CE 00 00 00 00 00 00 + 14.727546 1 1 Tx d 8 C7 CE 00 00 00 00 00 00 + 14.737546 1 1 Tx d 8 C7 CE 00 00 00 00 00 00 + 14.747556 1 1 Tx d 8 C7 CE 00 00 00 00 00 00 + 14.757058 1 1 Tx d 8 C7 CE 00 00 00 00 00 00 + 14.767055 1 1 Tx d 8 C7 CE 00 00 00 00 00 00 + 14.777057 1 1 Tx d 8 C7 CE 00 00 00 00 00 00 + 14.787055 1 1 Tx d 8 C7 CE 00 00 00 00 00 00 + 14.797055 1 1 Tx d 8 C7 CE 00 00 00 00 00 00 + 14.807055 1 1 Tx d 8 C7 CE 00 00 00 00 00 00 + 14.817054 1 1 Tx d 8 C7 CE 00 00 00 00 00 00 + 14.827006 1 1 Tx d 8 C7 CE 00 00 00 00 00 00 + 14.837055 1 1 Tx d 8 C7 CE 00 00 00 00 00 00 + 14.847561 1 1 Tx d 8 C7 CE 00 00 00 00 00 00 + 14.857563 1 1 Tx d 8 C7 CE 00 00 00 00 00 00 + 14.866981 1 1 Tx d 8 C7 CE 00 00 00 00 00 00 + 14.877963 1 1 Tx d 8 C7 CE 00 00 00 00 00 00 + 14.887495 1 1 Tx d 8 C7 CE 00 00 00 00 00 00 + 14.897528 1 1 Tx d 8 C7 CE 00 00 00 00 00 00 + 14.907542 1 1 Tx d 8 C7 CE 00 00 00 00 00 00 + 14.917530 1 1 Tx d 8 C7 CE 00 00 00 00 00 00 + 14.927523 1 1 Tx d 8 C7 CE 00 00 00 00 00 00 + 14.937734 1 1 Tx d 8 C7 CE 00 00 00 00 00 00 + 14.947652 1 1 Tx d 8 C7 CE 00 00 00 00 00 00 + 14.957192 1 1 Tx d 8 C7 CE 00 00 00 00 00 00 + 14.967146 1 1 Tx d 8 C7 CE 00 00 00 00 00 00 + 14.977151 1 1 Tx d 8 C7 CE 00 00 00 00 00 00 + 14.987207 1 1 Tx d 8 C7 CE 00 00 00 00 00 00 + 14.997184 1 1 Tx d 8 C7 CE 00 00 00 00 00 00 + 15.007186 1 1 Tx d 8 C7 CE 00 00 00 00 00 00 + 15.017198 1 1 Tx d 8 C7 CE 00 00 00 00 00 00 + 15.027254 1 1 Tx d 8 C7 CE 00 00 00 00 00 00 + 15.037254 1 1 Tx d 8 C7 CE 00 00 00 00 00 00 + 15.047752 1 1 Tx d 8 C7 CE 00 00 00 00 00 00 + 15.057751 1 1 Tx d 8 C7 CE 00 00 00 00 00 00 + 15.067748 1 1 Tx d 8 C7 CE 00 00 00 00 00 00 + 15.077766 1 1 Tx d 8 C7 CE 00 00 00 00 00 00 + 15.087759 1 1 Tx d 8 C7 CE 00 00 00 00 00 00 + 15.097025 1 1 Tx d 8 C7 CE 00 00 00 00 00 00 + 15.107044 1 1 Tx d 8 C7 CE 00 00 00 00 00 00 + 15.117115 1 1 Tx d 8 C7 CE 00 00 00 00 00 00 + 15.127131 1 1 Tx d 8 C7 CE 00 00 00 00 00 00 + 15.137215 1 1 Tx d 8 C7 CE 00 00 00 00 00 00 + 15.148032 1 1 Tx d 8 C7 CE 00 00 00 00 00 00 + 15.157126 1 1 Tx d 8 C7 CE 00 00 00 00 00 00 + 15.167129 1 1 Tx d 8 C7 CE 00 00 00 00 00 00 + 15.177119 1 1 Tx d 8 C7 CE 00 00 00 00 00 00 + 15.187124 1 1 Tx d 8 C7 CE 00 00 00 00 00 00 + 15.197120 1 1 Tx d 8 C7 CE 00 00 00 00 00 00 + 15.207115 1 1 Tx d 8 C7 CE 00 00 00 00 00 00 + 15.217122 1 1 Tx d 8 C7 CE 00 00 00 00 00 00 + 15.227116 1 1 Tx d 8 C7 CE 00 00 00 00 00 00 + 15.237122 1 1 Tx d 8 C7 CE 00 00 00 00 00 00 + 15.247117 1 1 Tx d 8 C7 CE 00 00 00 00 00 00 + 15.257619 1 1 Tx d 8 C7 CE 00 00 00 00 00 00 + 15.267620 1 1 Tx d 8 C7 CE 00 00 00 00 00 00 + 15.277619 1 1 Tx d 8 C7 CE 00 00 00 00 00 00 + 15.287622 1 1 Tx d 8 C7 CE 00 00 00 00 00 00 + 15.297622 1 1 Tx d 8 C7 CE 00 00 00 00 00 00 + 15.307622 1 1 Tx d 8 C7 CE 00 00 00 00 00 00 + 15.317619 1 1 Tx d 8 C7 CE 00 00 00 00 00 00 + 15.327631 1 1 Tx d 8 C7 CE 00 00 00 00 00 00 + 15.337623 1 1 Tx d 8 C7 CE 00 00 00 00 00 00 + 15.348124 1 1 Tx d 8 C7 CE 00 00 00 00 00 00 + 15.357129 1 1 Tx d 8 C7 CE 00 00 00 00 00 00 + 15.367125 1 1 Tx d 8 C7 CE 00 00 00 00 00 00 + 15.377124 1 1 Tx d 8 C7 CE 00 00 00 00 00 00 + 15.387132 1 1 Tx d 8 C7 CE 00 00 00 00 00 00 + 15.397131 1 1 Tx d 8 C7 CE 00 00 00 00 00 00 + 15.407131 1 1 Tx d 8 C7 CE 00 00 00 00 00 00 + 15.417134 1 1 Tx d 8 C7 CE 00 00 00 00 00 00 + 15.427632 1 1 Tx d 8 C7 CE 00 00 00 00 00 00 + 15.437630 1 1 Tx d 8 C7 CE 00 00 00 00 00 00 + 15.448148 1 1 Tx d 8 C7 CE 00 00 00 00 00 00 + 15.457243 1 1 Tx d 8 C7 CE 00 00 00 00 00 00 + 15.467149 1 1 Tx d 8 C7 CE 00 00 00 00 00 00 + 15.477285 1 1 Tx d 8 C7 CE 00 00 00 00 00 00 + 15.487242 1 1 Tx d 8 C7 CE 00 00 00 00 00 00 + 15.497266 1 1 Tx d 8 C7 CE 00 00 00 00 00 00 + 15.507270 1 1 Tx d 8 C7 CE 00 00 00 00 00 00 + 15.517369 1 1 Tx d 8 C7 CE 00 00 00 00 00 00 + 15.527362 1 1 Tx d 8 C7 CE 00 00 00 00 00 00 + 15.537358 1 1 Tx d 8 C7 CE 00 00 00 00 00 00 + 15.547366 1 1 Tx d 8 C7 CE 00 00 00 00 00 00 + 15.556870 1 1 Tx d 8 C7 CE 00 00 00 00 00 00 + 15.566866 1 1 Tx d 8 C7 CE 00 00 00 00 00 00 + 15.576864 1 1 Tx d 8 C7 CE 00 00 00 00 00 00 + 15.586847 1 1 Tx d 8 C7 CE 00 00 00 00 00 00 + 15.596866 1 1 Tx d 8 C7 CE 00 00 00 00 00 00 + 15.606860 1 1 Tx d 8 C7 CE 00 00 00 00 00 00 + 15.616866 1 1 Tx d 8 C7 CE 00 00 00 00 00 00 + 15.626869 1 1 Tx d 8 C7 CE 00 00 00 00 00 00 + 15.637222 1 1 Tx d 8 C7 CE 00 00 00 00 00 00 + 15.646866 1 1 Tx d 8 C7 CE 00 00 00 00 00 00 + 15.657381 1 1 Tx d 8 C7 CE 00 00 00 00 00 00 + 15.667341 1 1 Tx d 8 C7 CE 00 00 00 00 00 00 + 15.677367 1 1 Tx d 8 C7 CE 00 00 00 00 00 00 + 15.687370 1 1 Tx d 8 C7 CE 00 00 00 00 00 00 + 15.697377 1 1 Tx d 8 C7 CE 00 00 00 00 00 00 + 15.707373 1 1 Tx d 8 C7 CE 00 00 00 00 00 00 + 15.717370 1 1 Tx d 8 C7 CE 00 00 00 00 00 00 + 15.727375 1 1 Tx d 8 C7 CE 00 00 00 00 00 00 + 15.737374 1 1 Tx d 8 C7 CE 00 00 00 00 00 00 + 15.747667 1 1 Tx d 8 DB CA 00 00 00 00 00 00 + 15.757182 1 1 Tx d 8 DB CA 00 00 00 00 00 00 + 15.767179 1 1 Tx d 8 DB CA 00 00 00 00 00 00 + 15.777175 1 1 Tx d 8 DB CA 00 00 00 00 00 00 + 15.787171 1 1 Tx d 8 DB CA 00 00 00 00 00 00 + 15.797173 1 1 Tx d 8 DB CA 00 00 00 00 00 00 + 15.807168 1 1 Tx d 8 DB CA 00 00 00 00 00 00 + 15.817171 1 1 Tx d 8 DB CA 00 00 00 00 00 00 + 15.827174 1 1 Tx d 8 DB CA 00 00 00 00 00 00 + 15.837177 1 1 Tx d 8 DB CA 00 00 00 00 00 00 + 15.847179 1 1 Tx d 8 DB CA 00 00 00 00 00 00 + 15.857677 1 1 Tx d 8 DB CA 00 00 00 00 00 00 + 15.867673 1 1 Tx d 8 DB CA 00 00 00 00 00 00 + 15.877678 1 1 Tx d 8 DB CA 00 00 00 00 00 00 + 15.887676 1 1 Tx d 8 DB CA 00 00 00 00 00 00 + 15.897678 1 1 Tx d 8 DB CA 00 00 00 00 00 00 + 15.907805 1 1 Tx d 8 DB CA 00 00 00 00 00 00 + 15.917452 1 1 Tx d 8 DB CA 00 00 00 00 00 00 + 15.927450 1 1 Tx d 8 DB CA 00 00 00 00 00 00 + 15.937465 1 1 Tx d 8 DB CA 00 00 00 00 00 00 + 15.947469 1 1 Tx d 8 DB CA 00 00 00 00 00 00 + 15.956994 1 1 Tx d 8 DB CA 00 00 00 00 00 00 + 15.966972 1 1 Tx d 8 DB CA 00 00 00 00 00 00 + 15.977058 1 1 Tx d 8 DB CA 00 00 00 00 00 00 + 15.987058 1 1 Tx d 8 DB CA 00 00 00 00 00 00 + 15.997058 1 1 Tx d 8 DB CA 00 00 00 00 00 00 + 16.007150 1 1 Tx d 8 DB CA 00 00 00 00 00 00 + 16.017010 1 1 Tx d 8 DB CA 00 00 00 00 00 00 + 16.027058 1 1 Tx d 8 DB CA 00 00 00 00 00 00 + 16.037054 1 1 Tx d 8 DB CA 00 00 00 00 00 00 + 16.047063 1 1 Tx d 8 DB CA 00 00 00 00 00 00 + 16.057520 1 1 Tx d 8 DB CA 00 00 00 00 00 00 + 16.067537 1 1 Tx d 8 DB CA 00 00 00 00 00 00 + 16.077525 1 1 Tx d 8 DB CA 00 00 00 00 00 00 + 16.087521 1 1 Tx d 8 DB CA 00 00 00 00 00 00 + 16.097526 1 1 Tx d 8 DB CA 00 00 00 00 00 00 + 16.107540 1 1 Tx d 8 DB CA 00 00 00 00 00 00 + 16.117528 1 1 Tx d 8 DB CA 00 00 00 00 00 00 + 16.127527 1 1 Tx d 8 DB CA 00 00 00 00 00 00 + 16.137598 1 1 Tx d 8 DB CA 00 00 00 00 00 00 + 16.147648 1 1 Tx d 8 DB CA 00 00 00 00 00 00 + 16.157616 1 1 Tx d 8 DB CA 00 00 00 00 00 00 + 16.166848 1 1 Tx d 8 DB CA 00 00 00 00 00 00 + 16.176851 1 1 Tx d 8 DB CA 00 00 00 00 00 00 + 16.186847 1 1 Tx d 8 DB CA 00 00 00 00 00 00 + 16.196856 1 1 Tx d 8 DB CA 00 00 00 00 00 00 + 16.206854 1 1 Tx d 8 DB CA 00 00 00 00 00 00 + 16.216871 1 1 Tx d 8 DB CA 00 00 00 00 00 00 + 16.226850 1 1 Tx d 8 DB CA 00 00 00 00 00 00 + 16.237254 1 1 Tx d 8 DB CA 00 00 00 00 00 00 + 16.247218 1 1 Tx d 8 DB CA 00 00 00 00 00 00 + 16.257774 1 1 Tx d 8 DB CA 00 00 00 00 00 00 + 16.266804 1 1 Tx d 8 DB CA 00 00 00 00 00 00 + 16.276797 1 1 Tx d 8 DB CA 00 00 00 00 00 00 + 16.287794 1 1 Tx d 8 DB CA 00 00 00 00 00 00 + 16.296909 1 1 Tx d 8 DB CA 00 00 00 00 00 00 + 16.306992 1 1 Tx d 8 DB CA 00 00 00 00 00 00 + 16.317001 1 1 Tx d 8 DB CA 00 00 00 00 00 00 + 16.326995 1 1 Tx d 8 DB CA 00 00 00 00 00 00 + 16.337082 1 1 Tx d 8 DB CA 00 00 00 00 00 00 + 16.347569 1 1 Tx d 8 DB CA 00 00 00 00 00 00 + 16.357266 1 1 Tx d 8 DB CA 00 00 00 00 00 00 + 16.367261 1 1 Tx d 8 DB CA 00 00 00 00 00 00 + 16.376825 1 1 Tx d 8 DB CA 00 00 00 00 00 00 + 16.387347 1 1 Tx d 8 DB CA 00 00 00 00 00 00 + 16.397428 1 1 Tx d 8 DB CA 00 00 00 00 00 00 + 16.407429 1 1 Tx d 8 DB CA 00 00 00 00 00 00 + 16.417434 1 1 Tx d 8 DB CA 00 00 00 00 00 00 + 16.427430 1 1 Tx d 8 DB CA 00 00 00 00 00 00 + 16.437520 1 1 Tx d 8 DB CA 00 00 00 00 00 00 + 16.447528 1 1 Tx d 8 DB CA 00 00 00 00 00 00 + 16.457322 1 1 Tx d 8 DB CA 00 00 00 00 00 00 + 16.467218 1 1 Tx d 8 DB CA 00 00 00 00 00 00 + 16.477220 1 1 Tx d 8 DB CA 00 00 00 00 00 00 + 16.487224 1 1 Tx d 8 DB CA 00 00 00 00 00 00 + 16.497222 1 1 Tx d 8 DB CA 00 00 00 00 00 00 + 16.507225 1 1 Tx d 8 DB CA 00 00 00 00 00 00 + 16.517417 1 1 Tx d 8 DB CA 00 00 00 00 00 00 + 16.527897 1 1 Tx d 8 DB CA 00 00 00 00 00 00 + 16.536890 1 1 Tx d 8 DB CA 00 00 00 00 00 00 + 16.546999 1 1 Tx d 8 DB CA 00 00 00 00 00 00 + 16.557696 1 1 Tx d 8 DB CA 00 00 00 00 00 00 + 16.566993 1 1 Tx d 8 DB CA 00 00 00 00 00 00 + 16.577475 1 1 Tx d 8 DB CA 00 00 00 00 00 00 + 16.586998 1 1 Tx d 8 DB CA 00 00 00 00 00 00 + 16.597000 1 1 Tx d 8 DB CA 00 00 00 00 00 00 + 16.606993 1 1 Tx d 8 DB CA 00 00 00 00 00 00 + 16.616998 1 1 Tx d 8 DB CA 00 00 00 00 00 00 + 16.626992 1 1 Tx d 8 DB CA 00 00 00 00 00 00 + 16.637087 1 1 Tx d 8 DB CA 00 00 00 00 00 00 + 16.647091 1 1 Tx d 8 DB CA 00 00 00 00 00 00 + 16.657391 1 1 Tx d 8 DB CA 00 00 00 00 00 00 + 16.667392 1 1 Tx d 8 DB CA 00 00 00 00 00 00 + 16.677388 1 1 Tx d 8 DB CA 00 00 00 00 00 00 + 16.687385 1 1 Tx d 8 DB CA 00 00 00 00 00 00 + 16.697393 1 1 Tx d 8 DB CA 00 00 00 00 00 00 + 16.706809 1 1 Tx d 8 DB CA 00 00 00 00 00 00 + 16.716808 1 1 Tx d 8 DB CA 00 00 00 00 00 00 + 16.726803 1 1 Tx d 8 DB CA 00 00 00 00 00 00 + 16.736899 1 1 Tx d 8 DB CA 00 00 00 00 00 00 + 16.746898 1 1 Tx d 8 DB CA 00 00 00 00 00 00 + 16.757588 1 1 Tx d 8 DB CA 00 00 00 00 00 00 + 16.767588 1 1 Tx d 8 DB CA 00 00 00 00 00 00 + 16.777044 1 1 Tx d 8 DB CA 00 00 00 00 00 00 + 16.787113 1 1 Tx d 8 DB CA 00 00 00 00 00 00 + 16.797114 1 1 Tx d 8 DB CA 00 00 00 00 00 00 + 16.807129 1 1 Tx d 8 DB CA 00 00 00 00 00 00 + 16.817105 1 1 Tx d 8 DB CA 00 00 00 00 00 00 + 16.827108 1 1 Tx d 8 DB CA 00 00 00 00 00 00 + 16.837141 1 1 Tx d 8 DB CA 00 00 00 00 00 00 + 16.847211 1 1 Tx d 8 DB CA 00 00 00 00 00 00 + 16.856845 1 1 Tx d 8 DB CA 00 00 00 00 00 00 + 16.866888 1 1 Tx d 8 DB CA 00 00 00 00 00 00 + 16.877457 1 1 Tx d 8 DB CA 00 00 00 00 00 00 + 16.887608 1 1 Tx d 8 DB CA 00 00 00 00 00 00 + 16.897668 1 1 Tx d 8 DB CA 00 00 00 00 00 00 + 16.907564 1 1 Tx d 8 DB CA 00 00 00 00 00 00 + 16.917558 1 1 Tx d 8 DB CA 00 00 00 00 00 00 + 16.927588 1 1 Tx d 8 DB CA 00 00 00 00 00 00 + 16.937576 1 1 Tx d 8 DB CA 00 00 00 00 00 00 + 16.947577 1 1 Tx d 8 DB CA 00 00 00 00 00 00 + 16.957826 1 1 Tx d 8 DB CA 00 00 00 00 00 00 + 16.967279 1 1 Tx d 8 DB CA 00 00 00 00 00 00 + 16.977376 1 1 Tx d 8 DB CA 00 00 00 00 00 00 + 16.987365 1 1 Tx d 8 DB CA 00 00 00 00 00 00 + 16.997370 1 1 Tx d 8 DB CA 00 00 00 00 00 00 + 17.007364 1 1 Tx d 8 DB CA 00 00 00 00 00 00 + 17.017372 1 1 Tx d 8 DB CA 00 00 00 00 00 00 + 17.027389 1 1 Tx d 8 DB CA 00 00 00 00 00 00 + 17.036965 1 1 Tx d 8 DB CA 00 00 00 00 00 00 + 17.046968 1 1 Tx d 8 DB CA 00 00 00 00 00 00 + 17.057655 1 1 Tx d 8 DB CA 00 00 00 00 00 00 + 17.067660 1 1 Tx d 8 DB CA 00 00 00 00 00 00 + 17.077649 1 1 Tx d 8 DB CA 00 00 00 00 00 00 + 17.087647 1 1 Tx d 8 DB CA 00 00 00 00 00 00 + 17.097648 1 1 Tx d 8 DB CA 00 00 00 00 00 00 + 17.108100 1 1 Tx d 8 DB CA 00 00 00 00 00 00 + 17.117101 1 1 Tx d 8 DB CA 00 00 00 00 00 00 + 17.127067 1 1 Tx d 8 DB CA 00 00 00 00 00 00 + 17.137139 1 1 Tx d 8 DB CA 00 00 00 00 00 00 + 17.147106 1 1 Tx d 8 DB CA 00 00 00 00 00 00 + 17.157655 1 1 Tx d 8 DB CA 00 00 00 00 00 00 + 17.167609 1 1 Tx d 8 DB CA 00 00 00 00 00 00 + 17.177516 1 1 Tx d 8 DB CA 00 00 00 00 00 00 + 17.187555 1 1 Tx d 8 DB CA 00 00 00 00 00 00 + 17.197543 1 1 Tx d 8 DB CA 00 00 00 00 00 00 + 17.207541 1 1 Tx d 8 DB CA 00 00 00 00 00 00 + 17.217549 1 1 Tx d 8 DB CA 00 00 00 00 00 00 + 17.227541 1 1 Tx d 8 DB CA 00 00 00 00 00 00 + 17.237543 1 1 Tx d 8 DB CA 00 00 00 00 00 00 + 17.247543 1 1 Tx d 8 DB CA 00 00 00 00 00 00 + 17.257046 1 1 Tx d 8 DB CA 00 00 00 00 00 00 + 17.267055 1 1 Tx d 8 DB CA 00 00 00 00 00 00 + 17.277064 1 1 Tx d 8 DB CA 00 00 00 00 00 00 + 17.287113 1 1 Tx d 8 DB CA 00 00 00 00 00 00 + 17.297114 1 1 Tx d 8 DB CA 00 00 00 00 00 00 + 17.307459 1 1 Tx d 8 DB CA 00 00 00 00 00 00 + 17.317654 1 1 Tx d 8 EF C6 00 00 00 00 00 00 + 17.327524 1 1 Tx d 8 EF C6 00 00 00 00 00 00 + 17.337610 1 1 Tx d 8 EF C6 00 00 00 00 00 00 + 17.347614 1 1 Tx d 8 EF C6 00 00 00 00 00 00 + 17.357114 1 1 Tx d 8 EF C6 00 00 00 00 00 00 + 17.367067 1 1 Tx d 8 EF C6 00 00 00 00 00 00 + 17.377114 1 1 Tx d 8 EF C6 00 00 00 00 00 00 + 17.387124 1 1 Tx d 8 EF C6 00 00 00 00 00 00 + 17.397113 1 1 Tx d 8 EF C6 00 00 00 00 00 00 + 17.407121 1 1 Tx d 8 EF C6 00 00 00 00 00 00 + 17.417122 1 1 Tx d 8 EF C6 00 00 00 00 00 00 + 17.427860 1 1 Tx d 8 EF C6 00 00 00 00 00 00 + 17.437368 1 1 Tx d 8 EF C6 00 00 00 00 00 00 + 17.447311 1 1 Tx d 8 EF C6 00 00 00 00 00 00 + 17.457540 1 1 Tx d 8 EF C6 00 00 00 00 00 00 + 17.467544 1 1 Tx d 8 EF C6 00 00 00 00 00 00 + 17.477536 1 1 Tx d 8 EF C6 00 00 00 00 00 00 + 17.487546 1 1 Tx d 8 EF C6 00 00 00 00 00 00 + 17.497607 1 1 Tx d 8 EF C6 00 00 00 00 00 00 + 17.507614 1 1 Tx d 8 EF C6 00 00 00 00 00 00 + 17.517606 1 1 Tx d 8 EF C6 00 00 00 00 00 00 + 17.527621 1 1 Tx d 8 EF C6 00 00 00 00 00 00 + 17.537696 1 1 Tx d 8 EF C6 00 00 00 00 00 00 + 17.547698 1 1 Tx d 8 EF C6 00 00 00 00 00 00 + 17.557406 1 1 Tx d 8 EF C6 00 00 00 00 00 00 + 17.567296 1 1 Tx d 8 EF C6 00 00 00 00 00 00 + 17.577292 1 1 Tx d 8 EF C6 00 00 00 00 00 00 + 17.587393 1 1 Tx d 8 EF C6 00 00 00 00 00 00 + 17.596906 1 1 Tx d 8 EF C6 00 00 00 00 00 00 + 17.607355 1 1 Tx d 8 EF C6 00 00 00 00 00 00 + 17.617349 1 1 Tx d 8 EF C6 00 00 00 00 00 00 + 17.627350 1 1 Tx d 8 EF C6 00 00 00 00 00 00 + 17.637459 1 1 Tx d 8 EF C6 00 00 00 00 00 00 + 17.647552 1 1 Tx d 8 EF C6 00 00 00 00 00 00 + 17.657771 1 1 Tx d 8 EF C6 00 00 00 00 00 00 + 17.667764 1 1 Tx d 8 EF C6 00 00 00 00 00 00 + 17.677769 1 1 Tx d 8 EF C6 00 00 00 00 00 00 + 17.687766 1 1 Tx d 8 EF C6 00 00 00 00 00 00 + 17.697767 1 1 Tx d 8 EF C6 00 00 00 00 00 00 + 17.707765 1 1 Tx d 8 EF C6 00 00 00 00 00 00 + 17.717767 1 1 Tx d 8 EF C6 00 00 00 00 00 00 + 17.727762 1 1 Tx d 8 EF C6 00 00 00 00 00 00 + 17.737763 1 1 Tx d 8 EF C6 00 00 00 00 00 00 + 17.747763 1 1 Tx d 8 EF C6 00 00 00 00 00 00 + 17.757278 1 1 Tx d 8 EF C6 00 00 00 00 00 00 + 17.767273 1 1 Tx d 8 EF C6 00 00 00 00 00 00 + 17.777273 1 1 Tx d 8 EF C6 00 00 00 00 00 00 + 17.787303 1 1 Tx d 8 EF C6 00 00 00 00 00 00 + 17.797304 1 1 Tx d 8 EF C6 00 00 00 00 00 00 + 17.807275 1 1 Tx d 8 EF C6 00 00 00 00 00 00 + 17.816779 1 1 Tx d 8 EF C6 00 00 00 00 00 00 + 17.827798 1 1 Tx d 8 EF C6 00 00 00 00 00 00 + 17.837114 1 1 Tx d 8 EF C6 00 00 00 00 00 00 + 17.847209 1 1 Tx d 8 EF C6 00 00 00 00 00 00 + 17.856889 1 1 Tx d 8 EF C6 00 00 00 00 00 00 + 17.866889 1 1 Tx d 8 EF C6 00 00 00 00 00 00 + 17.876888 1 1 Tx d 8 EF C6 00 00 00 00 00 00 + 17.886895 1 1 Tx d 8 EF C6 00 00 00 00 00 00 + 17.896891 1 1 Tx d 8 EF C6 00 00 00 00 00 00 + 17.906902 1 1 Tx d 8 EF C6 00 00 00 00 00 00 + 17.916888 1 1 Tx d 8 EF C6 00 00 00 00 00 00 + 17.926890 1 1 Tx d 8 EF C6 00 00 00 00 00 00 + 17.936978 1 1 Tx d 8 EF C6 00 00 00 00 00 00 + 17.946977 1 1 Tx d 8 EF C6 00 00 00 00 00 00 + 17.957663 1 1 Tx d 8 EF C6 00 00 00 00 00 00 + 17.967669 1 1 Tx d 8 EF C6 00 00 00 00 00 00 + 17.977668 1 1 Tx d 8 EF C6 00 00 00 00 00 00 + 17.987628 1 1 Tx d 8 EF C6 00 00 00 00 00 00 + 17.997671 1 1 Tx d 8 EF C6 00 00 00 00 00 00 + 18.007618 1 1 Tx d 8 EF C6 00 00 00 00 00 00 + 18.017675 1 1 Tx d 8 EF C6 00 00 00 00 00 00 + 18.027661 1 1 Tx d 8 EF C6 00 00 00 00 00 00 + 18.037762 1 1 Tx d 8 EF C6 00 00 00 00 00 00 + 18.047854 1 1 Tx d 8 EF C6 00 00 00 00 00 00 + 18.057452 1 1 Tx d 8 EF C6 00 00 00 00 00 00 + 18.067576 1 1 Tx d 8 EF C6 00 00 00 00 00 00 + 18.077552 1 1 Tx d 8 EF C6 00 00 00 00 00 00 + 18.087446 1 1 Tx d 8 EF C6 00 00 00 00 00 00 + 18.097634 1 1 Tx d 8 EF C6 00 00 00 00 00 00 + 18.107727 1 1 Tx d 8 EF C6 00 00 00 00 00 00 + 18.117723 1 1 Tx d 8 EF C6 00 00 00 00 00 00 + 18.127678 1 1 Tx d 8 EF C6 00 00 00 00 00 00 + 18.136820 1 1 Tx d 8 EF C6 00 00 00 00 00 00 + 18.146832 1 1 Tx d 8 EF C6 00 00 00 00 00 00 + 18.157495 1 1 Tx d 8 EF C6 00 00 00 00 00 00 + 18.166817 1 1 Tx d 8 EF C6 00 00 00 00 00 00 + 18.176822 1 1 Tx d 8 EF C6 00 00 00 00 00 00 + 18.186816 1 1 Tx d 8 EF C6 00 00 00 00 00 00 + 18.196927 1 1 Tx d 8 EF C6 00 00 00 00 00 00 + 18.207817 1 1 Tx d 8 EF C6 00 00 00 00 00 00 + 18.216815 1 1 Tx d 8 EF C6 00 00 00 00 00 00 + 18.226827 1 1 Tx d 8 EF C6 00 00 00 00 00 00 + 18.237017 1 1 Tx d 8 EF C6 00 00 00 00 00 00 + 18.247035 1 1 Tx d 8 EF C6 00 00 00 00 00 00 + 18.257694 1 1 Tx d 8 EF C6 00 00 00 00 00 00 + 18.267697 1 1 Tx d 8 EF C6 00 00 00 00 00 00 + 18.277587 1 1 Tx d 8 EF C6 00 00 00 00 00 00 + 18.287593 1 1 Tx d 8 EF C6 00 00 00 00 00 00 + 18.297593 1 1 Tx d 8 EF C6 00 00 00 00 00 00 + 18.307590 1 1 Tx d 8 EF C6 00 00 00 00 00 00 + 18.317602 1 1 Tx d 8 EF C6 00 00 00 00 00 00 + 18.326810 1 1 Tx d 8 EF C6 00 00 00 00 00 00 + 18.336912 1 1 Tx d 8 EF C6 00 00 00 00 00 00 + 18.346909 1 1 Tx d 8 EF C6 00 00 00 00 00 00 + 18.357588 1 1 Tx d 8 EF C6 00 00 00 00 00 00 + 18.367587 1 1 Tx d 8 EF C6 00 00 00 00 00 00 + 18.377592 1 1 Tx d 8 EF C6 00 00 00 00 00 00 + 18.387599 1 1 Tx d 8 EF C6 00 00 00 00 00 00 + 18.397588 1 1 Tx d 8 EF C6 00 00 00 00 00 00 + 18.407598 1 1 Tx d 8 EF C6 00 00 00 00 00 00 + 18.417780 1 1 Tx d 8 EF C6 00 00 00 00 00 00 + 18.427906 1 1 Tx d 8 EF C6 00 00 00 00 00 00 + 18.436967 1 1 Tx d 8 EF C6 00 00 00 00 00 00 + 18.447054 1 1 Tx d 8 EF C6 00 00 00 00 00 00 + 18.457750 1 1 Tx d 8 EF C6 00 00 00 00 00 00 + 18.467738 1 1 Tx d 8 EF C6 00 00 00 00 00 00 + 18.477739 1 1 Tx d 8 EF C6 00 00 00 00 00 00 + 18.487742 1 1 Tx d 8 EF C6 00 00 00 00 00 00 + 18.497761 1 1 Tx d 8 EF C6 00 00 00 00 00 00 + 18.507761 1 1 Tx d 8 EF C6 00 00 00 00 00 00 + 18.517741 1 1 Tx d 8 EF C6 00 00 00 00 00 00 + 18.527740 1 1 Tx d 8 EF C6 00 00 00 00 00 00 + 18.538092 1 1 Tx d 8 EF C6 00 00 00 00 00 00 + 18.547141 1 1 Tx d 8 EF C6 00 00 00 00 00 00 + 18.557738 1 1 Tx d 8 EF C6 00 00 00 00 00 00 + 18.567760 1 1 Tx d 8 EF C6 00 00 00 00 00 00 + 18.577741 1 1 Tx d 8 EF C6 00 00 00 00 00 00 + 18.587743 1 1 Tx d 8 EF C6 00 00 00 00 00 00 + 18.597740 1 1 Tx d 8 EF C6 00 00 00 00 00 00 + 18.607021 1 1 Tx d 8 EF C6 00 00 00 00 00 00 + 18.617084 1 1 Tx d 8 EF C6 00 00 00 00 00 00 + 18.627088 1 1 Tx d 8 EF C6 00 00 00 00 00 00 + 18.637556 1 1 Tx d 8 EF C6 00 00 00 00 00 00 + 18.648077 1 1 Tx d 8 EF C6 00 00 00 00 00 00 + 18.657700 1 1 Tx d 8 EF C6 00 00 00 00 00 00 + 18.667697 1 1 Tx d 8 EF C6 00 00 00 00 00 00 + 18.677723 1 1 Tx d 8 EF C6 00 00 00 00 00 00 + 18.687698 1 1 Tx d 8 EF C6 00 00 00 00 00 00 + 18.697699 1 1 Tx d 8 EF C6 00 00 00 00 00 00 + 18.707699 1 1 Tx d 8 EF C6 00 00 00 00 00 00 + 18.717701 1 1 Tx d 8 EF C6 00 00 00 00 00 00 + 18.726887 1 1 Tx d 8 EF C6 00 00 00 00 00 00 + 18.736981 1 1 Tx d 8 EF C6 00 00 00 00 00 00 + 18.747073 1 1 Tx d 8 EF C6 00 00 00 00 00 00 + 18.757677 1 1 Tx d 8 EF C6 00 00 00 00 00 00 + 18.767752 1 1 Tx d 8 EF C6 00 00 00 00 00 00 + 18.777747 1 1 Tx d 8 EF C6 00 00 00 00 00 00 + 18.787750 1 1 Tx d 8 EF C6 00 00 00 00 00 00 + 18.797748 1 1 Tx d 8 EF C6 00 00 00 00 00 00 + 18.807746 1 1 Tx d 8 EF C6 00 00 00 00 00 00 + 18.817747 1 1 Tx d 8 EF C6 00 00 00 00 00 00 + 18.827745 1 1 Tx d 8 EF C6 00 00 00 00 00 00 + 18.837753 1 1 Tx d 8 EF C6 00 00 00 00 00 00 + 18.847750 1 1 Tx d 8 EF C6 00 00 00 00 00 00 + 18.857257 1 1 Tx d 8 EF C6 00 00 00 00 00 00 + 18.867499 1 1 Tx d 8 EF C6 00 00 00 00 00 00 + 18.877609 1 1 Tx d 8 FF FE 00 00 00 00 00 00 + 18.887519 1 1 Tx d 8 FF FE 00 00 00 00 00 00 + 18.897516 1 1 Tx d 8 FF FE 00 00 00 00 00 00 + 18.907524 1 1 Tx d 8 FF FE 00 00 00 00 00 00 + 18.917521 1 1 Tx d 8 FF FE 00 00 00 00 00 00 + 18.927512 1 1 Tx d 8 FF FE 00 00 00 00 00 00 + 18.937534 1 1 Tx d 8 FF FE 00 00 00 00 00 00 + 18.947511 1 1 Tx d 8 FF FE 00 00 00 00 00 00 + 18.957049 1 1 Tx d 8 FF FE 00 00 00 00 00 00 + 18.968196 1 1 Tx d 8 FF FE 00 00 00 00 00 00 + 18.977062 1 1 Tx d 8 FF FE 00 00 00 00 00 00 + 18.987067 1 1 Tx d 8 FF FE 00 00 00 00 00 00 + 18.997024 1 1 Tx d 8 FF FE 00 00 00 00 00 00 + 19.007064 1 1 Tx d 8 FF FE 00 00 00 00 00 00 + 19.017036 1 1 Tx d 8 FF FE 00 00 00 00 00 00 + 19.027151 1 1 Tx d 8 FF FE 00 00 00 00 00 00 + 19.037164 1 1 Tx d 8 FF FE 00 00 00 00 00 00 + 19.047261 1 1 Tx d 8 FF FE 00 00 00 00 00 00 + 19.056944 1 1 Tx d 8 FF FE 00 00 00 00 00 00 + 19.066939 1 1 Tx d 8 FF FE 00 00 00 00 00 00 + 19.076950 1 1 Tx d 8 FF FE 00 00 00 00 00 00 + 19.086942 1 1 Tx d 8 FF FE 00 00 00 00 00 00 + 19.097346 1 1 Tx d 8 FF FE 00 00 00 00 00 00 + 19.107338 1 1 Tx d 8 FF FE 00 00 00 00 00 00 + 19.117331 1 1 Tx d 8 FF FE 00 00 00 00 00 00 + 19.127646 1 1 Tx d 8 FF FE 00 00 00 00 00 00 + 19.137664 1 1 Tx d 8 FF FE 00 00 00 00 00 00 + 19.147745 1 1 Tx d 8 FF FE 00 00 00 00 00 00 + 19.157438 1 1 Tx d 8 FF FE 00 00 00 00 00 00 + 19.167437 1 1 Tx d 8 FF FE 00 00 00 00 00 00 + 19.177438 1 1 Tx d 8 FF FE 00 00 00 00 00 00 + 19.187478 1 1 Tx d 8 FF FE 00 00 00 00 00 00 + 19.197441 1 1 Tx d 8 FF FE 00 00 00 00 00 00 + 19.207034 1 1 Tx d 8 FF FE 00 00 00 00 00 00 + 19.217035 1 1 Tx d 8 FF FE 00 00 00 00 00 00 + 19.227063 1 1 Tx d 8 FF FE 00 00 00 00 00 00 + 19.237034 1 1 Tx d 8 FF FE 00 00 00 00 00 00 + 19.247085 1 1 Tx d 8 FF FE 00 00 00 00 00 00 + 19.257784 1 1 Tx d 8 FF FE 00 00 00 00 00 00 + 19.267763 1 1 Tx d 8 FF FE 00 00 00 00 00 00 + 19.277768 1 1 Tx d 8 FF FE 00 00 00 00 00 00 + 19.287764 1 1 Tx d 8 FF FE 00 00 00 00 00 00 + 19.297759 1 1 Tx d 8 FF FE 00 00 00 00 00 00 + 19.307741 1 1 Tx d 8 FF FE 00 00 00 00 00 00 + 19.317762 1 1 Tx d 8 FF FE 00 00 00 00 00 00 + 19.327758 1 1 Tx d 8 FF FE 00 00 00 00 00 00 + 19.337777 1 1 Tx d 8 FF FE 00 00 00 00 00 00 + 19.346872 1 1 Tx d 8 FF FE 00 00 00 00 00 00 + 19.357548 1 1 Tx d 8 FF FE 00 00 00 00 00 00 + 19.367548 1 1 Tx d 8 FF FE 00 00 00 00 00 00 + 19.377548 1 1 Tx d 8 FF FE 00 00 00 00 00 00 + 19.387549 1 1 Tx d 8 FF FE 00 00 00 00 00 00 + 19.397562 1 1 Tx d 8 FF FE 00 00 00 00 00 00 + 19.407549 1 1 Tx d 8 FF FE 00 00 00 00 00 00 + 19.417548 1 1 Tx d 8 FF FE 00 00 00 00 00 00 + 19.426828 1 1 Tx d 8 FF FE 00 00 00 00 00 00 + 19.437505 1 1 Tx d 8 FF FE 00 00 00 00 00 00 + 19.447606 1 1 Tx d 8 FF FE 00 00 00 00 00 00 + 19.457290 1 1 Tx d 8 FF FE 00 00 00 00 00 00 + 19.467301 1 1 Tx d 8 FF FE 00 00 00 00 00 00 + 19.477299 1 1 Tx d 8 FF FE 00 00 00 00 00 00 + 19.487237 1 1 Tx d 8 FF FE 00 00 00 00 00 00 + 19.497293 1 1 Tx d 8 FF FE 00 00 00 00 00 00 + 19.507301 1 1 Tx d 8 FF FE 00 00 00 00 00 00 + 19.517197 1 1 Tx d 8 FF FE 00 00 00 00 00 00 + 19.527355 1 1 Tx d 8 FF FE 00 00 00 00 00 00 + 19.537644 1 1 Tx d 8 FF FE 00 00 00 00 00 00 + 19.546998 1 1 Tx d 8 FF FE 00 00 00 00 00 00 + 19.557593 1 1 Tx d 8 FF FE 00 00 00 00 00 00 + 19.567206 1 1 Tx d 8 FF FE 00 00 00 00 00 00 + 19.577304 1 1 Tx d 8 FF FE 00 00 00 00 00 00 + 19.587312 1 1 Tx d 8 FF FE 00 00 00 00 00 00 + 19.597303 1 1 Tx d 8 FF FE 00 00 00 00 00 00 + 19.607297 1 1 Tx d 8 FF FE 00 00 00 00 00 00 + 19.617301 1 1 Tx d 8 FF FE 00 00 00 00 00 00 + 19.627301 1 1 Tx d 8 FF FE 00 00 00 00 00 00 + 19.636884 1 1 Tx d 8 FF FE 00 00 00 00 00 00 + 19.646977 1 1 Tx d 8 FF FE 00 00 00 00 00 00 + 19.657663 1 1 Tx d 8 FF FE 00 00 00 00 00 00 + 19.667663 1 1 Tx d 8 FF FE 00 00 00 00 00 00 + 19.677667 1 1 Tx d 8 FF FE 00 00 00 00 00 00 + 19.687663 1 1 Tx d 8 FF FE 00 00 00 00 00 00 + 19.697610 1 1 Tx d 8 FF FE 00 00 00 00 00 00 + 19.707662 1 1 Tx d 8 FF FE 00 00 00 00 00 00 + 19.717663 1 1 Tx d 8 FF FE 00 00 00 00 00 00 + 19.727663 1 1 Tx d 8 FF FE 00 00 00 00 00 00 + 19.737663 1 1 Tx d 8 FF FE 00 00 00 00 00 00 + 19.747427 1 1 Tx d 8 FF FE 00 00 00 00 00 00 + 19.757129 1 1 Tx d 8 FF FE 00 00 00 00 00 00 + 19.767130 1 1 Tx d 8 FF FE 00 00 00 00 00 00 + 19.777336 1 1 Tx d 8 FF FE 00 00 00 00 00 00 + 19.787331 1 1 Tx d 8 FF FE 00 00 00 00 00 00 + 19.797552 1 1 Tx d 8 FF FE 00 00 00 00 00 00 + 19.807561 1 1 Tx d 8 FF FE 00 00 00 00 00 00 + 19.817556 1 1 Tx d 8 FF FE 00 00 00 00 00 00 + 19.827565 1 1 Tx d 8 FF FE 00 00 00 00 00 00 + 19.840230 1 1 Tx d 8 FF FE 00 00 00 00 00 00 + 19.847000 1 1 Tx d 8 FF FE 00 00 00 00 00 00 + 19.857501 1 1 Tx d 8 FF FE 00 00 00 00 00 00 + 19.867495 1 1 Tx d 8 FF FE 00 00 00 00 00 00 + 19.877497 1 1 Tx d 8 FF FE 00 00 00 00 00 00 + 19.887499 1 1 Tx d 8 FF FE 00 00 00 00 00 00 + 19.897493 1 1 Tx d 8 FF FE 00 00 00 00 00 00 + 19.907511 1 1 Tx d 8 FF FE 00 00 00 00 00 00 + 19.917551 1 1 Tx d 8 FF FE 00 00 00 00 00 00 + 19.927545 1 1 Tx d 8 FF FE 00 00 00 00 00 00 + 19.937548 1 1 Tx d 8 FF FE 00 00 00 00 00 00 + 19.947553 1 1 Tx d 8 FF FE 00 00 00 00 00 00 + 19.957060 1 1 Tx d 8 FF FE 00 00 00 00 00 00 + 19.967056 1 1 Tx d 8 FF FE 00 00 00 00 00 00 + 19.977064 1 1 Tx d 8 FF FE 00 00 00 00 00 00 + 19.987061 1 1 Tx d 8 FF FE 00 00 00 00 00 00 + 19.997059 1 1 Tx d 8 FF FE 00 00 00 00 00 00 + 20.007058 1 1 Tx d 8 FF FE 00 00 00 00 00 00 + 20.017060 1 1 Tx d 8 FF FE 00 00 00 00 00 00 + 20.027068 1 1 Tx d 8 FF FE 00 00 00 00 00 00 + 20.037055 1 1 Tx d 8 FF FE 00 00 00 00 00 00 + 20.047066 1 1 Tx d 8 FF FE 00 00 00 00 00 00 + 20.057560 1 1 Tx d 8 FF FE 00 00 00 00 00 00 + 20.067564 1 1 Tx d 8 FF FE 00 00 00 00 00 00 + 20.077575 1 1 Tx d 8 FF FE 00 00 00 00 00 00 + 20.087565 1 1 Tx d 8 FF FE 00 00 00 00 00 00 + 20.097565 1 1 Tx d 8 FF FE 00 00 00 00 00 00 + 20.107607 1 1 Tx d 8 FF FE 00 00 00 00 00 00 + 20.117564 1 1 Tx d 8 FF FE 00 00 00 00 00 00 + 20.127560 1 1 Tx d 8 FF FE 00 00 00 00 00 00 + 20.137566 1 1 Tx d 8 FF FE 00 00 00 00 00 00 + 20.147568 1 1 Tx d 8 FF FE 00 00 00 00 00 00 + 20.157073 1 1 Tx d 8 FF FE 00 00 00 00 00 00 + 20.167072 1 1 Tx d 8 FF FE 00 00 00 00 00 00 + 20.177105 1 1 Tx d 8 FF FE 00 00 00 00 00 00 + 20.187022 1 1 Tx d 8 FF FE 00 00 00 00 00 00 + 20.197068 1 1 Tx d 8 FF FE 00 00 00 00 00 00 + 20.207069 1 1 Tx d 8 FF FE 00 00 00 00 00 00 + 20.217069 1 1 Tx d 8 FF FE 00 00 00 00 00 00 + 20.227067 1 1 Tx d 8 FF FE 00 00 00 00 00 00 + 20.237072 1 1 Tx d 8 FF FE 00 00 00 00 00 00 + 20.247073 1 1 Tx d 8 FF FE 00 00 00 00 00 00 + 20.257571 1 1 Tx d 8 FF FE 00 00 00 00 00 00 + 20.267579 1 1 Tx d 8 FF FE 00 00 00 00 00 00 + 20.277576 1 1 Tx d 8 FF FE 00 00 00 00 00 00 + 20.287583 1 1 Tx d 8 FF FE 00 00 00 00 00 00 + 20.297576 1 1 Tx d 8 FF FE 00 00 00 00 00 00 + 20.307575 1 1 Tx d 8 FF FE 00 00 00 00 00 00 + 20.318310 1 1 Tx d 8 FF FE 00 00 00 00 00 00 + 20.327432 1 1 Tx d 8 FF FE 00 00 00 00 00 00 + 20.337434 1 1 Tx d 8 FF FE 00 00 00 00 00 00 + 20.347424 1 1 Tx d 8 FF FE 00 00 00 00 00 00 + 20.356935 1 1 Tx d 8 FF FE 00 00 00 00 00 00 + 20.366932 1 1 Tx d 8 FF FE 00 00 00 00 00 00 + 20.376934 1 1 Tx d 8 FF FE 00 00 00 00 00 00 + 20.386935 1 1 Tx d 8 FF FE 00 00 00 00 00 00 + 20.399543 1 1 Tx d 8 FF FE 00 00 00 00 00 00 + 20.406852 1 1 Tx d 8 FF FE 00 00 00 00 00 00 + 20.416873 1 1 Tx d 8 FF FE 00 00 00 00 00 00 + 20.427289 1 1 Tx d 8 FF FE 00 00 00 00 00 00 + 20.437253 1 1 Tx d 8 FF FE 00 00 00 00 00 00 + 20.447303 1 1 Tx d 8 FF FE 00 00 00 00 00 00 + 20.456839 1 1 Tx d 8 FF FE 00 00 00 00 00 00 + 20.466838 1 1 Tx d 8 FF FE 00 00 00 00 00 00 + 20.476833 1 1 Tx d 8 FF FE 00 00 00 00 00 00 + 20.486837 1 1 Tx d 8 FF FE 00 00 00 00 00 00 + 20.496831 1 1 Tx d 8 FF FE 00 00 00 00 00 00 + 20.507332 1 1 Tx d 8 FF FE 00 00 00 00 00 00 + 20.517344 1 1 Tx d 8 FF FE 00 00 00 00 00 00 + 20.527325 1 1 Tx d 8 FF FE 00 00 00 00 00 00 + 20.539110 1 1 Tx d 8 FF FE 00 00 00 00 00 00 + 20.547593 1 1 Tx d 8 FF FE 00 00 00 00 00 00 + 20.557238 1 1 Tx d 8 FF FE 00 00 00 00 00 00 + 20.567286 1 1 Tx d 8 FF FE 00 00 00 00 00 00 + 20.577290 1 1 Tx d 8 FF FE 00 00 00 00 00 00 + 20.587280 1 1 Tx d 8 FF FE 00 00 00 00 00 00 + 20.597325 1 1 Tx d 8 FF FE 00 00 00 00 00 00 + 20.607284 1 1 Tx d 8 FF FE 00 00 00 00 00 00 + 20.617481 1 1 Tx d 8 FF FE 00 00 00 00 00 00 + 20.627482 1 1 Tx d 8 FF FE 00 00 00 00 00 00 + 20.637480 1 1 Tx d 8 FF FE 00 00 00 00 00 00 + 20.646861 1 1 Tx d 8 FF FE 00 00 00 00 00 00 + 20.657506 1 1 Tx d 8 FF FE 00 00 00 00 00 00 + 20.667693 1 1 Tx d 8 FF FE 00 00 00 00 00 00 + 20.677056 1 1 Tx d 8 FF FE 00 00 00 00 00 00 + 20.687058 1 1 Tx d 8 FF FE 00 00 00 00 00 00 + 20.697058 1 1 Tx d 8 FF FE 00 00 00 00 00 00 + 20.707061 1 1 Tx d 8 FF FE 00 00 00 00 00 00 + 20.717059 1 1 Tx d 8 FF FE 00 00 00 00 00 00 + 20.727416 1 1 Tx d 8 FF FE 00 00 00 00 00 00 + 20.737417 1 1 Tx d 8 FF FE 00 00 00 00 00 00 + 20.747516 1 1 Tx d 8 FF FE 00 00 00 00 00 00 + 20.757198 1 1 Tx d 8 FF FE 00 00 00 00 00 00 + 20.767201 1 1 Tx d 8 FF FE 00 00 00 00 00 00 + 20.777199 1 1 Tx d 8 FF FE 00 00 00 00 00 00 + 20.787241 1 1 Tx d 8 FF FE 00 00 00 00 00 00 + 20.797200 1 1 Tx d 8 FF FE 00 00 00 00 00 00 + 20.807462 1 1 Tx d 8 FF FE 00 00 00 00 00 00 + 20.817460 1 1 Tx d 8 FF FE 00 00 00 00 00 00 + 20.827461 1 1 Tx d 8 FF FE 00 00 00 00 00 00 + 20.837496 1 1 Tx d 8 FF FE 00 00 00 00 00 00 + 20.847634 1 1 Tx d 8 FF FE 00 00 00 00 00 00 + 20.857342 1 1 Tx d 8 FF FE 00 00 00 00 00 00 + 20.867341 1 1 Tx d 8 FF FE 00 00 00 00 00 00 + 20.877342 1 1 Tx d 8 FF FE 00 00 00 00 00 00 + 20.887339 1 1 Tx d 8 FF FE 00 00 00 00 00 00 + 20.897339 1 1 Tx d 8 FF FE 00 00 00 00 00 00 + 20.907351 1 1 Tx d 8 FF FE 00 00 00 00 00 00 + 20.917343 1 1 Tx d 8 FF FE 00 00 00 00 00 00 + 20.927339 1 1 Tx d 8 FF FE 00 00 00 00 00 00 + 20.937342 1 1 Tx d 8 FF FE 00 00 00 00 00 00 + 20.947339 1 1 Tx d 8 FF FE 00 00 00 00 00 00 + 20.957892 1 1 Tx d 8 FF FE 00 00 00 00 00 00 + 20.967790 1 1 Tx d 8 FF FE 00 00 00 00 00 00 + 20.977787 1 1 Tx d 8 FF FE 00 00 00 00 00 00 + 20.987790 1 1 Tx d 8 FF FE 00 00 00 00 00 00 + 20.996843 1 1 Tx d 8 FF FE 00 00 00 00 00 00 + 21.006849 1 1 Tx d 8 FF FE 00 00 00 00 00 00 + 21.016870 1 1 Tx d 8 FF FE 00 00 00 00 00 00 + 21.026850 1 1 Tx d 8 FF FE 00 00 00 00 00 00 + 21.037844 1 1 Tx d 8 FF FE 00 00 00 00 00 00 + 21.047045 1 1 Tx d 8 FF FE 00 00 00 00 00 00 + 21.057666 1 1 Tx d 8 FF FE 00 00 00 00 00 00 + 21.067567 1 1 Tx d 8 FF FE 00 00 00 00 00 00 + 21.077560 1 1 Tx d 8 FF FE 00 00 00 00 00 00 + 21.087572 1 1 Tx d 8 FF FE 00 00 00 00 00 00 + 21.097562 1 1 Tx d 8 FF FE 00 00 00 00 00 00 + 21.107565 1 1 Tx d 8 FF FE 00 00 00 00 00 00 + 21.117569 1 1 Tx d 8 FF FE 00 00 00 00 00 00 + 21.127527 1 1 Tx d 8 FF FE 00 00 00 00 00 00 + 21.137491 1 1 Tx d 8 FF FE 00 00 00 00 00 00 + 21.147518 1 1 Tx d 8 FF FE 00 00 00 00 00 00 + 21.157038 1 1 Tx d 8 FF FE 00 00 00 00 00 00 + 21.167042 1 1 Tx d 8 FF FE 00 00 00 00 00 00 + 21.177048 1 1 Tx d 8 FF FE 00 00 00 00 00 00 + 21.187038 1 1 Tx d 8 FF FE 00 00 00 00 00 00 + 21.197865 1 1 Tx d 8 FF FE 00 00 00 00 00 00 + 21.207214 1 1 Tx d 8 FF FE 00 00 00 00 00 00 + 21.217218 1 1 Tx d 8 FF FE 00 00 00 00 00 00 + 21.227218 1 1 Tx d 8 FF FE 00 00 00 00 00 00 + 21.237216 1 1 Tx d 8 FF FE 00 00 00 00 00 00 + 21.247314 1 1 Tx d 8 FF FE 00 00 00 00 00 00 + 21.256919 1 1 Tx d 8 FF FE 00 00 00 00 00 00 + 21.266910 1 1 Tx d 8 FF FE 00 00 00 00 00 00 + 21.276913 1 1 Tx d 8 FF FE 00 00 00 00 00 00 + 21.286924 1 1 Tx d 8 FF FE 00 00 00 00 00 00 + 21.296915 1 1 Tx d 8 FF FE 00 00 00 00 00 00 + 21.306954 1 1 Tx d 8 FF FE 00 00 00 00 00 00 + 21.316915 1 1 Tx d 8 FF FE 00 00 00 00 00 00 + 21.326914 1 1 Tx d 8 FF FE 00 00 00 00 00 00 + 21.336915 1 1 Tx d 8 FF FE 00 00 00 00 00 00 + 21.347016 1 1 Tx d 8 FF FE 00 00 00 00 00 00 + 21.357682 1 1 Tx d 8 FF FE 00 00 00 00 00 00 + 21.367691 1 1 Tx d 8 FF FE 00 00 00 00 00 00 + 21.377689 1 1 Tx d 8 FF FE 00 00 00 00 00 00 + 21.387690 1 1 Tx d 8 FF FE 00 00 00 00 00 00 + 21.397658 1 1 Tx d 8 FF FE 00 00 00 00 00 00 + 21.407706 1 1 Tx d 8 FF FE 00 00 00 00 00 00 + 21.417689 1 1 Tx d 8 FF FE 00 00 00 00 00 00 + 21.427733 1 1 Tx d 8 FF FE 00 00 00 00 00 00 + 21.437303 1 1 Tx d 8 FF FE 00 00 00 00 00 00 + 21.447485 1 1 Tx d 8 FF FE 00 00 00 00 00 00 + 21.457093 1 1 Tx d 8 FF FE 00 00 00 00 00 00 + 21.467192 1 1 Tx d 8 FF FE 00 00 00 00 00 00 + 21.477190 1 1 Tx d 8 FF FE 00 00 00 00 00 00 + 21.487100 1 1 Tx d 8 FF FE 00 00 00 00 00 00 + 21.497128 1 1 Tx d 8 FF FE 00 00 00 00 00 00 + 21.507195 1 1 Tx d 8 FF FE 00 00 00 00 00 00 + 21.517191 1 1 Tx d 8 FF FE 00 00 00 00 00 00 + 21.527099 1 1 Tx d 8 FF FE 00 00 00 00 00 00 + 21.537247 1 1 Tx d 8 FF FE 00 00 00 00 00 00 + 21.547396 1 1 Tx d 8 FF FE 00 00 00 00 00 00 + 21.556890 1 1 Tx d 8 FF FE 00 00 00 00 00 00 + 21.566993 1 1 Tx d 8 FF FE 00 00 00 00 00 00 + 21.576980 1 1 Tx d 8 FF FE 00 00 00 00 00 00 + 21.586987 1 1 Tx d 8 FF FE 00 00 00 00 00 00 + 21.596978 1 1 Tx d 8 FF FE 00 00 00 00 00 00 + 21.606982 1 1 Tx d 8 FF FE 00 00 00 00 00 00 + 21.616982 1 1 Tx d 8 FF FE 00 00 00 00 00 00 + 21.626979 1 1 Tx d 8 FF FE 00 00 00 00 00 00 + 21.636980 1 1 Tx d 8 FF FE 00 00 00 00 00 00 + 21.647075 1 1 Tx d 8 FF FE 00 00 00 00 00 00 + 21.657082 1 1 Tx d 8 FF FE 00 00 00 00 00 00 + 21.667396 1 1 Tx d 8 FF FE 00 00 00 00 00 00 + 21.677181 1 1 Tx d 8 FF FE 00 00 00 00 00 00 + 21.687602 1 1 Tx d 8 FF FE 00 00 00 00 00 00 + 21.697592 1 1 Tx d 8 FF FE 00 00 00 00 00 00 + 21.707613 1 1 Tx d 8 FF FE 00 00 00 00 00 00 + 21.717598 1 1 Tx d 8 FF FE 00 00 00 00 00 00 + 21.727583 1 1 Tx d 8 FF FE 00 00 00 00 00 00 + 21.737598 1 1 Tx d 8 FF FE 00 00 00 00 00 00 + 21.747778 1 1 Tx d 8 FF FE 00 00 00 00 00 00 + 21.757293 1 1 Tx d 8 FF FE 00 00 00 00 00 00 + 21.767394 1 1 Tx d 8 FF FE 00 00 00 00 00 00 + 21.777381 1 1 Tx d 8 FF FE 00 00 00 00 00 00 + 21.787389 1 1 Tx d 8 FF FE 00 00 00 00 00 00 + 21.797388 1 1 Tx d 8 FF FE 00 00 00 00 00 00 + 21.807384 1 1 Tx d 8 FF FE 00 00 00 00 00 00 + 21.817380 1 1 Tx d 8 FF FE 00 00 00 00 00 00 + 21.827381 1 1 Tx d 8 FF FE 00 00 00 00 00 00 + 21.837385 1 1 Tx d 8 FF FE 00 00 00 00 00 00 + 21.847479 1 1 Tx d 8 FF FE 00 00 00 00 00 00 + 21.857489 1 1 Tx d 8 FF FE 00 00 00 00 00 00 + 21.867272 1 1 Tx d 8 FF FE 00 00 00 00 00 00 + 21.877169 1 1 Tx d 8 FF FE 00 00 00 00 00 00 + 21.887183 1 1 Tx d 8 FF FE 00 00 00 00 00 00 + 21.897171 1 1 Tx d 8 FF FE 00 00 00 00 00 00 + 21.907276 1 1 Tx d 8 FF FE 00 00 00 00 00 00 + 21.917171 1 1 Tx d 8 FF FE 00 00 00 00 00 00 + 21.927270 1 1 Tx d 8 FF FE 00 00 00 00 00 00 + 21.937275 1 1 Tx d 8 FF FE 00 00 00 00 00 00 + 21.947363 1 1 Tx d 8 FF FE 00 00 00 00 00 00 + 21.957960 1 1 Tx d 8 FF FE 00 00 00 00 00 00 + 21.966949 1 1 Tx d 8 FF FE 00 00 00 00 00 00 + 21.976967 1 1 Tx d 8 FF FE 00 00 00 00 00 00 + 21.986945 1 1 Tx d 8 FF FE 00 00 00 00 00 00 + 21.996949 1 1 Tx d 8 FF FE 00 00 00 00 00 00 + 22.006942 1 1 Tx d 8 FF FE 00 00 00 00 00 00 + 22.016897 1 1 Tx d 8 FF FE 00 00 00 00 00 00 + 22.026980 1 1 Tx d 8 FF FE 00 00 00 00 00 00 + 22.036855 1 1 Tx d 8 FF FE 00 00 00 00 00 00 + 22.046953 1 1 Tx d 8 FF FE 00 00 00 00 00 00 + 22.056956 1 1 Tx d 8 FF FE 00 00 00 00 00 00 + 22.067652 1 1 Tx d 8 FF FE 00 00 00 00 00 00 + 22.077649 1 1 Tx d 8 FF FE 00 00 00 00 00 00 + 22.087647 1 1 Tx d 8 FF FE 00 00 00 00 00 00 + 22.097648 1 1 Tx d 8 FF FE 00 00 00 00 00 00 + 22.107837 1 1 Tx d 8 FF FE 00 00 00 00 00 00 + 22.117436 1 1 Tx d 8 FF FE 00 00 00 00 00 00 + 22.127433 1 1 Tx d 8 FF FE 00 00 00 00 00 00 + 22.137435 1 1 Tx d 8 FF FE 00 00 00 00 00 00 + 22.147530 1 1 Tx d 8 FF FE 00 00 00 00 00 00 + 22.158130 1 1 Tx d 8 FF FE 00 00 00 00 00 00 + 22.167220 1 1 Tx d 8 FF FE 00 00 00 00 00 00 + 22.177217 1 1 Tx d 8 FF FE 00 00 00 00 00 00 + 22.187220 1 1 Tx d 8 FF FE 00 00 00 00 00 00 + 22.197226 1 1 Tx d 8 FF FE 00 00 00 00 00 00 + 22.207219 1 1 Tx d 8 FF FE 00 00 00 00 00 00 + 22.217216 1 1 Tx d 8 FF FE 00 00 00 00 00 00 + 22.227260 1 1 Tx d 8 FF FE 00 00 00 00 00 00 + 22.237216 1 1 Tx d 8 FF FE 00 00 00 00 00 00 + 22.247324 1 1 Tx d 8 FF FE 00 00 00 00 00 00 + 22.257349 1 1 Tx d 8 FF FE 00 00 00 00 00 00 + 22.267008 1 1 Tx d 8 FF FE 00 00 00 00 00 00 + 22.277020 1 1 Tx d 8 FF FE 00 00 00 00 00 00 + 22.287018 1 1 Tx d 8 FF FE 00 00 00 00 00 00 + 22.297011 1 1 Tx d 8 FF FE 00 00 00 00 00 00 + 22.307008 1 1 Tx d 8 FF FE 00 00 00 00 00 00 + 22.317009 1 1 Tx d 8 FF FE 00 00 00 00 00 00 + 22.327019 1 1 Tx d 8 FF FE 00 00 00 00 00 00 + 22.337012 1 1 Tx d 8 FF FE 00 00 00 00 00 00 + 22.347109 1 1 Tx d 8 FF FE 00 00 00 00 00 00 + 22.357103 1 1 Tx d 8 FF FE 00 00 00 00 00 00 + 22.367779 1 1 Tx d 8 FF FE 00 00 00 00 00 00 + 22.377774 1 1 Tx d 8 FF FE 00 00 00 00 00 00 + 22.387789 1 1 Tx d 8 FF FE 00 00 00 00 00 00 + 22.397769 1 1 Tx d 8 FF FE 00 00 00 00 00 00 + 22.407770 1 1 Tx d 8 FF FE 00 00 00 00 00 00 + 22.417776 1 1 Tx d 8 FF FE 00 00 00 00 00 00 + 22.427775 1 1 Tx d 8 FF FE 00 00 00 00 00 00 + 22.437783 1 1 Tx d 8 FF FE 00 00 00 00 00 00 + 22.447776 1 1 Tx d 8 FF FE 00 00 00 00 00 00 + 22.457776 1 1 Tx d 8 FF FE 00 00 00 00 00 00 + 22.467475 1 1 Tx d 8 FF FE 00 00 00 00 00 00 + 22.477482 1 1 Tx d 8 FF FE 00 00 00 00 00 00 + 22.487475 1 1 Tx d 8 FF FE 00 00 00 00 00 00 + 22.497680 1 1 Tx d 8 FF FE 00 00 00 00 00 00 + 22.507676 1 1 Tx d 8 FF FE 00 00 00 00 00 00 + 22.517677 1 1 Tx d 8 FF FE 00 00 00 00 00 00 + 22.530322 1 1 Tx d 8 FF FE 00 00 00 00 00 00 + 22.537722 1 1 Tx d 8 FF FE 00 00 00 00 00 00 + 22.546877 1 1 Tx d 8 FF FE 00 00 00 00 00 00 + 22.557099 1 1 Tx d 8 FF FE 00 00 00 00 00 00 + 22.567875 1 1 Tx d 8 FF FE 00 00 00 00 00 00 + 22.577387 1 1 Tx d 8 FF FE 00 00 00 00 00 00 + 22.587399 1 1 Tx d 8 FF FE 00 00 00 00 00 00 + 22.597384 1 1 Tx d 8 FF FE 00 00 00 00 00 00 + 22.607381 1 1 Tx d 8 FF FE 00 00 00 00 00 00 + 22.617385 1 1 Tx d 8 FF FE 00 00 00 00 00 00 + 22.627383 1 1 Tx d 8 FF FE 00 00 00 00 00 00 + 22.637383 1 1 Tx d 8 FF FE 00 00 00 00 00 00 + 22.647490 1 1 Tx d 8 FF FE 00 00 00 00 00 00 + 22.657209 1 1 Tx d 8 FF FE 00 00 00 00 00 00 + 22.666885 1 1 Tx d 8 FF FE 00 00 00 00 00 00 + 22.676886 1 1 Tx d 8 FF FE 00 00 00 00 00 00 + 22.686895 1 1 Tx d 8 FF FE 00 00 00 00 00 00 + 22.697213 1 1 Tx d 8 FF FE 00 00 00 00 00 00 + 22.707109 1 1 Tx d 8 FF FE 00 00 00 00 00 00 + 22.716862 1 1 Tx d 8 FF FE 00 00 00 00 00 00 + 22.726902 1 1 Tx d 8 FF FE 00 00 00 00 00 00 + 22.736862 1 1 Tx d 8 FF FE 00 00 00 00 00 00 + 22.747170 1 1 Tx d 8 FF FE 00 00 00 00 00 00 + 22.757215 1 1 Tx d 8 FF FE 00 00 00 00 00 00 + 22.767369 1 1 Tx d 8 FF FE 00 00 00 00 00 00 + 22.777367 1 1 Tx d 8 FF FE 00 00 00 00 00 00 + 22.787416 1 1 Tx d 8 FF FE 00 00 00 00 00 00 + 22.797377 1 1 Tx d 8 FF FE 00 00 00 00 00 00 + 22.807368 1 1 Tx d 8 FF FE 00 00 00 00 00 00 + 22.817390 1 1 Tx d 8 FF FE 00 00 00 00 00 00 + 22.827622 1 1 Tx d 8 FF FE 00 00 00 00 00 00 + 22.837618 1 1 Tx d 8 FF FE 00 00 00 00 00 00 + 22.847850 1 1 Tx d 8 FF FE 00 00 00 00 00 00 + 22.857717 1 1 Tx d 8 FF FE 00 00 00 00 00 00 + 22.867407 1 1 Tx d 8 FF FE 00 00 00 00 00 00 + 22.877402 1 1 Tx d 8 FF FE 00 00 00 00 00 00 + 22.887402 1 1 Tx d 8 FF FE 00 00 00 00 00 00 + 22.897406 1 1 Tx d 8 FF FE 00 00 00 00 00 00 + 22.907401 1 1 Tx d 8 FF FE 00 00 00 00 00 00 + 22.917473 1 1 Tx d 8 FF FE 00 00 00 00 00 00 + 22.927484 1 1 Tx d 8 FF FE 00 00 00 00 00 00 + 22.937086 1 1 Tx d 8 FF FE 00 00 00 00 00 00 + 22.947183 1 1 Tx d 8 FF FE 00 00 00 00 00 00 + 22.957183 1 1 Tx d 8 FF FE 00 00 00 00 00 00 + 22.967587 1 1 Tx d 8 FF FE 00 00 00 00 00 00 + 22.977489 1 1 Tx d 8 FF FE 00 00 00 00 00 00 + 22.987482 1 1 Tx d 8 FF FE 00 00 00 00 00 00 + 22.998102 1 1 Tx d 8 FF FE 00 00 00 00 00 00 + 23.007541 1 1 Tx d 8 FF FE 00 00 00 00 00 00 + 23.017569 1 1 Tx d 8 FF FE 00 00 00 00 00 00 + 23.027583 1 1 Tx d 8 FF FE 00 00 00 00 00 00 + 23.037635 1 1 Tx d 8 FF FE 00 00 00 00 00 00 + 23.047666 1 1 Tx d 8 FF FE 00 00 00 00 00 00 + 23.057377 1 1 Tx d 8 FF FE 00 00 00 00 00 00 + 23.067695 1 1 Tx d 8 FF FE 00 00 00 00 00 00 + 23.077756 1 1 Tx d 8 FF FE 00 00 00 00 00 00 + 23.087756 1 1 Tx d 8 FF FE 00 00 00 00 00 00 + 23.097759 1 1 Tx d 8 FF FE 00 00 00 00 00 00 + 23.107792 1 1 Tx d 8 FF FE 00 00 00 00 00 00 + 23.117789 1 1 Tx d 8 FF FE 00 00 00 00 00 00 + 23.127785 1 1 Tx d 8 FF FE 00 00 00 00 00 00 + 23.137785 1 1 Tx d 8 FF FE 00 00 00 00 00 00 + 23.147789 1 1 Tx d 8 FF FE 00 00 00 00 00 00 + 23.157846 1 1 Tx d 8 FF FE 00 00 00 00 00 00 + 23.167359 1 1 Tx d 8 FF FE 00 00 00 00 00 00 + 23.177363 1 1 Tx d 8 FF FE 00 00 00 00 00 00 + 23.187359 1 1 Tx d 8 FF FE 00 00 00 00 00 00 + 23.197356 1 1 Tx d 8 FF FE 00 00 00 00 00 00 + 23.207354 1 1 Tx d 8 FF FE 00 00 00 00 00 00 + 23.217357 1 1 Tx d 8 FF FE 00 00 00 00 00 00 + 23.227371 1 1 Tx d 8 FF FE 00 00 00 00 00 00 + 23.237355 1 1 Tx d 8 FF FE 00 00 00 00 00 00 + 23.247357 1 1 Tx d 8 FF FE 00 00 00 00 00 00 + 23.257359 1 1 Tx d 8 FF FE 00 00 00 00 00 00 + 23.267867 1 1 Tx d 8 FF FE 00 00 00 00 00 00 + 23.277865 1 1 Tx d 8 FF FE 00 00 00 00 00 00 + 23.287871 1 1 Tx d 8 FF FE 00 00 00 00 00 00 + 23.297875 1 1 Tx d 8 FF FE 00 00 00 00 00 00 + 23.307867 1 1 Tx d 8 FF FE 00 00 00 00 00 00 + 23.317868 1 1 Tx d 8 FF FE 00 00 00 00 00 00 + 23.327863 1 1 Tx d 8 FF FE 00 00 00 00 00 00 + 23.337869 1 1 Tx d 8 FF FE 00 00 00 00 00 00 + 23.347868 1 1 Tx d 8 FF FE 00 00 00 00 00 00 + 23.357813 1 1 Tx d 8 FF FE 00 00 00 00 00 00 + 23.367378 1 1 Tx d 8 FF FE 00 00 00 00 00 00 + 23.377380 1 1 Tx d 8 FF FE 00 00 00 00 00 00 + 23.387377 1 1 Tx d 8 FF FE 00 00 00 00 00 00 + 23.397379 1 1 Tx d 8 FF FE 00 00 00 00 00 00 + 23.407373 1 1 Tx d 8 FF FE 00 00 00 00 00 00 + 23.417377 1 1 Tx d 8 FF FE 00 00 00 00 00 00 + 23.427372 1 1 Tx d 8 FF FE 00 00 00 00 00 00 + 23.437377 1 1 Tx d 8 FF FE 00 00 00 00 00 00 + 23.446964 1 1 Tx d 8 FF FE 00 00 00 00 00 00 + 23.456957 1 1 Tx d 8 FF FE 00 00 00 00 00 00 + 23.467338 1 1 Tx d 8 FF FE 00 00 00 00 00 00 + 23.477363 1 1 Tx d 8 FF FE 00 00 00 00 00 00 + 23.487687 1 1 Tx d 8 FF FE 00 00 00 00 00 00 + 23.497704 1 1 Tx d 8 FF FE 00 00 00 00 00 00 + 23.507696 1 1 Tx d 8 FF FE 00 00 00 00 00 00 + 23.517766 1 1 Tx d 8 FF FE 00 00 00 00 00 00 + 23.527774 1 1 Tx d 8 FF FE 00 00 00 00 00 00 + 23.537769 1 1 Tx d 8 FF FE 00 00 00 00 00 00 + 23.546856 1 1 Tx d 8 FF FE 00 00 00 00 00 00 + 23.556860 1 1 Tx d 8 FF FE 00 00 00 00 00 00 + 23.567456 1 1 Tx d 8 FF FE 00 00 00 00 00 00 +End TriggerBlock diff --git a/examples/vehicle_abstraction_example/vehicle_abstraction_example_receiver_ms.asc b/examples/vehicle_abstraction_example/vehicle_abstraction_example_receiver_ms.asc new file mode 100644 index 0000000..1eb74c9 --- /dev/null +++ b/examples/vehicle_abstraction_example/vehicle_abstraction_example_receiver_ms.asc @@ -0,0 +1,1505 @@ +date Sat Mar 28 14:50:11 2026 +base hex timestamps absolute +Begin TriggerBlock Sat Mar 28 14:50:11 2026 + 0.000000 Start of measurement + 0.101371 1 1 Tx d 8 05 8C 00 00 00 00 00 00 + 0.111491 1 1 Tx d 8 05 8C 00 00 00 00 00 00 + 0.121404 1 1 Tx d 8 05 8C 00 00 00 00 00 00 + 0.142536 1 1 Tx d 8 05 8C 00 00 00 00 00 00 + 0.153468 1 1 Tx d 8 05 8C 00 00 00 00 00 00 + 0.174450 1 1 Tx d 8 05 8C 00 00 00 00 00 00 + 0.184402 1 1 Tx d 8 05 8C 00 00 00 00 00 00 + 0.205528 1 1 Tx d 8 05 8C 00 00 00 00 00 00 + 0.216037 1 1 Tx d 8 05 8C 00 00 00 00 00 00 + 0.238151 1 1 Tx d 8 05 8C 00 00 00 00 00 00 + 0.249112 1 1 Tx d 8 05 8C 00 00 00 00 00 00 + 0.270032 1 1 Tx d 8 05 8C 00 00 00 00 00 00 + 0.280029 1 1 Tx d 8 05 8C 00 00 00 00 00 00 + 0.291122 1 1 Tx d 8 05 8C 00 00 00 00 00 00 + 0.311802 1 1 Tx d 8 05 8C 00 00 00 00 00 00 + 0.332892 1 1 Tx d 8 05 8C 00 00 00 00 00 00 + 0.343894 1 1 Tx d 8 05 8C 00 00 00 00 00 00 + 0.354913 1 1 Tx d 8 05 8C 00 00 00 00 00 00 + 0.376845 1 1 Tx d 8 05 8C 00 00 00 00 00 00 + 0.386808 1 1 Tx d 8 05 8C 00 00 00 00 00 00 + 0.407905 1 1 Tx d 8 05 8C 00 00 00 00 00 00 + 0.418689 1 1 Tx d 8 05 8C 00 00 00 00 00 00 + 0.439580 1 1 Tx d 8 05 8C 00 00 00 00 00 00 + 0.449587 1 1 Tx d 8 05 8C 00 00 00 00 00 00 + 0.471680 1 1 Tx d 8 05 8C 00 00 00 00 00 00 + 0.481594 1 1 Tx d 8 05 8C 00 00 00 00 00 00 + 0.502673 1 1 Tx d 8 05 8C 00 00 00 00 00 00 + 0.513281 1 1 Tx d 8 05 8C 00 00 00 00 00 00 + 0.533692 1 1 Tx d 8 05 8C 00 00 00 00 00 00 + 0.543894 1 1 Tx d 8 05 8C 00 00 00 00 00 00 + 0.565794 1 1 Tx d 8 05 8C 00 00 00 00 00 00 + 0.575755 1 1 Tx d 8 05 8C 00 00 00 00 00 00 + 0.595850 1 1 Tx d 8 05 8C 00 00 00 00 00 00 + 0.605877 1 1 Tx d 8 05 8C 00 00 00 00 00 00 + 0.626569 1 1 Tx d 8 05 8C 00 00 00 00 00 00 + 0.637684 1 1 Tx d 8 05 8C 00 00 00 00 00 00 + 0.658641 1 1 Tx d 8 05 8C 00 00 00 00 00 00 + 0.669643 1 1 Tx d 8 05 8C 00 00 00 00 00 00 + 0.690471 1 1 Tx d 8 05 8C 00 00 00 00 00 00 + 0.700659 1 1 Tx d 8 05 8C 00 00 00 00 00 00 + 0.722060 1 1 Tx d 8 05 8C 00 00 00 00 00 00 + 0.732106 1 1 Tx d 8 05 8C 00 00 00 00 00 00 + 0.752085 1 1 Tx d 8 05 8C 00 00 00 00 00 00 + 0.762102 1 1 Tx d 8 05 8C 00 00 00 00 00 00 + 0.783201 1 1 Tx d 8 05 8C 00 00 00 00 00 00 + 0.794292 1 1 Tx d 8 05 8C 00 00 00 00 00 00 + 0.815875 1 1 Tx d 8 05 8C 00 00 00 00 00 00 + 0.825900 1 1 Tx d 8 05 8C 00 00 00 00 00 00 + 0.846891 1 1 Tx d 8 05 8C 00 00 00 00 00 00 + 0.858001 1 1 Tx d 8 05 8C 00 00 00 00 00 00 + 0.878966 1 1 Tx d 8 05 8C 00 00 00 00 00 00 + 0.888892 1 1 Tx d 8 05 8C 00 00 00 00 00 00 + 0.910037 1 1 Tx d 8 05 8C 00 00 00 00 00 00 + 0.920641 1 1 Tx d 8 05 8C 00 00 00 00 00 00 + 0.931508 1 1 Tx d 8 05 8C 00 00 00 00 00 00 + 0.951549 1 1 Tx d 8 05 8C 00 00 00 00 00 00 + 0.973528 1 1 Tx d 8 05 8C 00 00 00 00 00 00 + 0.984617 1 1 Tx d 8 05 8C 00 00 00 00 00 00 + 0.995545 1 1 Tx d 8 05 8C 00 00 00 00 00 00 + 1.017005 1 1 Tx d 8 05 8C 00 00 00 00 00 00 + 1.028066 1 1 Tx d 8 05 8C 00 00 00 00 00 00 + 1.049892 1 1 Tx d 8 05 8C 00 00 00 00 00 00 + 1.060132 1 1 Tx d 8 05 8C 00 00 00 00 00 00 + 1.080773 1 1 Tx d 8 05 8C 00 00 00 00 00 00 + 1.090872 1 1 Tx d 8 05 8C 00 00 00 00 00 00 + 1.111944 1 1 Tx d 8 05 8C 00 00 00 00 00 00 + 1.122054 1 1 Tx d 8 05 8C 00 00 00 00 00 00 + 1.143146 1 1 Tx d 8 05 8C 00 00 00 00 00 00 + 1.153034 1 1 Tx d 8 05 8C 00 00 00 00 00 00 + 1.174570 1 1 Tx d 8 05 8C 00 00 00 00 00 00 + 1.184673 1 1 Tx d 8 05 8C 00 00 00 00 00 00 + 1.204911 1 1 Tx d 8 05 8C 00 00 00 00 00 00 + 1.215330 1 1 Tx d 8 05 8C 00 00 00 00 00 00 + 1.237063 1 1 Tx d 8 05 8C 00 00 00 00 00 00 + 1.248140 1 1 Tx d 8 05 8C 00 00 00 00 00 00 + 1.269061 1 1 Tx d 8 05 8C 00 00 00 00 00 00 + 1.279703 1 1 Tx d 8 05 8C 00 00 00 00 00 00 + 1.300665 1 1 Tx d 8 05 8C 00 00 00 00 00 00 + 1.311099 1 1 Tx d 8 05 8C 00 00 00 00 00 00 + 1.331781 1 1 Tx d 8 05 8C 00 00 00 00 00 00 + 1.342383 1 1 Tx d 8 05 8C 00 00 00 00 00 00 + 1.363384 1 1 Tx d 8 05 8C 00 00 00 00 00 00 + 1.373658 1 1 Tx d 8 05 8C 00 00 00 00 00 00 + 1.393495 1 1 Tx d 8 05 8C 00 00 00 00 00 00 + 1.403510 1 1 Tx d 8 05 8C 00 00 00 00 00 00 + 1.424101 1 1 Tx d 8 05 8C 00 00 00 00 00 00 + 1.435204 1 1 Tx d 8 05 8C 00 00 00 00 00 00 + 1.455831 1 1 Tx d 8 05 8C 00 00 00 00 00 00 + 1.465840 1 1 Tx d 8 05 8C 00 00 00 00 00 00 + 1.487938 1 1 Tx d 8 05 8C 00 00 00 00 00 00 + 1.497998 1 1 Tx d 8 05 8C 00 00 00 00 00 00 + 1.518439 1 1 Tx d 8 05 8C 00 00 00 00 00 00 + 1.528476 1 1 Tx d 8 05 8C 00 00 00 00 00 00 + 1.550527 1 1 Tx d 8 05 8C 00 00 00 00 00 00 + 1.560465 1 1 Tx d 8 05 8C 00 00 00 00 00 00 + 1.582524 1 1 Tx d 8 05 8C 00 00 00 00 00 00 + 1.593528 1 1 Tx d 8 05 8C 00 00 00 00 00 00 + 1.603530 1 1 Tx d 8 05 8C 00 00 00 00 00 00 + 1.624034 1 1 Tx d 8 05 8C 00 00 00 00 00 00 + 1.645020 1 1 Tx d 8 05 8C 00 00 00 00 00 00 + 1.658230 1 1 Tx d 8 0B 1A 00 00 00 00 00 00 + 1.668035 1 1 Tx d 8 0B 1A 00 00 00 00 00 00 + 1.688991 1 1 Tx d 8 0B 1A 00 00 00 00 00 00 + 1.698999 1 1 Tx d 8 0B 1A 00 00 00 00 00 00 + 1.719947 1 1 Tx d 8 0B 1A 00 00 00 00 00 00 + 1.730801 1 1 Tx d 8 0B 1A 00 00 00 00 00 00 + 1.751890 1 1 Tx d 8 0B 1A 00 00 00 00 00 00 + 1.762743 1 1 Tx d 8 0B 1A 00 00 00 00 00 00 + 1.784735 1 1 Tx d 8 0B 1A 00 00 00 00 00 00 + 1.795742 1 1 Tx d 8 0B 1A 00 00 00 00 00 00 + 1.815844 1 1 Tx d 8 0B 1A 00 00 00 00 00 00 + 1.825921 1 1 Tx d 8 0B 1A 00 00 00 00 00 00 + 1.845870 1 1 Tx d 8 0B 1A 00 00 00 00 00 00 + 1.856918 1 1 Tx d 8 0B 1A 00 00 00 00 00 00 + 1.877233 1 1 Tx d 8 0B 1A 00 00 00 00 00 00 + 1.888218 1 1 Tx d 8 0B 1A 00 00 00 00 00 00 + 1.908362 1 1 Tx d 8 0B 1A 00 00 00 00 00 00 + 1.919052 1 1 Tx d 8 0B 1A 00 00 00 00 00 00 + 1.939984 1 1 Tx d 8 0B 1A 00 00 00 00 00 00 + 1.950975 1 1 Tx d 8 0B 1A 00 00 00 00 00 00 + 1.972084 1 1 Tx d 8 0B 1A 00 00 00 00 00 00 + 1.983046 1 1 Tx d 8 0B 1A 00 00 00 00 00 00 + 2.002994 1 1 Tx d 8 0B 1A 00 00 00 00 00 00 + 2.013604 1 1 Tx d 8 0B 1A 00 00 00 00 00 00 + 2.033521 1 1 Tx d 8 0B 1A 00 00 00 00 00 00 + 2.043579 1 1 Tx d 8 0B 1A 00 00 00 00 00 00 + 2.064684 1 1 Tx d 8 0B 1A 00 00 00 00 00 00 + 2.075617 1 1 Tx d 8 0B 1A 00 00 00 00 00 00 + 2.095563 1 1 Tx d 8 0B 1A 00 00 00 00 00 00 + 2.106630 1 1 Tx d 8 0B 1A 00 00 00 00 00 00 + 2.127143 1 1 Tx d 8 0B 1A 00 00 00 00 00 00 + 2.137142 1 1 Tx d 8 0B 1A 00 00 00 00 00 00 + 2.158148 1 1 Tx d 8 0B 1A 00 00 00 00 00 00 + 2.168094 1 1 Tx d 8 0B 1A 00 00 00 00 00 00 + 2.189240 1 1 Tx d 8 0B 1A 00 00 00 00 00 00 + 2.199197 1 1 Tx d 8 0B 1A 00 00 00 00 00 00 + 2.220019 1 1 Tx d 8 0B 1A 00 00 00 00 00 00 + 2.231017 1 1 Tx d 8 0B 1A 00 00 00 00 00 00 + 2.251942 1 1 Tx d 8 0B 1A 00 00 00 00 00 00 + 2.263015 1 1 Tx d 8 0B 1A 00 00 00 00 00 00 + 2.285026 1 1 Tx d 8 0B 1A 00 00 00 00 00 00 + 2.296057 1 1 Tx d 8 0B 1A 00 00 00 00 00 00 + 2.307052 1 1 Tx d 8 0B 1A 00 00 00 00 00 00 + 2.327585 1 1 Tx d 8 0B 1A 00 00 00 00 00 00 + 2.348703 1 1 Tx d 8 0B 1A 00 00 00 00 00 00 + 2.359547 1 1 Tx d 8 0B 1A 00 00 00 00 00 00 + 2.369592 1 1 Tx d 8 0B 1A 00 00 00 00 00 00 + 2.391682 1 1 Tx d 8 0B 1A 00 00 00 00 00 00 + 2.402564 1 1 Tx d 8 0B 1A 00 00 00 00 00 00 + 2.423062 1 1 Tx d 8 0B 1A 00 00 00 00 00 00 + 2.433154 1 1 Tx d 8 0B 1A 00 00 00 00 00 00 + 2.454114 1 1 Tx d 8 0B 1A 00 00 00 00 00 00 + 2.464013 1 1 Tx d 8 0B 1A 00 00 00 00 00 00 + 2.485989 1 1 Tx d 8 0B 1A 00 00 00 00 00 00 + 2.496001 1 1 Tx d 8 0B 1A 00 00 00 00 00 00 + 2.516468 1 1 Tx d 8 0B 1A 00 00 00 00 00 00 + 2.527601 1 1 Tx d 8 0B 1A 00 00 00 00 00 00 + 2.549519 1 1 Tx d 8 0B 1A 00 00 00 00 00 00 + 2.560547 1 1 Tx d 8 0B 1A 00 00 00 00 00 00 + 2.582931 1 1 Tx d 8 0B 1A 00 00 00 00 00 00 + 2.593254 1 1 Tx d 8 0B 1A 00 00 00 00 00 00 + 2.604222 1 1 Tx d 8 0B 1A 00 00 00 00 00 00 + 2.625047 1 1 Tx d 8 0B 1A 00 00 00 00 00 00 + 2.635697 1 1 Tx d 8 0B 1A 00 00 00 00 00 00 + 2.656092 1 1 Tx d 8 0B 1A 00 00 00 00 00 00 + 2.667084 1 1 Tx d 8 0B 1A 00 00 00 00 00 00 + 2.687082 1 1 Tx d 8 0B 1A 00 00 00 00 00 00 + 2.707121 1 1 Tx d 8 0B 1A 00 00 00 00 00 00 + 2.717689 1 1 Tx d 8 0B 1A 00 00 00 00 00 00 + 2.728592 1 1 Tx d 8 0B 1A 00 00 00 00 00 00 + 2.748602 1 1 Tx d 8 0B 1A 00 00 00 00 00 00 + 2.769738 1 1 Tx d 8 0B 1A 00 00 00 00 00 00 + 2.780647 1 1 Tx d 8 0B 1A 00 00 00 00 00 00 + 2.791107 1 1 Tx d 8 0B 1A 00 00 00 00 00 00 + 2.811847 1 1 Tx d 8 0B 1A 00 00 00 00 00 00 + 2.832858 1 1 Tx d 8 0B 1A 00 00 00 00 00 00 + 2.842767 1 1 Tx d 8 0B 1A 00 00 00 00 00 00 + 2.853859 1 1 Tx d 8 0B 1A 00 00 00 00 00 00 + 2.875858 1 1 Tx d 8 0B 1A 00 00 00 00 00 00 + 2.886594 1 1 Tx d 8 0B 1A 00 00 00 00 00 00 + 2.906615 1 1 Tx d 8 0B 1A 00 00 00 00 00 00 + 2.917194 1 1 Tx d 8 0B 1A 00 00 00 00 00 00 + 2.938189 1 1 Tx d 8 0B 1A 00 00 00 00 00 00 + 2.949283 1 1 Tx d 8 0B 1A 00 00 00 00 00 00 + 2.970221 1 1 Tx d 8 0B 1A 00 00 00 00 00 00 + 2.980192 1 1 Tx d 8 0B 1A 00 00 00 00 00 00 + 3.001286 1 1 Tx d 8 0B 1A 00 00 00 00 00 00 + 3.011229 1 1 Tx d 8 0B 1A 00 00 00 00 00 00 + 3.031747 1 1 Tx d 8 0B 1A 00 00 00 00 00 00 + 3.042827 1 1 Tx d 8 0B 1A 00 00 00 00 00 00 + 3.063452 1 1 Tx d 8 0B 1A 00 00 00 00 00 00 + 3.074458 1 1 Tx d 8 0B 1A 00 00 00 00 00 00 + 3.095018 1 1 Tx d 8 0B 1A 00 00 00 00 00 00 + 3.105113 1 1 Tx d 8 0B 1A 00 00 00 00 00 00 + 3.125794 1 1 Tx d 8 0B 1A 00 00 00 00 00 00 + 3.136878 1 1 Tx d 8 0B 1A 00 00 00 00 00 00 + 3.157800 1 1 Tx d 8 0B 1A 00 00 00 00 00 00 + 3.168795 1 1 Tx d 8 0B 1A 00 00 00 00 00 00 + 3.190198 1 1 Tx d 8 0B 1A 00 00 00 00 00 00 + 3.200287 1 1 Tx d 8 0B 1A 00 00 00 00 00 00 + 3.222443 1 1 Tx d 8 10 A6 00 00 00 00 00 00 + 3.232027 1 1 Tx d 8 10 A6 00 00 00 00 00 00 + 3.252632 1 1 Tx d 8 10 A6 00 00 00 00 00 00 + 3.262606 1 1 Tx d 8 10 A6 00 00 00 00 00 00 + 3.283713 1 1 Tx d 8 10 A6 00 00 00 00 00 00 + 3.294655 1 1 Tx d 8 10 A6 00 00 00 00 00 00 + 3.315472 1 1 Tx d 8 10 A6 00 00 00 00 00 00 + 3.325337 1 1 Tx d 8 10 A6 00 00 00 00 00 00 + 3.346366 1 1 Tx d 8 10 A6 00 00 00 00 00 00 + 3.356297 1 1 Tx d 8 10 A6 00 00 00 00 00 00 + 3.376364 1 1 Tx d 8 10 A6 00 00 00 00 00 00 + 3.387451 1 1 Tx d 8 10 A6 00 00 00 00 00 00 + 3.407443 1 1 Tx d 8 10 A6 00 00 00 00 00 00 + 3.418070 1 1 Tx d 8 10 A6 00 00 00 00 00 00 + 3.438143 1 1 Tx d 8 10 A6 00 00 00 00 00 00 + 3.448143 1 1 Tx d 8 10 A6 00 00 00 00 00 00 + 3.469139 1 1 Tx d 8 10 A6 00 00 00 00 00 00 + 3.480074 1 1 Tx d 8 10 A6 00 00 00 00 00 00 + 3.501074 1 1 Tx d 8 10 A6 00 00 00 00 00 00 + 3.512090 1 1 Tx d 8 10 A6 00 00 00 00 00 00 + 3.533573 1 1 Tx d 8 10 A6 00 00 00 00 00 00 + 3.543586 1 1 Tx d 8 10 A6 00 00 00 00 00 00 + 3.564575 1 1 Tx d 8 10 A6 00 00 00 00 00 00 + 3.575543 1 1 Tx d 8 10 A6 00 00 00 00 00 00 + 3.596540 1 1 Tx d 8 10 A6 00 00 00 00 00 00 + 3.606645 1 1 Tx d 8 10 A6 00 00 00 00 00 00 + 3.628186 1 1 Tx d 8 10 A6 00 00 00 00 00 00 + 3.638147 1 1 Tx d 8 10 A6 00 00 00 00 00 00 + 3.659137 1 1 Tx d 8 10 A6 00 00 00 00 00 00 + 3.670197 1 1 Tx d 8 10 A6 00 00 00 00 00 00 + 3.691141 1 1 Tx d 8 10 A6 00 00 00 00 00 00 + 3.701140 1 1 Tx d 8 10 A6 00 00 00 00 00 00 + 3.721651 1 1 Tx d 8 10 A6 00 00 00 00 00 00 + 3.731652 1 1 Tx d 8 10 A6 00 00 00 00 00 00 + 3.752646 1 1 Tx d 8 10 A6 00 00 00 00 00 00 + 3.762647 1 1 Tx d 8 10 A6 00 00 00 00 00 00 + 3.782659 1 1 Tx d 8 10 A6 00 00 00 00 00 00 + 3.792652 1 1 Tx d 8 10 A6 00 00 00 00 00 00 + 3.813154 1 1 Tx d 8 10 A6 00 00 00 00 00 00 + 3.823152 1 1 Tx d 8 10 A6 00 00 00 00 00 00 + 3.845152 1 1 Tx d 8 10 A6 00 00 00 00 00 00 + 3.856164 1 1 Tx d 8 10 A6 00 00 00 00 00 00 + 3.877154 1 1 Tx d 8 10 A6 00 00 00 00 00 00 + 3.887154 1 1 Tx d 8 10 A6 00 00 00 00 00 00 + 3.907157 1 1 Tx d 8 10 A6 00 00 00 00 00 00 + 3.917557 1 1 Tx d 8 10 A6 00 00 00 00 00 00 + 3.938661 1 1 Tx d 8 10 A6 00 00 00 00 00 00 + 3.949668 1 1 Tx d 8 10 A6 00 00 00 00 00 00 + 3.969621 1 1 Tx d 8 10 A6 00 00 00 00 00 00 + 3.979605 1 1 Tx d 8 10 A6 00 00 00 00 00 00 + 4.000600 1 1 Tx d 8 10 A6 00 00 00 00 00 00 + 4.012109 1 1 Tx d 8 10 A6 00 00 00 00 00 00 + 4.033168 1 1 Tx d 8 10 A6 00 00 00 00 00 00 + 4.044164 1 1 Tx d 8 10 A6 00 00 00 00 00 00 + 4.065164 1 1 Tx d 8 10 A6 00 00 00 00 00 00 + 4.075116 1 1 Tx d 8 10 A6 00 00 00 00 00 00 + 4.097162 1 1 Tx d 8 10 A6 00 00 00 00 00 00 + 4.107174 1 1 Tx d 8 10 A6 00 00 00 00 00 00 + 4.128669 1 1 Tx d 8 10 A6 00 00 00 00 00 00 + 4.138675 1 1 Tx d 8 10 A6 00 00 00 00 00 00 + 4.159674 1 1 Tx d 8 10 A6 00 00 00 00 00 00 + 4.170671 1 1 Tx d 8 10 A6 00 00 00 00 00 00 + 4.191670 1 1 Tx d 8 10 A6 00 00 00 00 00 00 + 4.201668 1 1 Tx d 8 10 A6 00 00 00 00 00 00 + 4.222175 1 1 Tx d 8 10 A6 00 00 00 00 00 00 + 4.232176 1 1 Tx d 8 10 A6 00 00 00 00 00 00 + 4.252851 1 1 Tx d 8 10 A6 00 00 00 00 00 00 + 4.262820 1 1 Tx d 8 10 A6 00 00 00 00 00 00 + 4.282790 1 1 Tx d 8 10 A6 00 00 00 00 00 00 + 4.292807 1 1 Tx d 8 10 A6 00 00 00 00 00 00 + 4.313376 1 1 Tx d 8 10 A6 00 00 00 00 00 00 + 4.323315 1 1 Tx d 8 10 A6 00 00 00 00 00 00 + 4.344306 1 1 Tx d 8 10 A6 00 00 00 00 00 00 + 4.355366 1 1 Tx d 8 10 A6 00 00 00 00 00 00 + 4.376358 1 1 Tx d 8 10 A6 00 00 00 00 00 00 + 4.386302 1 1 Tx d 8 10 A6 00 00 00 00 00 00 + 4.407258 1 1 Tx d 8 10 A6 00 00 00 00 00 00 + 4.417804 1 1 Tx d 8 10 A6 00 00 00 00 00 00 + 4.439868 1 1 Tx d 8 10 A6 00 00 00 00 00 00 + 4.449869 1 1 Tx d 8 10 A6 00 00 00 00 00 00 + 4.469869 1 1 Tx d 8 10 A6 00 00 00 00 00 00 + 4.479865 1 1 Tx d 8 10 A6 00 00 00 00 00 00 + 4.499872 1 1 Tx d 8 10 A6 00 00 00 00 00 00 + 4.514450 1 1 Tx d 8 10 A6 00 00 00 00 00 00 + 4.534376 1 1 Tx d 8 10 A6 00 00 00 00 00 00 + 4.544377 1 1 Tx d 8 10 A6 00 00 00 00 00 00 + 4.565385 1 1 Tx d 8 10 A6 00 00 00 00 00 00 + 4.575487 1 1 Tx d 8 10 A6 00 00 00 00 00 00 + 4.595478 1 1 Tx d 8 10 A6 00 00 00 00 00 00 + 4.605642 1 1 Tx d 8 10 A6 00 00 00 00 00 00 + 4.626851 1 1 Tx d 8 10 A6 00 00 00 00 00 00 + 4.637886 1 1 Tx d 8 10 A6 00 00 00 00 00 00 + 4.658934 1 1 Tx d 8 10 A6 00 00 00 00 00 00 + 4.669830 1 1 Tx d 8 10 A6 00 00 00 00 00 00 + 4.690832 1 1 Tx d 8 10 A6 00 00 00 00 00 00 + 4.700860 1 1 Tx d 8 10 A6 00 00 00 00 00 00 + 4.721718 1 1 Tx d 8 10 A6 00 00 00 00 00 00 + 4.732716 1 1 Tx d 8 10 A6 00 00 00 00 00 00 + 4.753721 1 1 Tx d 8 10 A6 00 00 00 00 00 00 + 4.764716 1 1 Tx d 8 10 A6 00 00 00 00 00 00 + 4.785946 1 1 Tx d 8 16 34 00 00 00 00 00 00 + 4.795583 1 1 Tx d 8 16 34 00 00 00 00 00 00 + 4.816092 1 1 Tx d 8 16 34 00 00 00 00 00 00 + 4.826059 1 1 Tx d 8 16 34 00 00 00 00 00 00 + 4.848086 1 1 Tx d 8 16 34 00 00 00 00 00 00 + 4.859090 1 1 Tx d 8 16 34 00 00 00 00 00 00 + 4.870180 1 1 Tx d 8 16 34 00 00 00 00 00 00 + 4.891091 1 1 Tx d 8 16 34 00 00 00 00 00 00 + 4.901060 1 1 Tx d 8 16 34 00 00 00 00 00 00 + 4.922595 1 1 Tx d 8 16 34 00 00 00 00 00 00 + 4.932590 1 1 Tx d 8 16 34 00 00 00 00 00 00 + 4.952588 1 1 Tx d 8 16 34 00 00 00 00 00 00 + 4.963595 1 1 Tx d 8 16 34 00 00 00 00 00 00 + 4.984593 1 1 Tx d 8 16 34 00 00 00 00 00 00 + 4.994591 1 1 Tx d 8 16 34 00 00 00 00 00 00 + 5.015104 1 1 Tx d 8 16 34 00 00 00 00 00 00 + 5.036109 1 1 Tx d 8 16 34 00 00 00 00 00 00 + 5.046101 1 1 Tx d 8 16 34 00 00 00 00 00 00 + 5.061247 1 1 Tx d 8 16 34 00 00 00 00 00 00 + 5.081538 1 1 Tx d 8 16 34 00 00 00 00 00 00 + 5.091542 1 1 Tx d 8 16 34 00 00 00 00 00 00 + 5.112562 1 1 Tx d 8 16 34 00 00 00 00 00 00 + 5.122803 1 1 Tx d 8 16 34 00 00 00 00 00 00 + 5.143722 1 1 Tx d 8 16 34 00 00 00 00 00 00 + 5.154819 1 1 Tx d 8 16 34 00 00 00 00 00 00 + 5.176835 1 1 Tx d 8 16 34 00 00 00 00 00 00 + 5.187801 1 1 Tx d 8 16 34 00 00 00 00 00 00 + 5.197724 1 1 Tx d 8 16 34 00 00 00 00 00 00 + 5.218278 1 1 Tx d 8 16 34 00 00 00 00 00 00 + 5.229282 1 1 Tx d 8 16 34 00 00 00 00 00 00 + 5.251957 1 1 Tx d 8 16 34 00 00 00 00 00 00 + 5.261353 1 1 Tx d 8 16 34 00 00 00 00 00 00 + 5.283369 1 1 Tx d 8 16 34 00 00 00 00 00 00 + 5.294387 1 1 Tx d 8 16 34 00 00 00 00 00 00 + 5.314904 1 1 Tx d 8 16 34 00 00 00 00 00 00 + 5.324952 1 1 Tx d 8 16 34 00 00 00 00 00 00 + 5.344964 1 1 Tx d 8 16 34 00 00 00 00 00 00 + 5.355987 1 1 Tx d 8 16 34 00 00 00 00 00 00 + 5.376954 1 1 Tx d 8 16 34 00 00 00 00 00 00 + 5.386909 1 1 Tx d 8 16 34 00 00 00 00 00 00 + 5.406953 1 1 Tx d 8 16 34 00 00 00 00 00 00 + 5.417489 1 1 Tx d 8 16 34 00 00 00 00 00 00 + 5.438457 1 1 Tx d 8 16 34 00 00 00 00 00 00 + 5.448455 1 1 Tx d 8 16 34 00 00 00 00 00 00 + 5.469460 1 1 Tx d 8 16 34 00 00 00 00 00 00 + 5.480467 1 1 Tx d 8 16 34 00 00 00 00 00 00 + 5.501281 1 1 Tx d 8 16 34 00 00 00 00 00 00 + 5.512907 1 1 Tx d 8 16 34 00 00 00 00 00 00 + 5.533911 1 1 Tx d 8 16 34 00 00 00 00 00 00 + 5.544912 1 1 Tx d 8 16 34 00 00 00 00 00 00 + 5.565879 1 1 Tx d 8 16 34 00 00 00 00 00 00 + 5.576994 1 1 Tx d 8 16 34 00 00 00 00 00 00 + 5.587962 1 1 Tx d 8 16 34 00 00 00 00 00 00 + 5.608885 1 1 Tx d 8 16 34 00 00 00 00 00 00 + 5.628962 1 1 Tx d 8 16 34 00 00 00 00 00 00 + 5.640059 1 1 Tx d 8 16 34 00 00 00 00 00 00 + 5.660037 1 1 Tx d 8 16 34 00 00 00 00 00 00 + 5.670040 1 1 Tx d 8 16 34 00 00 00 00 00 00 + 5.692033 1 1 Tx d 8 16 34 00 00 00 00 00 00 + 5.702035 1 1 Tx d 8 16 34 00 00 00 00 00 00 + 5.713041 1 1 Tx d 8 16 34 00 00 00 00 00 00 + 5.733532 1 1 Tx d 8 16 34 00 00 00 00 00 00 + 5.744530 1 1 Tx d 8 16 34 00 00 00 00 00 00 + 5.765536 1 1 Tx d 8 16 34 00 00 00 00 00 00 + 5.775531 1 1 Tx d 8 16 34 00 00 00 00 00 00 + 5.797539 1 1 Tx d 8 16 34 00 00 00 00 00 00 + 5.807538 1 1 Tx d 8 16 34 00 00 00 00 00 00 + 5.828044 1 1 Tx d 8 16 34 00 00 00 00 00 00 + 5.838081 1 1 Tx d 8 16 34 00 00 00 00 00 00 + 5.859042 1 1 Tx d 8 16 34 00 00 00 00 00 00 + 5.880037 1 1 Tx d 8 16 34 00 00 00 00 00 00 + 5.890036 1 1 Tx d 8 16 34 00 00 00 00 00 00 + 5.909992 1 1 Tx d 8 16 34 00 00 00 00 00 00 + 5.920457 1 1 Tx d 8 16 34 00 00 00 00 00 00 + 5.941454 1 1 Tx d 8 16 34 00 00 00 00 00 00 + 5.952446 1 1 Tx d 8 16 34 00 00 00 00 00 00 + 5.963451 1 1 Tx d 8 16 34 00 00 00 00 00 00 + 5.984456 1 1 Tx d 8 16 34 00 00 00 00 00 00 + 5.994451 1 1 Tx d 8 16 34 00 00 00 00 00 00 + 6.014946 1 1 Tx d 8 16 34 00 00 00 00 00 00 + 6.035081 1 1 Tx d 8 16 34 00 00 00 00 00 00 + 6.045000 1 1 Tx d 8 16 34 00 00 00 00 00 00 + 6.064989 1 1 Tx d 8 16 34 00 00 00 00 00 00 + 6.076059 1 1 Tx d 8 16 34 00 00 00 00 00 00 + 6.096996 1 1 Tx d 8 16 34 00 00 00 00 00 00 + 6.106998 1 1 Tx d 8 16 34 00 00 00 00 00 00 + 6.127492 1 1 Tx d 8 16 34 00 00 00 00 00 00 + 6.137497 1 1 Tx d 8 16 34 00 00 00 00 00 00 + 6.159559 1 1 Tx d 8 16 34 00 00 00 00 00 00 + 6.170551 1 1 Tx d 8 16 34 00 00 00 00 00 00 + 6.191501 1 1 Tx d 8 16 34 00 00 00 00 00 00 + 6.201555 1 1 Tx d 8 16 34 00 00 00 00 00 00 + 6.222060 1 1 Tx d 8 16 34 00 00 00 00 00 00 + 6.232060 1 1 Tx d 8 16 34 00 00 00 00 00 00 + 6.253061 1 1 Tx d 8 16 34 00 00 00 00 00 00 + 6.263061 1 1 Tx d 8 16 34 00 00 00 00 00 00 + 6.284064 1 1 Tx d 8 16 34 00 00 00 00 00 00 + 6.295066 1 1 Tx d 8 16 34 00 00 00 00 00 00 + 6.315573 1 1 Tx d 8 16 34 00 00 00 00 00 00 + 6.326565 1 1 Tx d 8 16 34 00 00 00 00 00 00 + 6.361606 1 1 Tx d 8 1B C0 00 00 00 00 00 00 + 6.371543 1 1 Tx d 8 1B C0 00 00 00 00 00 00 + 6.393582 1 1 Tx d 8 1B C0 00 00 00 00 00 00 + 6.403478 1 1 Tx d 8 1B C0 00 00 00 00 00 00 + 6.424073 1 1 Tx d 8 1B C0 00 00 00 00 00 00 + 6.435086 1 1 Tx d 8 1B C0 00 00 00 00 00 00 + 6.456074 1 1 Tx d 8 1B C0 00 00 00 00 00 00 + 6.466687 1 1 Tx d 8 1B C0 00 00 00 00 00 00 + 6.488146 1 1 Tx d 8 1B C0 00 00 00 00 00 00 + 6.498102 1 1 Tx d 8 1B C0 00 00 00 00 00 00 + 6.518650 1 1 Tx d 8 1B C0 00 00 00 00 00 00 + 6.528653 1 1 Tx d 8 1B C0 00 00 00 00 00 00 + 6.549653 1 1 Tx d 8 1B C0 00 00 00 00 00 00 + 6.559657 1 1 Tx d 8 1B C0 00 00 00 00 00 00 + 6.581653 1 1 Tx d 8 1B C0 00 00 00 00 00 00 + 6.591627 1 1 Tx d 8 1B C0 00 00 00 00 00 00 + 6.613185 1 1 Tx d 8 1B C0 00 00 00 00 00 00 + 6.623170 1 1 Tx d 8 1B C0 00 00 00 00 00 00 + 6.644162 1 1 Tx d 8 1B C0 00 00 00 00 00 00 + 6.655182 1 1 Tx d 8 1B C0 00 00 00 00 00 00 + 6.666164 1 1 Tx d 8 1B C0 00 00 00 00 00 00 + 6.686162 1 1 Tx d 8 1B C0 00 00 00 00 00 00 + 6.707166 1 1 Tx d 8 1B C0 00 00 00 00 00 00 + 6.717666 1 1 Tx d 8 1B C0 00 00 00 00 00 00 + 6.728669 1 1 Tx d 8 1B C0 00 00 00 00 00 00 + 6.749663 1 1 Tx d 8 1B C0 00 00 00 00 00 00 + 6.760668 1 1 Tx d 8 1B C0 00 00 00 00 00 00 + 6.780667 1 1 Tx d 8 1B C0 00 00 00 00 00 00 + 6.791669 1 1 Tx d 8 1B C0 00 00 00 00 00 00 + 6.811611 1 1 Tx d 8 1B C0 00 00 00 00 00 00 + 6.832175 1 1 Tx d 8 1B C0 00 00 00 00 00 00 + 6.842120 1 1 Tx d 8 1B C0 00 00 00 00 00 00 + 6.864175 1 1 Tx d 8 1B C0 00 00 00 00 00 00 + 6.874172 1 1 Tx d 8 1B C0 00 00 00 00 00 00 + 6.885179 1 1 Tx d 8 1B C0 00 00 00 00 00 00 + 6.906179 1 1 Tx d 8 1B C0 00 00 00 00 00 00 + 6.916682 1 1 Tx d 8 1B C0 00 00 00 00 00 00 + 6.936686 1 1 Tx d 8 1B C0 00 00 00 00 00 00 + 6.957680 1 1 Tx d 8 1B C0 00 00 00 00 00 00 + 6.967679 1 1 Tx d 8 1B C0 00 00 00 00 00 00 + 6.978899 1 1 Tx d 8 1B C0 00 00 00 00 00 00 + 6.999463 1 1 Tx d 8 1B C0 00 00 00 00 00 00 + 7.020041 1 1 Tx d 8 1B C0 00 00 00 00 00 00 + 7.029965 1 1 Tx d 8 1B C0 00 00 00 00 00 00 + 7.051018 1 1 Tx d 8 1B C0 00 00 00 00 00 00 + 7.061556 1 1 Tx d 8 1B C0 00 00 00 00 00 00 + 7.081529 1 1 Tx d 8 1B C0 00 00 00 00 00 00 + 7.092524 1 1 Tx d 8 1B C0 00 00 00 00 00 00 + 7.112529 1 1 Tx d 8 1B C0 00 00 00 00 00 00 + 7.122977 1 1 Tx d 8 1B C0 00 00 00 00 00 00 + 7.144038 1 1 Tx d 8 1B C0 00 00 00 00 00 00 + 7.155040 1 1 Tx d 8 1B C0 00 00 00 00 00 00 + 7.166037 1 1 Tx d 8 1B C0 00 00 00 00 00 00 + 7.187033 1 1 Tx d 8 1B C0 00 00 00 00 00 00 + 7.207031 1 1 Tx d 8 1B C0 00 00 00 00 00 00 + 7.217533 1 1 Tx d 8 1B C0 00 00 00 00 00 00 + 7.238542 1 1 Tx d 8 1B C0 00 00 00 00 00 00 + 7.249537 1 1 Tx d 8 1B C0 00 00 00 00 00 00 + 7.269538 1 1 Tx d 8 1B C0 00 00 00 00 00 00 + 7.279537 1 1 Tx d 8 1B C0 00 00 00 00 00 00 + 7.299537 1 1 Tx d 8 1B C0 00 00 00 00 00 00 + 7.309539 1 1 Tx d 8 1B C0 00 00 00 00 00 00 + 7.330038 1 1 Tx d 8 1B C0 00 00 00 00 00 00 + 7.340044 1 1 Tx d 8 1B C0 00 00 00 00 00 00 + 7.360048 1 1 Tx d 8 1B C0 00 00 00 00 00 00 + 7.370046 1 1 Tx d 8 1B C0 00 00 00 00 00 00 + 7.390974 1 1 Tx d 8 1B C0 00 00 00 00 00 00 + 7.401064 1 1 Tx d 8 1B C0 00 00 00 00 00 00 + 7.421565 1 1 Tx d 8 1B C0 00 00 00 00 00 00 + 7.432484 1 1 Tx d 8 1B C0 00 00 00 00 00 00 + 7.454601 1 1 Tx d 8 1B C0 00 00 00 00 00 00 + 7.464590 1 1 Tx d 8 1B C0 00 00 00 00 00 00 + 7.485566 1 1 Tx d 8 1B C0 00 00 00 00 00 00 + 7.495597 1 1 Tx d 8 1B C0 00 00 00 00 00 00 + 7.516060 1 1 Tx d 8 1B C0 00 00 00 00 00 00 + 7.526035 1 1 Tx d 8 1B C0 00 00 00 00 00 00 + 7.548067 1 1 Tx d 8 1B C0 00 00 00 00 00 00 + 7.558096 1 1 Tx d 8 1B C0 00 00 00 00 00 00 + 7.579066 1 1 Tx d 8 1B C0 00 00 00 00 00 00 + 7.589067 1 1 Tx d 8 1B C0 00 00 00 00 00 00 + 7.610070 1 1 Tx d 8 1B C0 00 00 00 00 00 00 + 7.620582 1 1 Tx d 8 1B C0 00 00 00 00 00 00 + 7.641250 1 1 Tx d 8 1B C0 00 00 00 00 00 00 + 7.652308 1 1 Tx d 8 1B C0 00 00 00 00 00 00 + 7.672311 1 1 Tx d 8 1B C0 00 00 00 00 00 00 + 7.682310 1 1 Tx d 8 1B C0 00 00 00 00 00 00 + 7.703314 1 1 Tx d 8 1B C0 00 00 00 00 00 00 + 7.713801 1 1 Tx d 8 1B C0 00 00 00 00 00 00 + 7.733816 1 1 Tx d 8 1B C0 00 00 00 00 00 00 + 7.754813 1 1 Tx d 8 1B C0 00 00 00 00 00 00 + 7.767348 1 1 Tx d 8 1B C0 00 00 00 00 00 00 + 7.776828 1 1 Tx d 8 1B C0 00 00 00 00 00 00 + 7.797818 1 1 Tx d 8 1B C0 00 00 00 00 00 00 + 7.807819 1 1 Tx d 8 1B C0 00 00 00 00 00 00 + 7.828321 1 1 Tx d 8 1B C0 00 00 00 00 00 00 + 7.838326 1 1 Tx d 8 1B C0 00 00 00 00 00 00 + 7.859327 1 1 Tx d 8 1B C0 00 00 00 00 00 00 + 7.869331 1 1 Tx d 8 1B C0 00 00 00 00 00 00 + 7.890322 1 1 Tx d 8 1B C0 00 00 00 00 00 00 + 7.900327 1 1 Tx d 8 1B C0 00 00 00 00 00 00 + 7.921565 1 1 Tx d 8 21 4E 00 00 00 00 00 00 + 7.942131 1 1 Tx d 8 21 4E 00 00 00 00 00 00 + 7.952450 1 1 Tx d 8 21 4E 00 00 00 00 00 00 + 7.963354 1 1 Tx d 8 21 4E 00 00 00 00 00 00 + 7.984327 1 1 Tx d 8 21 4E 00 00 00 00 00 00 + 7.995328 1 1 Tx d 8 21 4E 00 00 00 00 00 00 + 8.015747 1 1 Tx d 8 21 4E 00 00 00 00 00 00 + 8.026787 1 1 Tx d 8 21 4E 00 00 00 00 00 00 + 8.047836 1 1 Tx d 8 21 4E 00 00 00 00 00 00 + 8.058830 1 1 Tx d 8 21 4E 00 00 00 00 00 00 + 8.079826 1 1 Tx d 8 21 4E 00 00 00 00 00 00 + 8.090832 1 1 Tx d 8 21 4E 00 00 00 00 00 00 + 8.110834 1 1 Tx d 8 21 4E 00 00 00 00 00 00 + 8.121338 1 1 Tx d 8 21 4E 00 00 00 00 00 00 + 8.142356 1 1 Tx d 8 21 4E 00 00 00 00 00 00 + 8.152337 1 1 Tx d 8 21 4E 00 00 00 00 00 00 + 8.172246 1 1 Tx d 8 21 4E 00 00 00 00 00 00 + 8.183234 1 1 Tx d 8 21 4E 00 00 00 00 00 00 + 8.204289 1 1 Tx d 8 21 4E 00 00 00 00 00 00 + 8.214849 1 1 Tx d 8 21 4E 00 00 00 00 00 00 + 8.234753 1 1 Tx d 8 21 4E 00 00 00 00 00 00 + 8.245839 1 1 Tx d 8 21 4E 00 00 00 00 00 00 + 8.266772 1 1 Tx d 8 21 4E 00 00 00 00 00 00 + 8.277843 1 1 Tx d 8 21 4E 00 00 00 00 00 00 + 8.303456 1 1 Tx d 8 21 4E 00 00 00 00 00 00 + 8.312806 1 1 Tx d 8 21 4E 00 00 00 00 00 00 + 8.323365 1 1 Tx d 8 21 4E 00 00 00 00 00 00 + 8.344376 1 1 Tx d 8 21 4E 00 00 00 00 00 00 + 8.355438 1 1 Tx d 8 21 4E 00 00 00 00 00 00 + 8.375436 1 1 Tx d 8 21 4E 00 00 00 00 00 00 + 8.385447 1 1 Tx d 8 21 4E 00 00 00 00 00 00 + 8.406443 1 1 Tx d 8 21 4E 00 00 00 00 00 00 + 8.416940 1 1 Tx d 8 21 4E 00 00 00 00 00 00 + 8.438943 1 1 Tx d 8 21 4E 00 00 00 00 00 00 + 8.449942 1 1 Tx d 8 21 4E 00 00 00 00 00 00 + 8.470911 1 1 Tx d 8 21 4E 00 00 00 00 00 00 + 8.481940 1 1 Tx d 8 21 4E 00 00 00 00 00 00 + 8.501887 1 1 Tx d 8 21 4E 00 00 00 00 00 00 + 8.512944 1 1 Tx d 8 21 4E 00 00 00 00 00 00 + 8.533450 1 1 Tx d 8 21 4E 00 00 00 00 00 00 + 8.543444 1 1 Tx d 8 21 4E 00 00 00 00 00 00 + 8.565448 1 1 Tx d 8 21 4E 00 00 00 00 00 00 + 8.575446 1 1 Tx d 8 21 4E 00 00 00 00 00 00 + 8.595451 1 1 Tx d 8 21 4E 00 00 00 00 00 00 + 8.605453 1 1 Tx d 8 21 4E 00 00 00 00 00 00 + 8.626951 1 1 Tx d 8 21 4E 00 00 00 00 00 00 + 8.637952 1 1 Tx d 8 21 4E 00 00 00 00 00 00 + 8.658952 1 1 Tx d 8 21 4E 00 00 00 00 00 00 + 8.668951 1 1 Tx d 8 21 4E 00 00 00 00 00 00 + 8.689956 1 1 Tx d 8 21 4E 00 00 00 00 00 00 + 8.699957 1 1 Tx d 8 21 4E 00 00 00 00 00 00 + 8.715590 1 1 Tx d 8 21 4E 00 00 00 00 00 00 + 8.735459 1 1 Tx d 8 21 4E 00 00 00 00 00 00 + 8.745457 1 1 Tx d 8 21 4E 00 00 00 00 00 00 + 8.766463 1 1 Tx d 8 21 4E 00 00 00 00 00 00 + 8.776468 1 1 Tx d 8 21 4E 00 00 00 00 00 00 + 8.797457 1 1 Tx d 8 21 4E 00 00 00 00 00 00 + 8.807458 1 1 Tx d 8 21 4E 00 00 00 00 00 00 + 8.827964 1 1 Tx d 8 21 4E 00 00 00 00 00 00 + 8.837971 1 1 Tx d 8 21 4E 00 00 00 00 00 00 + 8.858905 1 1 Tx d 8 21 4E 00 00 00 00 00 00 + 8.879895 1 1 Tx d 8 21 4E 00 00 00 00 00 00 + 8.889900 1 1 Tx d 8 21 4E 00 00 00 00 00 00 + 8.910981 1 1 Tx d 8 21 4E 00 00 00 00 00 00 + 8.921204 1 1 Tx d 8 21 4E 00 00 00 00 00 00 + 8.932203 1 1 Tx d 8 21 4E 00 00 00 00 00 00 + 8.954111 1 1 Tx d 8 21 4E 00 00 00 00 00 00 + 8.965103 1 1 Tx d 8 21 4E 00 00 00 00 00 00 + 8.985195 1 1 Tx d 8 21 4E 00 00 00 00 00 00 + 8.995191 1 1 Tx d 8 21 4E 00 00 00 00 00 00 + 9.015709 1 1 Tx d 8 21 4E 00 00 00 00 00 00 + 9.025704 1 1 Tx d 8 21 4E 00 00 00 00 00 00 + 9.046703 1 1 Tx d 8 21 4E 00 00 00 00 00 00 + 9.056705 1 1 Tx d 8 21 4E 00 00 00 00 00 00 + 9.078202 1 1 Tx d 8 21 4E 00 00 00 00 00 00 + 9.089209 1 1 Tx d 8 21 4E 00 00 00 00 00 00 + 9.111209 1 1 Tx d 8 21 4E 00 00 00 00 00 00 + 9.121712 1 1 Tx d 8 21 4E 00 00 00 00 00 00 + 9.142716 1 1 Tx d 8 21 4E 00 00 00 00 00 00 + 9.153718 1 1 Tx d 8 21 4E 00 00 00 00 00 00 + 9.173711 1 1 Tx d 8 21 4E 00 00 00 00 00 00 + 9.183706 1 1 Tx d 8 21 4E 00 00 00 00 00 00 + 9.204617 1 1 Tx d 8 21 4E 00 00 00 00 00 00 + 9.215167 1 1 Tx d 8 21 4E 00 00 00 00 00 00 + 9.237219 1 1 Tx d 8 21 4E 00 00 00 00 00 00 + 9.248215 1 1 Tx d 8 21 4E 00 00 00 00 00 00 + 9.269265 1 1 Tx d 8 21 4E 00 00 00 00 00 00 + 9.280215 1 1 Tx d 8 21 4E 00 00 00 00 00 00 + 9.300136 1 1 Tx d 8 21 4E 00 00 00 00 00 00 + 9.311004 1 1 Tx d 8 21 4E 00 00 00 00 00 00 + 9.332902 1 1 Tx d 8 21 4E 00 00 00 00 00 00 + 9.342842 1 1 Tx d 8 21 4E 00 00 00 00 00 00 + 9.363904 1 1 Tx d 8 21 4E 00 00 00 00 00 00 + 9.373800 1 1 Tx d 8 21 4E 00 00 00 00 00 00 + 9.384912 1 1 Tx d 8 21 4E 00 00 00 00 00 00 + 9.404844 1 1 Tx d 8 21 4E 00 00 00 00 00 00 + 9.426416 1 1 Tx d 8 21 4E 00 00 00 00 00 00 + 9.437407 1 1 Tx d 8 21 4E 00 00 00 00 00 00 + 9.447307 1 1 Tx d 8 21 4E 00 00 00 00 00 00 + 9.468423 1 1 Tx d 8 21 4E 00 00 00 00 00 00 + 9.491521 1 1 Tx d 8 26 DA 00 00 00 00 00 00 + 9.501326 1 1 Tx d 8 26 DA 00 00 00 00 00 00 + 9.511353 1 1 Tx d 8 26 DA 00 00 00 00 00 00 + 9.531380 1 1 Tx d 8 26 DA 00 00 00 00 00 00 + 9.541388 1 1 Tx d 8 26 DA 00 00 00 00 00 00 + 9.562333 1 1 Tx d 8 26 DA 00 00 00 00 00 00 + 9.572382 1 1 Tx d 8 26 DA 00 00 00 00 00 00 + 9.594451 1 1 Tx d 8 26 DA 00 00 00 00 00 00 + 9.604462 1 1 Tx d 8 26 DA 00 00 00 00 00 00 + 9.624632 1 1 Tx d 8 26 DA 00 00 00 00 00 00 + 9.634668 1 1 Tx d 8 26 DA 00 00 00 00 00 00 + 9.655690 1 1 Tx d 8 26 DA 00 00 00 00 00 00 + 9.666736 1 1 Tx d 8 26 DA 00 00 00 00 00 00 + 9.687746 1 1 Tx d 8 26 DA 00 00 00 00 00 00 + 9.698556 1 1 Tx d 8 26 DA 00 00 00 00 00 00 + 9.719860 1 1 Tx d 8 26 DA 00 00 00 00 00 00 + 9.730838 1 1 Tx d 8 26 DA 00 00 00 00 00 00 + 9.751794 1 1 Tx d 8 26 DA 00 00 00 00 00 00 + 9.761764 1 1 Tx d 8 26 DA 00 00 00 00 00 00 + 9.783834 1 1 Tx d 8 26 DA 00 00 00 00 00 00 + 9.793726 1 1 Tx d 8 26 DA 00 00 00 00 00 00 + 9.813770 1 1 Tx d 8 26 DA 00 00 00 00 00 00 + 9.823806 1 1 Tx d 8 26 DA 00 00 00 00 00 00 + 9.844811 1 1 Tx d 8 26 DA 00 00 00 00 00 00 + 9.855872 1 1 Tx d 8 26 DA 00 00 00 00 00 00 + 9.876979 1 1 Tx d 8 26 DA 00 00 00 00 00 00 + 9.886930 1 1 Tx d 8 26 DA 00 00 00 00 00 00 + 9.907970 1 1 Tx d 8 26 DA 00 00 00 00 00 00 + 9.918752 1 1 Tx d 8 26 DA 00 00 00 00 00 00 + 9.939785 1 1 Tx d 8 26 DA 00 00 00 00 00 00 + 9.950655 1 1 Tx d 8 26 DA 00 00 00 00 00 00 + 9.971657 1 1 Tx d 8 26 DA 00 00 00 00 00 00 + 9.981979 1 1 Tx d 8 26 DA 00 00 00 00 00 00 + 10.002807 1 1 Tx d 8 26 DA 00 00 00 00 00 00 + 10.013804 1 1 Tx d 8 26 DA 00 00 00 00 00 00 + 10.048659 1 1 Tx d 8 26 DA 00 00 00 00 00 00 + 10.059143 1 1 Tx d 8 26 DA 00 00 00 00 00 00 + 10.080915 1 1 Tx d 8 26 DA 00 00 00 00 00 00 + 10.090833 1 1 Tx d 8 26 DA 00 00 00 00 00 00 + 10.112033 1 1 Tx d 8 26 DA 00 00 00 00 00 00 + 10.122730 1 1 Tx d 8 26 DA 00 00 00 00 00 00 + 10.144613 1 1 Tx d 8 26 DA 00 00 00 00 00 00 + 10.154618 1 1 Tx d 8 26 DA 00 00 00 00 00 00 + 10.175614 1 1 Tx d 8 26 DA 00 00 00 00 00 00 + 10.186053 1 1 Tx d 8 26 DA 00 00 00 00 00 00 + 10.206683 1 1 Tx d 8 26 DA 00 00 00 00 00 00 + 10.216925 1 1 Tx d 8 26 DA 00 00 00 00 00 00 + 10.237921 1 1 Tx d 8 26 DA 00 00 00 00 00 00 + 10.247927 1 1 Tx d 8 26 DA 00 00 00 00 00 00 + 10.270015 1 1 Tx d 8 26 DA 00 00 00 00 00 00 + 10.280930 1 1 Tx d 8 26 DA 00 00 00 00 00 00 + 10.290928 1 1 Tx d 8 26 DA 00 00 00 00 00 00 + 10.311022 1 1 Tx d 8 26 DA 00 00 00 00 00 00 + 10.332708 1 1 Tx d 8 26 DA 00 00 00 00 00 00 + 10.343274 1 1 Tx d 8 26 DA 00 00 00 00 00 00 + 10.354216 1 1 Tx d 8 26 DA 00 00 00 00 00 00 + 10.375130 1 1 Tx d 8 26 DA 00 00 00 00 00 00 + 10.386224 1 1 Tx d 8 26 DA 00 00 00 00 00 00 + 10.407219 1 1 Tx d 8 26 DA 00 00 00 00 00 00 + 10.417719 1 1 Tx d 8 26 DA 00 00 00 00 00 00 + 10.437731 1 1 Tx d 8 26 DA 00 00 00 00 00 00 + 10.448724 1 1 Tx d 8 26 DA 00 00 00 00 00 00 + 10.468679 1 1 Tx d 8 26 DA 00 00 00 00 00 00 + 10.478724 1 1 Tx d 8 26 DA 00 00 00 00 00 00 + 10.499674 1 1 Tx d 8 26 DA 00 00 00 00 00 00 + 10.509722 1 1 Tx d 8 26 DA 00 00 00 00 00 00 + 10.530184 1 1 Tx d 8 26 DA 00 00 00 00 00 00 + 10.541235 1 1 Tx d 8 26 DA 00 00 00 00 00 00 + 10.562228 1 1 Tx d 8 26 DA 00 00 00 00 00 00 + 10.573230 1 1 Tx d 8 26 DA 00 00 00 00 00 00 + 10.593236 1 1 Tx d 8 26 DA 00 00 00 00 00 00 + 10.614266 1 1 Tx d 8 26 DA 00 00 00 00 00 00 + 10.624680 1 1 Tx d 8 26 DA 00 00 00 00 00 00 + 10.635669 1 1 Tx d 8 26 DA 00 00 00 00 00 00 + 10.656748 1 1 Tx d 8 26 DA 00 00 00 00 00 00 + 10.666747 1 1 Tx d 8 26 DA 00 00 00 00 00 00 + 10.690355 1 1 Tx d 8 26 DA 00 00 00 00 00 00 + 10.699746 1 1 Tx d 8 26 DA 00 00 00 00 00 00 + 10.720202 1 1 Tx d 8 26 DA 00 00 00 00 00 00 + 10.731189 1 1 Tx d 8 26 DA 00 00 00 00 00 00 + 10.751247 1 1 Tx d 8 26 DA 00 00 00 00 00 00 + 10.761235 1 1 Tx d 8 26 DA 00 00 00 00 00 00 + 10.781198 1 1 Tx d 8 26 DA 00 00 00 00 00 00 + 10.792239 1 1 Tx d 8 26 DA 00 00 00 00 00 00 + 10.812244 1 1 Tx d 8 26 DA 00 00 00 00 00 00 + 10.822748 1 1 Tx d 8 26 DA 00 00 00 00 00 00 + 10.844756 1 1 Tx d 8 26 DA 00 00 00 00 00 00 + 10.854744 1 1 Tx d 8 26 DA 00 00 00 00 00 00 + 10.875752 1 1 Tx d 8 26 DA 00 00 00 00 00 00 + 10.886761 1 1 Tx d 8 26 DA 00 00 00 00 00 00 + 10.906763 1 1 Tx d 8 26 DA 00 00 00 00 00 00 + 10.917254 1 1 Tx d 8 26 DA 00 00 00 00 00 00 + 10.937265 1 1 Tx d 8 26 DA 00 00 00 00 00 00 + 10.948253 1 1 Tx d 8 26 DA 00 00 00 00 00 00 + 10.969287 1 1 Tx d 8 26 DA 00 00 00 00 00 00 + 10.979252 1 1 Tx d 8 26 DA 00 00 00 00 00 00 + 10.999190 1 1 Tx d 8 26 DA 00 00 00 00 00 00 + 11.019768 1 1 Tx d 8 26 DA 00 00 00 00 00 00 + 11.029712 1 1 Tx d 8 26 DA 00 00 00 00 00 00 + 11.049765 1 1 Tx d 8 26 DA 00 00 00 00 00 00 + 11.060842 1 1 Tx d 8 2C 66 00 00 00 00 00 00 + 11.081995 1 1 Tx d 8 2C 66 00 00 00 00 00 00 + 11.093045 1 1 Tx d 8 2C 66 00 00 00 00 00 00 + 11.112981 1 1 Tx d 8 2C 66 00 00 00 00 00 00 + 11.123537 1 1 Tx d 8 2C 66 00 00 00 00 00 00 + 11.144543 1 1 Tx d 8 2C 66 00 00 00 00 00 00 + 11.155544 1 1 Tx d 8 2C 66 00 00 00 00 00 00 + 11.176545 1 1 Tx d 8 2C 66 00 00 00 00 00 00 + 11.186543 1 1 Tx d 8 2C 66 00 00 00 00 00 00 + 11.197545 1 1 Tx d 8 2C 66 00 00 00 00 00 00 + 11.217997 1 1 Tx d 8 2C 66 00 00 00 00 00 00 + 11.229668 1 1 Tx d 8 2C 66 00 00 00 00 00 00 + 11.248944 1 1 Tx d 8 2C 66 00 00 00 00 00 00 + 11.260089 1 1 Tx d 8 2C 66 00 00 00 00 00 00 + 11.280043 1 1 Tx d 8 2C 66 00 00 00 00 00 00 + 11.291050 1 1 Tx d 8 2C 66 00 00 00 00 00 00 + 11.311044 1 1 Tx d 8 2C 66 00 00 00 00 00 00 + 11.331546 1 1 Tx d 8 2C 66 00 00 00 00 00 00 + 11.341550 1 1 Tx d 8 2C 66 00 00 00 00 00 00 + 11.361555 1 1 Tx d 8 2C 66 00 00 00 00 00 00 + 11.371555 1 1 Tx d 8 2C 66 00 00 00 00 00 00 + 11.392546 1 1 Tx d 8 2C 66 00 00 00 00 00 00 + 11.402548 1 1 Tx d 8 2C 66 00 00 00 00 00 00 + 11.423063 1 1 Tx d 8 2C 66 00 00 00 00 00 00 + 11.433779 1 1 Tx d 8 2C 66 00 00 00 00 00 00 + 11.454946 1 1 Tx d 8 2C 66 00 00 00 00 00 00 + 11.464944 1 1 Tx d 8 2C 66 00 00 00 00 00 00 + 11.484945 1 1 Tx d 8 2C 66 00 00 00 00 00 00 + 11.497635 1 1 Tx d 8 2C 66 00 00 00 00 00 00 + 11.517450 1 1 Tx d 8 2C 66 00 00 00 00 00 00 + 11.528445 1 1 Tx d 8 2C 66 00 00 00 00 00 00 + 11.549448 1 1 Tx d 8 2C 66 00 00 00 00 00 00 + 11.559444 1 1 Tx d 8 2C 66 00 00 00 00 00 00 + 11.580407 1 1 Tx d 8 2C 66 00 00 00 00 00 00 + 11.591392 1 1 Tx d 8 2C 66 00 00 00 00 00 00 + 11.611401 1 1 Tx d 8 2C 66 00 00 00 00 00 00 + 11.621894 1 1 Tx d 8 2C 66 00 00 00 00 00 00 + 11.642924 1 1 Tx d 8 2C 66 00 00 00 00 00 00 + 11.652894 1 1 Tx d 8 2C 66 00 00 00 00 00 00 + 11.673895 1 1 Tx d 8 2C 66 00 00 00 00 00 00 + 11.684955 1 1 Tx d 8 2C 66 00 00 00 00 00 00 + 11.704889 1 1 Tx d 8 2C 66 00 00 00 00 00 00 + 11.716467 1 1 Tx d 8 2C 66 00 00 00 00 00 00 + 11.736467 1 1 Tx d 8 2C 66 00 00 00 00 00 00 + 11.746467 1 1 Tx d 8 2C 66 00 00 00 00 00 00 + 11.767357 1 1 Tx d 8 2C 66 00 00 00 00 00 00 + 11.777356 1 1 Tx d 8 2C 66 00 00 00 00 00 00 + 11.798461 1 1 Tx d 8 2C 66 00 00 00 00 00 00 + 11.808459 1 1 Tx d 8 2C 66 00 00 00 00 00 00 + 11.830005 1 1 Tx d 8 2C 66 00 00 00 00 00 00 + 11.839963 1 1 Tx d 8 2C 66 00 00 00 00 00 00 + 11.860966 1 1 Tx d 8 2C 66 00 00 00 00 00 00 + 11.870968 1 1 Tx d 8 2C 66 00 00 00 00 00 00 + 11.891967 1 1 Tx d 8 2C 66 00 00 00 00 00 00 + 11.901978 1 1 Tx d 8 2C 66 00 00 00 00 00 00 + 11.923471 1 1 Tx d 8 2C 66 00 00 00 00 00 00 + 11.933506 1 1 Tx d 8 2C 66 00 00 00 00 00 00 + 11.954375 1 1 Tx d 8 2C 66 00 00 00 00 00 00 + 11.965275 1 1 Tx d 8 2C 66 00 00 00 00 00 00 + 11.985883 1 1 Tx d 8 2C 66 00 00 00 00 00 00 + 11.995918 1 1 Tx d 8 2C 66 00 00 00 00 00 00 + 12.016390 1 1 Tx d 8 2C 66 00 00 00 00 00 00 + 12.026385 1 1 Tx d 8 2C 66 00 00 00 00 00 00 + 12.047394 1 1 Tx d 8 2C 66 00 00 00 00 00 00 + 12.057391 1 1 Tx d 8 2C 66 00 00 00 00 00 00 + 12.078341 1 1 Tx d 8 2C 66 00 00 00 00 00 00 + 12.088333 1 1 Tx d 8 2C 66 00 00 00 00 00 00 + 12.109343 1 1 Tx d 8 2C 66 00 00 00 00 00 00 + 12.119903 1 1 Tx d 8 2C 66 00 00 00 00 00 00 + 12.140896 1 1 Tx d 8 2C 66 00 00 00 00 00 00 + 12.150895 1 1 Tx d 8 2C 66 00 00 00 00 00 00 + 12.170887 1 1 Tx d 8 2C 66 00 00 00 00 00 00 + 12.181905 1 1 Tx d 8 2C 66 00 00 00 00 00 00 + 12.201900 1 1 Tx d 8 2C 66 00 00 00 00 00 00 + 12.222409 1 1 Tx d 8 2C 66 00 00 00 00 00 00 + 12.233401 1 1 Tx d 8 2C 66 00 00 00 00 00 00 + 12.253405 1 1 Tx d 8 2C 66 00 00 00 00 00 00 + 12.264405 1 1 Tx d 8 2C 66 00 00 00 00 00 00 + 12.284360 1 1 Tx d 8 2C 66 00 00 00 00 00 00 + 12.294402 1 1 Tx d 8 2C 66 00 00 00 00 00 00 + 12.315411 1 1 Tx d 8 2C 66 00 00 00 00 00 00 + 12.325912 1 1 Tx d 8 2C 66 00 00 00 00 00 00 + 12.345938 1 1 Tx d 8 2C 66 00 00 00 00 00 00 + 12.356915 1 1 Tx d 8 2C 66 00 00 00 00 00 00 + 12.376917 1 1 Tx d 8 2C 66 00 00 00 00 00 00 + 12.386918 1 1 Tx d 8 2C 66 00 00 00 00 00 00 + 12.407912 1 1 Tx d 8 2C 66 00 00 00 00 00 00 + 12.418411 1 1 Tx d 8 2C 66 00 00 00 00 00 00 + 12.439080 1 1 Tx d 8 2C 66 00 00 00 00 00 00 + 12.449055 1 1 Tx d 8 2C 66 00 00 00 00 00 00 + 12.470075 1 1 Tx d 8 2C 66 00 00 00 00 00 00 + 12.481078 1 1 Tx d 8 2C 66 00 00 00 00 00 00 + 12.502075 1 1 Tx d 8 2C 66 00 00 00 00 00 00 + 12.512074 1 1 Tx d 8 2C 66 00 00 00 00 00 00 + 12.532577 1 1 Tx d 8 2C 66 00 00 00 00 00 00 + 12.542588 1 1 Tx d 8 2C 66 00 00 00 00 00 00 + 12.562490 1 1 Tx d 8 2C 66 00 00 00 00 00 00 + 12.573580 1 1 Tx d 8 2C 66 00 00 00 00 00 00 + 12.593579 1 1 Tx d 8 2C 66 00 00 00 00 00 00 + 12.603565 1 1 Tx d 8 2C 66 00 00 00 00 00 00 + 12.625631 1 1 Tx d 8 31 F4 00 00 00 00 00 00 + 12.635041 1 1 Tx d 8 31 F4 00 00 00 00 00 00 + 12.656234 1 1 Tx d 8 31 F4 00 00 00 00 00 00 + 12.666478 1 1 Tx d 8 31 F4 00 00 00 00 00 00 + 12.687014 1 1 Tx d 8 31 F4 00 00 00 00 00 00 + 12.698096 1 1 Tx d 8 31 F4 00 00 00 00 00 00 + 12.718573 1 1 Tx d 8 31 F4 00 00 00 00 00 00 + 12.728574 1 1 Tx d 8 31 F4 00 00 00 00 00 00 + 12.749618 1 1 Tx d 8 31 F4 00 00 00 00 00 00 + 12.770649 1 1 Tx d 8 31 F4 00 00 00 00 00 00 + 12.781580 1 1 Tx d 8 31 F4 00 00 00 00 00 00 + 12.792578 1 1 Tx d 8 31 F4 00 00 00 00 00 00 + 12.808170 1 1 Tx d 8 31 F4 00 00 00 00 00 00 + 12.827982 1 1 Tx d 8 31 F4 00 00 00 00 00 00 + 12.837980 1 1 Tx d 8 31 F4 00 00 00 00 00 00 + 12.858082 1 1 Tx d 8 31 F4 00 00 00 00 00 00 + 12.879085 1 1 Tx d 8 31 F4 00 00 00 00 00 00 + 12.889094 1 1 Tx d 8 31 F4 00 00 00 00 00 00 + 12.909079 1 1 Tx d 8 31 F4 00 00 00 00 00 00 + 12.919584 1 1 Tx d 8 31 F4 00 00 00 00 00 00 + 12.939592 1 1 Tx d 8 31 F4 00 00 00 00 00 00 + 12.949588 1 1 Tx d 8 31 F4 00 00 00 00 00 00 + 12.970594 1 1 Tx d 8 31 F4 00 00 00 00 00 00 + 12.980592 1 1 Tx d 8 31 F4 00 00 00 00 00 00 + 13.000595 1 1 Tx d 8 31 F4 00 00 00 00 00 00 + 13.010592 1 1 Tx d 8 31 F4 00 00 00 00 00 00 + 13.032100 1 1 Tx d 8 31 F4 00 00 00 00 00 00 + 13.042092 1 1 Tx d 8 31 F4 00 00 00 00 00 00 + 13.063101 1 1 Tx d 8 31 F4 00 00 00 00 00 00 + 13.073100 1 1 Tx d 8 31 F4 00 00 00 00 00 00 + 13.094608 1 1 Tx d 8 31 F4 00 00 00 00 00 00 + 13.104605 1 1 Tx d 8 31 F4 00 00 00 00 00 00 + 13.125109 1 1 Tx d 8 31 F4 00 00 00 00 00 00 + 13.135097 1 1 Tx d 8 31 F4 00 00 00 00 00 00 + 13.155098 1 1 Tx d 8 31 F4 00 00 00 00 00 00 + 13.175100 1 1 Tx d 8 31 F4 00 00 00 00 00 00 + 13.186108 1 1 Tx d 8 31 F4 00 00 00 00 00 00 + 13.206057 1 1 Tx d 8 31 F4 00 00 00 00 00 00 + 13.216641 1 1 Tx d 8 31 F4 00 00 00 00 00 00 + 13.237608 1 1 Tx d 8 31 F4 00 00 00 00 00 00 + 13.247609 1 1 Tx d 8 31 F4 00 00 00 00 00 00 + 13.268620 1 1 Tx d 8 31 F4 00 00 00 00 00 00 + 13.279605 1 1 Tx d 8 31 F4 00 00 00 00 00 00 + 13.300609 1 1 Tx d 8 31 F4 00 00 00 00 00 00 + 13.310714 1 1 Tx d 8 31 F4 00 00 00 00 00 00 + 13.331114 1 1 Tx d 8 31 F4 00 00 00 00 00 00 + 13.341112 1 1 Tx d 8 31 F4 00 00 00 00 00 00 + 13.362120 1 1 Tx d 8 31 F4 00 00 00 00 00 00 + 13.372111 1 1 Tx d 8 31 F4 00 00 00 00 00 00 + 13.392109 1 1 Tx d 8 31 F4 00 00 00 00 00 00 + 13.402117 1 1 Tx d 8 31 F4 00 00 00 00 00 00 + 13.423618 1 1 Tx d 8 31 F4 00 00 00 00 00 00 + 13.433644 1 1 Tx d 8 31 F4 00 00 00 00 00 00 + 13.454619 1 1 Tx d 8 31 F4 00 00 00 00 00 00 + 13.465615 1 1 Tx d 8 31 F4 00 00 00 00 00 00 + 13.486619 1 1 Tx d 8 31 F4 00 00 00 00 00 00 + 13.497651 1 1 Tx d 8 31 F4 00 00 00 00 00 00 + 13.518075 1 1 Tx d 8 31 F4 00 00 00 00 00 00 + 13.528127 1 1 Tx d 8 31 F4 00 00 00 00 00 00 + 13.549129 1 1 Tx d 8 31 F4 00 00 00 00 00 00 + 13.560120 1 1 Tx d 8 31 F4 00 00 00 00 00 00 + 13.581126 1 1 Tx d 8 31 F4 00 00 00 00 00 00 + 13.591126 1 1 Tx d 8 31 F4 00 00 00 00 00 00 + 13.613126 1 1 Tx d 8 31 F4 00 00 00 00 00 00 + 13.623638 1 1 Tx d 8 31 F4 00 00 00 00 00 00 + 13.644641 1 1 Tx d 8 31 F4 00 00 00 00 00 00 + 13.654576 1 1 Tx d 8 31 F4 00 00 00 00 00 00 + 13.675629 1 1 Tx d 8 31 F4 00 00 00 00 00 00 + 13.685628 1 1 Tx d 8 31 F4 00 00 00 00 00 00 + 13.705632 1 1 Tx d 8 31 F4 00 00 00 00 00 00 + 13.715634 1 1 Tx d 8 31 F4 00 00 00 00 00 00 + 13.737141 1 1 Tx d 8 31 F4 00 00 00 00 00 00 + 13.747071 1 1 Tx d 8 31 F4 00 00 00 00 00 00 + 13.769144 1 1 Tx d 8 31 F4 00 00 00 00 00 00 + 13.779073 1 1 Tx d 8 31 F4 00 00 00 00 00 00 + 13.800884 1 1 Tx d 8 31 F4 00 00 00 00 00 00 + 13.810888 1 1 Tx d 8 31 F4 00 00 00 00 00 00 + 13.832669 1 1 Tx d 8 31 F4 00 00 00 00 00 00 + 13.842579 1 1 Tx d 8 31 F4 00 00 00 00 00 00 + 13.853669 1 1 Tx d 8 31 F4 00 00 00 00 00 00 + 13.873748 1 1 Tx d 8 31 F4 00 00 00 00 00 00 + 13.884837 1 1 Tx d 8 31 F4 00 00 00 00 00 00 + 13.904786 1 1 Tx d 8 31 F4 00 00 00 00 00 00 + 13.925634 1 1 Tx d 8 31 F4 00 00 00 00 00 00 + 13.936538 1 1 Tx d 8 31 F4 00 00 00 00 00 00 + 13.957418 1 1 Tx d 8 31 F4 00 00 00 00 00 00 + 13.967417 1 1 Tx d 8 31 F4 00 00 00 00 00 00 + 13.988422 1 1 Tx d 8 31 F4 00 00 00 00 00 00 + 13.998442 1 1 Tx d 8 31 F4 00 00 00 00 00 00 + 14.019027 1 1 Tx d 8 31 F4 00 00 00 00 00 00 + 14.029030 1 1 Tx d 8 31 F4 00 00 00 00 00 00 + 14.050026 1 1 Tx d 8 31 F4 00 00 00 00 00 00 + 14.061069 1 1 Tx d 8 31 F4 00 00 00 00 00 00 + 14.080950 1 1 Tx d 8 31 F4 00 00 00 00 00 00 + 14.090939 1 1 Tx d 8 31 F4 00 00 00 00 00 00 + 14.111972 1 1 Tx d 8 31 F4 00 00 00 00 00 00 + 14.122527 1 1 Tx d 8 31 F4 00 00 00 00 00 00 + 14.143483 1 1 Tx d 8 31 F4 00 00 00 00 00 00 + 14.153467 1 1 Tx d 8 31 F4 00 00 00 00 00 00 + 14.174533 1 1 Tx d 8 31 F4 00 00 00 00 00 00 + 14.186601 1 1 Tx d 8 37 80 00 00 00 00 00 00 + 14.207533 1 1 Tx d 8 37 80 00 00 00 00 00 00 + 14.218048 1 1 Tx d 8 37 80 00 00 00 00 00 00 + 14.229041 1 1 Tx d 8 37 80 00 00 00 00 00 00 + 14.248942 1 1 Tx d 8 37 80 00 00 00 00 00 00 + 14.268982 1 1 Tx d 8 37 80 00 00 00 00 00 00 + 14.278983 1 1 Tx d 8 37 80 00 00 00 00 00 00 + 14.301042 1 1 Tx d 8 37 80 00 00 00 00 00 00 + 14.311041 1 1 Tx d 8 37 80 00 00 00 00 00 00 + 14.332546 1 1 Tx d 8 37 80 00 00 00 00 00 00 + 14.342553 1 1 Tx d 8 37 80 00 00 00 00 00 00 + 14.353544 1 1 Tx d 8 37 80 00 00 00 00 00 00 + 14.374542 1 1 Tx d 8 37 80 00 00 00 00 00 00 + 14.385540 1 1 Tx d 8 37 80 00 00 00 00 00 00 + 14.405539 1 1 Tx d 8 37 80 00 00 00 00 00 00 + 14.417051 1 1 Tx d 8 37 80 00 00 00 00 00 00 + 14.438056 1 1 Tx d 8 37 80 00 00 00 00 00 00 + 14.448044 1 1 Tx d 8 37 80 00 00 00 00 00 00 + 14.469051 1 1 Tx d 8 37 80 00 00 00 00 00 00 + 14.479055 1 1 Tx d 8 37 80 00 00 00 00 00 00 + 14.500053 1 1 Tx d 8 37 80 00 00 00 00 00 00 + 14.510054 1 1 Tx d 8 37 80 00 00 00 00 00 00 + 14.531558 1 1 Tx d 8 37 80 00 00 00 00 00 00 + 14.541555 1 1 Tx d 8 37 80 00 00 00 00 00 00 + 14.562555 1 1 Tx d 8 37 80 00 00 00 00 00 00 + 14.572559 1 1 Tx d 8 37 80 00 00 00 00 00 00 + 14.592552 1 1 Tx d 8 37 80 00 00 00 00 00 00 + 14.612556 1 1 Tx d 8 37 80 00 00 00 00 00 00 + 14.623157 1 1 Tx d 8 37 80 00 00 00 00 00 00 + 14.643196 1 1 Tx d 8 37 80 00 00 00 00 00 00 + 14.653158 1 1 Tx d 8 37 80 00 00 00 00 00 00 + 14.674182 1 1 Tx d 8 37 80 00 00 00 00 00 00 + 14.685176 1 1 Tx d 8 37 80 00 00 00 00 00 00 + 14.705165 1 1 Tx d 8 37 80 00 00 00 00 00 00 + 14.715166 1 1 Tx d 8 37 80 00 00 00 00 00 00 + 14.736674 1 1 Tx d 8 37 80 00 00 00 00 00 00 + 14.746660 1 1 Tx d 8 37 80 00 00 00 00 00 00 + 14.766669 1 1 Tx d 8 37 80 00 00 00 00 00 00 + 14.776671 1 1 Tx d 8 37 80 00 00 00 00 00 00 + 14.798722 1 1 Tx d 8 37 80 00 00 00 00 00 00 + 14.808724 1 1 Tx d 8 37 80 00 00 00 00 00 00 + 14.830174 1 1 Tx d 8 37 80 00 00 00 00 00 00 + 14.840141 1 1 Tx d 8 37 80 00 00 00 00 00 00 + 14.862221 1 1 Tx d 8 37 80 00 00 00 00 00 00 + 14.873231 1 1 Tx d 8 37 80 00 00 00 00 00 00 + 14.893232 1 1 Tx d 8 37 80 00 00 00 00 00 00 + 14.903224 1 1 Tx d 8 37 80 00 00 00 00 00 00 + 14.923730 1 1 Tx d 8 37 80 00 00 00 00 00 00 + 14.934748 1 1 Tx d 8 37 80 00 00 00 00 00 00 + 14.956741 1 1 Tx d 8 37 80 00 00 00 00 00 00 + 14.966731 1 1 Tx d 8 37 80 00 00 00 00 00 00 + 14.987739 1 1 Tx d 8 37 80 00 00 00 00 00 00 + 14.998731 1 1 Tx d 8 37 80 00 00 00 00 00 00 + 15.019243 1 1 Tx d 8 37 80 00 00 00 00 00 00 + 15.029235 1 1 Tx d 8 37 80 00 00 00 00 00 00 + 15.049243 1 1 Tx d 8 37 80 00 00 00 00 00 00 + 15.060184 1 1 Tx d 8 37 80 00 00 00 00 00 00 + 15.081183 1 1 Tx d 8 37 80 00 00 00 00 00 00 + 15.091741 1 1 Tx d 8 37 80 00 00 00 00 00 00 + 15.112741 1 1 Tx d 8 37 80 00 00 00 00 00 00 + 15.123248 1 1 Tx d 8 37 80 00 00 00 00 00 00 + 15.143256 1 1 Tx d 8 37 80 00 00 00 00 00 00 + 15.154817 1 1 Tx d 8 37 80 00 00 00 00 00 00 + 15.175544 1 1 Tx d 8 37 80 00 00 00 00 00 00 + 15.185547 1 1 Tx d 8 37 80 00 00 00 00 00 00 + 15.205541 1 1 Tx d 8 37 80 00 00 00 00 00 00 + 15.215544 1 1 Tx d 8 37 80 00 00 00 00 00 00 + 15.236047 1 1 Tx d 8 37 80 00 00 00 00 00 00 + 15.246049 1 1 Tx d 8 37 80 00 00 00 00 00 00 + 15.267990 1 1 Tx d 8 37 80 00 00 00 00 00 00 + 15.278084 1 1 Tx d 8 37 80 00 00 00 00 00 00 + 15.298988 1 1 Tx d 8 37 80 00 00 00 00 00 00 + 15.309043 1 1 Tx d 8 37 80 00 00 00 00 00 00 + 15.330365 1 1 Tx d 8 37 80 00 00 00 00 00 00 + 15.340317 1 1 Tx d 8 37 80 00 00 00 00 00 00 + 15.362270 1 1 Tx d 8 37 80 00 00 00 00 00 00 + 15.372318 1 1 Tx d 8 37 80 00 00 00 00 00 00 + 15.393316 1 1 Tx d 8 37 80 00 00 00 00 00 00 + 15.403320 1 1 Tx d 8 37 80 00 00 00 00 00 00 + 15.423782 1 1 Tx d 8 37 80 00 00 00 00 00 00 + 15.433830 1 1 Tx d 8 37 80 00 00 00 00 00 00 + 15.454820 1 1 Tx d 8 37 80 00 00 00 00 00 00 + 15.464821 1 1 Tx d 8 37 80 00 00 00 00 00 00 + 15.484780 1 1 Tx d 8 37 80 00 00 00 00 00 00 + 15.495763 1 1 Tx d 8 37 80 00 00 00 00 00 00 + 15.515821 1 1 Tx d 8 37 80 00 00 00 00 00 00 + 15.526340 1 1 Tx d 8 37 80 00 00 00 00 00 00 + 15.547352 1 1 Tx d 8 37 80 00 00 00 00 00 00 + 15.558332 1 1 Tx d 8 37 80 00 00 00 00 00 00 + 15.578290 1 1 Tx d 8 37 80 00 00 00 00 00 00 + 15.588338 1 1 Tx d 8 37 80 00 00 00 00 00 00 + 15.609274 1 1 Tx d 8 37 80 00 00 00 00 00 00 + 15.619817 1 1 Tx d 8 37 80 00 00 00 00 00 00 + 15.641002 1 1 Tx d 8 37 80 00 00 00 00 00 00 + 15.650959 1 1 Tx d 8 37 80 00 00 00 00 00 00 + 15.671910 1 1 Tx d 8 37 80 00 00 00 00 00 00 + 15.681941 1 1 Tx d 8 37 80 00 00 00 00 00 00 + 15.703019 1 1 Tx d 8 37 80 00 00 00 00 00 00 + 15.712946 1 1 Tx d 8 37 80 00 00 00 00 00 00 + 15.734505 1 1 Tx d 8 37 80 00 00 00 00 00 00 + 15.745596 1 1 Tx d 8 3D 0E 00 00 00 00 00 00 + 15.766520 1 1 Tx d 8 3D 0E 00 00 00 00 00 00 + 15.776454 1 1 Tx d 8 3D 0E 00 00 00 00 00 00 + 15.797508 1 1 Tx d 8 3D 0E 00 00 00 00 00 00 + 15.807448 1 1 Tx d 8 3D 0E 00 00 00 00 00 00 + 15.828040 1 1 Tx d 8 3D 0E 00 00 00 00 00 00 + 15.838007 1 1 Tx d 8 3D 0E 00 00 00 00 00 00 + 15.860028 1 1 Tx d 8 3D 0E 00 00 00 00 00 00 + 15.870015 1 1 Tx d 8 3D 0E 00 00 00 00 00 00 + 15.891046 1 1 Tx d 8 3D 0E 00 00 00 00 00 00 + 15.900991 1 1 Tx d 8 3D 0E 00 00 00 00 00 00 + 15.921528 1 1 Tx d 8 3D 0E 00 00 00 00 00 00 + 15.932471 1 1 Tx d 8 3D 0E 00 00 00 00 00 00 + 15.952524 1 1 Tx d 8 3D 0E 00 00 00 00 00 00 + 15.973517 1 1 Tx d 8 3D 0E 00 00 00 00 00 00 + 15.984520 1 1 Tx d 8 3D 0E 00 00 00 00 00 00 + 15.995522 1 1 Tx d 8 3D 0E 00 00 00 00 00 00 + 16.015523 1 1 Tx d 8 3D 0E 00 00 00 00 00 00 + 16.026024 1 1 Tx d 8 3D 0E 00 00 00 00 00 00 + 16.047024 1 1 Tx d 8 3D 0E 00 00 00 00 00 00 + 16.056973 1 1 Tx d 8 3D 0E 00 00 00 00 00 00 + 16.078066 1 1 Tx d 8 3D 0E 00 00 00 00 00 00 + 16.088032 1 1 Tx d 8 3D 0E 00 00 00 00 00 00 + 16.108986 1 1 Tx d 8 3D 0E 00 00 00 00 00 00 + 16.119537 1 1 Tx d 8 3D 0E 00 00 00 00 00 00 + 16.139551 1 1 Tx d 8 3D 0E 00 00 00 00 00 00 + 16.150528 1 1 Tx d 8 3D 0E 00 00 00 00 00 00 + 16.172532 1 1 Tx d 8 3D 0E 00 00 00 00 00 00 + 16.182526 1 1 Tx d 8 3D 0E 00 00 00 00 00 00 + 16.203531 1 1 Tx d 8 3D 0E 00 00 00 00 00 00 + 16.214541 1 1 Tx d 8 3D 0E 00 00 00 00 00 00 + 16.235031 1 1 Tx d 8 3D 0E 00 00 00 00 00 00 + 16.245044 1 1 Tx d 8 3D 0E 00 00 00 00 00 00 + 16.267013 1 1 Tx d 8 3D 0E 00 00 00 00 00 00 + 16.276980 1 1 Tx d 8 3D 0E 00 00 00 00 00 00 + 16.298036 1 1 Tx d 8 3D 0E 00 00 00 00 00 00 + 16.308046 1 1 Tx d 8 3D 0E 00 00 00 00 00 00 + 16.328540 1 1 Tx d 8 3D 0E 00 00 00 00 00 00 + 16.338547 1 1 Tx d 8 3D 0E 00 00 00 00 00 00 + 16.359544 1 1 Tx d 8 3D 0E 00 00 00 00 00 00 + 16.369497 1 1 Tx d 8 3D 0E 00 00 00 00 00 00 + 16.390496 1 1 Tx d 8 3D 0E 00 00 00 00 00 00 + 16.400486 1 1 Tx d 8 3D 0E 00 00 00 00 00 00 + 16.421049 1 1 Tx d 8 3D 0E 00 00 00 00 00 00 + 16.432049 1 1 Tx d 8 3D 0E 00 00 00 00 00 00 + 16.454046 1 1 Tx d 8 3D 0E 00 00 00 00 00 00 + 16.463957 1 1 Tx d 8 3D 0E 00 00 00 00 00 00 + 16.484998 1 1 Tx d 8 3D 0E 00 00 00 00 00 00 + 16.495025 1 1 Tx d 8 3D 0E 00 00 00 00 00 00 + 16.516354 1 1 Tx d 8 3D 0E 00 00 00 00 00 00 + 16.526866 1 1 Tx d 8 3D 0E 00 00 00 00 00 00 + 16.547864 1 1 Tx d 8 3D 0E 00 00 00 00 00 00 + 16.557865 1 1 Tx d 8 3D 0E 00 00 00 00 00 00 + 16.579852 1 1 Tx d 8 3D 0E 00 00 00 00 00 00 + 16.590810 1 1 Tx d 8 3D 0E 00 00 00 00 00 00 + 16.611859 1 1 Tx d 8 3D 0E 00 00 00 00 00 00 + 16.622365 1 1 Tx d 8 3D 0E 00 00 00 00 00 00 + 16.643360 1 1 Tx d 8 3D 0E 00 00 00 00 00 00 + 16.653316 1 1 Tx d 8 3D 0E 00 00 00 00 00 00 + 16.674363 1 1 Tx d 8 3D 0E 00 00 00 00 00 00 + 16.684270 1 1 Tx d 8 3D 0E 00 00 00 00 00 00 + 16.705308 1 1 Tx d 8 3D 0E 00 00 00 00 00 00 + 16.716320 1 1 Tx d 8 3D 0E 00 00 00 00 00 00 + 16.737865 1 1 Tx d 8 3D 0E 00 00 00 00 00 00 + 16.747871 1 1 Tx d 8 3D 0E 00 00 00 00 00 00 + 16.768867 1 1 Tx d 8 3D 0E 00 00 00 00 00 00 + 16.778870 1 1 Tx d 8 3D 0E 00 00 00 00 00 00 + 16.799864 1 1 Tx d 8 3D 0E 00 00 00 00 00 00 + 16.809867 1 1 Tx d 8 3D 0E 00 00 00 00 00 00 + 16.830368 1 1 Tx d 8 3D 0E 00 00 00 00 00 00 + 16.841386 1 1 Tx d 8 3D 0E 00 00 00 00 00 00 + 16.861374 1 1 Tx d 8 3D 0E 00 00 00 00 00 00 + 16.871370 1 1 Tx d 8 3D 0E 00 00 00 00 00 00 + 16.892388 1 1 Tx d 8 3D 0E 00 00 00 00 00 00 + 16.903425 1 1 Tx d 8 3D 0E 00 00 00 00 00 00 + 16.923822 1 1 Tx d 8 3D 0E 00 00 00 00 00 00 + 16.934885 1 1 Tx d 8 3D 0E 00 00 00 00 00 00 + 16.956819 1 1 Tx d 8 3D 0E 00 00 00 00 00 00 + 16.967875 1 1 Tx d 8 3D 0E 00 00 00 00 00 00 + 16.988875 1 1 Tx d 8 3D 0E 00 00 00 00 00 00 + 16.999879 1 1 Tx d 8 3D 0E 00 00 00 00 00 00 + 17.009831 1 1 Tx d 8 3D 0E 00 00 00 00 00 00 + 17.030332 1 1 Tx d 8 3D 0E 00 00 00 00 00 00 + 17.051386 1 1 Tx d 8 3D 0E 00 00 00 00 00 00 + 17.061398 1 1 Tx d 8 3D 0E 00 00 00 00 00 00 + 17.081382 1 1 Tx d 8 3D 0E 00 00 00 00 00 00 + 17.091890 1 1 Tx d 8 3D 0E 00 00 00 00 00 00 + 17.112891 1 1 Tx d 8 3D 0E 00 00 00 00 00 00 + 17.123395 1 1 Tx d 8 3D 0E 00 00 00 00 00 00 + 17.144404 1 1 Tx d 8 3D 0E 00 00 00 00 00 00 + 17.155342 1 1 Tx d 8 3D 0E 00 00 00 00 00 00 + 17.166430 1 1 Tx d 8 3D 0E 00 00 00 00 00 00 + 17.188398 1 1 Tx d 8 3D 0E 00 00 00 00 00 00 + 17.198397 1 1 Tx d 8 3D 0E 00 00 00 00 00 00 + 17.218420 1 1 Tx d 8 3D 0E 00 00 00 00 00 00 + 17.228918 1 1 Tx d 8 3D 0E 00 00 00 00 00 00 + 17.249931 1 1 Tx d 8 3D 0E 00 00 00 00 00 00 + 17.259905 1 1 Tx d 8 3D 0E 00 00 00 00 00 00 + 17.280900 1 1 Tx d 8 3D 0E 00 00 00 00 00 00 + 17.291901 1 1 Tx d 8 3D 0E 00 00 00 00 00 00 + 17.323956 1 1 Tx d 8 42 9A 00 00 00 00 00 00 + 17.343397 1 1 Tx d 8 42 9A 00 00 00 00 00 00 + 17.364435 1 1 Tx d 8 42 9A 00 00 00 00 00 00 + 17.375485 1 1 Tx d 8 42 9A 00 00 00 00 00 00 + 17.385359 1 1 Tx d 8 42 9A 00 00 00 00 00 00 + 17.405516 1 1 Tx d 8 42 9A 00 00 00 00 00 00 + 17.425828 1 1 Tx d 8 42 9A 00 00 00 00 00 00 + 17.436836 1 1 Tx d 8 42 9A 00 00 00 00 00 00 + 17.447784 1 1 Tx d 8 42 9A 00 00 00 00 00 00 + 17.468778 1 1 Tx d 8 42 9A 00 00 00 00 00 00 + 17.479824 1 1 Tx d 8 42 9A 00 00 00 00 00 00 + 17.499831 1 1 Tx d 8 42 9A 00 00 00 00 00 00 + 17.509836 1 1 Tx d 8 42 9A 00 00 00 00 00 00 + 17.531339 1 1 Tx d 8 42 9A 00 00 00 00 00 00 + 17.541337 1 1 Tx d 8 42 9A 00 00 00 00 00 00 + 17.562340 1 1 Tx d 8 42 9A 00 00 00 00 00 00 + 17.572283 1 1 Tx d 8 42 9A 00 00 00 00 00 00 + 17.594360 1 1 Tx d 8 42 9A 00 00 00 00 00 00 + 17.604285 1 1 Tx d 8 42 9A 00 00 00 00 00 00 + 17.624975 1 1 Tx d 8 42 9A 00 00 00 00 00 00 + 17.635919 1 1 Tx d 8 42 9A 00 00 00 00 00 00 + 17.656967 1 1 Tx d 8 42 9A 00 00 00 00 00 00 + 17.667986 1 1 Tx d 8 42 9A 00 00 00 00 00 00 + 17.689921 1 1 Tx d 8 42 9A 00 00 00 00 00 00 + 17.699886 1 1 Tx d 8 42 9A 00 00 00 00 00 00 + 17.720738 1 1 Tx d 8 42 9A 00 00 00 00 00 00 + 17.730646 1 1 Tx d 8 42 9A 00 00 00 00 00 00 + 17.752414 1 1 Tx d 8 42 9A 00 00 00 00 00 00 + 17.762515 1 1 Tx d 8 42 9A 00 00 00 00 00 00 + 17.783391 1 1 Tx d 8 42 9A 00 00 00 00 00 00 + 17.794392 1 1 Tx d 8 42 9A 00 00 00 00 00 00 + 17.814481 1 1 Tx d 8 42 9A 00 00 00 00 00 00 + 17.825110 1 1 Tx d 8 42 9A 00 00 00 00 00 00 + 17.846114 1 1 Tx d 8 42 9A 00 00 00 00 00 00 + 17.856116 1 1 Tx d 8 42 9A 00 00 00 00 00 00 + 17.877116 1 1 Tx d 8 42 9A 00 00 00 00 00 00 + 17.888198 1 1 Tx d 8 42 9A 00 00 00 00 00 00 + 17.910130 1 1 Tx d 8 42 9A 00 00 00 00 00 00 + 17.920668 1 1 Tx d 8 42 9A 00 00 00 00 00 00 + 17.941751 1 1 Tx d 8 42 9A 00 00 00 00 00 00 + 17.952751 1 1 Tx d 8 42 9A 00 00 00 00 00 00 + 17.973752 1 1 Tx d 8 42 9A 00 00 00 00 00 00 + 17.983741 1 1 Tx d 8 42 9A 00 00 00 00 00 00 + 17.994744 1 1 Tx d 8 42 9A 00 00 00 00 00 00 + 18.014747 1 1 Tx d 8 42 9A 00 00 00 00 00 00 + 18.025447 1 1 Tx d 8 42 9A 00 00 00 00 00 00 + 18.046437 1 1 Tx d 8 42 9A 00 00 00 00 00 00 + 18.067531 1 1 Tx d 8 42 9A 00 00 00 00 00 00 + 18.077441 1 1 Tx d 8 42 9A 00 00 00 00 00 00 + 18.088540 1 1 Tx d 8 42 9A 00 00 00 00 00 00 + 18.110548 1 1 Tx d 8 42 9A 00 00 00 00 00 00 + 18.120611 1 1 Tx d 8 42 9A 00 00 00 00 00 00 + 18.141730 1 1 Tx d 8 42 9A 00 00 00 00 00 00 + 18.152730 1 1 Tx d 8 42 9A 00 00 00 00 00 00 + 18.173730 1 1 Tx d 8 42 9A 00 00 00 00 00 00 + 18.183641 1 1 Tx d 8 42 9A 00 00 00 00 00 00 + 18.204898 1 1 Tx d 8 42 9A 00 00 00 00 00 00 + 18.215897 1 1 Tx d 8 42 9A 00 00 00 00 00 00 + 18.237490 1 1 Tx d 8 42 9A 00 00 00 00 00 00 + 18.247445 1 1 Tx d 8 42 9A 00 00 00 00 00 00 + 18.268480 1 1 Tx d 8 42 9A 00 00 00 00 00 00 + 18.279485 1 1 Tx d 8 42 9A 00 00 00 00 00 00 + 18.300579 1 1 Tx d 8 42 9A 00 00 00 00 00 00 + 18.310574 1 1 Tx d 8 42 9A 00 00 00 00 00 00 + 18.331260 1 1 Tx d 8 42 9A 00 00 00 00 00 00 + 18.342366 1 1 Tx d 8 42 9A 00 00 00 00 00 00 + 18.363275 1 1 Tx d 8 42 9A 00 00 00 00 00 00 + 18.373265 1 1 Tx d 8 42 9A 00 00 00 00 00 00 + 18.395365 1 1 Tx d 8 42 9A 00 00 00 00 00 00 + 18.405309 1 1 Tx d 8 42 9A 00 00 00 00 00 00 + 18.425998 1 1 Tx d 8 42 9A 00 00 00 00 00 00 + 18.436995 1 1 Tx d 8 42 9A 00 00 00 00 00 00 + 18.457999 1 1 Tx d 8 42 9A 00 00 00 00 00 00 + 18.468989 1 1 Tx d 8 42 9A 00 00 00 00 00 00 + 18.478933 1 1 Tx d 8 42 9A 00 00 00 00 00 00 + 18.500008 1 1 Tx d 8 42 9A 00 00 00 00 00 00 + 18.509997 1 1 Tx d 8 42 9A 00 00 00 00 00 00 + 18.530503 1 1 Tx d 8 42 9A 00 00 00 00 00 00 + 18.551501 1 1 Tx d 8 42 9A 00 00 00 00 00 00 + 18.561506 1 1 Tx d 8 42 9A 00 00 00 00 00 00 + 18.572511 1 1 Tx d 8 42 9A 00 00 00 00 00 00 + 18.593497 1 1 Tx d 8 42 9A 00 00 00 00 00 00 + 18.603507 1 1 Tx d 8 42 9A 00 00 00 00 00 00 + 18.624006 1 1 Tx d 8 42 9A 00 00 00 00 00 00 + 18.645009 1 1 Tx d 8 42 9A 00 00 00 00 00 00 + 18.655009 1 1 Tx d 8 42 9A 00 00 00 00 00 00 + 18.666101 1 1 Tx d 8 42 9A 00 00 00 00 00 00 + 18.687035 1 1 Tx d 8 42 9A 00 00 00 00 00 00 + 18.698097 1 1 Tx d 8 42 9A 00 00 00 00 00 00 + 18.719021 1 1 Tx d 8 42 9A 00 00 00 00 00 00 + 18.729351 1 1 Tx d 8 42 9A 00 00 00 00 00 00 + 18.750410 1 1 Tx d 8 42 9A 00 00 00 00 00 00 + 18.760347 1 1 Tx d 8 42 9A 00 00 00 00 00 00 + 18.782444 1 1 Tx d 8 42 9A 00 00 00 00 00 00 + 18.793395 1 1 Tx d 8 42 9A 00 00 00 00 00 00 + 18.813548 1 1 Tx d 8 42 9A 00 00 00 00 00 00 + 18.824117 1 1 Tx d 8 42 9A 00 00 00 00 00 00 + 18.844129 1 1 Tx d 8 42 9A 00 00 00 00 00 00 + 18.855207 1 1 Tx d 8 42 9A 00 00 00 00 00 00 + 18.876223 1 1 Tx d 8 42 9A 00 00 00 00 00 00 + 18.944492 1 1 Tx d 8 48 28 00 00 00 00 00 00 + 18.953988 1 1 Tx d 8 48 28 00 00 00 00 00 00 + 18.965066 1 1 Tx d 8 48 28 00 00 00 00 00 00 + 18.987048 1 1 Tx d 8 48 28 00 00 00 00 00 00 + 18.998048 1 1 Tx d 8 48 28 00 00 00 00 00 00 + 19.018060 1 1 Tx d 8 48 28 00 00 00 00 00 00 + 19.028750 1 1 Tx d 8 48 28 00 00 00 00 00 00 + 19.049850 1 1 Tx d 8 48 28 00 00 00 00 00 00 + 19.060832 1 1 Tx d 8 48 28 00 00 00 00 00 00 + 19.080758 1 1 Tx d 8 48 28 00 00 00 00 00 00 + 19.091833 1 1 Tx d 8 48 28 00 00 00 00 00 00 + 19.113535 1 1 Tx d 8 48 28 00 00 00 00 00 00 + 19.124114 1 1 Tx d 8 48 28 00 00 00 00 00 00 + 19.135198 1 1 Tx d 8 48 28 00 00 00 00 00 00 + 19.156215 1 1 Tx d 8 48 28 00 00 00 00 00 00 + 19.167200 1 1 Tx d 8 48 28 00 00 00 00 00 00 + 19.189205 1 1 Tx d 8 48 28 00 00 00 00 00 00 + 19.200219 1 1 Tx d 8 48 28 00 00 00 00 00 00 + 19.221692 1 1 Tx d 8 48 28 00 00 00 00 00 00 + 19.231694 1 1 Tx d 8 48 28 00 00 00 00 00 00 + 19.252877 1 1 Tx d 8 48 28 00 00 00 00 00 00 + 19.262845 1 1 Tx d 8 48 28 00 00 00 00 00 00 + 19.283922 1 1 Tx d 8 48 28 00 00 00 00 00 00 + 19.293830 1 1 Tx d 8 48 28 00 00 00 00 00 00 + 19.314876 1 1 Tx d 8 48 28 00 00 00 00 00 00 + 19.325665 1 1 Tx d 8 48 28 00 00 00 00 00 00 + 19.346574 1 1 Tx d 8 48 28 00 00 00 00 00 00 + 19.357668 1 1 Tx d 8 48 28 00 00 00 00 00 00 + 19.378597 1 1 Tx d 8 48 28 00 00 00 00 00 00 + 19.389670 1 1 Tx d 8 48 28 00 00 00 00 00 00 + 19.409667 1 1 Tx d 8 48 28 00 00 00 00 00 00 + 19.420728 1 1 Tx d 8 48 28 00 00 00 00 00 00 + 19.440747 1 1 Tx d 8 48 28 00 00 00 00 00 00 + 19.451817 1 1 Tx d 8 48 28 00 00 00 00 00 00 + 19.472807 1 1 Tx d 8 48 28 00 00 00 00 00 00 + 19.483819 1 1 Tx d 8 48 28 00 00 00 00 00 00 + 19.494809 1 1 Tx d 8 48 28 00 00 00 00 00 00 + 19.514810 1 1 Tx d 8 48 28 00 00 00 00 00 00 + 19.535305 1 1 Tx d 8 48 28 00 00 00 00 00 00 + 19.545262 1 1 Tx d 8 48 28 00 00 00 00 00 00 + 19.566319 1 1 Tx d 8 48 28 00 00 00 00 00 00 + 19.576313 1 1 Tx d 8 48 28 00 00 00 00 00 00 + 19.596314 1 1 Tx d 8 48 28 00 00 00 00 00 00 + 19.606313 1 1 Tx d 8 48 28 00 00 00 00 00 00 + 19.626820 1 1 Tx d 8 48 28 00 00 00 00 00 00 + 19.636817 1 1 Tx d 8 48 28 00 00 00 00 00 00 + 19.658821 1 1 Tx d 8 48 28 00 00 00 00 00 00 + 19.668814 1 1 Tx d 8 48 28 00 00 00 00 00 00 + 19.688817 1 1 Tx d 8 48 28 00 00 00 00 00 00 + 19.698814 1 1 Tx d 8 48 28 00 00 00 00 00 00 + 19.719828 1 1 Tx d 8 48 28 00 00 00 00 00 00 + 19.730335 1 1 Tx d 8 48 28 00 00 00 00 00 00 + 19.751321 1 1 Tx d 8 48 28 00 00 00 00 00 00 + 19.761301 1 1 Tx d 8 48 28 00 00 00 00 00 00 + 19.782323 1 1 Tx d 8 48 28 00 00 00 00 00 00 + 19.792327 1 1 Tx d 8 48 28 00 00 00 00 00 00 + 19.812326 1 1 Tx d 8 48 28 00 00 00 00 00 00 + 19.822835 1 1 Tx d 8 48 28 00 00 00 00 00 00 + 19.843824 1 1 Tx d 8 48 28 00 00 00 00 00 00 + 19.853835 1 1 Tx d 8 48 28 00 00 00 00 00 00 + 19.874788 1 1 Tx d 8 48 28 00 00 00 00 00 00 + 19.884779 1 1 Tx d 8 48 28 00 00 00 00 00 00 + 19.905777 1 1 Tx d 8 48 28 00 00 00 00 00 00 + 19.926317 1 1 Tx d 8 48 28 00 00 00 00 00 00 + 19.936278 1 1 Tx d 8 48 28 00 00 00 00 00 00 + 19.947337 1 1 Tx d 8 48 28 00 00 00 00 00 00 + 19.967280 1 1 Tx d 8 48 28 00 00 00 00 00 00 + 19.988313 1 1 Tx d 8 48 28 00 00 00 00 00 00 + 19.998331 1 1 Tx d 8 48 28 00 00 00 00 00 00 + 20.019340 1 1 Tx d 8 48 28 00 00 00 00 00 00 + 20.029841 1 1 Tx d 8 48 28 00 00 00 00 00 00 + 20.049840 1 1 Tx d 8 48 28 00 00 00 00 00 00 + 20.059846 1 1 Tx d 8 48 28 00 00 00 00 00 00 + 20.080837 1 1 Tx d 8 48 28 00 00 00 00 00 00 + 20.090836 1 1 Tx d 8 48 28 00 00 00 00 00 00 + 20.111839 1 1 Tx d 8 48 28 00 00 00 00 00 00 + 20.122341 1 1 Tx d 8 48 28 00 00 00 00 00 00 + 20.142280 1 1 Tx d 8 48 28 00 00 00 00 00 00 + 20.152287 1 1 Tx d 8 48 28 00 00 00 00 00 00 + 20.173344 1 1 Tx d 8 48 28 00 00 00 00 00 00 + 20.183347 1 1 Tx d 8 48 28 00 00 00 00 00 00 + 20.204349 1 1 Tx d 8 48 28 00 00 00 00 00 00 + 20.214350 1 1 Tx d 8 48 28 00 00 00 00 00 00 + 20.234858 1 1 Tx d 8 48 28 00 00 00 00 00 00 + 20.244837 1 1 Tx d 8 48 28 00 00 00 00 00 00 + 20.264799 1 1 Tx d 8 48 28 00 00 00 00 00 00 + 20.275860 1 1 Tx d 8 48 28 00 00 00 00 00 00 + 20.295851 1 1 Tx d 8 48 28 00 00 00 00 00 00 + 20.315864 1 1 Tx d 8 48 28 00 00 00 00 00 00 + 20.326356 1 1 Tx d 8 48 28 00 00 00 00 00 00 + 20.346357 1 1 Tx d 8 48 28 00 00 00 00 00 00 + 20.356370 1 1 Tx d 8 48 28 00 00 00 00 00 00 + 20.377357 1 1 Tx d 8 48 28 00 00 00 00 00 00 + 20.388359 1 1 Tx d 8 48 28 00 00 00 00 00 00 + 20.408361 1 1 Tx d 8 48 28 00 00 00 00 00 00 + 20.418358 1 1 Tx d 8 48 28 00 00 00 00 00 00 + 20.438865 1 1 Tx d 8 48 28 00 00 00 00 00 00 + 20.449082 1 1 Tx d 8 48 28 00 00 00 00 00 00 + 20.469867 1 1 Tx d 8 48 28 00 00 00 00 00 00 + 20.480061 1 1 Tx d 8 48 28 00 00 00 00 00 00 + 20.509329 1 1 Tx d 8 4D B4 00 00 00 00 00 00 + 20.518810 1 1 Tx d 8 4D B4 00 00 00 00 00 00 + 20.529370 1 1 Tx d 8 4D B4 00 00 00 00 00 00 + 20.550365 1 1 Tx d 8 4D B4 00 00 00 00 00 00 + 20.560371 1 1 Tx d 8 4D B4 00 00 00 00 00 00 + 20.581370 1 1 Tx d 8 4D B4 00 00 00 00 00 00 + 20.591366 1 1 Tx d 8 4D B4 00 00 00 00 00 00 + 20.612220 1 1 Tx d 8 4D B4 00 00 00 00 00 00 + 20.622163 1 1 Tx d 8 4D B4 00 00 00 00 00 00 + 20.643714 1 1 Tx d 8 4D B4 00 00 00 00 00 00 + 20.654706 1 1 Tx d 8 4D B4 00 00 00 00 00 00 + 20.675704 1 1 Tx d 8 4D B4 00 00 00 00 00 00 + 20.686706 1 1 Tx d 8 4D B4 00 00 00 00 00 00 + 20.707708 1 1 Tx d 8 4D B4 00 00 00 00 00 00 + 20.717642 1 1 Tx d 8 4D B4 00 00 00 00 00 00 + 20.739207 1 1 Tx d 8 4D B4 00 00 00 00 00 00 + 20.749207 1 1 Tx d 8 4D B4 00 00 00 00 00 00 + 20.769209 1 1 Tx d 8 4D B4 00 00 00 00 00 00 + 20.779207 1 1 Tx d 8 4D B4 00 00 00 00 00 00 + 20.801208 1 1 Tx d 8 4D B4 00 00 00 00 00 00 + 20.811210 1 1 Tx d 8 4D B4 00 00 00 00 00 00 + 20.831719 1 1 Tx d 8 4D B4 00 00 00 00 00 00 + 20.841715 1 1 Tx d 8 4D B4 00 00 00 00 00 00 + 20.863728 1 1 Tx d 8 4D B4 00 00 00 00 00 00 + 20.873716 1 1 Tx d 8 4D B4 00 00 00 00 00 00 + 20.894719 1 1 Tx d 8 4D B4 00 00 00 00 00 00 + 20.904682 1 1 Tx d 8 4D B4 00 00 00 00 00 00 + 20.925234 1 1 Tx d 8 4D B4 00 00 00 00 00 00 + 20.935224 1 1 Tx d 8 4D B4 00 00 00 00 00 00 + 20.956223 1 1 Tx d 8 4D B4 00 00 00 00 00 00 + 20.966224 1 1 Tx d 8 4D B4 00 00 00 00 00 00 + 20.988223 1 1 Tx d 8 4D B4 00 00 00 00 00 00 + 20.998240 1 1 Tx d 8 4D B4 00 00 00 00 00 00 + 21.018225 1 1 Tx d 8 4D B4 00 00 00 00 00 00 + 21.028745 1 1 Tx d 8 4D B4 00 00 00 00 00 00 + 21.048731 1 1 Tx d 8 4D B4 00 00 00 00 00 00 + 21.059732 1 1 Tx d 8 4D B4 00 00 00 00 00 00 + 21.080729 1 1 Tx d 8 4D B4 00 00 00 00 00 00 + 21.090728 1 1 Tx d 8 4D B4 00 00 00 00 00 00 + 21.112633 1 1 Tx d 8 4D B4 00 00 00 00 00 00 + 21.122635 1 1 Tx d 8 4D B4 00 00 00 00 00 00 + 21.142635 1 1 Tx d 8 4D B4 00 00 00 00 00 00 + 21.153638 1 1 Tx d 8 4D B4 00 00 00 00 00 00 + 21.174631 1 1 Tx d 8 4D B4 00 00 00 00 00 00 + 21.184636 1 1 Tx d 8 4D B4 00 00 00 00 00 00 + 21.205241 1 1 Tx d 8 4D B4 00 00 00 00 00 00 + 21.215243 1 1 Tx d 8 4D B4 00 00 00 00 00 00 + 21.235747 1 1 Tx d 8 4D B4 00 00 00 00 00 00 + 21.245746 1 1 Tx d 8 4D B4 00 00 00 00 00 00 + 21.265745 1 1 Tx d 8 4D B4 00 00 00 00 00 00 + 21.276751 1 1 Tx d 8 4D B4 00 00 00 00 00 00 + 21.297780 1 1 Tx d 8 4D B4 00 00 00 00 00 00 + 21.307749 1 1 Tx d 8 4D B4 00 00 00 00 00 00 + 21.328251 1 1 Tx d 8 4D B4 00 00 00 00 00 00 + 21.338255 1 1 Tx d 8 4D B4 00 00 00 00 00 00 + 21.365885 1 1 Tx d 8 4D B4 00 00 00 00 00 00 + 21.375581 1 1 Tx d 8 4D B4 00 00 00 00 00 00 + 21.386633 1 1 Tx d 8 4D B4 00 00 00 00 00 00 + 21.407544 1 1 Tx d 8 4D B4 00 00 00 00 00 00 + 21.417641 1 1 Tx d 8 4D B4 00 00 00 00 00 00 + 21.438131 1 1 Tx d 8 4D B4 00 00 00 00 00 00 + 21.449133 1 1 Tx d 8 4D B4 00 00 00 00 00 00 + 21.469132 1 1 Tx d 8 4D B4 00 00 00 00 00 00 + 21.479597 1 1 Tx d 8 4D B4 00 00 00 00 00 00 + 21.500385 1 1 Tx d 8 4D B4 00 00 00 00 00 00 + 21.510387 1 1 Tx d 8 4D B4 00 00 00 00 00 00 + 21.530815 1 1 Tx d 8 4D B4 00 00 00 00 00 00 + 21.541942 1 1 Tx d 8 4D B4 00 00 00 00 00 00 + 21.561860 1 1 Tx d 8 4D B4 00 00 00 00 00 00 + 21.572895 1 1 Tx d 8 4D B4 00 00 00 00 00 00 + 21.592894 1 1 Tx d 8 4D B4 00 00 00 00 00 00 + 21.612807 1 1 Tx d 8 4D B4 00 00 00 00 00 00 + 21.623372 1 1 Tx d 8 4D B4 00 00 00 00 00 00 + 21.644360 1 1 Tx d 8 4D B4 00 00 00 00 00 00 + 21.655405 1 1 Tx d 8 4D B4 00 00 00 00 00 00 + 21.675306 1 1 Tx d 8 4D B4 00 00 00 00 00 00 + 21.686404 1 1 Tx d 8 4D B4 00 00 00 00 00 00 + 21.697413 1 1 Tx d 8 4D B4 00 00 00 00 00 00 + 21.718406 1 1 Tx d 8 4D B4 00 00 00 00 00 00 + 21.728922 1 1 Tx d 8 4D B4 00 00 00 00 00 00 + 21.749906 1 1 Tx d 8 4D B4 00 00 00 00 00 00 + 21.759906 1 1 Tx d 8 4D B4 00 00 00 00 00 00 + 21.781906 1 1 Tx d 8 4D B4 00 00 00 00 00 00 + 21.791911 1 1 Tx d 8 4D B4 00 00 00 00 00 00 + 21.812907 1 1 Tx d 8 4D B4 00 00 00 00 00 00 + 21.823418 1 1 Tx d 8 4D B4 00 00 00 00 00 00 + 21.843421 1 1 Tx d 8 4D B4 00 00 00 00 00 00 + 21.854413 1 1 Tx d 8 4D B4 00 00 00 00 00 00 + 21.876420 1 1 Tx d 8 4D B4 00 00 00 00 00 00 + 21.887415 1 1 Tx d 8 4D B4 00 00 00 00 00 00 + 21.902107 1 1 Tx d 8 4D B4 00 00 00 00 00 00 + 21.922350 1 1 Tx d 8 4D B4 00 00 00 00 00 00 + 21.932531 1 1 Tx d 8 4D B4 00 00 00 00 00 00 + 21.952516 1 1 Tx d 8 4D B4 00 00 00 00 00 00 + 21.963552 1 1 Tx d 8 4D B4 00 00 00 00 00 00 + 21.985628 1 1 Tx d 8 4D B4 00 00 00 00 00 00 + 21.995517 1 1 Tx d 8 4D B4 00 00 00 00 00 00 + 22.016569 1 1 Tx d 8 4D B4 00 00 00 00 00 00 + 22.027034 1 1 Tx d 8 4D B4 00 00 00 00 00 00 + 22.048084 1 1 Tx d 8 4D B4 00 00 00 00 00 00 + 22.059399 1 1 Tx d 8 53 40 00 00 00 00 00 00 + 22.079041 1 1 Tx d 8 53 40 00 00 00 00 00 00 + 22.090021 1 1 Tx d 8 53 40 00 00 00 00 00 00 + 22.111032 1 1 Tx d 8 53 40 00 00 00 00 00 00 + 22.121034 1 1 Tx d 8 53 40 00 00 00 00 00 00 + 22.141530 1 1 Tx d 8 53 40 00 00 00 00 00 00 + 22.151521 1 1 Tx d 8 53 40 00 00 00 00 00 00 + 22.171538 1 1 Tx d 8 53 40 00 00 00 00 00 00 + 22.191529 1 1 Tx d 8 53 40 00 00 00 00 00 00 + 22.201531 1 1 Tx d 8 53 40 00 00 00 00 00 00 + 22.222525 1 1 Tx d 8 53 40 00 00 00 00 00 00 + 22.233069 1 1 Tx d 8 53 40 00 00 00 00 00 00 + 22.253037 1 1 Tx d 8 53 40 00 00 00 00 00 00 + 22.263069 1 1 Tx d 8 53 40 00 00 00 00 00 00 + 22.283043 1 1 Tx d 8 53 40 00 00 00 00 00 00 + 22.293087 1 1 Tx d 8 53 40 00 00 00 00 00 00 + 22.314098 1 1 Tx d 8 53 40 00 00 00 00 00 00 + 22.324605 1 1 Tx d 8 53 40 00 00 00 00 00 00 + 22.345603 1 1 Tx d 8 53 40 00 00 00 00 00 00 + 22.356611 1 1 Tx d 8 53 40 00 00 00 00 00 00 + 22.377602 1 1 Tx d 8 53 40 00 00 00 00 00 00 + 22.387604 1 1 Tx d 8 53 40 00 00 00 00 00 00 + 22.407558 1 1 Tx d 8 53 40 00 00 00 00 00 00 + 22.418222 1 1 Tx d 8 53 40 00 00 00 00 00 00 + 22.438123 1 1 Tx d 8 53 40 00 00 00 00 00 00 + 22.448108 1 1 Tx d 8 53 40 00 00 00 00 00 00 + 22.469110 1 1 Tx d 8 53 40 00 00 00 00 00 00 + 22.479103 1 1 Tx d 8 53 40 00 00 00 00 00 00 + 22.500106 1 1 Tx d 8 53 40 00 00 00 00 00 00 + 22.510113 1 1 Tx d 8 53 40 00 00 00 00 00 00 + 22.530613 1 1 Tx d 8 53 40 00 00 00 00 00 00 + 22.541549 1 1 Tx d 8 53 40 00 00 00 00 00 00 + 22.561530 1 1 Tx d 8 53 40 00 00 00 00 00 00 + 22.572540 1 1 Tx d 8 53 40 00 00 00 00 00 00 + 22.593596 1 1 Tx d 8 53 40 00 00 00 00 00 00 + 22.603606 1 1 Tx d 8 53 40 00 00 00 00 00 00 + 22.623591 1 1 Tx d 8 53 40 00 00 00 00 00 00 + 22.645133 1 1 Tx d 8 53 40 00 00 00 00 00 00 + 22.655052 1 1 Tx d 8 53 40 00 00 00 00 00 00 + 22.676100 1 1 Tx d 8 53 40 00 00 00 00 00 00 + 22.686104 1 1 Tx d 8 53 40 00 00 00 00 00 00 + 22.708096 1 1 Tx d 8 53 40 00 00 00 00 00 00 + 22.718096 1 1 Tx d 8 53 40 00 00 00 00 00 00 + 22.728529 1 1 Tx d 8 53 40 00 00 00 00 00 00 + 22.749596 1 1 Tx d 8 53 40 00 00 00 00 00 00 + 22.769503 1 1 Tx d 8 53 40 00 00 00 00 00 00 + 22.779605 1 1 Tx d 8 53 40 00 00 00 00 00 00 + 22.799724 1 1 Tx d 8 53 40 00 00 00 00 00 00 + 22.809719 1 1 Tx d 8 53 40 00 00 00 00 00 00 + 22.830221 1 1 Tx d 8 53 40 00 00 00 00 00 00 + 22.840229 1 1 Tx d 8 53 40 00 00 00 00 00 00 + 22.861223 1 1 Tx d 8 53 40 00 00 00 00 00 00 + 22.871225 1 1 Tx d 8 53 40 00 00 00 00 00 00 + 22.893226 1 1 Tx d 8 53 40 00 00 00 00 00 00 + 22.904237 1 1 Tx d 8 53 40 00 00 00 00 00 00 + 22.924731 1 1 Tx d 8 53 40 00 00 00 00 00 00 + 22.934642 1 1 Tx d 8 53 40 00 00 00 00 00 00 + 22.955726 1 1 Tx d 8 53 40 00 00 00 00 00 00 + 22.965728 1 1 Tx d 8 53 40 00 00 00 00 00 00 + 22.985725 1 1 Tx d 8 53 40 00 00 00 00 00 00 + 22.996681 1 1 Tx d 8 53 40 00 00 00 00 00 00 + 23.017734 1 1 Tx d 8 53 40 00 00 00 00 00 00 + 23.028188 1 1 Tx d 8 53 40 00 00 00 00 00 00 + 23.049239 1 1 Tx d 8 53 40 00 00 00 00 00 00 + 23.059231 1 1 Tx d 8 53 40 00 00 00 00 00 00 + 23.080241 1 1 Tx d 8 53 40 00 00 00 00 00 00 + 23.090234 1 1 Tx d 8 53 40 00 00 00 00 00 00 + 23.111238 1 1 Tx d 8 53 40 00 00 00 00 00 00 + 23.121780 1 1 Tx d 8 53 40 00 00 00 00 00 00 + 23.142249 1 1 Tx d 8 53 40 00 00 00 00 00 00 + 23.152244 1 1 Tx d 8 53 40 00 00 00 00 00 00 + 23.173285 1 1 Tx d 8 53 40 00 00 00 00 00 00 + 23.183246 1 1 Tx d 8 53 40 00 00 00 00 00 00 + 23.203244 1 1 Tx d 8 53 40 00 00 00 00 00 00 + 23.213250 1 1 Tx d 8 53 40 00 00 00 00 00 00 + 23.233775 1 1 Tx d 8 53 40 00 00 00 00 00 00 + 23.253748 1 1 Tx d 8 53 40 00 00 00 00 00 00 + 23.263754 1 1 Tx d 8 53 40 00 00 00 00 00 00 + 23.284748 1 1 Tx d 8 53 40 00 00 00 00 00 00 + 23.295705 1 1 Tx d 8 53 40 00 00 00 00 00 00 + 23.306759 1 1 Tx d 8 53 40 00 00 00 00 00 00 + 23.327035 1 1 Tx d 8 53 40 00 00 00 00 00 00 + 23.347731 1 1 Tx d 8 53 40 00 00 00 00 00 00 + 23.357662 1 1 Tx d 8 53 40 00 00 00 00 00 00 + 23.379734 1 1 Tx d 8 53 40 00 00 00 00 00 00 + 23.389726 1 1 Tx d 8 53 40 00 00 00 00 00 00 + 23.400731 1 1 Tx d 8 53 40 00 00 00 00 00 00 + 23.420728 1 1 Tx d 8 53 40 00 00 00 00 00 00 + 23.441243 1 1 Tx d 8 53 40 00 00 00 00 00 00 + 23.451358 1 1 Tx d 8 53 40 00 00 00 00 00 00 + 23.471239 1 1 Tx d 8 53 40 00 00 00 00 00 00 + 23.482236 1 1 Tx d 8 53 40 00 00 00 00 00 00 + 23.502143 1 1 Tx d 8 53 40 00 00 00 00 00 00 + 23.512241 1 1 Tx d 8 53 40 00 00 00 00 00 00 + 23.532722 1 1 Tx d 8 53 40 00 00 00 00 00 00 + 23.542703 1 1 Tx d 8 53 40 00 00 00 00 00 00 + 23.563743 1 1 Tx d 8 53 40 00 00 00 00 00 00 + 23.574756 1 1 Tx d 8 53 40 00 00 00 00 00 00 + 23.595741 1 1 Tx d 8 53 40 00 00 00 00 00 00 + 23.605738 1 1 Tx d 8 53 40 00 00 00 00 00 00 +End TriggerBlock diff --git a/export/interfaces/ipc.idl b/export/interfaces/ipc.idl index a9db13c..6b537f1 100644 --- a/export/interfaces/ipc.idl +++ b/export/interfaces/ipc.idl @@ -72,9 +72,9 @@ module sdv }; /** - * @brief Connection status enumeration + * @brief Connection state enumeration */ - enum EConnectStatus : uint32 + enum EConnectState : uint32 { uninitialized = 0, ///< Initialization via IConnect pending/required initializing = 1, ///< Initiation channel access. @@ -96,8 +96,8 @@ module sdv { /** * @brief Establish a connection and start sending/receiving messages. - * @param[in] pReceiver Callback interface for receiving data and connect status. - * @return Returns 'true' when a channel connection could be initiated. Use GetStatus or IConnectEventCallback to + * @param[in] pReceiver Callback interface for receiving data and connect state. + * @return Returns 'true' when a channel connection could be initiated. Use GetConnectState or IConnectEventCallback to * check the connection state. */ boolean AsyncConnect(in IInterfaceAccess pReceiver); @@ -116,33 +116,32 @@ module sdv void CancelWait(); /** - * @brief Disconnect from a connection. This will set the connect status to disconnected and release the interface - * used for the status events. + * @brief Disconnect from a connection. This will set the connect state to disconnected and release the interface + * used for the state events. */ void Disconnect(); /** * @brief Register event callback interface. - * @details Register a connection status event callback interface. The exposed interface must be of type + * @details Register a connection state event callback interface. The exposed interface must be of type * IConnectEventCallback. The registration will exist until a call to the unregister function with the returned cookie * or until the connection is terminated. * @param[in] pEventCallback Pointer to the object exposing the IConnectEventCallback interface. * @return The cookie assigned to the registration or 0 when the registration wasn't successful. */ - uint64 RegisterStatusEventCallback(in IInterfaceAccess pEventCallback); + uint64 RegisterStateEventCallback(in IInterfaceAccess pEventCallback); /** - * @brief Unregister the status event callback with the returned cookie from the registration. + * @brief Unregister the state event callback with the returned cookie from the registration. * @param[in] uiCookie The cookie returned by a previous call to the registration function. */ - void UnregisterStatusEventCallback(in uint64 uiCookie); + void UnregisterStateEventCallback(in uint64 uiCookie); /** - * @brief Gets the current status of the IPC whether it is initialized/connected/disconnected or having connection - * error. - * @return Returns retrieved status of the IPC. + * @brief Get the current state of the IPC conection. + * @return Returns connection state. */ - EConnectStatus GetStatus() const; + EConnectState GetConnectState() const; }; /** @@ -151,10 +150,10 @@ module sdv interface IConnectEventCallback { /** - * @brief Set the current status. - * @param[in] eConnectStatus The connection status. + * @brief Set the current connect state. + * @param[in] eConnectState The connection state. */ - void SetStatus(in EConnectStatus eConnectStatus); + void SetConnectState(in EConnectState eConnectState); }; /** diff --git a/export/support/component_impl.h b/export/support/component_impl.h index 2ef9834..3a39a92 100644 --- a/export/support/component_impl.h +++ b/export/support/component_impl.h @@ -900,7 +900,7 @@ namespace sdv // Lock the parameter map - do not allow any more changes LockParamMap(); - // Set status + // Set state m_eObjectState = EObjectState::shutdown_in_progress; // Inform derived class @@ -1001,7 +1001,7 @@ namespace sdv END_SDV_INTERFACE_MAP() private: - std::atomic m_eObjectState = EObjectState::initialization_pending; ///< Object status + std::atomic m_eObjectState = EObjectState::initialization_pending; ///< Object state std::string m_ssObjectConfig; ///< Copy of the configuration TOML. }; diff --git a/sdv_executables/sdv_iso/main.cpp b/sdv_executables/sdv_iso/main.cpp index aa559ab..d979154 100644 --- a/sdv_executables/sdv_iso/main.cpp +++ b/sdv_executables/sdv_iso/main.cpp @@ -32,17 +32,17 @@ struct SConnectEventCallbackWrapper : sdv::IInterfaceAccess, sdv::ipc::IConnectE END_SDV_INTERFACE_MAP() /** - * @brief Set the current status. Overload of sdv::ipc::IConnectEventCallback::SetStatus. - * @param[in] eConnectStatus The connection status. + * @brief Set the current connect state. Overload of sdv::ipc::IConnectEventCallback::SetConnectState. + * @param[in] eConnectState The connection state. */ - virtual void SetStatus(/*in*/ sdv::ipc::EConnectStatus eConnectStatus) override + virtual void SetConnectState(/*in*/ sdv::ipc::EConnectState eConnectState) override { - switch (eConnectStatus) + switch (eConnectState) { - case sdv::ipc::EConnectStatus::disconnected: - case sdv::ipc::EConnectStatus::disconnected_forced: - case sdv::ipc::EConnectStatus::connection_error: - case sdv::ipc::EConnectStatus::communication_error: + case sdv::ipc::EConnectState::disconnected: + case sdv::ipc::EConnectState::disconnected_forced: + case sdv::ipc::EConnectState::connection_error: + case sdv::ipc::EConnectState::communication_error: { auto pRequestShutdown = sdv::core::GetObject("AppControlService"); if (!pRequestShutdown) break; @@ -237,13 +237,13 @@ extern "C" int main(int iArgc, const char* rgszArgv[]) return APP_CONTROL_INVALID_ISOLATION_CONFIG; } SConnectEventCallbackWrapper sConnectEventWrapper; - uint64_t uiCookie = pConnect->RegisterStatusEventCallback(&sConnectEventWrapper); + uint64_t uiCookie = pConnect->RegisterStateEventCallback(&sConnectEventWrapper); // Connect to the core repository sdv::com::IConnectionControl* pConnectionControl = sdv::core::GetObject("CommunicationControl"); if (!pConnectionControl) { - pConnect->UnregisterStatusEventCallback(uiCookie); + pConnect->UnregisterStateEventCallback(uiCookie); if (!bSilent) std::cerr << "ERROR: " << COMMUNICATION_CONTROL_SERVICE_ACCESS_ERROR_MSG << std::endl; return COMMUNICATION_CONTROL_SERVICE_ACCESS_ERROR; @@ -252,7 +252,7 @@ extern "C" int main(int iArgc, const char* rgszArgv[]) sdv::com::TConnectionID tConnection = pConnectionControl->AssignClientEndpoint(ptrChannelEndpoint, 3000, pCoreRepo); if (!tConnection.uiControl || !pCoreRepo) { - pConnect->UnregisterStatusEventCallback(uiCookie); + pConnect->UnregisterStateEventCallback(uiCookie); if (!bSilent) std::cerr << "ERROR: " << CONNECT_SDV_SERVER_ERROR_MSG << std::endl; return CONNECT_SDV_SERVER_ERROR; @@ -262,7 +262,7 @@ extern "C" int main(int iArgc, const char* rgszArgv[]) sdv::core::ILinkCoreRepository* pLinkCoreRepo = sdv::core::GetObject("RepositoryService"); if (!pLinkCoreRepo) { - pConnect->UnregisterStatusEventCallback(uiCookie); + pConnect->UnregisterStateEventCallback(uiCookie); if (!bSilent) std::cerr << "ERROR: " << LINK_REPO_SERVICE_ERROR_MSG << std::endl; return LINK_REPO_SERVICE_ERROR; @@ -281,7 +281,7 @@ extern "C" int main(int iArgc, const char* rgszArgv[]) sdv::core::IRepositoryUtilityCreate* pCreateUtility = sdv::core::GetObject("RepositoryService"); if (!pRepositoryInfo || !pRepositoryControl || !pObjectAccess || !pCreateUtility) { - pConnect->UnregisterStatusEventCallback(uiCookie); + pConnect->UnregisterStateEventCallback(uiCookie); if (!bSilent) std::cerr << "ERROR: " << REPOSITORY_SERVICE_ACCESS_ERROR_MSG << std::endl; return REPOSITORY_SERVICE_ACCESS_ERROR; @@ -356,7 +356,7 @@ extern "C" int main(int iArgc, const char* rgszArgv[]) } // Shutdwown - pConnect->UnregisterStatusEventCallback(uiCookie); + pConnect->UnregisterStateEventCallback(uiCookie); parser.Clear(); pLinkCoreRepo->UnlinkCoreRepository(); appcontrol.Shutdown(); diff --git a/sdv_executables/sdv_vss_util/vss_bs_codingrx.cpp b/sdv_executables/sdv_vss_util/vss_bs_codingrx.cpp index 384649b..6d94505 100644 --- a/sdv_executables/sdv_vss_util/vss_bs_codingrx.cpp +++ b/sdv_executables/sdv_vss_util/vss_bs_codingrx.cpp @@ -314,10 +314,10 @@ std::string CVSSBSCodingRX::Code_BS_RXConstructor(const SSignalVDDefinition& sig mapKeywords["signal_name"] = function.signalName; return ReplaceKeywords(R"code( - auto %signal_name%Device = sdv::core::GetObject("%vd_vss_original%_Device").GetInterface(); + auto %signal_name%Device = sdv::core::GetObject("%vd_vss_original%_Device").GetInterface(); if (!%signal_name%Device) { - SDV_LOG_ERROR("Could not get interface 'IVSS_%vd_function_name%': [CBasicService%vd_class_name%]"); + SDV_LOG_ERROR("Could not get interface 'IVSS_Read%vd_function_name%': [CBasicService%vd_class_name%]"); throw std::runtime_error("%vd_vss_original% mode device not found"); } %signal_name%Device->Register%vd_function_name%Event(dynamic_cast (this)); @@ -337,7 +337,7 @@ std::string CVSSBSCodingRX::Code_BS_RXDestructor(const SSignalVDDefinition& sign mapKeywords["vd_function_name"] = functionVD.functionName; return ReplaceKeywords(R"code( - auto %vd_signal_name%Device = sdv::core::GetObject("%vd_vss_original%_Device").GetInterface(); + auto %vd_signal_name%Device = sdv::core::GetObject("%vd_vss_original%_Device").GetInterface(); if (%vd_signal_name%Device) { %vd_signal_name%Device->Unregister%vd_function_name%Event(dynamic_cast (this)); diff --git a/sdv_executables/sdv_vss_util/vss_rx_templates.h b/sdv_executables/sdv_vss_util/vss_rx_templates.h index 6832f61..6a8508d 100644 --- a/sdv_executables/sdv_vss_util/vss_rx_templates.h +++ b/sdv_executables/sdv_vss_util/vss_rx_templates.h @@ -30,7 +30,7 @@ const char szRXVehicleDeviceHeaderTemplate[] = R"code(/** #include "../signal_identifier.h" /** - * @brief Vehicle device %vss_original% + * @brief Platform abstraction %vss_original% */ class CVehicleDevice%class_name% : public sdv::CSdvObject @@ -128,7 +128,7 @@ public: BEGIN_SDV_INTERFACE_MAP() %rx_bs_interface_entry_list% END_SDV_INTERFACE_MAP() - DECLARE_OBJECT_CLASS_TYPE(sdv::EObjectType::device) + DECLARE_OBJECT_CLASS_TYPE(sdv::EObjectType::sensor) DECLARE_OBJECT_CLASS_NAME("%vss_original%_Service") /** diff --git a/sdv_executables/sdv_vss_util/vss_tx_templates.h b/sdv_executables/sdv_vss_util/vss_tx_templates.h index de30c99..7e77801 100644 --- a/sdv_executables/sdv_vss_util/vss_tx_templates.h +++ b/sdv_executables/sdv_vss_util/vss_tx_templates.h @@ -30,7 +30,7 @@ const char szTXVehicleDeviceHeaderTemplate[] = R"code(/** /** - * @brief Vehicle device %vss_shorten_no_dot% + * @brief Platform abstraction %vss_shorten_no_dot% */ class CVehicleDevice%class_name% : public sdv::CSdvObject @@ -124,7 +124,7 @@ public: BEGIN_SDV_INTERFACE_MAP() %tx_bs_interface_entry_list% END_SDV_INTERFACE_MAP() - DECLARE_OBJECT_CLASS_TYPE(sdv::EObjectType::device) + DECLARE_OBJECT_CLASS_TYPE(sdv::EObjectType::actuator) DECLARE_OBJECT_CLASS_NAME("%vss_original%_Service") /** diff --git a/sdv_executables/sdv_vss_util/vss_vd_codingrx.cpp b/sdv_executables/sdv_vss_util/vss_vd_codingrx.cpp index 853c830..c1bf916 100644 --- a/sdv_executables/sdv_vss_util/vss_vd_codingrx.cpp +++ b/sdv_executables/sdv_vss_util/vss_vd_codingrx.cpp @@ -193,9 +193,9 @@ std::string CVSSVDCodingRX::Code_RXIDLDeviceInterface(const std::string& spaces, %multiple_spaces%}; %multiple_spaces%/** -%multiple_spaces%* @brief IVSS_%function_name% abstract %prefix% interface +%multiple_spaces%* @brief IVSS_Read%function_name% abstract %prefix% interface %multiple_spaces%*/ -%multiple_spaces%interface IVSS_%function_name% +%multiple_spaces%interface IVSS_Read%function_name% %multiple_spaces%{ %multiple_spaces% /** %multiple_spaces% * @brief Register Write%function_name% event on signal change @@ -276,7 +276,7 @@ std::string CVSSVDCodingRX::Code_VD_RXInterface(const std::string & functionName mapKeywords["function_name"] = functionName; mapKeywords["vss_shorten_with_colons"] = vssShortenWithColons; - return ReplaceKeywords(R"code( , public vss::%vss_shorten_with_colons%Device::IVSS_%function_name% + return ReplaceKeywords(R"code( , public vss::%vss_shorten_with_colons%Device::IVSS_Read%function_name% )code", mapKeywords); } @@ -286,7 +286,7 @@ std::string CVSSVDCodingRX::Code_VD_RXInterfaceEntry(const std::string& function mapKeywords["function_name"] = functionName; mapKeywords["vssWithColons"] = vssWithColons; - return ReplaceKeywords(R"code( SDV_INTERFACE_ENTRY(vss::%vssWithColons%Device::IVSS_%function_name%) + return ReplaceKeywords(R"code( SDV_INTERFACE_ENTRY(vss::%vssWithColons%Device::IVSS_Read%function_name%) )code", mapKeywords); } @@ -305,7 +305,7 @@ std::string CVSSVDCodingRX::Code_VD_RXReAndUnregisterEvent( const std::string& v void Register%function_name%Event(vss::%vssWithColons%Device::IVSS_Write%function_name%_Event* event) override; /** - * @brief Unregister IVSS_%function_name%_Event + * @brief Unregister IVSS_Read%function_name%_Event * @param[in] event function */ void Unregister%function_name%Event(vss::%vssWithColons%Device::IVSS_Write%function_name%_Event* event) override; @@ -339,7 +339,7 @@ std::string CVSSVDCodingRX::Code_VD_RXPrivateHeaderPart(const SFunctionVDDefinit */ void ExecuteAllCallBacksFor%start_with_uppercase%(sdv::any_t value); - sdv::core::CSignal m_%signal_name%Signal; ///< Signal of the vehicle device + sdv::core::CSignal m_%signal_name%Signal; ///< Signal of the platform abstraction mutable std::mutex m_%signal_name%MutexCallbacks; ///< Mutex protecting m_%signal_name%Callbacks std::set m_%signal_name%Callbacks; ///< collection of events to be called )code", mapKeywords); diff --git a/sdv_services/CMakeLists.txt b/sdv_services/CMakeLists.txt index db29f90..ba5db0a 100644 --- a/sdv_services/CMakeLists.txt +++ b/sdv_services/CMakeLists.txt @@ -212,8 +212,10 @@ add_subdirectory(task_timer) add_subdirectory(ipc_com) add_subdirectory(ipc_connect) add_subdirectory(ipc_shared_mem) -add_subdirectory(uds_win_sockets) add_subdirectory(uds_unix_sockets) +add_subdirectory(uds_unix_tunnel) +add_subdirectory(uds_win_sockets) +add_subdirectory(uds_win_tunnel) add_subdirectory(process_control) add_subdirectory(hardware_ident) add_subdirectory(manifest_util) diff --git a/sdv_services/core/iso_monitor.h b/sdv_services/core/iso_monitor.h index 388318e..1a24393 100644 --- a/sdv_services/core/iso_monitor.h +++ b/sdv_services/core/iso_monitor.h @@ -82,7 +82,7 @@ public: private: sdv::TInterfaceAccessPtr m_ptrObject; ///< Smart pointer to the object. sdv::IObjectControl* m_pObjectControl = nullptr; ///< Pointer to the object control of the application - sdv::EObjectState m_eObjectState = sdv::EObjectState::initialization_pending; ///< Object status (in case there is no object control). + sdv::EObjectState m_eObjectState = sdv::EObjectState::initialization_pending; ///< Object state (in case there is no object control). }; #endif // !defined ISOLATION_OBJECT_MONITOR_H \ No newline at end of file diff --git a/sdv_services/ipc_com/com_channel.cpp b/sdv_services/ipc_com/com_channel.cpp index 31656a6..4e7d23d 100644 --- a/sdv_services/ipc_com/com_channel.cpp +++ b/sdv_services/ipc_com/com_channel.cpp @@ -55,7 +55,7 @@ CChannelConnector::~CChannelConnector() sdv::ipc::IConnect* pConnection = m_ptrChannelEndpoint.GetInterface(); if (pConnection) { - if (m_uiConnectionStatusCookie) pConnection->UnregisterStatusEventCallback(m_uiConnectionStatusCookie); + if (m_uiConnectStateCookie) pConnection->UnregisterStateEventCallback(m_uiConnectStateCookie); pConnection->Disconnect(); } @@ -78,8 +78,8 @@ bool CChannelConnector::ServerConnect(sdv::IInterfaceAccess* pObject, bool bAllo // Establish connection... sdv::ipc::IConnect* pConnection = m_ptrChannelEndpoint.GetInterface(); if (!pConnection) return false; - m_uiConnectionStatusCookie = pConnection->RegisterStatusEventCallback(this); - return m_uiConnectionStatusCookie != 0 && pConnection->AsyncConnect(this); + m_uiConnectStateCookie = pConnection->RegisterStateEventCallback(this); + return m_uiConnectStateCookie != 0 && pConnection->AsyncConnect(this); } sdv::IInterfaceAccess* CChannelConnector::ClientConnect(uint32_t uiTimeoutMs) @@ -99,8 +99,8 @@ sdv::IInterfaceAccess* CChannelConnector::ClientConnect(uint32_t uiTimeoutMs) // Establish connection... sdv::ipc::IConnect* pConnection = m_ptrChannelEndpoint.GetInterface(); - if (pConnection) m_uiConnectionStatusCookie = pConnection->RegisterStatusEventCallback(this); - if (!pConnection || m_uiConnectionStatusCookie == 0 || !pConnection->AsyncConnect(this) || + if (pConnection) m_uiConnectStateCookie = pConnection->RegisterStateEventCallback(this); + if (!pConnection || m_uiConnectStateCookie == 0 || !pConnection->AsyncConnect(this) || !pConnection->WaitForConnection(uiTimeoutMs)) { SDV_LOG_ERROR("Could not establish a connection!"); @@ -113,17 +113,17 @@ sdv::IInterfaceAccess* CChannelConnector::ClientConnect(uint32_t uiTimeoutMs) bool CChannelConnector::IsConnected() const { - return m_eConnectStatus == sdv::ipc::EConnectStatus::connected; + return m_eConnectState == sdv::ipc::EConnectState::connected; } -void CChannelConnector::SetStatus(/*in*/ sdv::ipc::EConnectStatus eConnectStatus) +void CChannelConnector::SetConnectState(/*in*/ sdv::ipc::EConnectState eConnectState) { - auto eConnectStatusTemp = m_eConnectStatus; - m_eConnectStatus = eConnectStatus; - switch (m_eConnectStatus) + auto eConnectStateTemp = m_eConnectState; + m_eConnectState = eConnectState; + switch (m_eConnectState) { - case sdv::ipc::EConnectStatus::disconnected: - case sdv::ipc::EConnectStatus::disconnected_forced: + case sdv::ipc::EConnectState::disconnected: + case sdv::ipc::EConnectState::disconnected_forced: // Invalidate the proxy objects. for (auto& rvtProxyObject : m_mapProxyObjects) rvtProxyObject.second.reset(); @@ -131,7 +131,7 @@ void CChannelConnector::SetStatus(/*in*/ sdv::ipc::EConnectStatus eConnectStatus if (m_eEndpointType == EEndpointType::server) { // Report information (but only once) - if (sdv::app::ConsoleIsVerbose() && eConnectStatusTemp != sdv::ipc::EConnectStatus::disconnected) + if (sdv::app::ConsoleIsVerbose() && eConnectStateTemp != sdv::ipc::EConnectState::disconnected) std::cout << "Client disconnected (ID#" << m_tConnectionID.uiIdent << ")" << std::endl; // Remove the connection if reconnection is not enabled (normal case). @@ -139,7 +139,7 @@ void CChannelConnector::SetStatus(/*in*/ sdv::ipc::EConnectStatus eConnectStatus m_rcontrol.RemoveConnection(m_tConnectionID); } break; - case sdv::ipc::EConnectStatus::connected: + case sdv::ipc::EConnectState::connected: if (m_eEndpointType == EEndpointType::server) { // Report information diff --git a/sdv_services/ipc_com/com_channel.h b/sdv_services/ipc_com/com_channel.h index 175b157..b2ce3da 100644 --- a/sdv_services/ipc_com/com_channel.h +++ b/sdv_services/ipc_com/com_channel.h @@ -71,10 +71,10 @@ public: bool IsConnected() const; /** - * @brief Set the current status. Overload of sdv::ipc::IConnectEventCallback::SetStatus. - * @param[in] eConnectStatus The connection status. + * @brief Set the current connect statue. Overload of sdv::ipc::IConnectEventCallback::SetConnectState. + * @param[in] eConnectState The connection state. */ - virtual void SetStatus(/*in*/ sdv::ipc::EConnectStatus eConnectStatus) override; + virtual void SetConnectState(/*in*/ sdv::ipc::EConnectState eConnectState) override; /** * @brief Callback to be called by the IPC connection when receiving a data packet. Overload of @@ -139,14 +139,14 @@ private: } eState = EState::initialized; ///< Data processing state. sdv::sequence> seqResult; ///< The result data. std::mutex mtxWaitForResult; ///< Mutex to protect result processing. - std::condition_variable cvWaitForResult; ///< Condition variable to trigger result processing. + std::condition_variable cvWaitForResult; ///< Condition var to trigger result processing. }; CCommunicationControl& m_rcontrol; ///< Reference to the communication control class. sdv::TObjectPtr m_ptrChannelEndpoint; ///< Managed pointer to the channel endpoint. - uint64_t m_uiConnectionStatusCookie = 0; ///< Connection status cookie (received after registration). + uint64_t m_uiConnectStateCookie = 0; ///< Connection state cookie (received after registration). std::shared_ptr m_ptrInitialMarshallObject; ///< Initial marshall object used after a connect event. - sdv::ipc::EConnectStatus m_eConnectStatus = sdv::ipc::EConnectStatus::uninitialized; ///< Current connection status. + sdv::ipc::EConnectState m_eConnectState = sdv::ipc::EConnectState::uninitialized; ///< Current connection state. bool m_bAllowReconnect = false; ///< When set, allow reconnection of the server. EEndpointType m_eEndpointType = EEndpointType::client; ///< Endpoint type of this connector. std::recursive_mutex m_mtxMarshallObjects; ///< Synchronize access to the marshall object vector. diff --git a/sdv_services/ipc_shared_mem/connection.cpp b/sdv_services/ipc_shared_mem/connection.cpp index 7b7fd5e..9b7afc9 100644 --- a/sdv_services/ipc_shared_mem/connection.cpp +++ b/sdv_services/ipc_shared_mem/connection.cpp @@ -25,21 +25,21 @@ inline sdv::process::TProcessID GetProcessID() return pProcessInfo ? pProcessInfo->GetProcessID() : 0; } -inline std::string ConnectState(sdv::ipc::EConnectStatus eState) +inline std::string ConnectState2String(sdv::ipc::EConnectState eState) { switch (eState) { - case sdv::ipc::EConnectStatus::uninitialized: return "uninitialized"; - case sdv::ipc::EConnectStatus::initializing: return "initializing"; - case sdv::ipc::EConnectStatus::initialized: return "initialized"; - case sdv::ipc::EConnectStatus::connecting: return "connecting"; - case sdv::ipc::EConnectStatus::negotiating: return "negotiating"; - case sdv::ipc::EConnectStatus::connection_error: return "connection_error"; - case sdv::ipc::EConnectStatus::connected: return "connected"; - case sdv::ipc::EConnectStatus::communication_error: return "communication_error"; - case sdv::ipc::EConnectStatus::disconnected: return "disconnected"; - case sdv::ipc::EConnectStatus::disconnected_forced: return "disconnected_forced"; - case sdv::ipc::EConnectStatus::terminating: return "terminating"; + case sdv::ipc::EConnectState::uninitialized: return "uninitialized"; + case sdv::ipc::EConnectState::initializing: return "initializing"; + case sdv::ipc::EConnectState::initialized: return "initialized"; + case sdv::ipc::EConnectState::connecting: return "connecting"; + case sdv::ipc::EConnectState::negotiating: return "negotiating"; + case sdv::ipc::EConnectState::connection_error: return "connection_error"; + case sdv::ipc::EConnectState::connected: return "connected"; + case sdv::ipc::EConnectState::communication_error: return "communication_error"; + case sdv::ipc::EConnectState::disconnected: return "disconnected"; + case sdv::ipc::EConnectState::disconnected_forced: return "disconnected_forced"; + case sdv::ipc::EConnectState::terminating: return "terminating"; default: return "unknown"; } } @@ -86,11 +86,11 @@ CConnection::~CConnection() #endif // If still connected, disconnect. - if (m_eStatus == sdv::ipc::EConnectStatus::connected) + if (m_eConnectState == sdv::ipc::EConnectState::connected) Disconnect(); // Set to terminating to allow the threads to shut down. - m_eStatus = sdv::ipc::EConnectStatus::terminating; + m_eConnectState = sdv::ipc::EConnectState::terminating; // Stop the receive thread to prevent accepting any more messages from the server. if (m_threadReceive.joinable()) @@ -146,19 +146,19 @@ uint32_t CConnection::Send(const void* pData, uint32_t uiDataLength) #if ENABLE_REPORTING >= 2 switch (((SMsgHdr*)pData)->eType) { - case EMsgType::sync_request: TRACE(m_bServer ? "SERVER" : "CLIENT", " SEND SYNC_REQUEST (", ConnectState(m_eStatus), ")"); break; - case EMsgType::sync_answer: TRACE(m_bServer ? "SERVER" : "CLIENT", " SEND SYNC_ANSWER (", ConnectState(m_eStatus), ")"); break; - case EMsgType::connect_request: TRACE(m_bServer ? "SERVER" : "CLIENT", " SEND CONNECT_REQUEST (", ConnectState(m_eStatus), ")"); break; - case EMsgType::connect_answer: TRACE(m_bServer ? "SERVER" : "CLIENT", " SEND CONNECT_ANSWER (", ConnectState(m_eStatus), ")"); break; - case EMsgType::connect_term: TRACE(m_bServer ? "SERVER" : "CLIENT", " SEND CONNECT_TERM (", ConnectState(m_eStatus), ")"); break; + case EMsgType::sync_request: TRACE(m_bServer ? "SERVER" : "CLIENT", " SEND SYNC_REQUEST (", ConnectState2String(m_eConnectState), ")"); break; + case EMsgType::sync_answer: TRACE(m_bServer ? "SERVER" : "CLIENT", " SEND SYNC_ANSWER (", ConnectState2String(m_eConnectState), ")"); break; + case EMsgType::connect_request: TRACE(m_bServer ? "SERVER" : "CLIENT", " SEND CONNECT_REQUEST (", ConnectState2String(m_eConnectState), ")"); break; + case EMsgType::connect_answer: TRACE(m_bServer ? "SERVER" : "CLIENT", " SEND CONNECT_ANSWER (", ConnectState2String(m_eConnectState), ")"); break; + case EMsgType::connect_term: TRACE(m_bServer ? "SERVER" : "CLIENT", " SEND CONNECT_TERM (", ConnectState2String(m_eConnectState), ")"); break; #if ENABLE_REPORTING >= 3 - case EMsgType::data: TRACE(m_bServer ? "SERVER" : "CLIENT", " SEND DATA ", uiDataLength - sizeof(SMsgHdr), " bytes (", ConnectState(m_eStatus), ")"); break; - case EMsgType::data_fragment: TRACE(m_bServer ? "SERVER" : "CLIENT", " RECEIVE DATA FRAGMENT ", uiDataLength - sizeof(SFragmentedMsgHdr), " bytes (", ConnectState(m_eStatus), ")"); break; + case EMsgType::data: TRACE(m_bServer ? "SERVER" : "CLIENT", " SEND DATA ", uiDataLength - sizeof(SMsgHdr), " bytes (", ConnectState2String(m_eConnectState), ")"); break; + case EMsgType::data_fragment: TRACE(m_bServer ? "SERVER" : "CLIENT", " RECEIVE DATA FRAGMENT ", uiDataLength - sizeof(SFragmentedMsgHdr), " bytes (", ConnectState2String(m_eConnectState), ")"); break; #else case EMsgType::data: break; case EMsgType::data_fragment: break; #endif - default: TRACE(m_bServer ? "SERVER" : "CLIENT", " SEND UNKNOWN (", ConnectState(m_eStatus), ")"); break; + default: TRACE(m_bServer ? "SERVER" : "CLIENT", " SEND UNKNOWN (", ConnectState2String(m_eConnectState), ")"); break; } #endif @@ -186,10 +186,10 @@ bool CConnection::SendData(/*inout*/ sdv::sequence>& seqDa } TRACE("Send a sequence of data with ", seqData.size(), " pointers with the length {", sstreamReport.str(), "} bytes"); #endif - // Only allow sending messages when the status is connected - if (m_eStatus != sdv::ipc::EConnectStatus::connected) + // Only allow sending messages when the state is connected + if (m_eConnectState != sdv::ipc::EConnectState::connected) { - SetStatus(sdv::ipc::EConnectStatus::communication_error); + SetConnectState(sdv::ipc::EConnectState::communication_error); return false; } @@ -260,7 +260,7 @@ bool CConnection::SendData(/*inout*/ sdv::sequence>& seqDa auto optPacket = m_sender.Reserve(uiAllocSize); if (!optPacket) { - if (m_eStatus == sdv::ipc::EConnectStatus::connected) + if (m_eConnectState == sdv::ipc::EConnectState::connected) SDV_LOG_ERROR("Could not reserve a buffer to send a message of ", uiDataSize, " bytes."); return false; } @@ -277,7 +277,7 @@ bool CConnection::SendData(/*inout*/ sdv::sequence>& seqDa uiMsgOffset = sizeof(SFragmentedMsgHdr); #if ENABLE_REPORTING >= 3 - TRACE(m_bServer ? "SERVER" : "CLIENT", " SEND DATA FRAGMENT ", uiOffset, "-", uiOffset + uiDataSize - 1, " of ", uiRequiredSize, " bytes (", ConnectState(m_eStatus), ")"); + TRACE(m_bServer ? "SERVER" : "CLIENT", " SEND DATA FRAGMENT ", uiOffset, "-", uiOffset + uiDataSize - 1, " of ", uiRequiredSize, " bytes (", ConnectState2String(m_eConnectState), ")"); #endif } else @@ -288,7 +288,7 @@ bool CConnection::SendData(/*inout*/ sdv::sequence>& seqDa uiMsgOffset = sizeof(SMsgHdr); #if ENABLE_REPORTING >= 3 - TRACE(m_bServer ? "SERVER" : "CLIENT", " SEND DATA ", uiRequiredSize, " bytes (", ConnectState(m_eStatus), ")"); + TRACE(m_bServer ? "SERVER" : "CLIENT", " SEND DATA ", uiRequiredSize, " bytes (", ConnectState2String(m_eConnectState), ")"); #endif } @@ -352,29 +352,29 @@ bool CConnection::AsyncConnect(sdv::IInterfaceAccess* pReceiver) std::unique_lock lock(m_mtxConnect); // Allowed to connect? - if (m_eStatus != sdv::ipc::EConnectStatus::uninitialized) + if (m_eConnectState != sdv::ipc::EConnectState::uninitialized) { for (auto& rprEventCallback : m_lstEventCallbacks) if (rprEventCallback.pCallback && rprEventCallback.uiCookie) - rprEventCallback.pCallback->SetStatus(sdv::ipc::EConnectStatus::connection_error); + rprEventCallback.pCallback->SetConnectState(sdv::ipc::EConnectState::connection_error); return false; } - SetStatus(sdv::ipc::EConnectStatus::initializing); + SetConnectState(sdv::ipc::EConnectState::initializing); // Initialized? if (!m_sender.IsValid() && !m_receiver.IsValid()) { - SetStatus(sdv::ipc::EConnectStatus::connection_error); + SetConnectState(sdv::ipc::EConnectState::connection_error); SDV_LOG_ERROR("Could not establish connection: sender(", (m_sender.IsValid() ? "valid" : "invalid"), ") receiver(", (m_receiver.IsValid() ? "valid" : "invalid"), ")"); - SetStatus(sdv::ipc::EConnectStatus::uninitialized); + SetConnectState(sdv::ipc::EConnectState::uninitialized); return false; } // Assign the receiver m_pReceiver = sdv::TInterfaceAccessPtr(pReceiver).GetInterface(); - SetStatus(sdv::ipc::EConnectStatus::initialized); + SetConnectState(sdv::ipc::EConnectState::initialized); // Start the receiving thread (wait until started). m_threadReceive = std::thread(&CConnection::ReceiveMessages, this); @@ -397,13 +397,13 @@ bool CConnection::AsyncConnect(sdv::IInterfaceAccess* pReceiver) bool CConnection::WaitForConnection(/*in*/ uint32_t uiWaitMs) { #if ENABLE_REPORTING >= 1 - if (m_eStatus == sdv::ipc::EConnectStatus::connected) + if (m_eConnectState == sdv::ipc::EConnectState::connected) TRACE("Not waiting for a connection - already connected"); else TRACE("Waiting for a connection of ", uiWaitMs, "ms"); #endif - if (m_eStatus == sdv::ipc::EConnectStatus::connected) return true; + if (m_eConnectState == sdv::ipc::EConnectState::connected) return true; std::unique_lock lock(m_mtxConnect); @@ -413,13 +413,13 @@ bool CConnection::WaitForConnection(/*in*/ uint32_t uiWaitMs) m_cvConnect.wait_for(lock, std::chrono::milliseconds(uiWaitMs)); #if ENABLE_REPORTING >= 1 - if (m_eStatus == sdv::ipc::EConnectStatus::connected) + if (m_eConnectState == sdv::ipc::EConnectState::connected) TRACE("Waiting finished - connection established"); else TRACE("Waiting finished - timeout occurred"); #endif - return m_eStatus == sdv::ipc::EConnectStatus::connected; + return m_eConnectState == sdv::ipc::EConnectState::connected; } void CConnection::CancelWait() @@ -440,19 +440,19 @@ void CConnection::Disconnect() // Cancel any waits, just in case CancelWait(); - // Set the disconnect status - sdv::ipc::EConnectStatus eStatus = m_eStatus; - SetStatus(sdv::ipc::EConnectStatus::disconnected); + // Set the disconnect state + sdv::ipc::EConnectState eConnectState = m_eConnectState; + SetConnectState(sdv::ipc::EConnectState::disconnected); // Release the interface m_pReceiver = nullptr; // If connected, send termination message. - switch (eStatus) + switch (eConnectState) { - case sdv::ipc::EConnectStatus::connecting: - case sdv::ipc::EConnectStatus::negotiating: - case sdv::ipc::EConnectStatus::connected: + case sdv::ipc::EConnectState::connecting: + case sdv::ipc::EConnectState::negotiating: + case sdv::ipc::EConnectState::connected: Send(SMsgHdr{ SDVFrameworkInterfaceVersion, EMsgType::connect_term }); break; default: @@ -464,7 +464,7 @@ void CConnection::Disconnect() #endif } -uint64_t CConnection::RegisterStatusEventCallback(/*in*/ sdv::IInterfaceAccess* pEventCallback) +uint64_t CConnection::RegisterStateEventCallback(/*in*/ sdv::IInterfaceAccess* pEventCallback) { if (!pEventCallback) return 0; sdv::ipc::IConnectEventCallback* pCallback = pEventCallback->GetInterface(); @@ -477,7 +477,7 @@ uint64_t CConnection::RegisterStatusEventCallback(/*in*/ sdv::IInterfaceAccess* return uiCookie; } -void CConnection::UnregisterStatusEventCallback(/*in*/ uint64_t uiCookie) +void CConnection::UnregisterStateEventCallback(/*in*/ uint64_t uiCookie) { std::shared_lock lock(m_mtxEventCallbacks); auto itEventCallback = std::find_if(m_lstEventCallbacks.begin(), m_lstEventCallbacks.end(), @@ -486,9 +486,9 @@ void CConnection::UnregisterStatusEventCallback(/*in*/ uint64_t uiCookie) itEventCallback->pCallback = nullptr; } -sdv::ipc::EConnectStatus CConnection::GetStatus() const +sdv::ipc::EConnectState CConnection::GetConnectState() const { - return m_eStatus; + return m_eConnectState; } void CConnection::DestroyObject() @@ -500,8 +500,8 @@ void CConnection::DestroyObject() // Disconnect Disconnect(); - // Set termination status. - SetStatus(sdv::ipc::EConnectStatus::terminating); + // Set termination state. + SetConnectState(sdv::ipc::EConnectState::terminating); // Clear all events callbacks (if not done so already) std::shared_lock lock(m_mtxEventCallbacks); @@ -523,32 +523,32 @@ void CConnection::DestroyObject() #endif } -void CConnection::SetStatus(sdv::ipc::EConnectStatus eStatus) +void CConnection::SetConnectState(sdv::ipc::EConnectState eConnectState) { #if ENABLE_REPORTING >= 1 - TRACE(m_bServer ? "SERVER" : "CLIENT", " Changing connect status from '", ConnectState(m_eStatus), "' to '", ConnectState(eStatus), "'"); + TRACE(m_bServer ? "SERVER" : "CLIENT", " Changing connect state from '", ConnectState2String(m_eConnectState), "' to '", ConnectState2String(eConnectState), "'"); #endif - // Do not change the status when terminated. - if (m_eStatus == sdv::ipc::EConnectStatus::terminating) + // Do not change the state when terminated. + if (m_eConnectState == sdv::ipc::EConnectState::terminating) return; - // Only set the member variable if the status is not communication_error - if (eStatus != sdv::ipc::EConnectStatus::communication_error) - m_eStatus = eStatus; + // Only set the member variable if the state is not communication_error + if (eConnectState != sdv::ipc::EConnectState::communication_error) + m_eConnectState = eConnectState; std::shared_lock lock(m_mtxEventCallbacks); for (auto& rprEventCallback : m_lstEventCallbacks) { if (rprEventCallback.pCallback && rprEventCallback.uiCookie) - rprEventCallback.pCallback->SetStatus(eStatus); + rprEventCallback.pCallback->SetConnectState(eConnectState); } - // If disconnected by force update the disconnect status. - if (m_eStatus == sdv::ipc::EConnectStatus::disconnected_forced) - m_eStatus = sdv::ipc::EConnectStatus::disconnected; + // If disconnected by force update the disconnect state. + if (m_eConnectState == sdv::ipc::EConnectState::disconnected_forced) + m_eConnectState = sdv::ipc::EConnectState::disconnected; #if ENABLE_REPORTING >= 1 - TRACE("Status updated..."); + TRACE("State updated..."); #endif } @@ -574,7 +574,7 @@ void CConnection::ReceiveMessages() if (!m_sender.IsValid() || !m_receiver.IsValid()) { - SetStatus(sdv::ipc::EConnectStatus::communication_error); + SetConnectState(sdv::ipc::EConnectState::communication_error); SDV_LOG_ERROR("No valid shared memory for receiving."); lock.unlock(); m_cvStartConnect.notify_all(); @@ -590,7 +590,7 @@ void CConnection::ReceiveMessages() std::chrono::high_resolution_clock::time_point tpLastReceiveLoop = std::chrono::high_resolution_clock::now(); #endif SDataContext sDataCtxt; - while (m_eStatus != sdv::ipc::EConnectStatus::terminating) + while (m_eConnectState != sdv::ipc::EConnectState::terminating) { #ifdef TIME_TRACKING std::chrono::high_resolution_clock::time_point tpTrackNow = std::chrono::high_resolution_clock::now(); @@ -604,7 +604,7 @@ void CConnection::ReceiveMessages() { // Start communication, but only if connection is client based. Server based should not start the communication. If // there is no client, the server would otherwise fill its send-buffer. Repeat sending every 500ms. - if (!m_bServer && (/*m_eStatus == sdv::ipc::EConnectStatus::disconnected ||*/ m_eStatus == sdv::ipc::EConnectStatus::initialized)) + if (!m_bServer && (/*m_eConnectState == sdv::ipc::EConnectState::disconnected ||*/ m_eConnectState == sdv::ipc::EConnectState::initialized)) { // Send request auto tpNow = std::chrono::high_resolution_clock::now(); @@ -629,7 +629,7 @@ void CConnection::ReceiveMessages() if (!message.IsValid()) { auto ssError = m_receiver.GetError(); - SetStatus(sdv::ipc::EConnectStatus::communication_error); + SetConnectState(sdv::ipc::EConnectState::communication_error); SDV_LOG_ERROR("The message is invalid (invalid size or invalid type)."); continue; } @@ -638,7 +638,7 @@ void CConnection::ReceiveMessages() message.PrintHeader(*this); // Extra check to prevent race condition. - if (m_eStatus == sdv::ipc::EConnectStatus::terminating) break; + if (m_eConnectState == sdv::ipc::EConnectState::terminating) break; #if ENABLE_REPORTING >= 1 switch (message.GetMsgHdr().eType) @@ -846,7 +846,7 @@ bool CConnection::ReadDataChunk(CMessage& rMessage, uint32_t uiOffset, SDataCont std::unique_lock lockReceive(m_mtxReceive); while (m_queueReceive.size() >= 16) { - if (m_eStatus == sdv::ipc::EConnectStatus::terminating) return false; + if (m_eConnectState == sdv::ipc::EConnectState::terminating) return false; m_cvReceiveProcessed.wait_for(lockReceive, std::chrono::milliseconds(100)); } @@ -868,7 +868,7 @@ bool CConnection::ReadDataChunk(CMessage& rMessage, uint32_t uiOffset, SDataCont #if ENABLE_DECOUPLING > 0 void CConnection::DecoupleReceive() { - while (m_eStatus != sdv::ipc::EConnectStatus::terminating) + while (m_eConnectState != sdv::ipc::EConnectState::terminating) { // Wait for data std::unique_lock lock(m_mtxReceive); @@ -887,7 +887,7 @@ void CConnection::DecoupleReceive() size_t nSize = 0; for (const sdv::pointer& ptrData : seqData) nSize += ptrData.size(); - TRACE(m_bServer ? "SERVER" : "CLIENT", " DECOUPLED REVEICE DATA ", nSize, " bytes (", ConnectState(m_eStatus), ")"); + TRACE(m_bServer ? "SERVER" : "CLIENT", " DECOUPLED REVEICE DATA ", nSize, " bytes (", ConnectState2String(m_eConnectState), ")"); #endif // Process the data @@ -903,28 +903,28 @@ void CConnection::ReceiveSyncRequest(const CMessage& rMessage) { if (rMessage.GetSize() != sizeof(SMsgHdr)) { - SetStatus(sdv::ipc::EConnectStatus::connection_error); + SetConnectState(sdv::ipc::EConnectState::connection_error); SDV_LOG_ERROR("Sync request received but with incorrect structure size ", rMessage.GetSize(), " in the request, but ", sizeof(SMsgHdr), " needed!"); - SetStatus(sdv::ipc::EConnectStatus::disconnected); + SetConnectState(sdv::ipc::EConnectState::disconnected); return; } // Check for compatibility if (rMessage.GetMsgHdr().uiVersion != SDVFrameworkInterfaceVersion) { - SetStatus(sdv::ipc::EConnectStatus::connection_error); + SetConnectState(sdv::ipc::EConnectState::connection_error); SDV_LOG_ERROR("Sync request received for an incompatible communication; interface version ", rMessage.GetMsgHdr().uiVersion, " requested, but ", SDVFrameworkInterfaceVersion, " needed!"); - SetStatus(sdv::ipc::EConnectStatus::disconnected); + SetConnectState(sdv::ipc::EConnectState::disconnected); return; } else { // Start connecting - if (m_eStatus == sdv::ipc::EConnectStatus::disconnected || m_eStatus == sdv::ipc::EConnectStatus::initialized) + if (m_eConnectState == sdv::ipc::EConnectState::disconnected || m_eConnectState == sdv::ipc::EConnectState::initialized) { - SetStatus(sdv::ipc::EConnectStatus::connecting); + SetConnectState(sdv::ipc::EConnectState::connecting); // Send an answer Send(SMsgHdr{ SDVFrameworkInterfaceVersion, EMsgType::sync_answer }); @@ -935,18 +935,18 @@ void CConnection::ReceiveSyncRequest(const CMessage& rMessage) void CConnection::ReceiveConnectRequest(const CMessage& rMessage) { // Start negotiating - if (m_eStatus == sdv::ipc::EConnectStatus::connecting) + if (m_eConnectState == sdv::ipc::EConnectState::connecting) { // The connect message contains the process ID to monitor. m_rWatchDog.AddMonitor(rMessage.GetConnectHdr().tProcessID, this); // Replay to the request - SetStatus(sdv::ipc::EConnectStatus::negotiating); + SetConnectState(sdv::ipc::EConnectState::negotiating); Send(SConnectMsg{ {SDVFrameworkInterfaceVersion, EMsgType::connect_answer}, static_cast(GetProcessID()) }); // Connected - SetStatus(sdv::ipc::EConnectStatus::connected); + SetConnectState(sdv::ipc::EConnectState::connected); #if ENABLE_REPORTING >= 1 TRACE("Trigger connected"); #endif @@ -956,34 +956,34 @@ void CConnection::ReceiveConnectRequest(const CMessage& rMessage) void CConnection::ReceiveSyncAnswer(const CMessage& rMessage) { - if (m_eStatus != sdv::ipc::EConnectStatus::disconnected && m_eStatus != sdv::ipc::EConnectStatus::initialized) + if (m_eConnectState != sdv::ipc::EConnectState::disconnected && m_eConnectState != sdv::ipc::EConnectState::initialized) return; // Check for compatibility if (rMessage.GetMsgHdr().uiVersion != SDVFrameworkInterfaceVersion) { - SetStatus(sdv::ipc::EConnectStatus::communication_error); + SetConnectState(sdv::ipc::EConnectState::communication_error); SDV_LOG_ERROR("Sync answer received for an incompatible communication; interface version ", rMessage.GetMsgHdr().uiVersion, " requested, but ", SDVFrameworkInterfaceVersion, " needed!"); - SetStatus(sdv::ipc::EConnectStatus::disconnected); + SetConnectState(sdv::ipc::EConnectState::disconnected); return; } // Start negotiating - SetStatus(sdv::ipc::EConnectStatus::negotiating); + SetConnectState(sdv::ipc::EConnectState::negotiating); Send(SConnectMsg{ {SDVFrameworkInterfaceVersion, EMsgType::connect_request}, GetProcessID() }); } void CConnection::ReceiveConnectAnswer(const CMessage& rMessage) { // Connection established - if (m_eStatus == sdv::ipc::EConnectStatus::negotiating) + if (m_eConnectState == sdv::ipc::EConnectState::negotiating) { // The connect message contains the process ID to monitor. m_rWatchDog.AddMonitor(rMessage.GetConnectHdr().tProcessID, this); // Connected - SetStatus(sdv::ipc::EConnectStatus::connected); + SetConnectState(sdv::ipc::EConnectState::connected); #if ENABLE_REPORTING >= 1 TRACE("Trigger connected..."); #endif @@ -993,7 +993,7 @@ void CConnection::ReceiveConnectAnswer(const CMessage& rMessage) void CConnection::ReceiveConnectTerm(CMessage& /*rMessage*/) { - SetStatus(sdv::ipc::EConnectStatus::disconnected); + SetConnectState(sdv::ipc::EConnectState::disconnected); m_rWatchDog.RemoveMonitor(this); // Cancel any outstanding write... and reset the read position of the sender (otherwise any outstanding data will have @@ -1016,7 +1016,7 @@ void CConnection::ReceiveDataMessage(CMessage& rMessage, SDataContext& rsDataCtx uint32_t uiOffset = ReadDataTable(rMessage, rsDataCtxt); if (!uiOffset) { - SetStatus(sdv::ipc::EConnectStatus::communication_error); + SetConnectState(sdv::ipc::EConnectState::communication_error); return; } @@ -1033,7 +1033,7 @@ void CConnection::ReceiveDataMessage(CMessage& rMessage, SDataContext& rsDataCtx // Read data if (!ReadDataChunk(rMessage, uiOffset, rsDataCtxt)) { - SetStatus(sdv::ipc::EConnectStatus::communication_error); + SetConnectState(sdv::ipc::EConnectState::communication_error); return; } #if ENABLE_REPORTING >= 1 @@ -1057,7 +1057,7 @@ void CConnection::ReceiveDataFragementMessage(CMessage& rMessage, SDataContext& uiOffset = ReadDataTable(rMessage, rsDataCtxt); if (!uiOffset) { - SetStatus(sdv::ipc::EConnectStatus::communication_error); + SetConnectState(sdv::ipc::EConnectState::communication_error); return; } @@ -1075,7 +1075,7 @@ void CConnection::ReceiveDataFragementMessage(CMessage& rMessage, SDataContext& // Read data chunk if (!ReadDataChunk(rMessage, uiOffset, rsDataCtxt)) { - SetStatus(sdv::ipc::EConnectStatus::communication_error); + SetConnectState(sdv::ipc::EConnectState::communication_error); return; } } @@ -1147,19 +1147,19 @@ void CConnection::CMessage::PrintHeader([[maybe_unused]] const CConnection& rCon #if ENABLE_REPORTING >= 2 switch (GetMsgHdr().eType) { - case EMsgType::sync_request: rConnection.TRACE(rConnection.IsServer() ? "SERVER" : "CLIENT", " RECEIVE SYNC_REQUEST (", ConnectState(rConnection.GetStatus()), ")"); break; - case EMsgType::sync_answer: rConnection.TRACE(rConnection.IsServer() ? "SERVER" : "CLIENT", " RECEIVE SYNC_ANSWER (", ConnectState(rConnection.GetStatus()), ")"); break; - case EMsgType::connect_request: rConnection.TRACE(rConnection.IsServer() ? "SERVER" : "CLIENT", " RECEIVE CONNECT_REQUEST (", ConnectState(rConnection.GetStatus()), ")"); break; - case EMsgType::connect_answer: rConnection.TRACE(rConnection.IsServer() ? "SERVER" : "CLIENT", " RECEIVE CONNECT_ANSWER (", ConnectState(rConnection.GetStatus()), ")"); break; - case EMsgType::connect_term: rConnection.TRACE(rConnection.IsServer() ? "SERVER" : "CLIENT", " RECEIVE CONNECT_TERM (", ConnectState(rConnection.GetStatus()), ")"); break; + case EMsgType::sync_request: rConnection.TRACE(rConnection.IsServer() ? "SERVER" : "CLIENT", " RECEIVE SYNC_REQUEST (", ConnectState2String(rConnection.GetConnectState()), ")"); break; + case EMsgType::sync_answer: rConnection.TRACE(rConnection.IsServer() ? "SERVER" : "CLIENT", " RECEIVE SYNC_ANSWER (", ConnectState2String(rConnection.GetConnectState()), ")"); break; + case EMsgType::connect_request: rConnection.TRACE(rConnection.IsServer() ? "SERVER" : "CLIENT", " RECEIVE CONNECT_REQUEST (", ConnectState2String(rConnection.GetConnectState()), ")"); break; + case EMsgType::connect_answer: rConnection.TRACE(rConnection.IsServer() ? "SERVER" : "CLIENT", " RECEIVE CONNECT_ANSWER (", ConnectState2String(rConnection.GetConnectState()), ")"); break; + case EMsgType::connect_term: rConnection.TRACE(rConnection.IsServer() ? "SERVER" : "CLIENT", " RECEIVE CONNECT_TERM (", ConnectState2String(rConnection.GetConnectState()), ")"); break; #if ENABLE_REPORTING >= 3 - case EMsgType::data: rConnection.TRACE(rConnection.IsServer() ? "SERVER" : "CLIENT", " RECEIVE DATA ", GetSize() - sizeof(SMsgHdr), " bytes (", ConnectState(rConnection.GetStatus()), ")"); break; - case EMsgType::data_fragment: rConnection.TRACE(rConnection.IsServer() ? "SERVER" : "CLIENT", " RECEIVE DATA FRAGMENT ", GetSize() - sizeof(SFragmentedMsgHdr), " bytes (", ConnectState(rConnection.GetStatus()), ")"); break; + case EMsgType::data: rConnection.TRACE(rConnection.IsServer() ? "SERVER" : "CLIENT", " RECEIVE DATA ", GetSize() - sizeof(SMsgHdr), " bytes (", ConnectState2String(rConnection.GetConnectState()), ")"); break; + case EMsgType::data_fragment: rConnection.TRACE(rConnection.IsServer() ? "SERVER" : "CLIENT", " RECEIVE DATA FRAGMENT ", GetSize() - sizeof(SFragmentedMsgHdr), " bytes (", ConnectState2String(rConnection.GetConnectState()), ")"); break; #else case EMsgType::data: break; case EMsgType::data_fragment: break; #endif - default: rConnection.TRACE(rConnection.IsServer() ? "SERVER" : "CLIENT", " RECEIVE UNKNOWN version=", static_cast(GetMsgHdr().uiVersion), " type=", static_cast(GetMsgHdr().eType), "(", ConnectState(rConnection.GetStatus()), ")"); break; + default: rConnection.TRACE(rConnection.IsServer() ? "SERVER" : "CLIENT", " RECEIVE UNKNOWN version=", static_cast(GetMsgHdr().uiVersion), " type=", static_cast(GetMsgHdr().eType), "(", ConnectState2String(rConnection.GetConnectState()), ")"); break; } #endif } \ No newline at end of file diff --git a/sdv_services/ipc_shared_mem/connection.h b/sdv_services/ipc_shared_mem/connection.h index 3a627e4..22f32c4 100644 --- a/sdv_services/ipc_shared_mem/connection.h +++ b/sdv_services/ipc_shared_mem/connection.h @@ -106,7 +106,7 @@ public: * @brief Establish a connection and start sending/receiving messages. Overload of * sdv::ipc::IConnect::AsyncConnect. * @param[in] pReceiver The message has to be forwarded. - * @return Returns 'true' when a connection could be established. Use IConnectStatus or IConnectEventCallback to check the + * @return Returns 'true' when a connection could be established. Use IConnect or IConnectEventCallback to check the * connection state. */ virtual bool AsyncConnect(/*in*/ sdv::IInterfaceAccess* pReceiver) override; @@ -127,33 +127,33 @@ public: // Suppress cppcheck warning. The destructor calls Disconnect without dynamic binding. This is correct so. // cppcheck-suppress virtualCallInConstructor /** - * @brief Disconnect from a connection. This will set the connect status to disconnected. Overload of + * @brief Disconnect from a connection. This will set the connect state to disconnected. Overload of * sdv::ipc::IConnect::Disconnect. */ virtual void Disconnect() override; /** - * @brief Register event callback interface. Overload of sdv::ipc::IConnect::RegisterStatusEventCallback. - * @details Register a connection status event callback interface. The exposed interface must be of type + * @brief Register event callback interface. Overload of sdv::ipc::IConnect::RegisterStateEventCallback. + * @details Register a connection state event callback interface. The exposed interface must be of type * IConnectEventCallback. The registration will exist until a call to the unregister function with the returned cookie * or until the connection is terminated. * @param[in] pEventCallback Pointer to the object exposing the IConnectEventCallback interface. * @return The cookie assigned to the registration. */ - virtual uint64_t RegisterStatusEventCallback(/*in*/ sdv::IInterfaceAccess* pEventCallback) override; + virtual uint64_t RegisterStateEventCallback(/*in*/ sdv::IInterfaceAccess* pEventCallback) override; /** - * @brief Unregister the status event callback with the returned cookie from the registration. Overload of - * sdv::ipc::IConnect::UnregisterStatusEventCallback. + * @brief Unregister the state event callback with the returned cookie from the registration. Overload of + * sdv::ipc::IConnect::UnregisterStateEventCallback. * @param[in] uiCookie The cookie returned by a previous call to the registration function. */ - virtual void UnregisterStatusEventCallback(/*in*/ uint64_t uiCookie) override; + virtual void UnregisterStateEventCallback(/*in*/ uint64_t uiCookie) override; /** - * @brief Get status of the connection. Overload of sdv::ipc::IConnect::GetStatus. - * @return Returns the ipc::EConnectStatus struct + * @brief Get the current state of the IPC conection. Overload of sdv::ipc::IConnect::GetConnectState. + * @return Returns connection state. */ - virtual sdv::ipc::EConnectStatus GetStatus() const override; + virtual sdv::ipc::EConnectState GetConnectState() const override; /** * @brief Destroy the object. Overload of IObjectDestroy::DestroyObject. @@ -162,10 +162,10 @@ public: virtual void DestroyObject() override; /** - * @brief Set the connection status and if needed call the event callback. - * @param[in] eStatus The new status. + * @brief Set the connection state and if needed call the event callback. + * @param[in] eConnectState The new state. */ - void SetStatus(sdv::ipc::EConnectStatus eStatus); + void SetConnectState(sdv::ipc::EConnectState eConnectState); /** * @brief Returns whether this is a server connection or a client connection. @@ -258,14 +258,14 @@ private: CSharedMemBufferTx m_sender; ///< Shared buffer for sending. CSharedMemBufferRx m_receiver; ///< Shared buffer for receiving. std::thread m_threadReceive; ///< Thread which receives data from the socket. - std::atomic m_eStatus = sdv::ipc::EConnectStatus::uninitialized; ///< the status of the connection + std::atomic m_eConnectState = sdv::ipc::EConnectState::uninitialized; ///< the state of the connection sdv::ipc::IDataReceiveCallback* m_pReceiver = nullptr; ///< Receiver to pass the messages to if available std::shared_mutex m_mtxEventCallbacks; ///< Protect access to callback list. Only locking when ///< inserting. std::list m_lstEventCallbacks; ///< List containing event callbacks. New callbacks will ///< be inserted in front (called first). Removed ///< callbacks are NULL; the entry stays to allow - ///< removal during a SetStatus call. + ///< removal during a SetConnectState call. mutable std::mutex m_mtxSend; ///< Synchronize all packages to be sent. std::mutex m_mtxConnect; ///< Connection mutex. std::condition_variable m_cvConnect; ///< Connection variable for connecting. diff --git a/sdv_services/ipc_shared_mem/watchdog.cpp b/sdv_services/ipc_shared_mem/watchdog.cpp index 9cbb30d..b0ba781 100644 --- a/sdv_services/ipc_shared_mem/watchdog.cpp +++ b/sdv_services/ipc_shared_mem/watchdog.cpp @@ -196,7 +196,7 @@ void CWatchDog::ProcessTerminated(/*in*/ sdv::process::TProcessID tProcessID, /* // Inform the connection about the removed process. for (auto& rptrConnection : vecDisconnectedConnections) { - rptrConnection->SetStatus(sdv::ipc::EConnectStatus::disconnected_forced); + rptrConnection->SetConnectState(sdv::ipc::EConnectState::disconnected_forced); #if ENABLE_REPORTING > 0 TRACE("Forced disconnection for PID#", tProcessID); diff --git a/sdv_services/uds_unix_sockets/CMakeLists.txt b/sdv_services/uds_unix_sockets/CMakeLists.txt index 361990c..290f3c1 100644 --- a/sdv_services/uds_unix_sockets/CMakeLists.txt +++ b/sdv_services/uds_unix_sockets/CMakeLists.txt @@ -13,16 +13,13 @@ if(UNIX) project(uds_unix_sockets VERSION 1.0 LANGUAGES CXX) # Define target -add_library(uds_unix_sockets SHARED - "channel_mgnt.h" - "channel_mgnt.cpp" - "connection.h" - "connection.cpp" +add_library(uds_unix_sockets STATIC + channel_mgnt.cpp + connection.cpp ) target_link_libraries(uds_unix_sockets rt ${CMAKE_DL_LIBS} ${CMAKE_THREAD_LIBS_INIT}) -target_link_options(uds_unix_sockets PRIVATE) target_include_directories(uds_unix_sockets PRIVATE ./include/) set_target_properties(uds_unix_sockets PROPERTIES PREFIX "") set_target_properties(uds_unix_sockets PROPERTIES SUFFIX ".sdv") diff --git a/sdv_services/uds_unix_sockets/connection.cpp b/sdv_services/uds_unix_sockets/connection.cpp index d7d621d..c28160f 100644 --- a/sdv_services/uds_unix_sockets/connection.cpp +++ b/sdv_services/uds_unix_sockets/connection.cpp @@ -105,7 +105,7 @@ CUnixSocketConnection::CUnixSocketConnection(int preconfiguredFd, , m_UdsPath(udsPath) , m_StopReceiveThread(false) , m_StopConnectThread(false) - , m_eStatus(sdv::ipc::EConnectStatus::uninitialized) + , m_eConnectState(sdv::ipc::EConnectState::uninitialized) , m_pReceiver(nullptr) , m_pEvent(nullptr) { @@ -170,10 +170,10 @@ bool CUnixSocketConnection::SendData(sdv::sequence>& seqDa #endif // Only send when connected and FD valid - if (m_eStatus != sdv::ipc::EConnectStatus::connected || m_Fd < 0) + if (m_eConnectState != sdv::ipc::EConnectState::connected || m_Fd < 0) { - SetStatus(sdv::ipc::EConnectStatus::communication_error); - SDV_LOG_WARNING("[UDS][TX] Send requested while not connected or FD invalid (status=", static_cast(m_eStatus.load()), ")"); + SetConnectState(sdv::ipc::EConnectState::communication_error); + SDV_LOG_WARNING("[UDS][TX] Send requested while not connected or FD invalid (state=", static_cast(m_eConnectState.load()), ")"); return false; } @@ -328,7 +328,7 @@ bool CUnixSocketConnection::SendData(sdv::sequence>& seqDa return true; } -uint64_t CUnixSocketConnection::RegisterStatusEventCallback(sdv::IInterfaceAccess* pEventCallback) +uint64_t CUnixSocketConnection::RegisterStateEventCallback(sdv::IInterfaceAccess* pEventCallback) { if (!pEventCallback) return 0; @@ -351,7 +351,7 @@ uint64_t CUnixSocketConnection::RegisterStatusEventCallback(sdv::IInterfaceAcces return cookie; } -void CUnixSocketConnection::UnregisterStatusEventCallback(uint64_t uiCookie) +void CUnixSocketConnection::UnregisterStateEventCallback(uint64_t uiCookie) { if (!uiCookie) return; @@ -386,7 +386,7 @@ bool CUnixSocketConnection::AsyncConnect(sdv::IInterfaceAccess* pReceiver) std::lock_guard lk(m_StateMtx); m_pReceiver = sdv::TInterfaceAccessPtr(pReceiver).GetInterface(); m_pEvent = sdv::TInterfaceAccessPtr(pReceiver).GetInterface(); - m_eStatus = sdv::ipc::EConnectStatus::initializing; + m_eConnectState = sdv::ipc::EConnectState::initializing; // Reset stop flags m_StopReceiveThread.store(false); @@ -450,22 +450,22 @@ int CUnixSocketConnection::AcceptConnection() // deprecated bool CUnixSocketConnection::WaitForConnection(uint32_t uiWaitMs) { - if (m_eStatus.load(std::memory_order_acquire) == sdv::ipc::EConnectStatus::connected) return true; + if (m_eConnectState.load(std::memory_order_acquire) == sdv::ipc::EConnectState::connected) return true; std::unique_lock lk(m_MtxConnect); if (uiWaitMs == 0xFFFFFFFFu) { m_CvConnect.wait(lk, [this]{ - return m_eStatus.load(std::memory_order_acquire) == sdv::ipc::EConnectStatus::connected; + return m_eConnectState.load(std::memory_order_acquire) == sdv::ipc::EConnectState::connected; }); return true; } if (uiWaitMs == 0u) - return (m_eStatus.load(std::memory_order_acquire) == sdv::ipc::EConnectStatus::connected); + return (m_eConnectState.load(std::memory_order_acquire) == sdv::ipc::EConnectState::connected); return m_CvConnect.wait_for(lk, std::chrono::milliseconds(uiWaitMs), - [this]{ return m_eStatus.load(std::memory_order_acquire) == sdv::ipc::EConnectStatus::connected; }); + [this]{ return m_eConnectState.load(std::memory_order_acquire) == sdv::ipc::EConnectState::connected; }); } void CUnixSocketConnection::CancelWait() @@ -476,33 +476,33 @@ void CUnixSocketConnection::CancelWait() void CUnixSocketConnection::Disconnect() { StopThreadsAndCloseSockets(/*unlinkPath*/ false); - SetStatus(sdv::ipc::EConnectStatus::disconnected); + SetConnectState(sdv::ipc::EConnectState::disconnected); } -sdv::ipc::EConnectStatus CUnixSocketConnection::GetStatus() const +sdv::ipc::EConnectState CUnixSocketConnection::GetConnectState() const { - return m_eStatus; + return m_eConnectState; } void CUnixSocketConnection::DestroyObject() { m_StopReceiveThread.store(true); - m_eStatus = sdv::ipc::EConnectStatus::disconnected; + m_eConnectState = sdv::ipc::EConnectState::disconnected; } -void CUnixSocketConnection::SetStatus(sdv::ipc::EConnectStatus eStatus) +void CUnixSocketConnection::SetConnectState(sdv::ipc::EConnectState eConnectState) { // Update internal state atomically and wake up waiters. { std::lock_guard lk(m_MtxConnect); - m_eStatus.store(eStatus, std::memory_order_release); + m_eConnectState.store(eConnectState, std::memory_order_release); } m_CvConnect.notify_all(); // Notify the legacy single-listener (kept for backward compatibility). try { - m_pEvent->SetStatus(eStatus); + m_pEvent->SetConnectState(eConnectState); } catch (...) { /* swallow: user callback must not crash transport */ } @@ -515,7 +515,7 @@ void CUnixSocketConnection::SetStatus(sdv::ipc::EConnectStatus eStatus) if (!entry.pCallback) { needCompact = true; continue; } try { - entry.pCallback->SetStatus(eStatus); + entry.pCallback->SetConnectState(eConnectState); } catch (...) { /* swallow per-listener */ } } @@ -588,7 +588,7 @@ void CUnixSocketConnection::ConnectWorker() if (!EnsureDir(dir)) { SDV_LOG_ERROR("[UDS][Server] ensure_dir('", dir, "') failed: ", std::strerror(errno)); - SetStatus(sdv::ipc::EConnectStatus::connection_error); + SetConnectState(sdv::ipc::EConnectState::connection_error); return; } @@ -596,7 +596,7 @@ void CUnixSocketConnection::ConnectWorker() if (m_ListenFd < 0) { SDV_LOG_ERROR("[UDS][Server] socket() failed: ", std::strerror(errno)); - SetStatus(sdv::ipc::EConnectStatus::connection_error); + SetConnectState(sdv::ipc::EConnectState::connection_error); return; } @@ -608,7 +608,7 @@ void CUnixSocketConnection::ConnectWorker() { SDV_LOG_ERROR("[UDS][Server] bind('", m_UdsPath, "') failed: ", std::strerror(errno)); ::close(m_ListenFd); m_ListenFd = -1; - SetStatus(sdv::ipc::EConnectStatus::connection_error); + SetConnectState(sdv::ipc::EConnectState::connection_error); return; } @@ -618,7 +618,7 @@ void CUnixSocketConnection::ConnectWorker() { SDV_LOG_ERROR("[UDS][Server] listen() failed: ", std::strerror(errno)); ::close(m_ListenFd); m_ListenFd = -1; - SetStatus(sdv::ipc::EConnectStatus::connection_error); + SetConnectState(sdv::ipc::EConnectState::connection_error); return; } @@ -670,12 +670,12 @@ void CUnixSocketConnection::ConnectWorker() } if (clientFd < 0) { - SetStatus(sdv::ipc::EConnectStatus::connection_error); + SetConnectState(sdv::ipc::EConnectState::connection_error); return; } m_Fd = clientFd; - SetStatus(sdv::ipc::EConnectStatus::connected); + SetConnectState(sdv::ipc::EConnectState::connected); StartReceiveThread_Unsafe(); return; } @@ -686,7 +686,7 @@ void CUnixSocketConnection::ConnectWorker() if (m_Fd < 0) { SDV_LOG_ERROR("[UDS][Client] socket() failed: ", std::strerror(errno)); - SetStatus(sdv::ipc::EConnectStatus::connection_error); + SetConnectState(sdv::ipc::EConnectState::connection_error); return; } @@ -702,7 +702,7 @@ void CUnixSocketConnection::ConnectWorker() { if (::connect(m_Fd, reinterpret_cast(&addr), sizeof(addr)) == 0) { - SetStatus(sdv::ipc::EConnectStatus::connected); + SetConnectState(sdv::ipc::EConnectState::connected); #if ENABLE_REPORTING >= 1 TRACE("[UDS][Client] Connected"); #endif @@ -719,19 +719,19 @@ void CUnixSocketConnection::ConnectWorker() } ::close(m_Fd); m_Fd = -1; - SetStatus(sdv::ipc::EConnectStatus::connection_error); + SetConnectState(sdv::ipc::EConnectState::connection_error); return; } } catch (const std::exception& ex) { SDV_LOG_ERROR("[UDS][ConnectWorker] exception: ", ex.what()); - SetStatus(sdv::ipc::EConnectStatus::connection_error); + SetConnectState(sdv::ipc::EConnectState::connection_error); } catch (...) { SDV_LOG_ERROR("[UDS][ConnectWorker] unknown exception"); - SetStatus(sdv::ipc::EConnectStatus::connection_error); + SetConnectState(sdv::ipc::EConnectState::connection_error); } } @@ -741,7 +741,7 @@ void CUnixSocketConnection::ReceiveSyncAnswer(const CMessage& message) const auto hdr = message.GetMsgHdr(); if (hdr.uiVersion != SDVFrameworkInterfaceVersion) { - SetStatus(sdv::ipc::EConnectStatus::communication_error); + SetConnectState(sdv::ipc::EConnectState::communication_error); SDV_LOG_WARNING("[UDS][RX] sync_answer with invalid version"); return; } @@ -772,13 +772,13 @@ void CUnixSocketConnection::ReceiveMessages() while (!m_StopReceiveThread.load()) { - if (m_eStatus == sdv::ipc::EConnectStatus::terminating) break; + if (m_eConnectState == sdv::ipc::EConnectState::terminating) break; // Snapshot FD const int fd = m_Fd; if (fd < 0) { - SetStatus(sdv::ipc::EConnectStatus::disconnected); + SetConnectState(sdv::ipc::EConnectState::disconnected); SDV_LOG_WARNING("[UDS][RX] FD invalidated -> disconnected"); break; } @@ -789,7 +789,7 @@ void CUnixSocketConnection::ReceiveMessages() if (pr == 0) { - if (!m_AcceptConnectionRequired && (m_eStatus == sdv::ipc::EConnectStatus::initialized)) + if (!m_AcceptConnectionRequired && (m_eConnectState == sdv::ipc::EConnectState::initialized)) { auto now = std::chrono::high_resolution_clock::now(); if (std::chrono::duration(now - tpStart).count() > 0.5) @@ -805,7 +805,7 @@ void CUnixSocketConnection::ReceiveMessages() if (pr < 0 || (pfd.revents & (POLLERR | POLLHUP | POLLNVAL)) != 0) { - SetStatus(sdv::ipc::EConnectStatus::disconnected); + SetConnectState(sdv::ipc::EConnectState::disconnected); SDV_LOG_WARNING("[UDS][RX] poll() hangup/error -> disconnected"); break; } @@ -815,7 +815,7 @@ void CUnixSocketConnection::ReceiveMessages() uint32_t packetSize = 0; if (!ReadTransportHeader(packetSize)) { - SetStatus(sdv::ipc::EConnectStatus::disconnected); + SetConnectState(sdv::ipc::EConnectState::disconnected); SDV_LOG_WARNING("[UDS][RX] Invalid/missing transport header -> disconnected"); break; } @@ -824,7 +824,7 @@ void CUnixSocketConnection::ReceiveMessages() std::vector payload(packetSize); if (!ReadNumberOfBytes(reinterpret_cast(payload.data()), packetSize)) { - SetStatus(sdv::ipc::EConnectStatus::disconnected); + SetConnectState(sdv::ipc::EConnectState::disconnected); SDV_LOG_WARNING("[UDS][RX] Incomplete payload read -> disconnected"); break; } @@ -832,12 +832,12 @@ void CUnixSocketConnection::ReceiveMessages() CMessage msg(std::move(payload)); if (!msg.IsValid()) { - SetStatus(sdv::ipc::EConnectStatus::communication_error); + SetConnectState(sdv::ipc::EConnectState::communication_error); SDV_LOG_WARNING("[UDS][RX] Invalid SDV message (envelope)"); continue; } - if (m_eStatus == sdv::ipc::EConnectStatus::terminating) break; + if (m_eConnectState == sdv::ipc::EConnectState::terminating) break; #if ENABLE_REPORTING >= 1 switch (msg.GetMsgHdr().eType) @@ -874,12 +874,12 @@ void CUnixSocketConnection::ReceiveMessages() catch (const std::exception& ex) { SDV_LOG_ERROR("[UDS][RX] exception: ", ex.what()); - SetStatus(sdv::ipc::EConnectStatus::disconnected); + SetConnectState(sdv::ipc::EConnectState::disconnected); } catch (...) { SDV_LOG_ERROR("[UDS][RX] unknown exception"); - SetStatus(sdv::ipc::EConnectStatus::disconnected); + SetConnectState(sdv::ipc::EConnectState::disconnected); } } @@ -888,7 +888,7 @@ void CUnixSocketConnection::ReceiveSyncRequest(const CMessage& message) const auto hdr = message.GetMsgHdr(); if (hdr.uiVersion != SDVFrameworkInterfaceVersion) { - SetStatus(sdv::ipc::EConnectStatus::communication_error); + SetConnectState(sdv::ipc::EConnectState::communication_error); SDV_LOG_WARNING("[UDS][RX] sync_request with invalid version"); return; } @@ -907,7 +907,7 @@ void CUnixSocketConnection::ReceiveConnectRequest(const CMessage& message) const auto hdr = message.GetConnectHdr(); if (hdr.uiVersion != SDVFrameworkInterfaceVersion) { - SetStatus(sdv::ipc::EConnectStatus::communication_error); + SetConnectState(sdv::ipc::EConnectState::communication_error); SDV_LOG_WARNING("[UDS][RX] connect_request with invalid version"); return; } @@ -927,19 +927,19 @@ void CUnixSocketConnection::ReceiveConnectAnswer(const CMessage& message) const auto hdr = message.GetConnectHdr(); if (hdr.uiVersion != SDVFrameworkInterfaceVersion) { - SetStatus(sdv::ipc::EConnectStatus::communication_error); + SetConnectState(sdv::ipc::EConnectState::communication_error); SDV_LOG_WARNING("[UDS][RX] connect_answer with invalid version"); return; } // Fully established - SetStatus(sdv::ipc::EConnectStatus::connected); + SetConnectState(sdv::ipc::EConnectState::connected); } void CUnixSocketConnection::ReceiveConnectTerm(const CMessage& /*message*/) { // Peer requested termination - SetStatus(sdv::ipc::EConnectStatus::disconnected); + SetConnectState(sdv::ipc::EConnectState::disconnected); m_StopReceiveThread.store(true); } @@ -999,7 +999,7 @@ void CUnixSocketConnection::ReceiveDataMessage(const CMessage& rMessage, SDataCo uint32_t uiOffset = ReadDataTable(rMessage, rsDataCtxt); if (!uiOffset) { - SetStatus(sdv::ipc::EConnectStatus::communication_error); + SetConnectState(sdv::ipc::EConnectState::communication_error); SDV_LOG_WARNING("[UDS][RX] Invalid data table"); return; } @@ -1012,7 +1012,7 @@ void CUnixSocketConnection::ReceiveDataMessage(const CMessage& rMessage, SDataCo if (!ReadDataChunk(rMessage, uiOffset, rsDataCtxt)) { - SetStatus(sdv::ipc::EConnectStatus::communication_error); + SetConnectState(sdv::ipc::EConnectState::communication_error); SDV_LOG_WARNING("[UDS][RX] Failed to read data chunk"); return; } @@ -1034,7 +1034,7 @@ void CUnixSocketConnection::ReceiveDataFragmentMessage(const CMessage& rMessage, uiOffset = ReadDataTable(rMessage, rsDataCtxt); if (!uiOffset) { - SetStatus(sdv::ipc::EConnectStatus::communication_error); + SetConnectState(sdv::ipc::EConnectState::communication_error); SDV_LOG_WARNING("[UDS][RX] Invalid fragmented data table"); return; } @@ -1048,7 +1048,7 @@ void CUnixSocketConnection::ReceiveDataFragmentMessage(const CMessage& rMessage, if (!ReadDataChunk(rMessage, uiOffset, rsDataCtxt)) { - SetStatus(sdv::ipc::EConnectStatus::communication_error); + SetConnectState(sdv::ipc::EConnectState::communication_error); SDV_LOG_WARNING("[UDS][RX] Failed to read fragmented chunk"); return; } diff --git a/sdv_services/uds_unix_sockets/connection.h b/sdv_services/uds_unix_sockets/connection.h index 8962df9..1ce1730 100644 --- a/sdv_services/uds_unix_sockets/connection.h +++ b/sdv_services/uds_unix_sockets/connection.h @@ -82,24 +82,24 @@ public: /** @brief Optionally cancel WaitForConnection (no-op in current implementation). */ void CancelWait() override; - /** @brief Disconnect and teardown threads/FDs; sets status to 'disconnected'. */ + /** @brief Disconnect and teardown threads/FDs; sets state to 'disconnected'. */ void Disconnect() override; // ---------- IConnect: event callbacks ---------- - /** @brief Register a status event callback (no-op storage in UDS). */ - uint64_t RegisterStatusEventCallback(/*in*/ sdv::IInterfaceAccess* pEventCallback) override; + /** @brief Register a state event callback (no-op storage in UDS). */ + uint64_t RegisterStateEventCallback(/*in*/ sdv::IInterfaceAccess* pEventCallback) override; /** @brief Unregister a previously registered callback (no-op storage in UDS). */ - void UnregisterStatusEventCallback(/*in*/ uint64_t uiCookie) override; + void UnregisterStateEventCallback(/*in*/ uint64_t uiCookie) override; - /** @brief Get current connection status. */ - sdv::ipc::EConnectStatus GetStatus() const override; + /** @brief Get current connection state. */ + sdv::ipc::EConnectState GetConnectState() const override; /** @brief Destroy object (IObjectDestroy). */ void DestroyObject() override; - /** @brief Set status and notify listeners (callback-safe). */ - void SetStatus(sdv::ipc::EConnectStatus eStatus); + /** @brief Set state and notify listeners (callback-safe). */ + void SetConnectState(sdv::ipc::EConnectState eConnectState); /** @brief @return true if this side is server (needs accept()), false otherwise. */ bool IsServer() const; @@ -297,7 +297,7 @@ public: * @brief Handle an incoming connect_term message. * * Indicates that the peer requests immediate termination of the connection. - * Sets status to disconnected and stops the RX loop. + * Sets state to disconnected and stops the RX loop. * * @param message SDV envelope containing the connect_term header. */ @@ -352,7 +352,7 @@ public: // ---------- Internal threading ---------- /** @brief Connect worker (server accept loop or client connect retry). */ void ConnectWorker(); - /** @brief Start RX thread (precondition: status=connected, FD valid). */ + /** @brief Start RX thread (precondition: state=connected, FD valid). */ void StartReceiveThread_Unsafe(); /** * @brief Stop workers and close sockets, then optionally unlink path. @@ -373,9 +373,9 @@ private: std::thread m_ReceiveThread; std::thread m_ConnectThread; - //Status & synchronization + //State & synchronization std::condition_variable m_StateCv; - std::atomic m_eStatus { sdv::ipc::EConnectStatus::uninitialized }; + std::atomic m_eConnectState { sdv::ipc::EConnectState::uninitialized }; sdv::ipc::IDataReceiveCallback* m_pReceiver { nullptr }; sdv::ipc::IConnectEventCallback* m_pEvent { nullptr }; std::mutex m_MtxConnect; diff --git a/sdv_services/uds_unix_tunnel/CMakeLists.txt b/sdv_services/uds_unix_tunnel/CMakeLists.txt new file mode 100644 index 0000000..f40e862 --- /dev/null +++ b/sdv_services/uds_unix_tunnel/CMakeLists.txt @@ -0,0 +1,33 @@ +#******************************************************************************* +# Copyright (c) 2025-2026 ZF Friedrichshafen AG +# +# This program and the accompanying materials are made available under the +# terms of the Apache License Version 2.0 which is available at +# https://www.apache.org/licenses/LICENSE-2.0 +# +# SPDX-License-Identifier: Apache-2.0 +#******************************************************************************* + +if(UNIX) +# Define project +project(uds_unix_tunnel VERSION 1.0 LANGUAGES CXX) + +# Define target +add_library(uds_unix_tunnel STATIC + channel_mgnt.cpp + connection.cpp + ) + +target_link_libraries(uds_unix_tunnel rt ${CMAKE_DL_LIBS} ${CMAKE_THREAD_LIBS_INIT}) + +target_include_directories(uds_unix_tunnel PRIVATE ./include/) +set_target_properties(uds_unix_tunnel PROPERTIES PREFIX "") +set_target_properties(uds_unix_tunnel PROPERTIES SUFFIX ".sdv") + +# Build dependencies +add_dependencies(uds_unix_tunnel CompileCoreIDL) + +# Appending the service in the service list +set(SDV_Service_List ${SDV_Service_List} uds_unix_tunnel PARENT_SCOPE) + +endif() \ No newline at end of file diff --git a/sdv_services/uds_unix_tunnel/channel_mgnt.cpp b/sdv_services/uds_unix_tunnel/channel_mgnt.cpp new file mode 100644 index 0000000..97ae63c --- /dev/null +++ b/sdv_services/uds_unix_tunnel/channel_mgnt.cpp @@ -0,0 +1,178 @@ +#if defined(__unix__) + +#include "channel_mgnt.h" +#include "connection.h" // CUnixTunnelConnection +#include "../sdv_services/uds_unix_sockets/connection.h" // CUnixSocketConnection + +#include + +#include +#include +#include +#include +#include +#include + +namespace +{ + +/** + * @brief Parses a semicolon-separated list of key=value pairs into a map. + * + * Example input: "proto=tunnel;role=server;path=/tmp/tunnel.sock;" + * Example output: { {"proto","tunnel"}, {"role","server"}, {"path","/tmp/tunnel.sock"} } + * + * @param s The input string to parse. + * @return Map of key-value pairs. + */ +static std::map ParseKV(const std::string& s) +{ + std::map kv; + std::stringstream ss(s); + std::string item; + while (std::getline(ss, item, ';')) + { + auto pos = item.find('='); + if (pos != std::string::npos) + kv[item.substr(0, pos)] = item.substr(pos + 1); + } + return kv; +} + + +/** + * @brief Clamps an AF_UNIX pathname to the maximum allowed size for sockaddr_un::sun_path. + * + * @param p The path to clamp. + * @return The clamped path string. + */ +static std::string ClampSunPath(const std::string& p) +{ + constexpr size_t MaxLen = sizeof(sockaddr_un::sun_path); + return (p.size() < MaxLen) ? p : p.substr(0, MaxLen - 1); +} + +} // anonymous namespace + +std::string CUnixTunnelChannelMgnt::MakeUserRuntimeDir() +{ + std::ostringstream oss; + oss << "/run/user/" << ::getuid(); + + struct stat st{}; + if (::stat(oss.str().c_str(), &st) == 0) + { + std::string path = oss.str() + "/sdv"; + ::mkdir(path.c_str(), 0770); + return path; + } + + ::mkdir("/tmp/sdv", 0770); + return "/tmp/sdv"; +} + +bool CUnixTunnelChannelMgnt::OnInitialize() +{ + return true; +} + +void CUnixTunnelChannelMgnt::OnShutdown() +{ + // Actual cleanup is handled by destructors of CUnixTunnelConnection + // and CUnixSocketConnection (shared_ptr). +} + +sdv::ipc::SChannelEndpoint CUnixTunnelChannelMgnt::CreateEndpoint( + const sdv::u8string& ssChannelConfig) +{ + sdv::ipc::SChannelEndpoint endpoint{}; + + const std::string baseDir = MakeUserRuntimeDir(); + std::string name = "TUNNEL_" + std::to_string(::getpid()); + std::string path = baseDir + "/" + name + ".sock"; + + // Parse optional TOML config for custom name/path + if (!ssChannelConfig.empty()) + { + sdv::toml::CTOMLParser cfg(ssChannelConfig.c_str()); + auto nameNode = cfg.GetDirect("IpcChannel.Name"); + if (nameNode.GetType() == sdv::toml::ENodeType::node_string) + name = static_cast(nameNode.GetValue()); + + auto pathNode = cfg.GetDirect("IpcChannel.Path"); + if (pathNode.GetType() == sdv::toml::ENodeType::node_string) + path = static_cast(pathNode.GetValue()); + else + path = baseDir + "/" + name + ".sock"; + } + + path = ClampSunPath(path); + + // Create underlying UDS server transport + auto udsServer = std::make_shared( + -1, + /*acceptConnectionRequired*/ true, + path); + + // Create tunnel wrapper on top of UDS + auto tunnelServer = std::make_shared( + udsServer, + /*channelId*/ 0u); + + m_ServerTunnels.push_back(tunnelServer); + + endpoint.pConnection = static_cast(tunnelServer.get()); + endpoint.ssConnectString = "proto=tunnel;role=server;path=" + path + ";"; + + return endpoint; +} + +sdv::IInterfaceAccess* CUnixTunnelChannelMgnt::Access( + const sdv::u8string& ssConnectString) +{ + const auto kv = ParseKV(static_cast(ssConnectString)); + + // Only handle proto=tunnel + if (!kv.count("proto") || kv.at("proto") != "tunnel") + { + return nullptr; + } + + const bool isServer = + (kv.count("role") && kv.at("role") == "server"); + + const std::string path = + kv.count("path") + ? kv.at("path") + : (MakeUserRuntimeDir() + "/TUNNEL_auto.sock"); + + if (isServer) + { + // For simplicity, create a new server tunnel instance for each Access(). + // The SDV framework is expected to call Access(serverCS) only once in normal cases. + auto udsServer = std::make_shared( + -1, + /*acceptConnectionRequired*/ true, + path); + + auto tunnelServer = std::make_shared( + udsServer, + /*channelId*/ 0u); + + m_ServerTunnels.push_back(tunnelServer); + return static_cast(tunnelServer.get()); + } + + // Client: allocate raw pointer (expected to be managed by SDV framework via IObjectDestroy) + auto udsClient = std::make_shared( + -1, + /*acceptConnectionRequired*/ false, + path); + + auto* tunnelClient = + new CUnixTunnelConnection(udsClient, /*channelId*/ 0u); + + return static_cast(tunnelClient); +} + +#endif // defined(__unix__) \ No newline at end of file diff --git a/sdv_services/uds_unix_tunnel/channel_mgnt.h b/sdv_services/uds_unix_tunnel/channel_mgnt.h new file mode 100644 index 0000000..09484c5 --- /dev/null +++ b/sdv_services/uds_unix_tunnel/channel_mgnt.h @@ -0,0 +1,140 @@ +/******************************************************************************** +* Copyright (c) 2025-2026 ZF Friedrichshafen AG +* +* This program and the accompanying materials are made available under the +* terms of the Apache License Version 2.0 which is available at +* https://www.apache.org/licenses/LICENSE-2.0 +* +* SPDX-License-Identifier: Apache-2.0 +* +* Contributors: +* Denisa Ros - initial API and implementation +********************************************************************************/ +#if defined(__unix__) +#ifndef UNIX_TUNNEL_CHANNEL_MGNT_H +#define UNIX_TUNNEL_CHANNEL_MGNT_H + +#include +#include +#include "../sdv_services/uds_unix_sockets/channel_mgnt.h" // existing UDS transport + +class CUnixTunnelConnection; +/** + * @brief Initialize WinSock on Windows (idempotent). + * + * This helper ensures WSAStartup() is called only once in the process. + * On non-Windows platforms, this is a no-op and always returns success. + * + * @return 0 on success, otherwise a WinSock error code (Windows only). + */ +inline int StartUpWinSock() +{ +#ifdef _WIN32 + static bool isInitialized = false; + if (isInitialized) + { + return 0; + } + + WSADATA wsaData {}; + const int error = WSAStartup(MAKEWORD(2, 2), &wsaData); + if (error != 0) + { + SDV_LOG_ERROR("WSAStartup failed with error: ", std::to_string(error)); + } + else + { + SDV_LOG_INFO("WSAStartup initialized"); + isInitialized = true; + } + return error; +#else + // Non-Windows: nothing to do. Return success for symmetry + return 0; +#endif +} +/** + * @class CUnixTunnelChannelMgnt + * @brief IPC channel management class for Unix Domain Socket tunnel communication. + * + * This manager exposes the "tunnel" IPC type, similar to UDS, with channel multiplexing planned. + * It provides creation and access to tunnel endpoints, manages server-side tunnel lifetimes, + * and integrates with the SDV object/component framework. + */ +class CUnixTunnelChannelMgnt : + public sdv::CSdvObject, + public sdv::ipc::ICreateEndpoint, + public sdv::ipc::IChannelAccess +{ +public: + // Interface map + BEGIN_SDV_INTERFACE_MAP() + SDV_INTERFACE_ENTRY(sdv::ipc::IChannelAccess) + SDV_INTERFACE_ENTRY(sdv::ipc::ICreateEndpoint) + END_SDV_INTERFACE_MAP() + + // Object declarations + DECLARE_OBJECT_CLASS_TYPE(sdv::EObjectType::system_object) + DECLARE_OBJECT_CLASS_NAME("UnixTunnelChannelControl") + DECLARE_OBJECT_CLASS_ALIAS("TunnelChannelControl") + DECLARE_DEFAULT_OBJECT_NAME("TunnelChannelControl") + DECLARE_OBJECT_SINGLETON() + + /** + * @brief Destructor for CUnixTunnelChannelMgnt. + */ + virtual ~CUnixTunnelChannelMgnt() = default; + + /** + * @brief Initialization event, called after object configuration was loaded. Overload of sdv::CSdvObject::OnInitialize. + * @return Returns 'true' when the initialization was successful, 'false' when not. + */ + virtual bool OnInitialize() override; + + /** + * @brief Shutdown the object. Overload of sdv::CSdvObject::OnShutdown. + */ + virtual void OnShutdown() override; + + /** + * @brief Creates a tunnel endpoint (server side) and returns endpoint info. + * + * Optionally uses a TOML config string for custom endpoint parameters. + * + * @param ssChannelConfig Optional config string (TOML). + * @return The channel endpoint structure. + */ + sdv::ipc::SChannelEndpoint CreateEndpoint(const sdv::u8string& ssChannelConfig) override; + + /** + * @brief Creates or accesses a connection object from the channel connect string. + * + * Parses the connect string and returns a pointer to the appropriate connection access interface. + * + * @param ssConnectString The channel connect string. + * @return Pointer to connection access interface. + */ + sdv::IInterfaceAccess* Access(const sdv::u8string& ssConnectString) override; + +private: + /** + * @brief Helper: chooses runtime dir (/run/user//sdv) or fallback (/tmp/sdv). + * + * Used for determining the directory path for runtime sockets. + * + * @return Directory path for runtime sockets. + */ + static std::string MakeUserRuntimeDir(); + + /** + * @brief Keeps server-side tunnel connections alive for the lifetime of the manager. + * + * This ensures that server tunnel objects are not destroyed while the manager is active. + */ + std::vector> m_ServerTunnels; +}; + +DEFINE_SDV_OBJECT(CUnixTunnelChannelMgnt) + +#endif // UNIX_TUNNEL_CHANNEL_MGNT_H +#endif // defined(__unix__) \ No newline at end of file diff --git a/sdv_services/uds_unix_tunnel/connection.cpp b/sdv_services/uds_unix_tunnel/connection.cpp new file mode 100644 index 0000000..4d274fc --- /dev/null +++ b/sdv_services/uds_unix_tunnel/connection.cpp @@ -0,0 +1,230 @@ +/******************************************************************************** +* Copyright (c) 2025-2026 ZF Friedrichshafen AG +* +* This program and the accompanying materials are made available under the +* terms of the Apache License Version 2.0 which is available at +* https://www.apache.org/licenses/LICENSE-2.0 +* +* SPDX-License-Identifier: Apache-2.0 +* +* Contributors: +* Denisa Ros - initial API and implementation +********************************************************************************/ +#if defined(__unix__) +#include "connection.h" + + +/** + * @brief Constructs a tunnel connection wrapper on top of an existing UDS transport. + * @param transport Shared pointer to the underlying UDS transport. + * @param channelId Logical channel ID for this tunnel instance. + */ +CUnixTunnelConnection::CUnixTunnelConnection( + std::shared_ptr transport, + uint16_t channelId) + : m_Transport(std::move(transport)) + , m_ChannelId(channelId) +{ + // No additional initialization required; acts as a thin wrapper. +} + + +/** + * @brief Prepends a tunnel header and forwards the data to the underlying transport. + * @param seqData Sequence of message buffers to send (may be modified). + * @return true if data was sent successfully, false otherwise. + */ +bool CUnixTunnelConnection::SendData(/*inout*/ sdv::sequence>& seqData) +{ + if (!m_Transport) + { + return false; + } + + // Build tunnel header buffer + sdv::pointer hdrBuf; + hdrBuf.resize(sizeof(STunnelHeader)); + + STunnelHeader hdr{}; + hdr.uiChannelId = m_ChannelId; // Logical channel for this connection + hdr.uiFlags = 0; // Reserved for future use + + // Copy header structure into the first buffer (little-endian host layout) + std::memcpy(hdrBuf.get(), &hdr, sizeof(STunnelHeader)); + + // Compose new sequence: [header] + original payload chunks + sdv::sequence> seqWithHdr; + seqWithHdr.push_back(hdrBuf); + for (auto& chunk : seqData) + { + seqWithHdr.push_back(chunk); + } + + return m_Transport->SendData(seqWithHdr); +} + + +/** + * @brief Starts asynchronous connect and registers upper-layer callbacks. + * @param pReceiver Pointer to callback interface for data and state notifications. + * @return true if connect started, false otherwise. + */ +bool CUnixTunnelConnection::AsyncConnect(/*in*/ sdv::IInterfaceAccess* pReceiver) +{ + if (!m_Transport) + { + return false; + } + + // Store upper-layer callbacks (safe for null) + { + std::lock_guard lock(m_CallbackMtx); + sdv::TInterfaceAccessPtr acc(pReceiver); + m_pUpperReceiver = acc.GetInterface(); + m_pUpperEvent = acc.GetInterface(); + } + + // Register this tunnel as the data/event receiver in the UDS transport. + // Do NOT pass pReceiver to UDS, only to our upper fields! + return m_Transport->AsyncConnect(this); +} + +bool CUnixTunnelConnection::WaitForConnection(/*in*/ uint32_t uiWaitMs) +{ + if (!m_Transport) + { + return false; + } + return m_Transport->WaitForConnection(uiWaitMs); +} + +void CUnixTunnelConnection::CancelWait() +{ + if (!m_Transport) + { + return; + } + m_Transport->CancelWait(); +} + +void CUnixTunnelConnection::Disconnect() +{ + if (!m_Transport) + { + return; + } + m_Transport->Disconnect(); + + // Clear upper-layer callbacks (thread-safe) + std::lock_guard lock(m_CallbackMtx); + m_pUpperReceiver = nullptr; + m_pUpperEvent = nullptr; +} + +uint64_t CUnixTunnelConnection::RegisterStateEventCallback(/*in*/ sdv::IInterfaceAccess* pEventCallback) +{ + if (!m_Transport) + { + return 0ULL; + } + + // Directly forward to the underlying transport. This allows external + // components to receive connect-state changes without the tunnel + // having to implement IConnectEventCallback itself + return m_Transport->RegisterStateEventCallback(pEventCallback); +} + +void CUnixTunnelConnection::UnregisterStateEventCallback(/*in*/ uint64_t uiCookie) +{ + if (!m_Transport || uiCookie == 0ULL) + { + return; + } + m_Transport->UnregisterStateEventCallback(uiCookie); +} + +sdv::ipc::EConnectState CUnixTunnelConnection::GetConnectState() const +{ + if (!m_Transport) + { + // Reasonable default if transport is missing + return sdv::ipc::EConnectState::uninitialized; + } + return m_Transport->GetConnectState(); +} + +void CUnixTunnelConnection::DestroyObject() +{ + // Disconnect underlying transport and clear callbacks. + Disconnect(); + + std::lock_guard lock(m_CallbackMtx); + m_Transport.reset(); +} + +void CUnixTunnelConnection::ReceiveData(/*inout*/ sdv::sequence>& seqData) +{ +#ifdef DEBUG_TUNNEL_RECEIVE + // Optional debug: count every call and print buffer size. + static std::atomic s_counter{0}; + auto id = ++s_counter; + std::cerr << "[Tunnel] ReceiveData call #" << id + << ", seqData.size=" << seqData.size() << std::endl; +#endif + + if (seqData.empty()) + { + return; + } + + // Extract and validate tunnel header, then remove it + const auto& hdrChunk = seqData[0]; + if (hdrChunk.size() < sizeof(STunnelHeader)) + { + return; + } + + STunnelHeader hdr{}; + std::memcpy(&hdr, hdrChunk.get(), sizeof(STunnelHeader)); + + seqData.erase(seqData.begin()); // remove header chunk + + // Forward rest of data to upper-layer receiver (set by AsyncConnect) + sdv::ipc::IDataReceiveCallback* upper = nullptr; + { + std::lock_guard lock(m_CallbackMtx); + upper = m_pUpperReceiver; + } + + if (upper) + { + upper->ReceiveData(seqData); + } +} + +void CUnixTunnelConnection::SetChannelId(uint16_t channelId) +{ + m_ChannelId = channelId; +} + +void CUnixTunnelConnection::SetConnectState(sdv::ipc::EConnectState state) +{ + sdv::ipc::IConnectEventCallback* upper = nullptr; + { + std::lock_guard lock(m_CallbackMtx); + upper = m_pUpperEvent; + } + + if (upper) + { + try + { + upper->SetConnectState(state); + } + catch (...) + { + // Never let user callback crash the transport. + } + } +} +#endif // defined(__unix__) \ No newline at end of file diff --git a/sdv_services/uds_unix_tunnel/connection.h b/sdv_services/uds_unix_tunnel/connection.h new file mode 100644 index 0000000..7d30ccb --- /dev/null +++ b/sdv_services/uds_unix_tunnel/connection.h @@ -0,0 +1,191 @@ +/******************************************************************************** +* Copyright (c) 2025-2026 ZF Friedrichshafen AG +* +* This program and the accompanying materials are made available under the +* terms of the Apache License Version 2.0 which is available at +* https://www.apache.org/licenses/LICENSE-2.0 +* +* SPDX-License-Identifier: Apache-2.0 +* +* Contributors: +* Denisa Ros - initial API and implementation +********************************************************************************/ +#if defined(__unix__) +#ifndef UNIX_SOCKET_TUNNEL_CONNECTION_H +#define UNIX_SOCKET_TUNNEL_CONNECTION_H + +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#include "../sdv_services/uds_unix_sockets/connection.h" // existing UDS transport + +/** + * @class CUnixTunnelConnection + * @brief Logical tunnel connection on top of a shared Unix Domain Socket (UDS) transport. + * + * This class wraps an existing CUnixSocketConnection (physical tunnel port) and provides + * optional tunneling capabilities such as a prepended tunnel header (channelId, flags). + * It is designed to enable demultiplexing of incoming payloads per logical channel in future versions. + * + * Currently, this acts as a simple pass-through wrapper. Tunnel header parsing and multi-channel + * routing are planned as future improvements (see TODOs in implementation). + */ +class CUnixTunnelConnection : + public sdv::IInterfaceAccess, + public sdv::IObjectDestroy, + public sdv::ipc::IDataSend, + public sdv::ipc::IConnect, + public sdv::ipc::IDataReceiveCallback, + public sdv::ipc::IConnectEventCallback +{ +public: + /** + * @struct STunnelHeader + * @brief Small header prepended to each tunneled SDV message. + * + * Used for logical channel identification and control flags. This enables future support for + * multiplexing and advanced features (QoS, direction, etc.). + */ + struct STunnelHeader + { + uint16_t uiChannelId; ///< Logical channel ID (e.g., IPC_x / REMOTE_IPC_x) + uint16_t uiFlags; ///< Reserved for future use (QoS, direction, etc.) + }; + + /** + * @brief Constructs a tunnel connection wrapper. + * @param transport Shared pointer to the underlying UDS transport (physical tunnel port). + * @param channelId Default logical channel ID for this object view (may be ignored if full demux is implemented later). + */ + explicit CUnixTunnelConnection( + std::shared_ptr transport, + uint16_t channelId); + + /** + * @brief Destructor. Cleans up resources if needed. + */ + virtual ~CUnixTunnelConnection() = default; + + BEGIN_SDV_INTERFACE_MAP() + SDV_INTERFACE_ENTRY(sdv::ipc::IDataSend) + SDV_INTERFACE_ENTRY(sdv::ipc::IConnect) + SDV_INTERFACE_ENTRY(sdv::ipc::IDataReceiveCallback) + SDV_INTERFACE_ENTRY(sdv::ipc::IConnectEventCallback) + SDV_INTERFACE_ENTRY(sdv::IObjectDestroy) + END_SDV_INTERFACE_MAP() + + // ---------- IDataSend ---------- + /** + * @brief Sends a sequence of buffers via the tunnel. + * + * Prepends a STunnelHeader to the outgoing message buffers and forwards them to the underlying UDS transport. + * In the current implementation, the channel header is always present but not yet used for multiplexing. + * + * @param seqData Sequence of message buffers (may be modified by callee). + * @return true on successful send, false otherwise. + */ + bool SendData(/*inout*/ sdv::sequence>& seqData) override; + + // ---------- IConnect ---------- + /** + * @brief Starts asynchronous connect on the underlying transport and registers this object as receiver. + * @param pReceiver Pointer to callback interface for data and state notifications. + * @return true if connect started, false otherwise. + */ + bool AsyncConnect(/*in*/ sdv::IInterfaceAccess* pReceiver) override; + + /** + * @brief Waits until the underlying transport becomes 'connected'. + * + * Forwards to CUnixSocketConnection::WaitForConnection. + * @param uiWaitMs Timeout in milliseconds to wait. + * @return true if connection established, false on timeout or error. + */ + bool WaitForConnection(/*in*/ uint32_t uiWaitMs) override; + + /** + * @brief Cancels any pending connect or wait operation. + * Delegated to the underlying transport, if needed. + */ + void CancelWait() override; + + /** + * @brief Disconnects the tunnel and underlying transport. + */ + void Disconnect() override; + + /** + * @brief Registers a state event callback (forwards to transport). + * @param pEventCallback Pointer to event callback interface. + * @return Registration cookie (nonzero) or 0 on failure. + */ + uint64_t RegisterStateEventCallback(/*in*/ sdv::IInterfaceAccess* pEventCallback) override; + + /** + * @brief Unregisters a previously registered state event callback. + * @param uiCookie Registration cookie returned by RegisterStateEventCallback. + */ + void UnregisterStateEventCallback(/*in*/ uint64_t uiCookie) override; + + /** + * @brief Gets the current state from the underlying transport. + * @return The current connection state. + */ + sdv::ipc::EConnectState GetConnectState() const override; + + // ---------- IObjectDestroy ---------- + /** + * @brief Releases and cleans up all resources associated with this object. + */ + void DestroyObject() override; + + // ---------- IDataReceiveCallback ---------- + /** + * @brief Receives data from the underlying UDS transport. + * + * Expects the first chunk of seqData to be a STunnelHeader, strips and processes it, + * and delivers the remaining payload to the upper-layer receiver registered via AsyncConnect. + * + * @param seqData Sequence of received message buffers (header chunk is removed by this call). + */ + void ReceiveData(/*inout*/ sdv::sequence>& seqData) override; + + // ---------- IConnectEventCallback ---------- + /** + * @brief Forwards state changes from the underlying UDS transport to the upper layer. + * @param state New connection state. + */ + void SetConnectState(sdv::ipc::EConnectState state) override; + + /** + * @brief Assigns a logical channel ID for this connection. + * Optional helper; you may extend with more routing metadata for advanced tunnel use-cases. + * @param channelId The logical channel ID to set. + */ + void SetChannelId(uint16_t channelId); + + /** + * @brief Get the logical channel ID for this connection + * @return The channel ID + */ + uint16_t GetChannelId() const noexcept { return m_ChannelId; } + +private: + std::shared_ptr m_Transport; ///< shared physical tunnel port + uint16_t m_ChannelId {0}; ///< default logical channel id + sdv::ipc::IDataReceiveCallback* m_pUpperReceiver {nullptr}; ///< Callback to upper layer (data receive) + sdv::ipc::IConnectEventCallback* m_pUpperEvent {nullptr}; ///< Callback to upper layer (state event) + mutable std::mutex m_CallbackMtx; ///< Mutex to guard callback access +}; + +#endif // UNIX_SOCKET_TUNNEL_CONNECTION_H +#endif // defined(__unix__) \ No newline at end of file diff --git a/sdv_services/uds_win_sockets/CMakeLists.txt b/sdv_services/uds_win_sockets/CMakeLists.txt index 290ec77..ac138e3 100644 --- a/sdv_services/uds_win_sockets/CMakeLists.txt +++ b/sdv_services/uds_win_sockets/CMakeLists.txt @@ -13,16 +13,22 @@ if(WIN32) project(uds_win_sockets VERSION 1.0 LANGUAGES CXX) # Define target -add_library(uds_win_sockets SHARED - "channel_mgnt.h" - "channel_mgnt.cpp" - "connection.h" - "connection.cpp") +add_library(uds_win_sockets STATIC + channel_mgnt.cpp + connection.cpp +) -target_link_libraries(uds_win_sockets ${CMAKE_THREAD_LIBS_INIT} Ws2_32.lib) +target_include_directories(uds_win_sockets + PUBLIC + ${CMAKE_CURRENT_SOURCE_DIR} + ./include/ +) -target_link_options(uds_win_sockets PRIVATE) -target_include_directories(uds_win_sockets PRIVATE ./include/) +target_link_libraries(uds_win_sockets + PUBLIC + ${CMAKE_THREAD_LIBS_INIT} + Ws2_32.lib +) set_target_properties(uds_win_sockets PROPERTIES PREFIX "") set_target_properties(uds_win_sockets PROPERTIES SUFFIX ".sdv") diff --git a/sdv_services/uds_win_sockets/channel_mgnt.cpp b/sdv_services/uds_win_sockets/channel_mgnt.cpp index 840e111..6e42b9c 100644 --- a/sdv_services/uds_win_sockets/channel_mgnt.cpp +++ b/sdv_services/uds_win_sockets/channel_mgnt.cpp @@ -10,6 +10,7 @@ * Contributors: * Denisa Ros - initial API and implementation ********************************************************************************/ +#ifdef _WIN32 #include "channel_mgnt.h" #include "connection.h" @@ -323,15 +324,12 @@ static SOCKET ConnectUnixSocket( const auto deadline = std::chrono::steady_clock::now() + std::chrono::milliseconds(totalTimeoutMs); - int lastError = 0; - while (true) { SOCKET s = socket(AF_UNIX, SOCK_STREAM, 0); if (s == INVALID_SOCKET) { - lastError = WSAGetLastError(); - SDV_LOG_ERROR("[AF_UNIX] socket() FAIL (client), WSA=", lastError); + SDV_LOG_ERROR("[AF_UNIX] socket() FAIL (client), WSA=", WSAGetLastError()); return INVALID_SOCKET; } @@ -341,7 +339,7 @@ static SOCKET ConnectUnixSocket( return s; } - lastError = WSAGetLastError(); + int lastError = WSAGetLastError(); closesocket(s); if (std::chrono::steady_clock::now() >= deadline) @@ -364,7 +362,7 @@ bool CSocketsChannelMgnt::OnInitialize() return true; } -void CSocketsChannelMgnt::OnServerClosed(const std::string& udsPath, CConnection* ptr) +void CSocketsChannelMgnt::OnServerClosed(const std::string& udsPath, CWinsockConnection* ptr) { std::lock_guard lock(m_udsMtx); @@ -411,8 +409,8 @@ sdv::ipc::SChannelEndpoint CSocketsChannelMgnt::CreateEndpoint(const sdv::u8stri return {}; } - // Server-side CConnection, it will accept() a client on first use - auto server = std::make_shared(listenSocket, /*acceptRequired*/ true); + // Server-side CWinsockConnection, it will accept() a client on first use + auto server = std::make_shared(listenSocket, /*acceptRequired*/ true); { std::lock_guard lock(m_udsMtx); @@ -481,5 +479,7 @@ sdv::IInterfaceAccess* CSocketsChannelMgnt::Access(const sdv::u8string& cs) // Client-side connection object (acceptRequired=false) // Ownership is transferred to the caller (VAPI runtime) - return new CConnection(s, /*acceptRequired*/ false); -} \ No newline at end of file + return new CWinsockConnection(s, /*acceptRequired*/ false); +} + +#endif \ No newline at end of file diff --git a/sdv_services/uds_win_sockets/channel_mgnt.h b/sdv_services/uds_win_sockets/channel_mgnt.h index 1934285..81ab015 100644 --- a/sdv_services/uds_win_sockets/channel_mgnt.h +++ b/sdv_services/uds_win_sockets/channel_mgnt.h @@ -10,9 +10,10 @@ * Contributors: * Denisa Ros - initial API and implementation ********************************************************************************/ +#ifdef _WIN32 -#ifndef CHANNEL_MGNT_H -#define CHANNEL_MGNT_H +#ifndef WIN_SOCKETS_CHANNEL_MGNT_H +#define WIN_SOCKETS_CHANNEL_MGNT_H #include #include @@ -150,6 +151,8 @@ public: DECLARE_DEFAULT_OBJECT_NAME("LocalChannelControl") DECLARE_OBJECT_SINGLETON() + virtual ~CSocketsChannelMgnt() = default; + /** * @brief Initialization event, called after object configuration was loaded. Overload of sdv::CSdvObject::OnInitialize. * @return Returns 'true' when the initialization was successful, 'false' when not. @@ -169,7 +172,7 @@ public: * The endpoint is implemented as a local AF_UNIX server socket * (listen socket) on Windows. The connect string has the format: * - * proto=uds;path=<udsPath> + * proto=uds;path=; * * If no configuration is provided or no path is specified, a default * path is used (under %LOCALAPPDATA%/sdv) @@ -193,7 +196,7 @@ public: * Overload of sdv::ipc::IChannelAccess::Access * * The connect string is expected to contain: - * proto=uds;path=<udsPath> + * proto=uds;path=; * * For the first Access() call with a given path, the server-side * connection object created by CreateEndpoint() can be returned. @@ -206,18 +209,17 @@ public: sdv::IInterfaceAccess* Access(const sdv::u8string& ssConnectString) override; /** - * @brief Called by a CConnection instance when the server side is closed + * @brief Called by a CWinsockConnection instance when the server side is closed * * Used to clean up internal registries for a given UDS path * - * @param udsPath Path to the UDS endpoint - * @param ptr Pointer to the CConnection instance that was closed + * @param ptr Pointer to the CWinsockConnection instance that was closed */ - void OnServerClosed(const std::string& udsPath, CConnection* ptr); + void OnServerClosed(const std::string& udsPath, CWinsockConnection* ptr); private: /// @brief Registry of AF_UNIX server connections keyed by normalized UDS path - std::map> m_udsServers; + std::map> m_udsServers; /** * @brief Set of UDS paths that already returned their server-side @@ -234,4 +236,5 @@ private: // SDV object factory macro DEFINE_SDV_OBJECT(CSocketsChannelMgnt) -#endif // ! defined CHANNEL_MGNT_H \ No newline at end of file +#endif // ! defined WIN_SOCKETS_CHANNEL_MGNT_H +#endif \ No newline at end of file diff --git a/sdv_services/uds_win_sockets/connection.cpp b/sdv_services/uds_win_sockets/connection.cpp index 05c9377..1e73b92 100644 --- a/sdv_services/uds_win_sockets/connection.cpp +++ b/sdv_services/uds_win_sockets/connection.cpp @@ -10,7 +10,7 @@ * Contributors: * Denisa Ros - initial API and implementation ********************************************************************************/ - +#ifdef _WIN32 #include "connection.h" #include @@ -18,8 +18,8 @@ #include #include -CConnection::CConnection(SOCKET preconfiguredSocket, bool acceptConnectionRequired) - : m_ConnectionStatus(sdv::ipc::EConnectStatus::uninitialized) +CWinsockConnection::CWinsockConnection(unsigned long long preconfiguredSocket, bool acceptConnectionRequired) + : m_ConnectionState(sdv::ipc::EConnectState::uninitialized) , m_AcceptConnectionRequired(acceptConnectionRequired) , m_CancelWait(false) { @@ -30,23 +30,34 @@ CConnection::CConnection(SOCKET preconfiguredSocket, bool acceptConnectionRequir if (m_AcceptConnectionRequired) { // Server side: we own a listening socket, active socket is not yet accepted - m_ListenSocket = preconfiguredSocket; + m_ListenSocket = static_cast(preconfiguredSocket); m_ConnectionSocket = INVALID_SOCKET; } else { // Client side: we already have a connected socket m_ListenSocket = INVALID_SOCKET; - m_ConnectionSocket = preconfiguredSocket; + m_ConnectionSocket = static_cast(preconfiguredSocket); } } -CConnection::~CConnection() +/*CWinsockConnection::CWinsockConnection() + : m_ConnectionState(sdv::ipc::EConnectState::uninitialized) + , m_AcceptConnectionRequired(false) + , m_CancelWait(false) +{ + m_ListenSocket = INVALID_SOCKET; + m_ConnectionSocket = INVALID_SOCKET; + std::fill(std::begin(m_SendBuffer), std::end(m_SendBuffer), '\0'); + std::fill(std::begin(m_ReceiveBuffer), std::end(m_ReceiveBuffer), '\0'); +}*/ + +CWinsockConnection::~CWinsockConnection() { try { StopThreadsAndCloseSockets(); - m_ConnectionStatus = sdv::ipc::EConnectStatus::disconnected; + m_ConnectionState = sdv::ipc::EConnectState::disconnected; } catch (...) { @@ -54,11 +65,11 @@ CConnection::~CConnection() } } -void CConnection::SetStatus(sdv::ipc::EConnectStatus status) +void CWinsockConnection::SetConnectState(sdv::ipc::EConnectState state) { { std::lock_guard lk(m_MtxConnect); - m_ConnectionStatus.store(status, std::memory_order_release); + m_ConnectionState.store(state, std::memory_order_release); } // Wake up any waiter @@ -69,7 +80,7 @@ void CConnection::SetStatus(sdv::ipc::EConnectStatus status) { try { - m_pEvent->SetStatus(status); + m_pEvent->SetConnectState(state); } catch (...) { @@ -78,7 +89,7 @@ void CConnection::SetStatus(sdv::ipc::EConnectStatus status) } } -int32_t CConnection::Send(const char* data, int32_t dataLength) +int32_t CWinsockConnection::Send(const char* data, int32_t dataLength) { int32_t total = 0; @@ -88,7 +99,7 @@ int32_t CConnection::Send(const char* data, int32_t dataLength) if (n == SOCKET_ERROR) { SDV_LOG_ERROR("send failed with error: ", std::to_string(WSAGetLastError())); - m_ConnectionStatus = sdv::ipc::EConnectStatus::communication_error; + m_ConnectionState = sdv::ipc::EConnectState::communication_error; m_ConnectionSocket = INVALID_SOCKET; return (total > 0) ? total : SOCKET_ERROR; } @@ -98,7 +109,7 @@ int32_t CConnection::Send(const char* data, int32_t dataLength) return total; } -int CConnection::SendExact(const char* data, int len) +int CWinsockConnection::SendExact(const char* data, int len) { int total = 0; @@ -115,13 +126,13 @@ int CConnection::SendExact(const char* data, int len) return total; } -bool CConnection::SendData(/*inout*/ sdv::sequence>& seqData) +bool CWinsockConnection::SendData(/*inout*/ sdv::sequence>& seqData) { // Must be connected and have a valid socket - if (m_ConnectionStatus != sdv::ipc::EConnectStatus::connected || + if (m_ConnectionState != sdv::ipc::EConnectState::connected || m_ConnectionSocket == INVALID_SOCKET) { - m_ConnectionStatus = sdv::ipc::EConnectStatus::communication_error; + m_ConnectionState = sdv::ipc::EConnectState::communication_error; return false; } @@ -288,11 +299,11 @@ bool CConnection::SendData(/*inout*/ sdv::sequence>& seqDa return (sentOffset == required); } -SOCKET CConnection::AcceptConnection() +SOCKET CWinsockConnection::AcceptConnection() { if (m_ListenSocket == INVALID_SOCKET) { - SetStatus(sdv::ipc::EConnectStatus::connection_error); + SetConnectState(sdv::ipc::EConnectState::connection_error); return INVALID_SOCKET; } @@ -310,7 +321,7 @@ SOCKET CConnection::AcceptConnection() if (sr == SOCKET_ERROR) { SDV_LOG_ERROR("[AF_UNIX] select(listen) FAIL, WSA=", WSAGetLastError()); - SetStatus(sdv::ipc::EConnectStatus::connection_error); + SetConnectState(sdv::ipc::EConnectState::connection_error); return INVALID_SOCKET; } @@ -329,7 +340,7 @@ SOCKET CConnection::AcceptConnection() } SDV_LOG_ERROR("[AF_UNIX] accept FAIL, WSA=", err); - SetStatus(sdv::ipc::EConnectStatus::connection_error); + SetConnectState(sdv::ipc::EConnectState::connection_error); return INVALID_SOCKET; } @@ -338,11 +349,11 @@ SOCKET CConnection::AcceptConnection() } SDV_LOG_ERROR("[AF_UNIX] accept canceled (stop flag)"); - SetStatus(sdv::ipc::EConnectStatus::connection_error); + SetConnectState(sdv::ipc::EConnectState::connection_error); return INVALID_SOCKET; } -bool CConnection::AsyncConnect(sdv::IInterfaceAccess* pReceiver) +bool CWinsockConnection::AsyncConnect(sdv::IInterfaceAccess* pReceiver) { // Store callbacks m_pReceiver = sdv::TInterfaceAccessPtr(pReceiver).GetInterface(); @@ -360,14 +371,14 @@ bool CConnection::AsyncConnect(sdv::IInterfaceAccess* pReceiver) m_ConnectThread.join(); // Start the connect worker - m_ConnectThread = std::thread(&CConnection::ConnectWorker, this); + m_ConnectThread = std::thread(&CWinsockConnection::ConnectWorker, this); return true; } -bool CConnection::WaitForConnection(uint32_t uiWaitMs) +bool CWinsockConnection::WaitForConnection(uint32_t uiWaitMs) { - if (m_ConnectionStatus.load(std::memory_order_acquire) == - sdv::ipc::EConnectStatus::connected) + if (m_ConnectionState.load(std::memory_order_acquire) == + sdv::ipc::EConnectState::connected) { return true; } @@ -380,16 +391,16 @@ bool CConnection::WaitForConnection(uint32_t uiWaitMs) lk, [this] { - return m_ConnectionStatus.load(std::memory_order_acquire) == - sdv::ipc::EConnectStatus::connected; + return m_ConnectionState.load(std::memory_order_acquire) == + sdv::ipc::EConnectState::connected; }); return true; } if (uiWaitMs == 0u) // zero wait { - return (m_ConnectionStatus.load(std::memory_order_acquire) == - sdv::ipc::EConnectStatus::connected); + return (m_ConnectionState.load(std::memory_order_acquire) == + sdv::ipc::EConnectState::connected); } // finite wait @@ -398,24 +409,24 @@ bool CConnection::WaitForConnection(uint32_t uiWaitMs) std::chrono::milliseconds(uiWaitMs), [this] { - return m_ConnectionStatus.load(std::memory_order_acquire) == - sdv::ipc::EConnectStatus::connected; + return m_ConnectionState.load(std::memory_order_acquire) == + sdv::ipc::EConnectState::connected; }); } -void CConnection::CancelWait() +void CWinsockConnection::CancelWait() { m_CancelWait.store(true); } -void CConnection::Disconnect() +void CWinsockConnection::Disconnect() { m_CancelWait.store(true); StopThreadsAndCloseSockets(); - SetStatus(sdv::ipc::EConnectStatus::disconnected); + SetConnectState(sdv::ipc::EConnectState::disconnected); } -uint64_t CConnection::RegisterStatusEventCallback(/*in*/ sdv::IInterfaceAccess* pEventCallback) +uint64_t CWinsockConnection::RegisterStateEventCallback(/*in*/ sdv::IInterfaceAccess* pEventCallback) { // Extract IConnectEventCallback interface m_pEvent = sdv::TInterfaceAccessPtr(pEventCallback).GetInterface(); @@ -424,7 +435,7 @@ uint64_t CConnection::RegisterStatusEventCallback(/*in*/ sdv::IInterfaceAccess* return (m_pEvent != nullptr) ? 1ULL : 0ULL; } -void CConnection::UnregisterStatusEventCallback(/*in*/ uint64_t uiCookie) +void CWinsockConnection::UnregisterStateEventCallback(/*in*/ uint64_t uiCookie) { // Only one callback supported -> cookie value is 1 if (uiCookie == 1ULL) @@ -433,21 +444,21 @@ void CConnection::UnregisterStatusEventCallback(/*in*/ uint64_t uiCookie) } } -sdv::ipc::EConnectStatus CConnection::GetStatus() const +sdv::ipc::EConnectState CWinsockConnection::GetConnectState() const { - return m_ConnectionStatus; + return m_ConnectionState; } -void CConnection::DestroyObject() +void CWinsockConnection::DestroyObject() { m_StopReceiveThread = true; m_StopConnectThread = true; StopThreadsAndCloseSockets(); - m_ConnectionStatus = sdv::ipc::EConnectStatus::disconnected; + m_ConnectionState = sdv::ipc::EConnectState::disconnected; } -void CConnection::ConnectWorker() +void CWinsockConnection::ConnectWorker() { try { @@ -456,18 +467,18 @@ void CConnection::ConnectWorker() // SERVER SIDE if (m_ListenSocket == INVALID_SOCKET) { - SetStatus(sdv::ipc::EConnectStatus::connection_error); + SetConnectState(sdv::ipc::EConnectState::connection_error); return; } - SetStatus(sdv::ipc::EConnectStatus::initializing); + SetConnectState(sdv::ipc::EConnectState::initializing); SDV_LOG_INFO("[AF_UNIX] Srv ConnectWorker start: listen=%llu", static_cast(m_ListenSocket)); SOCKET c = AcceptConnection(); - SDV_LOG_INFO("[AF_UNIX] Srv AcceptConnection returned: sock=%llu status=%d", + SDV_LOG_INFO("[AF_UNIX] Srv AcceptConnection returned: sock=%llu state=%d", static_cast(c), - static_cast(m_ConnectionStatus.load())); + static_cast(m_ConnectionState.load())); if (c == INVALID_SOCKET) { @@ -475,7 +486,7 @@ void CConnection::ConnectWorker() { try { - m_pEvent->SetStatus(m_ConnectionStatus); + m_pEvent->SetConnectState(m_ConnectionState); } catch (...) { @@ -485,7 +496,7 @@ void CConnection::ConnectWorker() } m_ConnectionSocket = c; - SetStatus(sdv::ipc::EConnectStatus::connected); + SetConnectState(sdv::ipc::EConnectState::connected); StartReceiveThread_Unsafe(); return; } @@ -494,22 +505,22 @@ void CConnection::ConnectWorker() // CLIENT SIDE if (m_ConnectionSocket == INVALID_SOCKET) { - SetStatus(sdv::ipc::EConnectStatus::connection_error); + SetConnectState(sdv::ipc::EConnectState::connection_error); return; } } // Client side: socket is already connected - SetStatus(sdv::ipc::EConnectStatus::connected); + SetConnectState(sdv::ipc::EConnectState::connected); StartReceiveThread_Unsafe(); } catch (...) { - SetStatus(sdv::ipc::EConnectStatus::connection_error); + SetConnectState(sdv::ipc::EConnectState::connection_error); } } -void CConnection::StartReceiveThread_Unsafe() +void CWinsockConnection::StartReceiveThread_Unsafe() { if (m_ReceiveThread.joinable()) { @@ -517,10 +528,10 @@ void CConnection::StartReceiveThread_Unsafe() } m_StopReceiveThread.store(false); - m_ReceiveThread = std::thread(&CConnection::ReceiveMessages, this); + m_ReceiveThread = std::thread(&CWinsockConnection::ReceiveMessages, this); } -void CConnection::StopThreadsAndCloseSockets() +void CWinsockConnection::StopThreadsAndCloseSockets() { // Signal stop m_StopReceiveThread.store(true); @@ -574,7 +585,7 @@ void CConnection::StopThreadsAndCloseSockets() static_cast(s)); } -bool CConnection::ReadNumberOfBytes(char* buffer, uint32_t length) +bool CWinsockConnection::ReadNumberOfBytes(char* buffer, uint32_t length) { uint32_t received = 0; @@ -607,7 +618,7 @@ bool CConnection::ReadNumberOfBytes(char* buffer, uint32_t length) return (received == length); } -bool CConnection::ValidateHeader(const SMsgHeader& msgHeader) +bool CWinsockConnection::ValidateHeader(const SMsgHeader& msgHeader) { // Kept only for compatibility with any legacy users (not used in SDV path) if ((msgHeader.msgStart == m_MsgStart) && (msgHeader.msgEnd == m_MsgEnd)) @@ -618,7 +629,7 @@ bool CConnection::ValidateHeader(const SMsgHeader& msgHeader) } -uint32_t CConnection::ReadDataTable(const CMessage& message, SDataContext& dataCtx) +uint32_t CWinsockConnection::ReadDataTable(const CMessage& message, SDataContext& dataCtx) { uint32_t offset = 0; @@ -691,7 +702,7 @@ uint32_t CConnection::ReadDataTable(const CMessage& message, SDataContext& dataC return offset; } -bool CConnection::ReadDataChunk(const CMessage& message, uint32_t offset, SDataContext& dataCtx) +bool CWinsockConnection::ReadDataChunk(const CMessage& message, uint32_t offset, SDataContext& dataCtx) { if (offset < sizeof(SMsgHdr)) return false; @@ -743,12 +754,12 @@ bool CConnection::ReadDataChunk(const CMessage& message, uint32_t offset, SDataC return true; } -void CConnection::ReceiveSyncRequest(const CMessage& message) +void CWinsockConnection::ReceiveSyncRequest(const CMessage& message) { const auto hdr = message.GetMsgHdr(); if (hdr.uiVersion != SDVFrameworkInterfaceVersion) { - SetStatus(sdv::ipc::EConnectStatus::communication_error); + SetConnectState(sdv::ipc::EConnectState::communication_error); return; } @@ -764,12 +775,12 @@ void CConnection::ReceiveSyncRequest(const CMessage& message) return; } -void CConnection::ReceiveSyncAnswer(const CMessage& message) +void CWinsockConnection::ReceiveSyncAnswer(const CMessage& message) { const auto hdr = message.GetMsgHdr(); if (hdr.uiVersion != SDVFrameworkInterfaceVersion) { - SetStatus(sdv::ipc::EConnectStatus::communication_error); + SetConnectState(sdv::ipc::EConnectState::communication_error); return; } @@ -786,12 +797,12 @@ void CConnection::ReceiveSyncAnswer(const CMessage& message) return; } -void CConnection::ReceiveConnectRequest(const CMessage& message) +void CWinsockConnection::ReceiveConnectRequest(const CMessage& message) { const auto hdr = message.GetConnectHdr(); if (hdr.uiVersion != SDVFrameworkInterfaceVersion) { - SetStatus(sdv::ipc::EConnectStatus::communication_error); + SetConnectState(sdv::ipc::EConnectState::communication_error); return; } @@ -808,42 +819,42 @@ void CConnection::ReceiveConnectRequest(const CMessage& message) return; } -void CConnection::ReceiveConnectAnswer(const CMessage& message) +void CWinsockConnection::ReceiveConnectAnswer(const CMessage& message) { const auto hdr = message.GetConnectHdr(); if (hdr.uiVersion != SDVFrameworkInterfaceVersion) { - SetStatus(sdv::ipc::EConnectStatus::communication_error); + SetConnectState(sdv::ipc::EConnectState::communication_error); return; } // Handshake complete (client side) - SetStatus(sdv::ipc::EConnectStatus::connected); + SetConnectState(sdv::ipc::EConnectState::connected); } -void CConnection::ReceiveConnectTerm(const CMessage& /*message*/) +void CWinsockConnection::ReceiveConnectTerm(const CMessage& /*message*/) { - SetStatus(sdv::ipc::EConnectStatus::disconnected); + SetConnectState(sdv::ipc::EConnectState::disconnected); m_StopReceiveThread.store(true); } -void CConnection::ReceiveDataMessage(const CMessage& message, SDataContext& dataCtx) +void CWinsockConnection::ReceiveDataMessage(const CMessage& message, SDataContext& dataCtx) { const uint32_t payloadOffset = ReadDataTable(message, dataCtx); if (payloadOffset == 0) { - SetStatus(sdv::ipc::EConnectStatus::communication_error); + SetConnectState(sdv::ipc::EConnectState::communication_error); return; } if (!ReadDataChunk(message, payloadOffset, dataCtx)) { - SetStatus(sdv::ipc::EConnectStatus::communication_error); + SetConnectState(sdv::ipc::EConnectState::communication_error); return; } } -void CConnection::ReceiveDataFragmentMessage(const CMessage& message, SDataContext& dataCtx) +void CWinsockConnection::ReceiveDataFragmentMessage(const CMessage& message, SDataContext& dataCtx) { uint32_t offset = static_cast(sizeof(SFragmentedMsgHdr)); @@ -852,19 +863,19 @@ void CConnection::ReceiveDataFragmentMessage(const CMessage& message, SDataConte offset = ReadDataTable(message, dataCtx); if (offset == 0) { - SetStatus(sdv::ipc::EConnectStatus::communication_error); + SetConnectState(sdv::ipc::EConnectState::communication_error); return; } } if (!ReadDataChunk(message, offset, dataCtx)) { - SetStatus(sdv::ipc::EConnectStatus::communication_error); + SetConnectState(sdv::ipc::EConnectState::communication_error); return; } } -void CConnection::ReceiveMessages() +void CWinsockConnection::ReceiveMessages() { try { @@ -898,14 +909,14 @@ void CConnection::ReceiveMessages() if (!ReadNumberOfBytes(reinterpret_cast(&packetSize), sizeof(packetSize))) { - SetStatus(sdv::ipc::EConnectStatus::disconnected); + SetConnectState(sdv::ipc::EConnectState::disconnected); SDV_LOG_WARNING("[UDS][RX] Incomplete payload read -> disconnected"); break; } if (packetSize == 0 || packetSize > kMaxUdsPacketSize) { - SetStatus(sdv::ipc::EConnectStatus::communication_error); + SetConnectState(sdv::ipc::EConnectState::communication_error); break; } @@ -914,7 +925,7 @@ void CConnection::ReceiveMessages() if (!ReadNumberOfBytes(reinterpret_cast(payload.data()), packetSize)) { - SetStatus(sdv::ipc::EConnectStatus::communication_error); + SetConnectState(sdv::ipc::EConnectState::communication_error); SDV_LOG_WARNING("[UDS][RX] Invalid SDV message (envelope)"); break; } @@ -923,7 +934,7 @@ void CConnection::ReceiveMessages() CMessage msg(std::move(payload)); if (!msg.IsValid()) { - SetStatus(sdv::ipc::EConnectStatus::communication_error); + SetConnectState(sdv::ipc::EConnectState::communication_error); continue; } @@ -941,12 +952,13 @@ void CConnection::ReceiveMessages() break; } - if (m_ConnectionStatus == sdv::ipc::EConnectStatus::terminating) + if (m_ConnectionState == sdv::ipc::EConnectState::terminating) break; } } catch (...) { - SetStatus(sdv::ipc::EConnectStatus::disconnected); + SetConnectState(sdv::ipc::EConnectState::disconnected); } -} \ No newline at end of file +} +#endif \ No newline at end of file diff --git a/sdv_services/uds_win_sockets/connection.h b/sdv_services/uds_win_sockets/connection.h index 2235ace..340fe6c 100644 --- a/sdv_services/uds_win_sockets/connection.h +++ b/sdv_services/uds_win_sockets/connection.h @@ -10,9 +10,9 @@ * Contributors: * Denisa Ros - initial API and implementation ********************************************************************************/ - -#ifndef CONNECTION_H -#define CONNECTION_H +#ifdef _WIN32 +#ifndef WIN_SOCKETS_CONNECTION_H +#define WIN_SOCKETS_CONNECTION_H #include #include @@ -35,6 +35,7 @@ # endif #endif + /// @brief Legacy framing markers for the old message header (not used by SDV envelope) constexpr uint32_t m_MsgStart = 0x01020304; ///< Value to mark the start of the legacy message header constexpr uint32_t m_MsgEnd = 0x05060708; ///< Value to mark the end of the legacy message header @@ -67,10 +68,10 @@ struct SMsgHeader * * Exposes: * - sdv::ipc::IDataSend : sending SDV-framed messages - * - sdv::ipc::IConnect : async connect / wait / status / events + * - sdv::ipc::IConnect : async connect / wait / state / events * - sdv::IObjectDestroy : explicit destruction hook for SDV runtime */ -class CConnection +class CWinsockConnection : public sdv::IInterfaceAccess , public sdv::ipc::IDataSend , public sdv::ipc::IConnect @@ -80,19 +81,19 @@ public: /** * @brief default constructor used by create endpoint - allocates new buffers m_Sender and m_Receiver */ - CConnection(); + CWinsockConnection(); /** * @brief access existing connection * @param[in] preconfiguredSocket Prepared socket for the connection * @param[in] acceptConnectionRequired If true connection has to be accepted before receive thread can be started */ - CConnection(SOCKET preconfiguredSocket, bool acceptConnectionRequired); + CWinsockConnection(unsigned long long preconfiguredSocket, bool acceptConnectionRequired); /** * @brief Virtual destructor needed for "delete this;" */ - virtual ~CConnection(); + virtual ~CWinsockConnection(); BEGIN_SDV_INTERFACE_MAP() SDV_INTERFACE_ENTRY(sdv::ipc::IDataSend) @@ -122,7 +123,7 @@ public: * @param[in] pReceiver Object that exposes IDataReceiveCallback and * optionally IConnectEventCallback * @return true when the connect worker was started successfully - * Use GetStatus / WaitForConnection / callbacks for status + * Use GetConnectState / WaitForConnection / callbacks for state */ bool AsyncConnect(/*in*/ sdv::IInterfaceAccess* pReceiver) override; @@ -132,7 +133,7 @@ public: * Overload of sdv::ipc::IConnect::WaitForConnection * * @param[in] uiWaitMs - * - 0 : do not wait, just check current status + * - 0 : do not wait, just check current state * - 0xFFFFFFFF: wait indefinitely * - otherwise : wait up to uiWaitMs milliseconds * @return true if the connection is in connected state when returning @@ -149,36 +150,36 @@ public: /** * @brief Disconnect from the peer and stop I/O threads * - * This sets the status to disconnected and releases the event interface + * This sets the state to disconnected and releases the event interface */ void Disconnect() override; /** - * @brief Register event callback interface for connection status updates + * @brief Register event callback interface for connection state updates * - * Overload of sdv::ipc::IConnect::RegisterStatusEventCallback + * Overload of sdv::ipc::IConnect::RegisterStateEventCallback * * @param[in] pEventCallback Pointer to an object exposing * sdv::ipc::IConnectEventCallback. * @return A registration cookie (1 = valid, 0 = failure) */ - uint64_t RegisterStatusEventCallback(/*in*/ sdv::IInterfaceAccess* pEventCallback) override; + uint64_t RegisterStateEventCallback(/*in*/ sdv::IInterfaceAccess* pEventCallback) override; /** - * @brief Unregister the status event callback using its cookie + * @brief Unregister the state event callback using its cookie * - * Overload of sdv::ipc::IConnect::UnregisterStatusEventCallback + * Overload of sdv::ipc::IConnect::UnregisterStateEventCallback * - * @param[in] uiCookie Cookie returned by RegisterStatusEventCallback + * @param[in] uiCookie Cookie returned by RegisterStateEventCallback */ - void UnregisterStatusEventCallback(/*in*/ uint64_t uiCookie) override; + void UnregisterStateEventCallback(/*in*/ uint64_t uiCookie) override; /** - * @brief Get current status of the connection + * @brief Get current state of the connection * - * @return Current sdv::ipc::EConnectStatus + * @return Current sdv::ipc::EConnectState */ - sdv::ipc::EConnectStatus GetStatus() const override; + sdv::ipc::EConnectState GetConnectState() const override; /** * @brief Destroy the object @@ -196,7 +197,7 @@ private: std::thread m_ConnectThread; std::atomic m_StopReceiveThread{false}; ///< bool variable to stop thread std::atomic m_StopConnectThread{false}; - std::atomic m_ConnectionStatus; ///< the status of the connection + std::atomic m_ConnectionState; ///< the state of the connection sdv::ipc::IDataReceiveCallback* m_pReceiver = nullptr; ///< Receiver to pass the messages if available sdv::ipc::IConnectEventCallback* m_pEvent = nullptr; ///< Event receiver @@ -217,7 +218,7 @@ private: /// @brief Server accept loop / client connect confirmation void ConnectWorker(); - /// @brief Start the RX thread (pre: status=connected, socket valid) + /// @brief Start the RX thread (pre: state=connected, socket valid) void StartReceiveThread_Unsafe(); /// @brief Stop worker threads and close all sockets cleanly @@ -451,7 +452,7 @@ private: * @brief Handle an incoming connect_term message * * Indicates that the peer requests immediate termination of the connection - * Sets status to disconnected and stops the RX loop + * Sets state to disconnected and stops the RX loop * * @param message SDV envelope containing the connect_term header */ @@ -503,8 +504,9 @@ private: */ bool ReadDataChunk(const CMessage& rMessage, uint32_t uiOffset, SDataContext& rsDataCtxt); - /// @brief Centralized status update (notifies waiters and callbacks) - void SetStatus(sdv::ipc::EConnectStatus status); + /// @brief Centralized state update (notifies waiters and callbacks) + void SetConnectState(sdv::ipc::EConnectState state); }; -#endif // CONNECTION_H +#endif // WIN_SOCKETS_CONNECTION_H +#endif \ No newline at end of file diff --git a/sdv_services/uds_win_tunnel/CMakeLists.txt b/sdv_services/uds_win_tunnel/CMakeLists.txt new file mode 100644 index 0000000..3493a2a --- /dev/null +++ b/sdv_services/uds_win_tunnel/CMakeLists.txt @@ -0,0 +1,40 @@ +#******************************************************************************* +# Copyright (c) 2025-2026 ZF Friedrichshafen AG +# +# This program and the accompanying materials are made available under the +# terms of the Apache License Version 2.0 which is available at +# https://www.apache.org/licenses/LICENSE-2.0 +# +# SPDX-License-Identifier: Apache-2.0 +#******************************************************************************* + +# Define project +project(uds_win_tunnel VERSION 1.0 LANGUAGES CXX) + +# Define target +add_library(uds_win_tunnel STATIC + channel_mgnt.cpp + connection.cpp +) + +target_link_libraries(uds_win_tunnel + PRIVATE + uds_win_sockets + Ws2_32.lib + ${CMAKE_THREAD_LIBS_INIT} +) + +target_include_directories(uds_win_tunnel + PRIVATE + ./include/ + ../uds_win_sockets/ +) + +set_target_properties(uds_win_tunnel PROPERTIES PREFIX "") +set_target_properties(uds_win_tunnel PROPERTIES SUFFIX ".sdv") + +# Build dependencies +add_dependencies(uds_win_tunnel CompileCoreIDL) + +# Appending the service in the service list +set(SDV_Service_List ${SDV_Service_List} uds_win_tunnel PARENT_SCOPE) diff --git a/sdv_services/uds_win_tunnel/channel_mgnt.cpp b/sdv_services/uds_win_tunnel/channel_mgnt.cpp new file mode 100644 index 0000000..bfd29e4 --- /dev/null +++ b/sdv_services/uds_win_tunnel/channel_mgnt.cpp @@ -0,0 +1,414 @@ +/******************************************************************************** +* Copyright (c) 2025-2026 ZF Friedrichshafen AG +* +* This program and the accompanying materials are made available under the +* terms of the Apache License Version 2.0 which is available at +* https://www.apache.org/licenses/LICENSE-2.0 +* +* SPDX-License-Identifier: Apache-2.0 +* +* Contributors: +* Denisa Ros - initial API and implementation +********************************************************************************/ +#ifdef _WIN32 +#include "channel_mgnt.h" + +#include "../../global/base64.h" +#include +#include + +#include + +#pragma push_macro("interface") +#undef interface + +#pragma push_macro("GetObject") +#undef GetObject + +#ifndef NOMINMAX +#define NOMINMAX +#endif + +#include +#include +#include +#include + +#include + +#pragma pop_macro("GetObject") +#pragma pop_macro("interface") + +extern int StartUpWinSock(); + +namespace +{ +/** + * @brief Parse a tunnel connect/config string and extract the path. + * + * Expected format: + * "proto=tunnel;path=;" + * + * Behavior: + * - If "proto=tunnel" missing -> false + * - If "path=" missing -> true and outPath.clear() + * + * @param[in] cs The connect/config string to parse. + * @param[out] outPath The extracted path, or empty if not found. + * @return true if parsing succeeded, false otherwise. + */ +static bool ParseTunnelPath(const std::string& cs, std::string& outPath) +{ + constexpr const char* protoKey = "proto=tunnel"; + constexpr const char* pathKey = "path="; + + if (cs.find(protoKey) == std::string::npos) + { + return false; + } + + const auto p = cs.find(pathKey); + if (p == std::string::npos) + { + outPath.clear(); + return true; + } + + const auto start = p + std::strlen(pathKey); + const auto end = cs.find(';', start); + if (end == std::string::npos) + { + outPath = cs.substr(start); + } + else + { + outPath = cs.substr(start, end - start); + } + return true; +} + +/** + * @brief Expands Windows environment variables in a string (e.g., %TEMP%). + * @param[in] in Input string possibly containing environment variables. + * @return String with environment variables expanded, or original if expansion fails. + */ +static std::string ExpandEnvVars(const std::string& in) +{ + if (in.find('%') == std::string::npos) + { + return in; + } + char buf[4096] = {}; + DWORD n = ExpandEnvironmentStringsA(in.c_str(), buf, static_cast(sizeof(buf))); + if (n > 0 && n < sizeof(buf)) + { + return std::string(buf); + } + return in; +} + +/** + * @brief Clamps a UDS path to the maximum allowed by SOCKADDR_UN. + * @param[in] p The input path. + * @return The clamped path. + */ +static std::string ClampUdsPath(const std::string& p) +{ + SOCKADDR_UN tmp{}; + constexpr auto kMax = sizeof(tmp.sun_path) - 1; + if (p.size() <= kMax) + { + return p; + } + return p.substr(0, kMax); +} + +// Only for logging – basename +/** + * @brief Normalizes a raw UDS path for Windows, extracting the basename and ensuring a default if empty. + * @param[in] raw The raw path string. + * @return The normalized basename, clamped to max length. + */ +static std::string NormalizeUdsPathForWindows(const std::string& raw) +{ + std::string p = ExpandEnvVars(raw); + const size_t pos = p.find_last_of("/\\"); + std::string base = (pos == std::string::npos) ? p : p.substr(pos + 1); + if (base.empty()) + { + base = "sdv_tunnel.sock"; + } + SDV_LOG_INFO("[AF_UNIX][Tunnel] Normalize raw='", raw, "' -> base='", base, "'"); + return ClampUdsPath(base); +} + +/** + * @brief Creates a short, safe UDS path in the Windows temp directory. + * @param[in] raw The raw path string. + * @return The full path in %TEMP%\sdv\, clamped to max length. + */ +static std::string MakeShortWinUdsPath(const std::string& raw) +{ + std::string p = ExpandEnvVars(raw); + const size_t pos = p.find_last_of("/\\"); + std::string base = (pos == std::string::npos) ? p : p.substr(pos + 1); + if (base.empty()) + { + base = "sdv_tunnel.sock"; + } + + std::string dir = ExpandEnvVars("%TEMP%\\sdv\\"); + CreateDirectoryA(dir.c_str(), nullptr); + const std::string full = dir + base; + + return ClampUdsPath(full); +} + +/** + * @brief Creates an AF_UNIX listen socket at the specified path. + * @param[in] rawPath The raw path for the socket. + * @return The created socket handle, or INVALID_SOCKET on failure. + */ +static SOCKET CreateUnixListenSocket(const std::string& rawPath) +{ + SOCKET s = socket(AF_UNIX, SOCK_STREAM, 0); + if (s == INVALID_SOCKET) + { + SDV_LOG_ERROR("[AF_UNIX][Tunnel] socket() FAIL (listen), WSA=", WSAGetLastError()); + return INVALID_SOCKET; + } + + std::string udsPath = MakeShortWinUdsPath(rawPath); + SOCKADDR_UN addr{}; + addr.sun_family = AF_UNIX; + strcpy_s(addr.sun_path, sizeof(addr.sun_path), udsPath.c_str()); + + const int addrlen = static_cast( + offsetof(SOCKADDR_UN, sun_path) + std::strlen(addr.sun_path) + 1); + + ::remove(udsPath.c_str()); + if (bind(s, reinterpret_cast(&addr), addrlen) == SOCKET_ERROR) + { + SDV_LOG_ERROR("[AF_UNIX][Tunnel] bind FAIL, WSA=", + WSAGetLastError(), ", path='", udsPath, "'"); + closesocket(s); + return INVALID_SOCKET; + } + if (listen(s, SOMAXCONN) == SOCKET_ERROR) + { + SDV_LOG_ERROR("[AF_UNIX][Tunnel] listen FAIL, WSA=", + WSAGetLastError(), ", path='", udsPath, "'"); + closesocket(s); + return INVALID_SOCKET; + } + + SDV_LOG_INFO("[AF_UNIX][Tunnel] bind+listen OK, path='", udsPath, "'"); + return s; +} + +/** + * @brief Connects to an AF_UNIX socket at the specified path, retrying until timeout. + * @param[in] rawPath The raw path to connect to. + * @param[in] totalTimeoutMs Total timeout in milliseconds. + * @param[in] retryDelayMs Delay between retries in milliseconds. + * @return The connected socket handle, or INVALID_SOCKET on failure. + */ +static SOCKET ConnectUnixSocket( + const std::string& rawPath, + uint32_t totalTimeoutMs, + uint32_t retryDelayMs) +{ + const std::string udsPath = MakeShortWinUdsPath(rawPath); + SOCKADDR_UN addr{}; + addr.sun_family = AF_UNIX; + strcpy_s(addr.sun_path, sizeof(addr.sun_path), udsPath.c_str()); + + const int addrlen = static_cast( + offsetof(SOCKADDR_UN, sun_path) + std::strlen(addr.sun_path) + 1); + + const auto deadline = std::chrono::steady_clock::now() + + std::chrono::milliseconds(totalTimeoutMs); + + int lastError = 0; + while (true) + { + SOCKET s = socket(AF_UNIX, SOCK_STREAM, 0); + if (s == INVALID_SOCKET) + { + lastError = WSAGetLastError(); + SDV_LOG_ERROR("[AF_UNIX][Tunnel] socket() FAIL (client), WSA=", lastError); + return INVALID_SOCKET; + } + + if (connect(s, reinterpret_cast(&addr), addrlen) == 0) + { + SDV_LOG_INFO("[AF_UNIX][Tunnel] connect OK, path='", udsPath, "'"); + return s; + } + + lastError = WSAGetLastError(); + closesocket(s); + + if (std::chrono::steady_clock::now() >= deadline) + { + SDV_LOG_ERROR("[AF_UNIX][Tunnel] connect TIMEOUT, last WSA=", + lastError, ", path='", udsPath, "'"); + return INVALID_SOCKET; + } + + std::this_thread::sleep_for(std::chrono::milliseconds(retryDelayMs)); + } +} + +} // anonymous namespace + +bool CSocketsTunnelChannelMgnt::OnInitialize() +{ + return true; +} + +void CSocketsTunnelChannelMgnt::OnShutdown() +{} + +// -------- Server bookkeeping (optional) -------- +void CSocketsTunnelChannelMgnt::OnServerClosed(const std::string& udsPath, CWinTunnelConnection* ptr) +{ + std::lock_guard lock(m_udsMtx); + auto it = m_udsServers.find(udsPath); + if (it != m_udsServers.end() && it->second.get() == ptr) + { + m_udsServers.erase(it); + } + m_udsServerClaimed.erase(udsPath); +} + +// -------- ICreateEndpoint -------- +sdv::ipc::SChannelEndpoint CSocketsTunnelChannelMgnt::CreateEndpoint(const sdv::u8string& cfgStr) +{ + sdv::ipc::SChannelEndpoint ep{}; + + if (StartUpWinSock() != 0) + { + SDV_LOG_ERROR("[AF_UNIX][Tunnel] WinSock startup failed in CreateEndpoint"); + return ep; + } + + // Optional TOML config: [IpcChannel] Path = "..." + std::string udsRaw; + if (!cfgStr.empty()) + { + //for toml file + bool isTOML = cfgStr.find('=') == std::string::npos; + if(isTOML) + { + sdv::toml::CTOMLParser cfg(cfgStr.c_str()); + auto pathNode = cfg.GetDirect("IpcChannel.Path"); + if (pathNode.GetType() == sdv::toml::ENodeType::node_string) + { + udsRaw = static_cast(pathNode.GetValue()); + } + } + + //for connect string + if (udsRaw.empty()) + { + const std::string s(cfgStr); + const std::string key = "path="; + auto pos = s.find(key); + if (pos != std::string::npos) + { + auto end = s.find(';', pos + key.size()); + if (end == std::string::npos) + udsRaw = s.substr(pos + key.size()); + else + udsRaw = s.substr(pos + key.size(), end - pos - key.size()); + } + } + } + if (udsRaw.empty()) + { + udsRaw = "%LOCALAPPDATA%/sdv/tunnel.sock"; + } + + std::string udsPathBase = NormalizeUdsPathForWindows(udsRaw); + SDV_LOG_INFO("[AF_UNIX][Tunnel] endpoint udsPath=", udsPathBase); + + SOCKET listenSocket = CreateUnixListenSocket(udsPathBase); + if (listenSocket == INVALID_SOCKET) + { + SDV_LOG_ERROR("[AF_UNIX][Tunnel] Failed to create listen socket for endpoint: ", udsPathBase); + return ep; + } + + auto serverTransport = std::make_shared( static_cast(listenSocket), true); + auto serverTunnel = std::make_shared(serverTransport, /*channelId*/ static_cast(0u)); + + { + std::lock_guard lock(m_udsMtx); + m_udsServers[udsPathBase] = serverTunnel; + m_udsServerClaimed.erase(udsPathBase); + } + + ep.pConnection = static_cast(serverTunnel.get()); + ep.ssConnectString = "proto=tunnel;role=server;path=" + udsPathBase + ";"; + + return ep; +} + +sdv::IInterfaceAccess* CSocketsTunnelChannelMgnt::Access(const sdv::u8string& cs) +{ + if (StartUpWinSock() != 0) + { + SDV_LOG_ERROR("[AF_UNIX][Tunnel] WinSock startup failed in Access()" ); + return nullptr; + } + + std::string connectStr = static_cast(cs); + std::string udsRaw; + if (!ParseTunnelPath(connectStr, udsRaw)) + { + SDV_LOG_ERROR("[AF_UNIX][Tunnel] Invalid tunnel connect string: ", connectStr); + return nullptr; + } + + if (udsRaw.empty()) + { + udsRaw = "%LOCALAPPDATA%/sdv/tunnel.sock"; + } + + std::string udsPathBase = NormalizeUdsPathForWindows(udsRaw); + SDV_LOG_INFO("[AF_UNIX][Tunnel] Access udsPath=", udsPathBase); + + const bool isServer = + (connectStr.find("role=server") != std::string::npos); + + { + std::lock_guard lock(m_udsMtx); + auto it = m_udsServers.find(udsPathBase); + if (isServer && it != m_udsServers.end() && it->second != nullptr) + { + if (!m_udsServerClaimed.count(udsPathBase)) + { + m_udsServerClaimed.insert(udsPathBase); + SDV_LOG_INFO("[AF_UNIX][Tunnel] Access -> RETURN SERVER for ", udsPathBase); + return it->second.get(); // Ownership: managed by m_udsServers (do not delete) + } + } + } + + // CLIENT: create AF_UNIX client socket and wrap it in a tunnel + SOCKET s = ConnectUnixSocket(udsPathBase, /*totalTimeoutMs*/ 5000, /*retryDelayMs*/ 50); + if (s == INVALID_SOCKET) + { + SDV_LOG_ERROR("[AF_UNIX][Tunnel] Failed to connect client socket for ", udsPathBase); + return nullptr; + } + + SDV_LOG_INFO("[AF_UNIX][Tunnel] Access -> CREATE CLIENT for ", udsPathBase); + auto clientTransport = std::make_shared(s, /*acceptRequired*/ false); + // Ownership: The returned pointer must be managed and deleted by the SDV framework via IObjectDestroy + auto* tunnelClient = new CWinTunnelConnection(clientTransport, /*channelId*/ 0u); + + return static_cast(tunnelClient); +} +#endif \ No newline at end of file diff --git a/sdv_services/uds_win_tunnel/channel_mgnt.h b/sdv_services/uds_win_tunnel/channel_mgnt.h new file mode 100644 index 0000000..89c48e6 --- /dev/null +++ b/sdv_services/uds_win_tunnel/channel_mgnt.h @@ -0,0 +1,106 @@ +/******************************************************************************** +* Copyright (c) 2025-2026 ZF Friedrichshafen AG +* +* This program and the accompanying materials are made available under the +* terms of the Apache License Version 2.0 which is available at +* https://www.apache.org/licenses/LICENSE-2.0 +* +* SPDX-License-Identifier: Apache-2.0 +* +* Contributors: +* Denisa Ros - initial API and implementation +********************************************************************************/ +#ifdef _WIN32 +#ifndef WIN_TUNNEL_CHANNEL_MGNT_H +#define WIN_TUNNEL_CHANNEL_MGNT_H + +#include +#include +#include "../sdv_services/uds_win_sockets/channel_mgnt.h" +#include "../sdv_services/uds_win_sockets/connection.h" +#include "connection.h" + +#include +#include +#include +#include +#include + +// Winsock headers are required for SOCKET / AF_UNIX / WSAStartup +// NOTE: The actual initialization is done via StartUpWinSock() +#include + +class CWinTunnelConnection; + +/** + * @class CSocketsTunnelChannelMgnt + * @brief IPC channel management class for Windows AF_UNIX tunnel communication. + * + * Similar to CSocketsChannelMgnt (proto=uds), but: + * - uses CWinTunnelConnection (tunnel wrapper) on top of CWinsockConnection + * - uses proto=tunnel in connect strings + * + * Provides creation and access to tunnel endpoints, manages server-side tunnel lifetimes, + * and integrates with the SDV object/component framework. + */ +class CSocketsTunnelChannelMgnt : + public sdv::CSdvObject, + public sdv::ipc::ICreateEndpoint, + public sdv::ipc::IChannelAccess +{ +public: + BEGIN_SDV_INTERFACE_MAP() + SDV_INTERFACE_ENTRY(sdv::ipc::IChannelAccess) + SDV_INTERFACE_ENTRY(sdv::ipc::ICreateEndpoint) + END_SDV_INTERFACE_MAP() + + DECLARE_OBJECT_CLASS_TYPE(sdv::EObjectType::system_object) + DECLARE_OBJECT_CLASS_NAME("WinTunnelChannelControl") + DECLARE_OBJECT_CLASS_ALIAS("LocalChannelControl") + DECLARE_DEFAULT_OBJECT_NAME("LocalChannelControl") + DECLARE_OBJECT_SINGLETON() + + virtual ~CSocketsTunnelChannelMgnt() = default; + + /** + * @brief Initialization event, called after object configuration was loaded. Overload of sdv::CSdvObject::OnInitialize. + * @return Returns 'true' when the initialization was successful, 'false' when not. + */ + virtual bool OnInitialize() override; + + /** + * @brief Shutdown the object. Overload of sdv::CSdvObject::OnShutdown. + */ + virtual void OnShutdown() override; + + /** + * @brief Creates a tunnel endpoint (server side) and returns endpoint info. + * @param[in] cfgStr Optional config string (TOML or connect string). + * @return The channel endpoint structure. + */ + sdv::ipc::SChannelEndpoint CreateEndpoint(const sdv::u8string& cfgStr) override; + + /** + * @brief Creates or accesses a connection object from the channel connect string. + * @param[in] cs The channel connect string. + * @return Pointer to connection access interface. + */ + sdv::IInterfaceAccess* Access(const sdv::u8string& cs) override; + + /** + * @brief Called by server tunnel when closing (bookkeeping). + * @param[in] udsPath The UDS path for the server. + * @param[in] ptr Pointer to the tunnel connection being closed. + */ + void OnServerClosed(const std::string& udsPath, CWinTunnelConnection* ptr); + +private: + std::mutex m_udsMtx; + std::map> m_udsServers; + std::set m_udsServerClaimed; +}; + +DEFINE_SDV_OBJECT(CSocketsTunnelChannelMgnt) + +#endif // ! defined WIN_TUNNEL_CHANNEL_MGNT_H +#endif \ No newline at end of file diff --git a/sdv_services/uds_win_tunnel/connection.cpp b/sdv_services/uds_win_tunnel/connection.cpp new file mode 100644 index 0000000..9aa27fa --- /dev/null +++ b/sdv_services/uds_win_tunnel/connection.cpp @@ -0,0 +1,226 @@ +/************************************************************ + * Copyright (c) 2025-2026 ZF Friedrichshafen AG + * + * This program and the accompanying materials are made available under the + * terms of the Apache License Version 2.0 which is available at + * https://www.apache.org/licenses/LICENSE-2.0 + * + * SPDX-License-Identifier: Apache-2.0 + * + * Contributors: + * Denisa Ros - initial API and implementation + ************************************************************/ + +#ifdef _WIN32 +#include "connection.h" + + +CWinTunnelConnection::CWinTunnelConnection( + std::shared_ptr transport, + uint16_t channelId) + : m_Transport(std::move(transport)) + , m_ChannelId(channelId) +{ + // No additional initialization required; acts as a thin wrapper. +} + +bool CWinTunnelConnection::SendData( + /*inout*/ sdv::sequence>& seqData) +{ + if (!m_Transport) + { + SDV_LOG_ERROR("[WinTunnel] SendData failed: transport is null"); + return false; + } + + // Build tunnel header buffer + sdv::pointer hdrBuf; + hdrBuf.resize(sizeof(STunnelHeader)); + + STunnelHeader hdr{}; + hdr.uiChannelId = m_ChannelId; // Logical channel for this connection + hdr.uiFlags = 0; // Reserved for future use + + std::memcpy(hdrBuf.get(), &hdr, sizeof(STunnelHeader)); + + // Compose new sequence: [header] + original payload chunks + sdv::sequence> seqWithHdr; + seqWithHdr.push_back(hdrBuf); + for (auto& chunk : seqData) + { + seqWithHdr.push_back(chunk); + } + + bool result = m_Transport->SendData(seqWithHdr); + if (!result) { + SDV_LOG_ERROR("[WinTunnel] SendData failed in underlying transport"); + } + return result; +} + +bool CWinTunnelConnection::AsyncConnect(/*in*/ sdv::IInterfaceAccess* pReceiver) +{ + if (!m_Transport) + { + SDV_LOG_ERROR("[WinTunnel] AsyncConnect failed: transport is null"); + return false; + } + + // Store upper-layer callbacks (safe for null) + { + std::lock_guard lock(m_CallbackMtx); + sdv::TInterfaceAccessPtr acc(pReceiver); + m_pUpperReceiver = acc.GetInterface(); + m_pUpperEvent = acc.GetInterface(); + } + + // Register this tunnel as the data/event receiver in the AF_UNIX transport. + bool result = m_Transport->AsyncConnect(this); + if (!result) { + SDV_LOG_ERROR("[WinTunnel] AsyncConnect failed in underlying transport"); + } + return result; +} + +bool CWinTunnelConnection::WaitForConnection(/*in*/ uint32_t uiWaitMs) +{ + if (!m_Transport) + { + SDV_LOG_ERROR("[WinTunnel] WaitForConnection failed: transport is null"); + return false; + } + return m_Transport->WaitForConnection(uiWaitMs); +} + +void CWinTunnelConnection::CancelWait() +{ + if (!m_Transport) + { + SDV_LOG_ERROR("[WinTunnel] CancelWait failed: transport is null"); + return; + } + m_Transport->CancelWait(); +} + +void CWinTunnelConnection::Disconnect() +{ + if (!m_Transport) + { + SDV_LOG_ERROR("[WinTunnel] Disconnect failed: transport is null"); + return; + } + + m_Transport->Disconnect(); + + // Clear upper-layer callbacks (thread-safe) + { + std::lock_guard lock(m_CallbackMtx); + m_pUpperReceiver = nullptr; + m_pUpperEvent = nullptr; + } +} + +uint64_t CWinTunnelConnection::RegisterStateEventCallback( + /*in*/ sdv::IInterfaceAccess* pEventCallback) +{ + if (!m_Transport) + { + SDV_LOG_ERROR("[WinTunnel] RegisterStateEventCallback failed: transport is null"); + return 0ULL; + } + + // Forward directly to underlying CWinsockConnection + return m_Transport->RegisterStateEventCallback(pEventCallback); +} + +void CWinTunnelConnection::UnregisterStateEventCallback( + /*in*/ uint64_t uiCookie) +{ + if (!m_Transport || uiCookie == 0ULL) + { + SDV_LOG_ERROR("[WinTunnel] UnregisterStateEventCallback failed: transport is null or cookie is 0"); + return; + } + + m_Transport->UnregisterStateEventCallback(uiCookie); +} + +sdv::ipc::EConnectState CWinTunnelConnection::GetConnectState() const +{ + if (!m_Transport) + { + return sdv::ipc::EConnectState::uninitialized; + } + return m_Transport->GetConnectState(); +} + +void CWinTunnelConnection::DestroyObject() +{ + Disconnect(); + std::lock_guard lock(m_CallbackMtx); + m_Transport.reset(); +} + +void CWinTunnelConnection::ReceiveData( + /*inout*/ sdv::sequence>& seqData) +{ + // Expect at least one chunk (the tunnel header) + if (seqData.empty()) + { + SDV_LOG_ERROR("[WinTunnel] ReceiveData: empty sequence"); + return; // nothing to do + } + + const auto& hdrChunk = seqData[0]; + if (hdrChunk.size() < sizeof(STunnelHeader)) + { + SDV_LOG_ERROR("[WinTunnel] ReceiveData: invalid tunnel header size"); + // Invalid tunnel frame; drop it for now (could set communication_error) + return; + } + + STunnelHeader hdr{}; + std::memcpy(&hdr, hdrChunk.get(), sizeof(STunnelHeader)); + + + // TODO: use channelId for multiplexing later + + // Build payload-only sequence: drop header chunk, keep others + sdv::sequence> payload; + for (size_t i = 1; i < seqData.size(); ++i) + { + payload.push_back(seqData[i]); + } + + if (m_pUpperReceiver) + { + try { + m_pUpperReceiver->ReceiveData(payload); // header stripped + } catch (...) { + SDV_LOG_ERROR("[WinTunnel] Exception in upper receiver's ReceiveData"); + } + } +} + +void CWinTunnelConnection::SetConnectState(sdv::ipc::EConnectState state) +{ + sdv::ipc::IConnectEventCallback* upper = nullptr; + { + std::lock_guard lock(m_CallbackMtx); + upper = m_pUpperEvent; + } + + if (upper) + { + try + { + upper->SetConnectState(state); + } + catch (...) + { + SDV_LOG_ERROR("[WinTunnel] Exception in upper event callback's SetConnectState"); + // Never let user callback crash the transport. + } + } +} +#endif \ No newline at end of file diff --git a/sdv_services/uds_win_tunnel/connection.h b/sdv_services/uds_win_tunnel/connection.h new file mode 100644 index 0000000..5b0fcdd --- /dev/null +++ b/sdv_services/uds_win_tunnel/connection.h @@ -0,0 +1,168 @@ +/************************************************************ + * Copyright (c) 2025-2026 ZF Friedrichshafen AG + * + * This program and the accompanying materials are made available under the + * terms of the Apache License Version 2.0 which is available at + * https://www.apache.org/licenses/LICENSE-2.0 + * + * SPDX-License-Identifier: Apache-2.0 + * + * Contributors: + * Denisa Ros - initial API and implementation + ************************************************************/ +#ifdef _WIN32 +#ifndef UDS_WIN_TUNNEL_CONNECTION_H +#define UDS_WIN_TUNNEL_CONNECTION_H + +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#include "../sdv_services/uds_win_sockets/connection.h" // existing AF_UNIX transport: CWinsockConnection + + +/** + * @brief Logical tunnel connection on top of a shared Windows AF_UNIX transport. + * + * This class does NOT create sockets by itself. It wraps an existing + * CWinsockConnection (Winsock AF_UNIX) and adds: + * - tunnel header (channelId, flags) + * - (later) demultiplexing of incoming payloads per logical channel. + */ +class CWinTunnelConnection : + public sdv::IInterfaceAccess, + public sdv::IObjectDestroy, + public sdv::ipc::IDataSend, + public sdv::ipc::IConnect, + public sdv::ipc::IDataReceiveCallback, + public sdv::ipc::IConnectEventCallback +{ +public: + + /** + * @struct STunnelHeader + * @brief Header prepended to each tunneled SDV message for logical channel identification and flags. + */ + struct STunnelHeader + { + uint16_t uiChannelId; ///< Logical channel ID (IPC_x / REMOTE_IPC_x) + uint16_t uiFlags; ///< Reserved for future use (QoS, direction, etc.) + }; + + /** + * @brief Construct a tunnel wrapper over an existing AF_UNIX transport. + * @param[in] transport Shared pointer to the underlying AF_UNIX transport. + * @param[in] channelId Logical channel ID for this tunnel instance. + */ + explicit CWinTunnelConnection( + std::shared_ptr transport, + uint16_t channelId); + + /** + * @brief Destructor. + */ + virtual ~CWinTunnelConnection() = default; + + BEGIN_SDV_INTERFACE_MAP() + SDV_INTERFACE_ENTRY(sdv::ipc::IDataSend) + SDV_INTERFACE_ENTRY(sdv::ipc::IConnect) + SDV_INTERFACE_ENTRY(sdv::ipc::IDataReceiveCallback) + SDV_INTERFACE_ENTRY(sdv::ipc::IConnectEventCallback) + SDV_INTERFACE_ENTRY(sdv::IObjectDestroy) + END_SDV_INTERFACE_MAP() + + // ---------- IDataSend ---------- + /** + * @brief Send a sequence of buffers via the tunnel. + * @param[in,out] seqData Sequence of message buffers (may be modified by callee). + * @return true on successful send, false otherwise. + */ + bool SendData(/*inout*/ sdv::sequence>& seqData) override; + + // ---------- IConnect ---------- + /** + * @brief Start asynchronous connect and register this object as receiver. + * @param[in] pReceiver Pointer to callback interface for data and state notifications. + * @return true if connect started, false otherwise. + */ + bool AsyncConnect(/*in*/ sdv::IInterfaceAccess* pReceiver) override; + + /** + * @brief Wait until the underlying transport becomes 'connected'. + * @param[in] uiWaitMs Timeout in milliseconds to wait. + * @return true if connection established, false on timeout or error. + */ + bool WaitForConnection(/*in*/ uint32_t uiWaitMs) override; + + /** + * @brief Cancel any pending connect or wait operation. + */ + void CancelWait() override; + + /** + * @brief Disconnect the tunnel and underlying transport. + */ + void Disconnect() override; + + /** + * @brief Register a state event callback (forwards to transport). + * @param[in] pEventCallback Pointer to event callback interface. + * @return Registration cookie (nonzero) or 0 on failure. + */ + uint64_t RegisterStateEventCallback(/*in*/ sdv::IInterfaceAccess* pEventCallback) override; + + /** + * @brief Unregister a previously registered state event callback. + * @param[in] uiCookie Registration cookie returned by RegisterStateEventCallback. + */ + void UnregisterStateEventCallback(/*in*/ uint64_t uiCookie) override; + /** + * @brief Get the current state from the underlying transport. + * @return The current connection state. + */ + sdv::ipc::EConnectState GetConnectState() const override; + + // ---------- IObjectDestroy ---------- + /** + * @brief Release and clean up all resources associated with this object. + */ + void DestroyObject() override; + + // ---------- IDataReceiveCallback ---------- + /** + * @brief Receive data from the underlying AF_UNIX transport. + * @param[in,out] seqData Sequence of received message buffers (header chunk is removed by this call). + */ + void ReceiveData(/*inout*/ sdv::sequence>& seqData) override; + + // ---------- IConnectEventCallback ---------- + /** + * @brief Forward state changes from the underlying transport to the upper layer. + * @param[in] state New connection state. + */ + void SetConnectState(sdv::ipc::EConnectState state) override; + + // Helpers + void SetChannelId(uint16_t channelId) { m_ChannelId = channelId; } + uint16_t GetChannelId() const noexcept { return m_ChannelId; } + +private: + std::shared_ptr m_Transport; ///< shared physical tunnel port + uint16_t m_ChannelId{0}; ///< default logical channel id + + // Upper layer callbacks (original VAPI receiver) + sdv::ipc::IDataReceiveCallback* m_pUpperReceiver{nullptr}; + sdv::ipc::IConnectEventCallback* m_pUpperEvent{nullptr}; + mutable std::mutex m_CallbackMtx; +}; + +#endif // UDS_WIN_TUNNEL_CONNECTION_H +#endif \ No newline at end of file diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index b3da6e0..cc93e23 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -85,12 +85,10 @@ add_subdirectory(unit_tests/sdv_control) add_subdirectory(unit_tests/sdv_macro_test) add_subdirectory(unit_tests/install_package_composer) add_subdirectory(unit_tests/path_match) -if(UNIX) add_subdirectory(unit_tests/unix_sockets) -endif() -if(WIN32) +add_subdirectory(unit_tests/unix_tunnel) add_subdirectory(unit_tests/win_sockets) -endif() +add_subdirectory(unit_tests/win_tunnel) add_subdirectory(component_tests/config) add_subdirectory(component_tests/logger) add_subdirectory(component_tests/data_dispatch_service) diff --git a/tests/unit_tests/ipc_com/ipc_com.cpp b/tests/unit_tests/ipc_com/ipc_com.cpp index 950bd27..8c2e643 100644 --- a/tests/unit_tests/ipc_com/ipc_com.cpp +++ b/tests/unit_tests/ipc_com/ipc_com.cpp @@ -1085,9 +1085,9 @@ ViewFilter = "Fatal" // Disconnect from the client sdv::ipc::IConnect* pClientConnect = ptrClientEndpoint.GetInterface(); ASSERT_NE(pClientConnect, nullptr); - EXPECT_EQ(pClientConnect->GetStatus(), sdv::ipc::EConnectStatus::connected); + EXPECT_EQ(pClientConnect->GetConnectState(), sdv::ipc::EConnectState::connected); control.RemoveConnection(tConnectionID); - EXPECT_EQ(pClientConnect->GetStatus(), sdv::ipc::EConnectStatus::disconnected); + EXPECT_EQ(pClientConnect->GetConnectState(), sdv::ipc::EConnectState::disconnected); ptrClientEndpoint.Clear(); // Lifetime taken over by communication control. // Create another client endpoint @@ -1155,9 +1155,9 @@ ViewFilter = "Fatal" // Disconnect from the client sdv::ipc::IConnect* pClientConnect = ptrClientEndpoint.GetInterface(); ASSERT_NE(pClientConnect, nullptr); - EXPECT_EQ(pClientConnect->GetStatus(), sdv::ipc::EConnectStatus::connected); + EXPECT_EQ(pClientConnect->GetConnectState(), sdv::ipc::EConnectState::connected); control.RemoveConnection(tConnectionID); - EXPECT_EQ(pClientConnect->GetStatus(), sdv::ipc::EConnectStatus::disconnected); + EXPECT_EQ(pClientConnect->GetConnectState(), sdv::ipc::EConnectState::disconnected); ptrClientEndpoint.Clear(); // Lifetime taken over by communication control. // Create another client endpoint diff --git a/tests/unit_tests/shared_mem/app_connect.cpp b/tests/unit_tests/shared_mem/app_connect.cpp index 12e6187..ed72b5e 100644 --- a/tests/unit_tests/shared_mem/app_connect.cpp +++ b/tests/unit_tests/shared_mem/app_connect.cpp @@ -117,14 +117,14 @@ public: } /** - * @brief Set the current status. Overload of sdv::ipc::IConnectEventCallback::SetStatus. - * @param[in] eConnectStatus The connection status. + * @brief Set the current state. Overload of sdv::ipc::IConnectEventCallback::SetConnectState. + * @param[in] eConnectState The connection state. */ - virtual void SetStatus(/*in*/ sdv::ipc::EConnectStatus eConnectStatus) override + virtual void SetConnectState(/*in*/ sdv::ipc::EConnectState eConnectState) override { - switch (eConnectStatus) + switch (eConnectState) { - case sdv::ipc::EConnectStatus::disconnected: + case sdv::ipc::EConnectState::disconnected: // Disconnect only when connected before if (m_bConnected) { @@ -133,7 +133,7 @@ public: m_cvDisconnect.notify_all(); } break; - case sdv::ipc::EConnectStatus::disconnected_forced: + case sdv::ipc::EConnectState::disconnected_forced: // Disconnect only when connected before if (m_bConnected) { @@ -142,7 +142,7 @@ public: m_cvDisconnect.notify_all(); } break; - case sdv::ipc::EConnectStatus::connected: + case sdv::ipc::EConnectState::connected: m_bConnected = true; break; default: @@ -339,11 +339,11 @@ extern "C" int main(int argc, char* argv[]) if (!ptrControlConnection) return -12; pControlConnect = ptrControlConnection.GetInterface(); if (!pControlConnect) return -13; - uiControlEventCookie = pControlConnect->RegisterStatusEventCallback(&receiverControl); + uiControlEventCookie = pControlConnect->RegisterStateEventCallback(&receiverControl); if (!uiControlEventCookie) return -20; if (!pControlConnect->AsyncConnect(&receiverControl)) return -14; if (!pControlConnect->WaitForConnection(250)) return -5; // Note: Connection should be possible within 250ms. - if (pControlConnect->GetStatus() != sdv::ipc::EConnectStatus::connected) return -15; + if (pControlConnect->GetConnectState() != sdv::ipc::EConnectState::connected) return -15; TRACE(bServer ? "Server" : "Client", ": Connected to control channel..."); } @@ -393,11 +393,11 @@ Size = 1024000 sdv::ipc::IConnect* pDataConnect = ptrDataConnection.GetInterface(); uint64_t uiDataEventCookie = 0; if (!pDataConnect) return -3; - uiDataEventCookie = pDataConnect->RegisterStatusEventCallback(&receiverData); + uiDataEventCookie = pDataConnect->RegisterStateEventCallback(&receiverData); if (!uiDataEventCookie) return -21; if (!pDataConnect->AsyncConnect(&receiverData)) return -4; if (!pDataConnect->WaitForConnection(10000)) return -5; // Note: Connection should be possible within 10000ms. - if (pDataConnect->GetStatus() != sdv::ipc::EConnectStatus::connected) return -5; + if (pDataConnect->GetConnectState() != sdv::ipc::EConnectState::connected) return -5; TRACE("Connected to data channel... waiting for data exchange"); @@ -430,11 +430,11 @@ Size = 1024000 } // Initiate shutdown - if (pDataConnect && uiDataEventCookie) pDataConnect->UnregisterStatusEventCallback(uiDataEventCookie); + if (pDataConnect && uiDataEventCookie) pDataConnect->UnregisterStateEventCallback(uiDataEventCookie); ptrDataConnection.Clear(); mgntDataMgntChannel.Shutdown(); if (mgntDataMgntChannel.GetObjectState() != sdv::EObjectState::destruction_pending) return -6; - if (pControlConnect && uiControlEventCookie) pControlConnect->UnregisterStatusEventCallback(uiControlEventCookie); + if (pControlConnect && uiControlEventCookie) pControlConnect->UnregisterStateEventCallback(uiControlEventCookie); ptrControlConnection.Clear(); mgntControlMgntChannel.Shutdown(); if (mgntControlMgntChannel.GetObjectState() != sdv::EObjectState::destruction_pending) return -16; diff --git a/tests/unit_tests/shared_mem/shared_mem_connect.cpp b/tests/unit_tests/shared_mem/shared_mem_connect.cpp index 3e6ea57..6277f2c 100644 --- a/tests/unit_tests/shared_mem/shared_mem_connect.cpp +++ b/tests/unit_tests/shared_mem/shared_mem_connect.cpp @@ -89,33 +89,33 @@ public: } /** - * @brief Set the current status. Overload of sdv::ipc::IConnectEventCallback::SetStatus. - * @param[in] eConnectStatus The connection status. + * @brief Set the current state. Overload of sdv::ipc::IConnectEventCallback::SetConnectState. + * @param[in] eConnectState The connection state. */ - virtual void SetStatus(/*in*/ sdv::ipc::EConnectStatus eConnectStatus) override + virtual void SetConnectState(/*in*/ sdv::ipc::EConnectState eConnectState) override { - switch (eConnectStatus) + switch (eConnectState) { - case sdv::ipc::EConnectStatus::connection_error: + case sdv::ipc::EConnectState::connection_error: m_bConnectError = true; break; - case sdv::ipc::EConnectStatus::communication_error: + case sdv::ipc::EConnectState::communication_error: m_bCommError = true; break; - case sdv::ipc::EConnectStatus::disconnected_forced: + case sdv::ipc::EConnectState::disconnected_forced: m_bForcedDisconnect = true; break; default: break; } - m_eStatus = eConnectStatus; + m_eConnectState = eConnectState; } /** - * @brief Return the received connection status. - * @return The received connection status. + * @brief Return the received connection state. + * @return The received connection state. */ - sdv::ipc::EConnectStatus GetReceivedStatus() const { return m_eStatus; } + sdv::ipc::EConnectState GetReceivedState() const { return m_eConnectState; } /** * @brief Has a connection error occurred? @@ -136,9 +136,9 @@ public: bool ForcedDisconnectOccurred() const { return m_bForcedDisconnect; } /** - * @brief Reset the received status event flags. + * @brief Reset the received state event flags. */ - void ResetStatusEvents() + void ResetStateEvents() { m_bConnectError = false; m_bCommError = false; @@ -182,7 +182,7 @@ private: sdv::ipc::IDataSend* m_pSend = nullptr; ///< Send interface to implement repeating function. mutable std::mutex m_mtxData; ///< Protect data access. sdv::sequence> m_seqDataCopy; ///< Copy of the data. - sdv::ipc::EConnectStatus m_eStatus = sdv::ipc::EConnectStatus::uninitialized; ///< Current received status. + sdv::ipc::EConnectState m_eConnectState = sdv::ipc::EConnectState::uninitialized; ///< Current received state. bool m_bConnectError = false; ///< Connection error ocurred. bool m_bCommError = false; ///< Communication error occurred. bool m_bForcedDisconnect = false; ///< Force disconnect. @@ -355,8 +355,8 @@ TEST(SharedMemChannelService, WaitForConnection) // Wait for connection with timeout EXPECT_FALSE(pConnection->WaitForConnection(100)); - EXPECT_TRUE(pConnection->GetStatus() == sdv::ipc::EConnectStatus::initialized || - pConnection->GetStatus() == sdv::ipc::EConnectStatus::connecting); + EXPECT_TRUE(pConnection->GetConnectState() == sdv::ipc::EConnectState::initialized || + pConnection->GetConnectState() == sdv::ipc::EConnectState::connecting); // Wait for connection for infinite period with cancel. std::thread threadCancelWait([&]() @@ -366,8 +366,8 @@ TEST(SharedMemChannelService, WaitForConnection) }); EXPECT_FALSE(pConnection->WaitForConnection(0xffffffff)); // Note: wait indefinitely. Cancel get called in 500ms. threadCancelWait.join(); - EXPECT_TRUE(pConnection->GetStatus() == sdv::ipc::EConnectStatus::initialized || - pConnection->GetStatus() == sdv::ipc::EConnectStatus::connecting); + EXPECT_TRUE(pConnection->GetConnectState() == sdv::ipc::EConnectState::initialized || + pConnection->GetConnectState() == sdv::ipc::EConnectState::connecting); EXPECT_NO_THROW(ptrConnection.Clear()); @@ -427,8 +427,8 @@ TEST(SharedMemChannelService, AsyncConnect) ASSERT_NE(pServerConnect, nullptr); EXPECT_TRUE(pServerConnect->AsyncConnect(&receiverServer)); EXPECT_FALSE(pServerConnect->WaitForConnection(25)); // Note: 25ms will not get a connection. - EXPECT_TRUE(pServerConnect->GetStatus() == sdv::ipc::EConnectStatus::initialized || - pServerConnect->GetStatus() == sdv::ipc::EConnectStatus::connecting); + EXPECT_TRUE(pServerConnect->GetConnectState() == sdv::ipc::EConnectState::initialized || + pServerConnect->GetConnectState() == sdv::ipc::EConnectState::connecting); // Try send; should fail, since not connected EXPECT_FALSE(pServerSend->SendData(seqPattern)); @@ -439,8 +439,8 @@ TEST(SharedMemChannelService, AsyncConnect) EXPECT_TRUE(pClientConnection->AsyncConnect(&receiverClient)); EXPECT_TRUE(pClientConnection->WaitForConnection(2000)); // Note: Connection should be possible within 2000ms. EXPECT_TRUE(pServerConnect->WaitForConnection(1000)); // Note: 1000ms to also receive the connection at the server. - EXPECT_EQ(pClientConnection->GetStatus(), sdv::ipc::EConnectStatus::connected); - EXPECT_EQ(pServerConnect->GetStatus(), sdv::ipc::EConnectStatus::connected); + EXPECT_EQ(pClientConnection->GetConnectState(), sdv::ipc::EConnectState::connected); + EXPECT_EQ(pServerConnect->GetConnectState(), sdv::ipc::EConnectState::connected); // Try send; should succeed, since connected EXPECT_TRUE(pServerSend->SendData(seqPattern)); @@ -520,39 +520,39 @@ TEST(SharedMemChannelService, EstablishConnectionEvents) EXPECT_FALSE(receiverServer.CommunicationbErrorOccurred()); EXPECT_FALSE(pServerSend->SendData(seqPattern)); EXPECT_FALSE(receiverServer.CommunicationbErrorOccurred()); // No events registered yet... only after a call to AsyncConnect. - receiverServer.ResetStatusEvents(); + receiverServer.ResetStateEvents(); // Establish the server connection sdv::ipc::IConnect* pServerConnect = ptrServerConnection.GetInterface(); ASSERT_NE(pServerConnect, nullptr); - EXPECT_NE(pServerConnect->RegisterStatusEventCallback(&receiverServer), 0); + EXPECT_NE(pServerConnect->RegisterStateEventCallback(&receiverServer), 0); EXPECT_TRUE(pServerConnect->AsyncConnect(&receiverServer)); EXPECT_FALSE(pServerConnect->WaitForConnection(25)); // Note: 25ms will not get a connection. - EXPECT_TRUE(pServerConnect->GetStatus() == sdv::ipc::EConnectStatus::initialized || - pServerConnect->GetStatus() == sdv::ipc::EConnectStatus::connecting); - EXPECT_TRUE(receiverServer.GetReceivedStatus() == sdv::ipc::EConnectStatus::initialized || - receiverServer.GetReceivedStatus() == sdv::ipc::EConnectStatus::connecting); + EXPECT_TRUE(pServerConnect->GetConnectState() == sdv::ipc::EConnectState::initialized || + pServerConnect->GetConnectState() == sdv::ipc::EConnectState::connecting); + EXPECT_TRUE(receiverServer.GetReceivedState() == sdv::ipc::EConnectState::initialized || + receiverServer.GetReceivedState() == sdv::ipc::EConnectState::connecting); EXPECT_FALSE(receiverServer.ConnectionErrorOccurred()); // Try send; should fail, since not connected EXPECT_FALSE(receiverServer.CommunicationbErrorOccurred()); EXPECT_FALSE(pServerSend->SendData(seqPattern)); EXPECT_TRUE(receiverServer.CommunicationbErrorOccurred()); - receiverServer.ResetStatusEvents(); + receiverServer.ResetStateEvents(); // Establish the client connection sdv::ipc::IConnect* pClientConnection = ptrClientConnection.GetInterface(); ASSERT_NE(pClientConnection, nullptr); - EXPECT_NE(pClientConnection->RegisterStatusEventCallback(&receiverClient), 0); + EXPECT_NE(pClientConnection->RegisterStateEventCallback(&receiverClient), 0); EXPECT_TRUE(pClientConnection->AsyncConnect(&receiverClient)); EXPECT_TRUE(pClientConnection->WaitForConnection(2000)); // Note: Connection should be possible within 2000ms. EXPECT_TRUE(pServerConnect->WaitForConnection(1000)); // Note: 1000ms to also receive the connection at the server. - EXPECT_EQ(pClientConnection->GetStatus(), sdv::ipc::EConnectStatus::connected); - EXPECT_EQ(pServerConnect->GetStatus(), sdv::ipc::EConnectStatus::connected); + EXPECT_EQ(pClientConnection->GetConnectState(), sdv::ipc::EConnectState::connected); + EXPECT_EQ(pServerConnect->GetConnectState(), sdv::ipc::EConnectState::connected); EXPECT_FALSE(receiverServer.ConnectionErrorOccurred()); EXPECT_FALSE(receiverClient.ConnectionErrorOccurred()); - EXPECT_EQ(receiverServer.GetReceivedStatus(), sdv::ipc::EConnectStatus::connected); - EXPECT_EQ(receiverClient.GetReceivedStatus(), sdv::ipc::EConnectStatus::connected); + EXPECT_EQ(receiverServer.GetReceivedState(), sdv::ipc::EConnectState::connected); + EXPECT_EQ(receiverClient.GetReceivedState(), sdv::ipc::EConnectState::connected); // Try send; should succeed, since connected EXPECT_TRUE(pServerSend->SendData(seqPattern)); @@ -621,8 +621,8 @@ TEST(SharedMemChannelService, EstablishReconnect) ASSERT_NE(pServerConnect, nullptr); EXPECT_TRUE(pServerConnect->AsyncConnect(&receiverServer)); EXPECT_FALSE(pServerConnect->WaitForConnection(25)); // Note: 25ms will not get a connection. - EXPECT_TRUE(pServerConnect->GetStatus() == sdv::ipc::EConnectStatus::initialized || - pServerConnect->GetStatus() == sdv::ipc::EConnectStatus::connecting); + EXPECT_TRUE(pServerConnect->GetConnectState() == sdv::ipc::EConnectState::initialized || + pServerConnect->GetConnectState() == sdv::ipc::EConnectState::connecting); std::cout << "Connect client 1..." << std::endl; // Establish the client1 connection @@ -631,8 +631,8 @@ TEST(SharedMemChannelService, EstablishReconnect) EXPECT_TRUE(pClientConnection->AsyncConnect(&receiverClient1)); EXPECT_TRUE(pClientConnection->WaitForConnection(2000)); // Note: Connection should be possible within 2000ms. EXPECT_TRUE(pServerConnect->WaitForConnection(1000)); // Note: 1000ms to also receive the connection at the server. - EXPECT_EQ(pClientConnection->GetStatus(), sdv::ipc::EConnectStatus::connected); - EXPECT_EQ(pServerConnect->GetStatus(), sdv::ipc::EConnectStatus::connected); + EXPECT_EQ(pClientConnection->GetConnectState(), sdv::ipc::EConnectState::connected); + EXPECT_EQ(pServerConnect->GetConnectState(), sdv::ipc::EConnectState::connected); std::cout << "Disconnect client 1..." << std::endl; @@ -640,10 +640,10 @@ TEST(SharedMemChannelService, EstablishReconnect) EXPECT_NO_THROW(ptrClient1Connection.Clear()); std::this_thread::sleep_for(std::chrono::milliseconds(10)); // Note: Handle connection terminate. EXPECT_NO_THROW(mgntClient1.Shutdown()); - EXPECT_TRUE(pServerConnect->GetStatus() == sdv::ipc::EConnectStatus::disconnected || - pServerConnect->GetStatus() == sdv::ipc::EConnectStatus::connecting); + EXPECT_TRUE(pServerConnect->GetConnectState() == sdv::ipc::EConnectState::disconnected || + pServerConnect->GetConnectState() == sdv::ipc::EConnectState::connecting); - std::cout << "Connect client 2... (eStatus = " << (uint32_t) pServerConnect->GetStatus() << ")" << std::endl; + std::cout << "Connect client 2... (eConnectState = " << (uint32_t) pServerConnect->GetConnectState() << ")" << std::endl; // Establish the client2 connection sdv::TObjectPtr ptrClient2Connection = mgntClient2.Access(sChannelEndpoint.ssConnectString); @@ -653,8 +653,8 @@ TEST(SharedMemChannelService, EstablishReconnect) EXPECT_TRUE(pClientConnection->AsyncConnect(&receiverClient2)); EXPECT_TRUE(pClientConnection->WaitForConnection(2000)); // Note: Connection should be possible within 2000ms. EXPECT_TRUE(pServerConnect->WaitForConnection(1000)); // Note: 1000ms to also receive the connection at the server. - EXPECT_EQ(pClientConnection->GetStatus(), sdv::ipc::EConnectStatus::connected); - EXPECT_EQ(pServerConnect->GetStatus(), sdv::ipc::EConnectStatus::connected); + EXPECT_EQ(pClientConnection->GetConnectState(), sdv::ipc::EConnectState::connected); + EXPECT_EQ(pServerConnect->GetConnectState(), sdv::ipc::EConnectState::connected); std::cout << "Disconnect client 2..." << std::endl; @@ -662,8 +662,8 @@ TEST(SharedMemChannelService, EstablishReconnect) EXPECT_NO_THROW(ptrClient2Connection.Clear()); std::this_thread::sleep_for(std::chrono::milliseconds(10)); // Note: Handle connection terminate EXPECT_NO_THROW(mgntClient2.Shutdown()); - EXPECT_TRUE(pServerConnect->GetStatus() == sdv::ipc::EConnectStatus::disconnected || - pServerConnect->GetStatus() == sdv::ipc::EConnectStatus::connecting); + EXPECT_TRUE(pServerConnect->GetConnectState() == sdv::ipc::EConnectState::disconnected || + pServerConnect->GetConnectState() == sdv::ipc::EConnectState::connecting); std::cout << "Shutdown server..." << std::endl; @@ -702,13 +702,13 @@ TEST(SharedMemChannelService, EstablishReconnectEvents) // Establish the server connection sdv::ipc::IConnect* pServerConnect = ptrServerConnection.GetInterface(); ASSERT_NE(pServerConnect, nullptr); - EXPECT_NE(pServerConnect->RegisterStatusEventCallback(&receiverServer), 0); + EXPECT_NE(pServerConnect->RegisterStateEventCallback(&receiverServer), 0); EXPECT_TRUE(pServerConnect->AsyncConnect(&receiverServer)); EXPECT_FALSE(pServerConnect->WaitForConnection(25)); // Note: 25ms will not get a connection. - EXPECT_TRUE(pServerConnect->GetStatus() == sdv::ipc::EConnectStatus::initialized || - pServerConnect->GetStatus() == sdv::ipc::EConnectStatus::connecting); - EXPECT_TRUE(receiverServer.GetReceivedStatus() == sdv::ipc::EConnectStatus::initialized || - receiverServer.GetReceivedStatus() == sdv::ipc::EConnectStatus::connecting); + EXPECT_TRUE(pServerConnect->GetConnectState() == sdv::ipc::EConnectState::initialized || + pServerConnect->GetConnectState() == sdv::ipc::EConnectState::connecting); + EXPECT_TRUE(receiverServer.GetReceivedState() == sdv::ipc::EConnectState::initialized || + receiverServer.GetReceivedState() == sdv::ipc::EConnectState::connecting); EXPECT_FALSE(receiverServer.ConnectionErrorOccurred()); std::cout << "Connect client 1..." << std::endl; @@ -716,16 +716,16 @@ TEST(SharedMemChannelService, EstablishReconnectEvents) // Establish the client1 connection sdv::ipc::IConnect* pClientConnection = ptrClient1Connection.GetInterface(); ASSERT_NE(pClientConnection, nullptr); - EXPECT_NE(pClientConnection->RegisterStatusEventCallback(&receiverClient1), 0); + EXPECT_NE(pClientConnection->RegisterStateEventCallback(&receiverClient1), 0); EXPECT_TRUE(pClientConnection->AsyncConnect(&receiverClient1)); EXPECT_TRUE(pClientConnection->WaitForConnection(2000)); // Note: Connection should be possible within 2000ms. EXPECT_TRUE(pServerConnect->WaitForConnection(1000)); // Note: 1000ms to also receive the connection at the server. - EXPECT_EQ(pClientConnection->GetStatus(), sdv::ipc::EConnectStatus::connected); - EXPECT_EQ(pServerConnect->GetStatus(), sdv::ipc::EConnectStatus::connected); + EXPECT_EQ(pClientConnection->GetConnectState(), sdv::ipc::EConnectState::connected); + EXPECT_EQ(pServerConnect->GetConnectState(), sdv::ipc::EConnectState::connected); EXPECT_FALSE(receiverServer.ConnectionErrorOccurred()); EXPECT_FALSE(receiverClient1.ConnectionErrorOccurred()); - EXPECT_EQ(receiverServer.GetReceivedStatus(), sdv::ipc::EConnectStatus::connected); - EXPECT_EQ(receiverClient1.GetReceivedStatus(), sdv::ipc::EConnectStatus::connected); + EXPECT_EQ(receiverServer.GetReceivedState(), sdv::ipc::EConnectState::connected); + EXPECT_EQ(receiverClient1.GetReceivedState(), sdv::ipc::EConnectState::connected); std::cout << "Disconnect client 1..." << std::endl; @@ -733,10 +733,10 @@ TEST(SharedMemChannelService, EstablishReconnectEvents) EXPECT_NO_THROW(ptrClient1Connection.Clear()); std::this_thread::sleep_for(std::chrono::milliseconds(10)); // Note: Handle connection terminate. EXPECT_NO_THROW(mgntClient1.Shutdown()); - EXPECT_TRUE(pServerConnect->GetStatus() == sdv::ipc::EConnectStatus::disconnected || - pServerConnect->GetStatus() == sdv::ipc::EConnectStatus::connecting); - EXPECT_TRUE(receiverServer.GetReceivedStatus() == sdv::ipc::EConnectStatus::disconnected || - receiverServer.GetReceivedStatus() == sdv::ipc::EConnectStatus::connecting); + EXPECT_TRUE(pServerConnect->GetConnectState() == sdv::ipc::EConnectState::disconnected || + pServerConnect->GetConnectState() == sdv::ipc::EConnectState::connecting); + EXPECT_TRUE(receiverServer.GetReceivedState() == sdv::ipc::EConnectState::disconnected || + receiverServer.GetReceivedState() == sdv::ipc::EConnectState::connecting); EXPECT_FALSE(receiverServer.ConnectionErrorOccurred()); std::cout << "Connect client 2..." << std::endl; @@ -746,16 +746,16 @@ TEST(SharedMemChannelService, EstablishReconnectEvents) EXPECT_TRUE(ptrClient2Connection); pClientConnection = ptrClient2Connection.GetInterface(); ASSERT_NE(pClientConnection, nullptr); - EXPECT_NE(pClientConnection->RegisterStatusEventCallback(&receiverClient2), 0); + EXPECT_NE(pClientConnection->RegisterStateEventCallback(&receiverClient2), 0); EXPECT_TRUE(pClientConnection->AsyncConnect(&receiverClient2)); EXPECT_TRUE(pClientConnection->WaitForConnection(2000)); // Note: Connection should be possible within 2000ms. EXPECT_TRUE(pServerConnect->WaitForConnection(1000)); // Note: 1000ms to also receive the connection at the server. - EXPECT_EQ(pClientConnection->GetStatus(), sdv::ipc::EConnectStatus::connected); - EXPECT_EQ(pServerConnect->GetStatus(), sdv::ipc::EConnectStatus::connected); + EXPECT_EQ(pClientConnection->GetConnectState(), sdv::ipc::EConnectState::connected); + EXPECT_EQ(pServerConnect->GetConnectState(), sdv::ipc::EConnectState::connected); EXPECT_FALSE(receiverServer.ConnectionErrorOccurred()); EXPECT_FALSE(receiverClient2.ConnectionErrorOccurred()); - EXPECT_EQ(receiverServer.GetReceivedStatus(), sdv::ipc::EConnectStatus::connected); - EXPECT_EQ(receiverClient2.GetReceivedStatus(), sdv::ipc::EConnectStatus::connected); + EXPECT_EQ(receiverServer.GetReceivedState(), sdv::ipc::EConnectState::connected); + EXPECT_EQ(receiverClient2.GetReceivedState(), sdv::ipc::EConnectState::connected); std::cout << "Disconnect client 2..." << std::endl; @@ -763,10 +763,10 @@ TEST(SharedMemChannelService, EstablishReconnectEvents) EXPECT_NO_THROW(ptrClient2Connection.Clear()); std::this_thread::sleep_for(std::chrono::milliseconds(25)); // Note: Handle connection terminate EXPECT_NO_THROW(mgntClient2.Shutdown()); - EXPECT_TRUE(pServerConnect->GetStatus() == sdv::ipc::EConnectStatus::disconnected || - pServerConnect->GetStatus() == sdv::ipc::EConnectStatus::connecting); - EXPECT_TRUE(receiverServer.GetReceivedStatus() == sdv::ipc::EConnectStatus::disconnected || - receiverServer.GetReceivedStatus() == sdv::ipc::EConnectStatus::connecting); + EXPECT_TRUE(pServerConnect->GetConnectState() == sdv::ipc::EConnectState::disconnected || + pServerConnect->GetConnectState() == sdv::ipc::EConnectState::connecting); + EXPECT_TRUE(receiverServer.GetReceivedState() == sdv::ipc::EConnectState::disconnected || + receiverServer.GetReceivedState() == sdv::ipc::EConnectState::connecting); EXPECT_FALSE(receiverServer.ConnectionErrorOccurred()); std::cout << "Shutdown server..." << std::endl; @@ -828,7 +828,7 @@ Mode = "Essential")code")); ASSERT_NE(pServerConnect, nullptr); EXPECT_TRUE(pServerConnect->AsyncConnect(&receiverServer)); EXPECT_TRUE(pServerConnect->WaitForConnection(2000)); // Note: Connection should be possible within 2000ms. - EXPECT_EQ(pServerConnect->GetStatus(), sdv::ipc::EConnectStatus::connected); + EXPECT_EQ(pServerConnect->GetConnectState(), sdv::ipc::EConnectState::connected); // Try send; should succeed, since connected EXPECT_TRUE(pServerSend->SendData(seqPattern)); @@ -909,11 +909,11 @@ Mode = "Essential")code")); ASSERT_NE(pServerConnect, nullptr); EXPECT_TRUE(pServerConnect->AsyncConnect(&receiverServer)); EXPECT_TRUE(pServerConnect->WaitForConnection(2000)); // Note: Connection should be possible within 2000ms. - EXPECT_EQ(pServerConnect->GetStatus(), sdv::ipc::EConnectStatus::connected); + EXPECT_EQ(pServerConnect->GetConnectState(), sdv::ipc::EConnectState::connected); // Try send; should succeed, since connected auto tpStart = std::chrono::high_resolution_clock::now(); - while (pServerConnect->GetStatus() == sdv::ipc::EConnectStatus::connected) + while (pServerConnect->GetConnectState() == sdv::ipc::EConnectState::connected) { EXPECT_TRUE(pServerSend->SendData(seqPattern)); std::this_thread::sleep_for(std::chrono::milliseconds(300)); @@ -925,7 +925,7 @@ Mode = "Essential")code")); break; // Max 4000 ms } } - EXPECT_EQ(pServerConnect->GetStatus(), sdv::ipc::EConnectStatus::disconnected); + EXPECT_EQ(pServerConnect->GetConnectState(), sdv::ipc::EConnectState::disconnected); EXPECT_NO_THROW(ptrServerConnection.Clear()); @@ -984,14 +984,14 @@ Mode = "Essential")code")); // Establish the server connection sdv::ipc::IConnect* pServerConnect = ptrServerConnection.GetInterface(); ASSERT_NE(pServerConnect, nullptr); - EXPECT_NE(pServerConnect->RegisterStatusEventCallback(&receiverServer), 0); + EXPECT_NE(pServerConnect->RegisterStateEventCallback(&receiverServer), 0); EXPECT_TRUE(pServerConnect->AsyncConnect(&receiverServer)); EXPECT_TRUE(pServerConnect->WaitForConnection(2000)); // Note: Connection should be possible within 2000ms. - EXPECT_EQ(pServerConnect->GetStatus(), sdv::ipc::EConnectStatus::connected); + EXPECT_EQ(pServerConnect->GetConnectState(), sdv::ipc::EConnectState::connected); // Try send; should succeed, since connected auto tpStart = std::chrono::high_resolution_clock::now(); - while (pServerConnect->GetStatus() == sdv::ipc::EConnectStatus::connected) + while (pServerConnect->GetConnectState() == sdv::ipc::EConnectState::connected) { EXPECT_TRUE(pServerSend->SendData(seqPattern)); std::this_thread::sleep_for(std::chrono::milliseconds(300)); @@ -1004,7 +1004,7 @@ Mode = "Essential")code")); } } EXPECT_TRUE(receiverServer.ForcedDisconnectOccurred()); - EXPECT_EQ(pServerConnect->GetStatus(), sdv::ipc::EConnectStatus::disconnected); + EXPECT_EQ(pServerConnect->GetConnectState(), sdv::ipc::EConnectState::disconnected); EXPECT_NO_THROW(ptrServerConnection.Clear()); @@ -1048,7 +1048,7 @@ Mode = "Essential")code")); ASSERT_NE(pControlConnect1, nullptr); EXPECT_TRUE(pControlConnect1->AsyncConnect(&receiverControl1)); EXPECT_TRUE(pControlConnect1->WaitForConnection(2000)); // Note: Connection should be possible within 2000ms. - EXPECT_EQ(pControlConnect1->GetStatus(), sdv::ipc::EConnectStatus::connected); + EXPECT_EQ(pControlConnect1->GetConnectState(), sdv::ipc::EConnectState::connected); // Wait for data sdv::sequence> seqDataConnectString; @@ -1084,7 +1084,7 @@ Mode = "Essential")code")); ASSERT_NE(pControlConnect2, nullptr); EXPECT_TRUE(pControlConnect2->AsyncConnect(&receiverControl2)); EXPECT_TRUE(pControlConnect2->WaitForConnection(2000)); // Note: Connection should be possible within 2000ms. - EXPECT_EQ(pControlConnect2->GetStatus(), sdv::ipc::EConnectStatus::connected); + EXPECT_EQ(pControlConnect2->GetConnectState(), sdv::ipc::EConnectState::connected); // Wait for process termination pProcessLifetime->WaitForTerminate(tProcessID1, 0xffffffff); @@ -1131,7 +1131,7 @@ Mode = "Essential")code")); ASSERT_NE(pControlConnect1, nullptr); EXPECT_TRUE(pControlConnect1->AsyncConnect(&receiverControl1)); EXPECT_TRUE(pControlConnect1->WaitForConnection(2000)); // Note: Connection should be possible within 5000ms. - EXPECT_EQ(pControlConnect1->GetStatus(), sdv::ipc::EConnectStatus::connected); + EXPECT_EQ(pControlConnect1->GetConnectState(), sdv::ipc::EConnectState::connected); // Wait for connection string (max. 500*10ms). sdv::sequence> seqDataConnectString; @@ -1167,7 +1167,7 @@ Mode = "Essential")code")); ASSERT_NE(pControlConnect2, nullptr); EXPECT_TRUE(pControlConnect2->AsyncConnect(&receiverControl2)); EXPECT_TRUE(pControlConnect2->WaitForConnection(2000)); // Note: Connection should be possible within 2000ms. - EXPECT_EQ(pControlConnect2->GetStatus(), sdv::ipc::EConnectStatus::connected); + EXPECT_EQ(pControlConnect2->GetConnectState(), sdv::ipc::EConnectState::connected); // Wait for process termination pProcessLifetime->WaitForTerminate(tProcessID1, 0xffffffff); @@ -1212,7 +1212,7 @@ Mode = "Essential")code")); ASSERT_NE(pControlConnect1, nullptr); EXPECT_TRUE(pControlConnect1->AsyncConnect(&receiverControl1)); EXPECT_TRUE(pControlConnect1->WaitForConnection(2000)); // Note: Connection should be possible within 2000ms. - EXPECT_EQ(pControlConnect1->GetStatus(), sdv::ipc::EConnectStatus::connected); + EXPECT_EQ(pControlConnect1->GetConnectState(), sdv::ipc::EConnectState::connected); // Wait for connection string (max. 500*10ms). sdv::sequence> seqDataConnectString; @@ -1248,7 +1248,7 @@ Mode = "Essential")code")); ASSERT_NE(pControlConnect2, nullptr); EXPECT_TRUE(pControlConnect2->AsyncConnect(&receiverControl2)); EXPECT_TRUE(pControlConnect2->WaitForConnection(2000)); // Note: Connection should be possible within 2000ms. - EXPECT_EQ(pControlConnect2->GetStatus(), sdv::ipc::EConnectStatus::connected); + EXPECT_EQ(pControlConnect2->GetConnectState(), sdv::ipc::EConnectState::connected); // Wait for process termination pProcessLifetime->WaitForTerminate(tProcessID1, 0xffffffff); diff --git a/tests/unit_tests/shared_mem/shared_mem_large_data_tests.cpp b/tests/unit_tests/shared_mem/shared_mem_large_data_tests.cpp index 45393e6..946eab1 100644 --- a/tests/unit_tests/shared_mem/shared_mem_large_data_tests.cpp +++ b/tests/unit_tests/shared_mem/shared_mem_large_data_tests.cpp @@ -115,7 +115,7 @@ public: // Wait until there is no activity any more. std::chrono::high_resolution_clock::time_point tpTickSent = std::chrono::high_resolution_clock::now(); std::chrono::high_resolution_clock::time_point tpTickReceive = std::chrono::high_resolution_clock::now(); - while (m_eStatus != sdv::ipc::EConnectStatus::disconnected) + while (m_eConnectState != sdv::ipc::EConnectState::disconnected) { std::unique_lock lock(m_mtxData); m_cvReceived.wait_for(lock, std::chrono::milliseconds(50)); @@ -141,33 +141,33 @@ public: } /** - * @brief Set the current status. Overload of sdv::ipc::IConnectEventCallback::SetStatus. - * @param[in] eConnectStatus The connection status. + * @brief Set the current state. Overload of sdv::ipc::IConnectEventCallback::SetConnectState. + * @param[in] eConnectState The connection state. */ - virtual void SetStatus(/*in*/ sdv::ipc::EConnectStatus eConnectStatus) override + virtual void SetConnectState(/*in*/ sdv::ipc::EConnectState eConnectState) override { - switch (eConnectStatus) + switch (eConnectState) { - case sdv::ipc::EConnectStatus::connection_error: + case sdv::ipc::EConnectState::connection_error: m_bConnectError = true; break; - case sdv::ipc::EConnectStatus::communication_error: + case sdv::ipc::EConnectState::communication_error: m_bCommError = true; break; - case sdv::ipc::EConnectStatus::disconnected_forced: + case sdv::ipc::EConnectState::disconnected_forced: m_bForcedDisconnect = true; break; default: break; } - m_eStatus = eConnectStatus; + m_eConnectState = eConnectState; } /** - * @brief Return the received connection status. - * @return The received connection status. + * @brief Return the received connection state. + * @return The received connection state. */ - sdv::ipc::EConnectStatus GetReceivedStatus() const { return m_eStatus; } + sdv::ipc::EConnectState GetReceivedState() const { return m_eConnectState; } /** * @brief Has a connection error occurred? @@ -207,9 +207,9 @@ public: } /** - * @brief Reset the received status event flags. + * @brief Reset the received state event flags. */ - void ResetStatusEvents() + void ResetStateEvents() { m_bConnectError = false; m_bCommError = false; @@ -256,7 +256,7 @@ private: sdv::ipc::IDataSend* m_pSend = nullptr; ///< Send interface to implement repeating function. mutable std::mutex m_mtxData; ///< Protect data access. std::queue> m_queueDataCopy; ///< Copy of the data. - std::atomic m_eStatus = sdv::ipc::EConnectStatus::uninitialized; ///< Current received status. + std::atomic m_eConnectState = sdv::ipc::EConnectState::uninitialized; ///< Current received state. bool m_bConnectError = false; ///< Connection error ocurred. bool m_bCommError = false; ///< Communication error occurred. bool m_bForcedDisconnect = false; ///< Force disconnect. @@ -322,8 +322,8 @@ Size = 1024000 ASSERT_NE(pServerConnection, nullptr); EXPECT_TRUE(pServerConnection->AsyncConnect(&receiverServer)); EXPECT_FALSE(pServerConnection->WaitForConnection(50)); // Note: 50ms will not get a connection. - EXPECT_TRUE(pServerConnection->GetStatus() == sdv::ipc::EConnectStatus::initialized || - pServerConnection->GetStatus() == sdv::ipc::EConnectStatus::connecting); + EXPECT_TRUE(pServerConnection->GetConnectState() == sdv::ipc::EConnectState::initialized || + pServerConnection->GetConnectState() == sdv::ipc::EConnectState::connecting); // Establish the client connection sdv::ipc::IConnect* pClientConnection = ptrClientConnection.GetInterface(); @@ -331,8 +331,8 @@ Size = 1024000 EXPECT_TRUE(pClientConnection->AsyncConnect(&receiverClient)); EXPECT_TRUE(pClientConnection->WaitForConnection(2000)); // Note: Connection should be possible within 2000ms. EXPECT_TRUE(pServerConnection->WaitForConnection(50)); // Note: 50ms to also receive the connection at the server. - EXPECT_EQ(pClientConnection->GetStatus(), sdv::ipc::EConnectStatus::connected); - EXPECT_EQ(pServerConnection->GetStatus(), sdv::ipc::EConnectStatus::connected); + EXPECT_EQ(pClientConnection->GetConnectState(), sdv::ipc::EConnectState::connected); + EXPECT_EQ(pServerConnection->GetConnectState(), sdv::ipc::EConnectState::connected); appcontrol.SetRunningMode(); // Try send; should succeed, since connected @@ -419,8 +419,8 @@ Size = 1024000 ASSERT_NE(pServerConnection, nullptr); EXPECT_TRUE(pServerConnection->AsyncConnect(&receiverServer)); EXPECT_FALSE(pServerConnection->WaitForConnection(50)); // Note: 50ms will not get a connection. - EXPECT_TRUE(pServerConnection->GetStatus() == sdv::ipc::EConnectStatus::initialized || - pServerConnection->GetStatus() == sdv::ipc::EConnectStatus::connecting); + EXPECT_TRUE(pServerConnection->GetConnectState() == sdv::ipc::EConnectState::initialized || + pServerConnection->GetConnectState() == sdv::ipc::EConnectState::connecting); // Establish the client connection sdv::ipc::IConnect* pClientConnection = ptrClientConnection.GetInterface(); @@ -428,8 +428,8 @@ Size = 1024000 EXPECT_TRUE(pClientConnection->AsyncConnect(&receiverClient)); EXPECT_TRUE(pClientConnection->WaitForConnection(2000)); // Note: Connection should be possible within 2000ms. EXPECT_TRUE(pServerConnection->WaitForConnection(50)); // Note: 50ms to also receive the connection at the server. - EXPECT_EQ(pClientConnection->GetStatus(), sdv::ipc::EConnectStatus::connected); - EXPECT_EQ(pServerConnection->GetStatus(), sdv::ipc::EConnectStatus::connected); + EXPECT_EQ(pClientConnection->GetConnectState(), sdv::ipc::EConnectState::connected); + EXPECT_EQ(pServerConnection->GetConnectState(), sdv::ipc::EConnectState::connected); appcontrol.SetRunningMode(); // Try send; should succeed, since connected @@ -551,8 +551,8 @@ Size = 1024000 ASSERT_NE(pServerConnection, nullptr); EXPECT_TRUE(pServerConnection->AsyncConnect(&receiverServer)); EXPECT_FALSE(pServerConnection->WaitForConnection(50)); // Note: 50ms will not get a connection. - EXPECT_TRUE(pServerConnection->GetStatus() == sdv::ipc::EConnectStatus::initialized || - pServerConnection->GetStatus() == sdv::ipc::EConnectStatus::connecting); + EXPECT_TRUE(pServerConnection->GetConnectState() == sdv::ipc::EConnectState::initialized || + pServerConnection->GetConnectState() == sdv::ipc::EConnectState::connecting); // Establish the client connection sdv::ipc::IConnect* pClientConnection = ptrClientConnection.GetInterface(); @@ -560,8 +560,8 @@ Size = 1024000 EXPECT_TRUE(pClientConnection->AsyncConnect(&receiverClient)); EXPECT_TRUE(pClientConnection->WaitForConnection(2000)); // Note: Connection should be possible within 2000ms. EXPECT_TRUE(pServerConnection->WaitForConnection(50)); // Note: 50ms to also receive the connection at the server. - EXPECT_EQ(pClientConnection->GetStatus(), sdv::ipc::EConnectStatus::connected); - EXPECT_EQ(pServerConnection->GetStatus(), sdv::ipc::EConnectStatus::connected); + EXPECT_EQ(pClientConnection->GetConnectState(), sdv::ipc::EConnectState::connected); + EXPECT_EQ(pServerConnection->GetConnectState(), sdv::ipc::EConnectState::connected); appcontrol.SetRunningMode(); // Try send; should succeed, since connected @@ -666,7 +666,7 @@ Size = 1024000 ASSERT_NE(pServerConnection, nullptr); EXPECT_TRUE(pServerConnection->AsyncConnect(&receiverServer)); EXPECT_TRUE(pServerConnection->WaitForConnection(2000)); // Note: Connection should be possible within 2000ms. - EXPECT_EQ(pServerConnection->GetStatus(), sdv::ipc::EConnectStatus::connected); + EXPECT_EQ(pServerConnection->GetConnectState(), sdv::ipc::EConnectState::connected); TRACE("Connection estabished..."); appcontrol.SetRunningMode(); @@ -750,7 +750,7 @@ Size = 1024000 ASSERT_NE(pServerConnection, nullptr); EXPECT_TRUE(pServerConnection->AsyncConnect(&receiverServer)); EXPECT_TRUE(pServerConnection->WaitForConnection(2000)); // Note: Connection should be possible within 2000ms. - EXPECT_EQ(pServerConnection->GetStatus(), sdv::ipc::EConnectStatus::connected); + EXPECT_EQ(pServerConnection->GetConnectState(), sdv::ipc::EConnectState::connected); TRACE("Connection estabished..."); appcontrol.SetRunningMode(); diff --git a/tests/unit_tests/unix_sockets/CMakeLists.txt b/tests/unit_tests/unix_sockets/CMakeLists.txt index b022ab8..65c71a0 100644 --- a/tests/unit_tests/unix_sockets/CMakeLists.txt +++ b/tests/unit_tests/unix_sockets/CMakeLists.txt @@ -6,8 +6,11 @@ # https://www.apache.org/licenses/LICENSE-2.0 # # SPDX-License-Identifier: Apache-2.0 +# +# Contributors: +# Denisa Ros - initial API and implementation #******************************************************************************* - +if(UNIX) cmake_minimum_required(VERSION 3.20) project(UnixSocketCommunicationTests LANGUAGES CXX) @@ -26,11 +29,19 @@ target_include_directories(UnitTest_UnixSocketConnectTests PUBLIC if (CMAKE_CXX_COMPILER_ID STREQUAL "GNU") target_link_libraries(UnitTest_UnixSocketConnectTests - GTest::GTest ${CMAKE_THREAD_LIBS_INIT} stdc++fs ${CMAKE_DL_LIBS} rt + GTest::GTest + ${CMAKE_THREAD_LIBS_INIT} + stdc++fs ${CMAKE_DL_LIBS} + rt + uds_unix_sockets ) else() target_link_libraries(UnitTest_UnixSocketConnectTests - GTest::GTest Ws2_32 Winmm Rpcrt4.lib + GTest::GTest + Ws2_32 + Winmm + Rpcrt4.lib + uds_unix_sockets ) endif() @@ -46,4 +57,6 @@ add_custom_command(TARGET UnitTest_UnixSocketConnectTests POST_BUILD --gtest_output=xml:${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/UnitTest_UnixSocketConnectTests.xml VERBATIM ) +endif() + endif() \ No newline at end of file diff --git a/tests/unit_tests/unix_sockets/uds_connect_tests.cpp b/tests/unit_tests/unix_sockets/uds_connect_tests.cpp index 5738d94..a035fbe 100644 --- a/tests/unit_tests/unix_sockets/uds_connect_tests.cpp +++ b/tests/unit_tests/unix_sockets/uds_connect_tests.cpp @@ -1,12 +1,15 @@ /******************************************************************************** -* Copyright (c) 2025-2026 ZF Friedrichshafen AG -* -* This program and the accompanying materials are made available under the -* terms of the Apache License Version 2.0 which is available at -* https://www.apache.org/licenses/LICENSE-2.0 -* -* SPDX-License-Identifier: Apache-2.0 -********************************************************************************/ + * Copyright (c) 2025-2026 ZF Friedrichshafen AG + * + * This program and the accompanying materials are made available under the + * terms of the Apache License Version 2.0 which is available at + * https://www.apache.org/licenses/LICENSE-2.0 + * + * SPDX-License-Identifier: Apache-2.0 + * + * Contributors: + * Denisa Ros - initial API and implementation + ********************************************************************************/ #if defined __unix__ @@ -15,8 +18,8 @@ #include #include -#include "../../../sdv_services/uds_unix_sockets/channel_mgnt.cpp" -#include "../../../sdv_services/uds_unix_sockets/connection.cpp" +#include "../../../sdv_services/uds_unix_sockets/channel_mgnt.h" +#include "../../../sdv_services/uds_unix_sockets/connection.h" #include #include @@ -24,6 +27,7 @@ #include #include #include +#include #include #include @@ -44,24 +48,24 @@ public: // don't test data path yet void ReceiveData(sdv::sequence>& /*seqData*/) override {} - void SetStatus(sdv::ipc::EConnectStatus s) override { + void SetConnectState(sdv::ipc::EConnectState s) override { { std::lock_guard lk(m_mtx); - m_status = s; + m_state = s; } m_cv.notify_all(); } - bool WaitForStatus(sdv::ipc::EConnectStatus expected, uint32_t ms = 2000) + bool WaitForState(sdv::ipc::EConnectState expected, uint32_t ms = 2000) { std::unique_lock lk(m_mtx); - if (m_status == expected) + if (m_state == expected) return true; auto deadline = std::chrono::steady_clock::now() + std::chrono::milliseconds(ms); - while (m_status != expected) + while (m_state != expected) { if (m_cv.wait_until(lk, deadline) == std::cv_status::timeout) return false; @@ -69,13 +73,13 @@ public: return true; } - sdv::ipc::EConnectStatus GetStatus() const { + sdv::ipc::EConnectState GetConnectState() const { std::lock_guard lk(m_mtx); - return m_status; + return m_state; } private: - sdv::ipc::EConnectStatus m_status { sdv::ipc::EConnectStatus::uninitialized }; + sdv::ipc::EConnectState m_state { sdv::ipc::EConnectState::uninitialized }; mutable std::mutex m_mtx; std::condition_variable m_cv; }; @@ -101,24 +105,24 @@ public: m_cv.notify_all(); } - void SetStatus(sdv::ipc::EConnectStatus s) override { + void SetConnectState(sdv::ipc::EConnectState s) override { { std::lock_guard lk(m_mtx); - m_status = s; + m_state = s; } m_cv.notify_all(); } - bool WaitForStatus(sdv::ipc::EConnectStatus expected, uint32_t ms = 2000) + bool WaitForState(sdv::ipc::EConnectState expected, uint32_t ms = 2000) { std::unique_lock lk(m_mtx); - if (m_status == expected) + if (m_state == expected) return true; auto deadline = std::chrono::steady_clock::now() + std::chrono::milliseconds(ms); - while (m_status != expected) + while (m_state != expected) { if (m_cv.wait_until(lk, deadline) == std::cv_status::timeout) return false; @@ -141,13 +145,13 @@ public: private: mutable std::mutex m_mtx; std::condition_variable m_cv; - sdv::ipc::EConnectStatus m_status{ sdv::ipc::EConnectStatus::uninitialized }; + sdv::ipc::EConnectState m_state{ sdv::ipc::EConnectState::uninitialized }; sdv::sequence> m_lastData; bool m_received{ false }; }; -// A receiver that intentionally throws from SetStatus(...) to test callback-safety. +// A receiver that intentionally throws from SetConnectState(...) to test callback-safety. class CUDSThrowingReceiver : public sdv::IInterfaceAccess, public sdv::ipc::IDataReceiveCallback, @@ -161,9 +165,9 @@ public: void ReceiveData(sdv::sequence>& /*seq*/) override {} - void SetStatus(sdv::ipc::EConnectStatus s) override + void SetConnectState(sdv::ipc::EConnectState s) override { - // Store the last status and then throw to simulate misbehaving user code. + // Store the last state and then throw to simulate misbehaving user code. { std::lock_guard lk(m_mtx); m_last = s; @@ -172,7 +176,7 @@ public: throw std::runtime_error("Intentional user callback failure"); } - bool WaitForStatus(sdv::ipc::EConnectStatus expected, uint32_t ms = 2000) + bool WaitForState(sdv::ipc::EConnectState expected, uint32_t ms = 2000) { std::unique_lock lk(m_mtx); @@ -193,7 +197,7 @@ public: private: std::mutex m_mtx; std::condition_variable m_cv; - sdv::ipc::EConnectStatus m_last{ sdv::ipc::EConnectStatus::uninitialized }; + sdv::ipc::EConnectState m_last{ sdv::ipc::EConnectState::uninitialized }; }; @@ -328,7 +332,7 @@ TEST(UnixSocketIPC, BasicConnectDisconnect) if (!clientConn->WaitForConnection(5000)) { clientResult = 4; return; } - if (clientConn->GetStatus() != sdv::ipc::EConnectStatus::connected) { clientResult = 5; return; } + if (clientConn->GetConnectState() != sdv::ipc::EConnectState::connected) { clientResult = 5; return; } // Wait for the server to be conected before disconecting the client while (!allowClientDisconnect.load()) @@ -339,7 +343,7 @@ TEST(UnixSocketIPC, BasicConnectDisconnect) // SERVER must also report connected EXPECT_TRUE(serverConn->WaitForConnection(5000)); - EXPECT_EQ(serverConn->GetStatus(), sdv::ipc::EConnectStatus::connected); + EXPECT_EQ(serverConn->GetConnectState(), sdv::ipc::EConnectState::connected); // Allow client to dissconect now, because the Server is connected allowClientDisconnect = true; @@ -348,7 +352,7 @@ TEST(UnixSocketIPC, BasicConnectDisconnect) //DISCONNECT both serverConn->Disconnect(); - EXPECT_EQ(serverConn->GetStatus(), sdv::ipc::EConnectStatus::disconnected); + EXPECT_EQ(serverConn->GetConnectState(), sdv::ipc::EConnectState::disconnected); // Shutdown Manager / Framework EXPECT_NO_THROW(mgr.Shutdown()); @@ -403,7 +407,7 @@ TEST(UnixSocketIPC, ReconnectAfterDisconnect_SamePath) if (!clientConn->AsyncConnect(&cRcvr)) { clientResult = 3; return; } if (!clientConn->WaitForConnection(5000)) { clientResult = 4; return; } - if (clientConn->GetStatus() != sdv::ipc::EConnectStatus::connected) { clientResult = 5; return; } + if (clientConn->GetConnectState() != sdv::ipc::EConnectState::connected) { clientResult = 5; return; } //waits for confirmation before disconect while (!allowClientDisconnect.load()) @@ -415,7 +419,7 @@ TEST(UnixSocketIPC, ReconnectAfterDisconnect_SamePath) // Server has to be connected (same timeout with client?) EXPECT_TRUE(serverConn->WaitForConnection(5000)) << "Server didn't reach 'connected' in time"; - EXPECT_EQ(serverConn->GetStatus(), sdv::ipc::EConnectStatus::connected); + EXPECT_EQ(serverConn->GetConnectState(), sdv::ipc::EConnectState::connected); // Allows Client to disconnect and waits for finishing allowClientDisconnect = true; @@ -424,7 +428,7 @@ TEST(UnixSocketIPC, ReconnectAfterDisconnect_SamePath) // Disconnect server serverConn->Disconnect(); - EXPECT_EQ(serverConn->GetStatus(), sdv::ipc::EConnectStatus::disconnected); + EXPECT_EQ(serverConn->GetConnectState(), sdv::ipc::EConnectState::disconnected); // SESSION 2 // SERVER @@ -450,7 +454,7 @@ TEST(UnixSocketIPC, ReconnectAfterDisconnect_SamePath) if (!clientConn2->AsyncConnect(&cRcvr2)) { clientResult2 = 3; return; } if (!clientConn2->WaitForConnection(5000)) { clientResult2 = 4; return; } - if (clientConn2->GetStatus() != sdv::ipc::EConnectStatus::connected) { clientResult2 = 5; return; } + if (clientConn2->GetConnectState() != sdv::ipc::EConnectState::connected) { clientResult2 = 5; return; } //waits for confirmation before disconect while (!allowClientDisconnect2.load()) @@ -463,7 +467,7 @@ TEST(UnixSocketIPC, ReconnectAfterDisconnect_SamePath) // Server has to be connected // if unlink(path) from session 1 worked, bind/listen/accept works again EXPECT_TRUE(serverConn2->WaitForConnection(5000)) << "Server didn't reach 'connected' in time (session 2)"; - EXPECT_EQ(serverConn2->GetStatus(), sdv::ipc::EConnectStatus::connected); + EXPECT_EQ(serverConn2->GetConnectState(), sdv::ipc::EConnectState::connected); // Allows Client to disconnect and waits for finishing allowClientDisconnect2 = true; @@ -472,7 +476,7 @@ TEST(UnixSocketIPC, ReconnectAfterDisconnect_SamePath) // Disconnect server serverConn2->Disconnect(); - EXPECT_EQ(serverConn2->GetStatus(), sdv::ipc::EConnectStatus::disconnected); + EXPECT_EQ(serverConn2->GetConnectState(), sdv::ipc::EConnectState::disconnected); //Shutdown manager/framework EXPECT_NO_THROW(mgr.Shutdown()); @@ -550,8 +554,8 @@ TEST(UnixSocketIPC, CreateEndpoint_WithConfigAndPathClamping) EXPECT_TRUE(serverConn->WaitForConnection(5000)); EXPECT_TRUE(clientConn->WaitForConnection(5000)); - EXPECT_EQ(serverConn->GetStatus(), sdv::ipc::EConnectStatus::connected); - EXPECT_EQ(clientConn->GetStatus(), sdv::ipc::EConnectStatus::connected); + EXPECT_EQ(serverConn->GetConnectState(), sdv::ipc::EConnectState::connected); + EXPECT_EQ(clientConn->GetConnectState(), sdv::ipc::EConnectState::connected); // Cleanup clientConn->Disconnect(); @@ -592,8 +596,8 @@ TEST(UnixSocketIPC, Access_DefaultPath_ServerClientConnect) EXPECT_TRUE(serverConn->WaitForConnection(5000)); EXPECT_TRUE(clientConn->WaitForConnection(5000)); - EXPECT_EQ(serverConn->GetStatus(), sdv::ipc::EConnectStatus::connected); - EXPECT_EQ(clientConn->GetStatus(), sdv::ipc::EConnectStatus::connected); + EXPECT_EQ(serverConn->GetConnectState(), sdv::ipc::EConnectState::connected); + EXPECT_EQ(clientConn->GetConnectState(), sdv::ipc::EConnectState::connected); clientConn->Disconnect(); serverConn->Disconnect(); @@ -637,7 +641,7 @@ TEST(UnixSocketIPC, WaitForConnection_InfiniteWait_SlowClient) // INFINITE wait (0xFFFFFFFFu) EXPECT_TRUE(serverConn->WaitForConnection(0xFFFFFFFFu)); - EXPECT_EQ(serverConn->GetStatus(), sdv::ipc::EConnectStatus::connected); + EXPECT_EQ(serverConn->GetConnectState(), sdv::ipc::EConnectState::connected); // Cleanup delayedClient.join(); @@ -691,7 +695,8 @@ TEST(UnixSocketIPC, WaitForConnection_ZeroTimeout_BeforeAndAfter) TEST(UnixSocketIPC, ClientTimeout_NoServer) { sdv::app::CAppControl app; - ASSERT_TRUE(app.Startup("")); + ASSERT_TRUE(app.Startup(R"toml([LogHandler] +ViewFilter = "Fatal")toml")); app.SetRunningMode(); CUnixDomainSocketsChannelMgnt mgr; @@ -714,7 +719,7 @@ TEST(UnixSocketIPC, ClientTimeout_NoServer) EXPECT_FALSE(clientConn->WaitForConnection(1500)); // after another ~1s it should be in connection_error std::this_thread::sleep_for(std::chrono::milliseconds(800)); - EXPECT_EQ(clientConn->GetStatus(), sdv::ipc::EConnectStatus::connection_error); + EXPECT_EQ(clientConn->GetConnectState(), sdv::ipc::EConnectState::connection_error); clientConn->Disconnect(); // cleanup (joins threads) EXPECT_NO_THROW(mgr.Shutdown()); @@ -754,9 +759,9 @@ TEST(UnixSocketIPC, ServerDisconnectPropagatesToClient) serverConn->Disconnect(); // Deterministic wait for client-side transition to 'disconnected' - EXPECT_TRUE(cRcvr.WaitForStatus(sdv::ipc::EConnectStatus::disconnected, /*ms*/ 3000)) << "Client did not observe 'disconnected' after server closed the socket."; + EXPECT_TRUE(cRcvr.WaitForState(sdv::ipc::EConnectState::disconnected, /*ms*/ 3000)) << "Client did not observe 'disconnected' after server closed the socket."; - EXPECT_EQ(clientConn->GetStatus(), sdv::ipc::EConnectStatus::disconnected); + EXPECT_EQ(clientConn->GetConnectState(), sdv::ipc::EConnectState::disconnected); clientConn->Disconnect(); EXPECT_NO_THROW(mgr.Shutdown()); @@ -794,7 +799,7 @@ TEST(UnixSocketIPC, ReconnectOnSameServerInstance) ASSERT_TRUE(clientConn->WaitForConnection(5000)); clientConn->Disconnect(); serverConn->Disconnect(); - EXPECT_EQ(serverConn->GetStatus(), sdv::ipc::EConnectStatus::disconnected); + EXPECT_EQ(serverConn->GetConnectState(), sdv::ipc::EConnectState::disconnected); } // Second session on the same serverConn object @@ -1129,7 +1134,7 @@ TEST(UnixSocketIPC, PeerCloseMidTransfer_ClientSeesDisconnected_AndSendMayFailOr std::cout << "[Debug] SendData result = " << sendResult.load() << std::endl; // But client MUST observe disconnected: - EXPECT_TRUE(cRcvr.WaitForStatus(sdv::ipc::EConnectStatus::disconnected, 3000)); + EXPECT_TRUE(cRcvr.WaitForState(sdv::ipc::EConnectState::disconnected, 3000)); clientConn->Disconnect(); EXPECT_NO_THROW(mgr.Shutdown()); @@ -1162,8 +1167,8 @@ TEST(UnixSocketIPC, ClientCancelConnect_NoServer_CleansUpPromptly) std::this_thread::sleep_for(std::chrono::milliseconds(150)); clientConn->Disconnect(); - // Immediately after disconnect, status should be 'disconnected' (no hangs). - EXPECT_EQ(clientConn->GetStatus(), sdv::ipc::EConnectStatus::disconnected); + // Immediately after disconnect, state should be 'disconnected' (no hangs). + EXPECT_EQ(clientConn->GetConnectState(), sdv::ipc::EConnectState::disconnected); EXPECT_NO_THROW(mgr.Shutdown()); app.Shutdown(); } @@ -1172,7 +1177,8 @@ TEST(UnixSocketIPC, ClientCancelConnect_NoServer_CleansUpPromptly) TEST(UnixSocketIPC, ServerStartThenImmediateDisconnect_NoClient) { sdv::app::CAppControl app; - ASSERT_TRUE(app.Startup("")); + ASSERT_TRUE(app.Startup(R"toml([LogHandler] +ViewFilter = "Fatal")toml")); app.SetRunningMode(); CUnixDomainSocketsChannelMgnt mgr; EXPECT_NO_THROW(mgr.Initialize("")); @@ -1192,15 +1198,15 @@ TEST(UnixSocketIPC, ServerStartThenImmediateDisconnect_NoClient) // The server may be still listening; ensure we can disconnect cleanly std::this_thread::sleep_for(std::chrono::milliseconds(50)); serverConn->Disconnect(); - EXPECT_EQ(serverConn->GetStatus(), sdv::ipc::EConnectStatus::disconnected); + EXPECT_EQ(serverConn->GetConnectState(), sdv::ipc::EConnectState::disconnected); EXPECT_NO_THROW(mgr.Shutdown()); app.Shutdown(); } -//Callback throws in SetStatus -> transport threads keep running -TEST(UnixSocketIPC, CallbackThrowsInSetStatus_DoesNotCrashTransport) +//Callback throws in SetConnectState -> transport threads keep running +TEST(UnixSocketIPC, CallbackThrowsInSetConnectState_DoesNotCrashTransport) { sdv::app::CAppControl app; ASSERT_TRUE(app.Startup("")); @@ -1227,7 +1233,7 @@ TEST(UnixSocketIPC, CallbackThrowsInSetStatus_DoesNotCrashTransport) CUDSThrowingReceiver cRcvr; ASSERT_TRUE(clientConn->AsyncConnect(&cRcvr)); - // Despite exceptions thrown inside SetStatus, the transport should still reach connected. + // Despite exceptions thrown inside SetConnectState, the transport should still reach connected. EXPECT_TRUE(serverConn->WaitForConnection(5000)); EXPECT_TRUE(clientConn->WaitForConnection(5000)); @@ -1239,8 +1245,8 @@ TEST(UnixSocketIPC, CallbackThrowsInSetStatus_DoesNotCrashTransport) app.Shutdown(); } -//RegisterStatusEventCallback: multiple listeners receive status updates -TEST(UnixSocketIPC, RegisterStatusEventCallback_MultipleCallbacksReceiveStatus) +//RegisterStateEventCallback: multiple listeners receive state updates +TEST(UnixSocketIPC, RegisterStateEventCallback_MultipleCallbacksReceiveState) { sdv::app::CAppControl app; ASSERT_TRUE(app.Startup("")); @@ -1266,12 +1272,12 @@ TEST(UnixSocketIPC, RegisterStatusEventCallback_MultipleCallbacksReceiveStatus) // --- Callback receiver 1 --- CUDSConnectReceiver recv1; - uint64_t cookie1 = server->RegisterStatusEventCallback(&recv1); + uint64_t cookie1 = server->RegisterStateEventCallback(&recv1); EXPECT_NE(cookie1, 0u); // --- Callback receiver 2 --- CUDSConnectReceiver recv2; - uint64_t cookie2 = server->RegisterStatusEventCallback(&recv2); + uint64_t cookie2 = server->RegisterStateEventCallback(&recv2); EXPECT_NE(cookie2, 0u); EXPECT_NE(cookie1, cookie2); @@ -1283,22 +1289,22 @@ TEST(UnixSocketIPC, RegisterStatusEventCallback_MultipleCallbacksReceiveStatus) ASSERT_TRUE(client->WaitForConnection(5000)); // Both receivers should have received 'connected' - EXPECT_TRUE(recv1.WaitForStatus(sdv::ipc::EConnectStatus::connected, 1000)); - EXPECT_TRUE(recv2.WaitForStatus(sdv::ipc::EConnectStatus::connected, 1000)); + EXPECT_TRUE(recv1.WaitForState(sdv::ipc::EConnectState::connected, 1000)); + EXPECT_TRUE(recv2.WaitForState(sdv::ipc::EConnectState::connected, 1000)); // --- Disconnect --- client->Disconnect(); - EXPECT_TRUE(recv1.WaitForStatus(sdv::ipc::EConnectStatus::disconnected, 1000)); - EXPECT_TRUE(recv2.WaitForStatus(sdv::ipc::EConnectStatus::disconnected, 1000)); + EXPECT_TRUE(recv1.WaitForState(sdv::ipc::EConnectState::disconnected, 1000)); + EXPECT_TRUE(recv2.WaitForState(sdv::ipc::EConnectState::disconnected, 1000)); server->Disconnect(); EXPECT_NO_THROW(mgr.Shutdown()); app.Shutdown(); } -//UnregisterStatusEventCallback: removed listener stops receiving events -TEST(UnixSocketIPC, UnregisterStatusEventCallback_RemovedListenerStopsReceiving) +//UnregisterStateEventCallback: removed listener stops receiving events +TEST(UnixSocketIPC, UnregisterStateEventCallback_RemovedListenerStopsReceiving) { // Framework + Manager sdv::app::CAppControl app; @@ -1328,13 +1334,13 @@ TEST(UnixSocketIPC, UnregisterStatusEventCallback_RemovedListenerStopsReceiving) ASSERT_NE(client, nullptr); // --------- Two distinct receivers ---------- - // recvReg: used ONLY for the status-callback registry + // recvReg: used ONLY for the state-callback registry // recvConn: used ONLY for AsyncConnect (m_pReceiver / m_pEvent path) CUDSConnectReceiver recvReg; CUDSConnectReceiver recvConn; - // Register recvReg as a status listener (registry path) - uint64_t cookie = server->RegisterStatusEventCallback(&recvReg); + // Register recvReg as a state listener (registry path) + uint64_t cookie = server->RegisterStateEventCallback(&recvReg); ASSERT_NE(cookie, 0u) << "Cookie must be non-zero"; // Start connections (server uses recvReg only for registry; recvConn is the IConnect/IDataReceive side) @@ -1346,21 +1352,21 @@ TEST(UnixSocketIPC, UnregisterStatusEventCallback_RemovedListenerStopsReceiving) ASSERT_TRUE(client->WaitForConnection(5000)); // Both the connection receiver (recvConn) and the registry listener (recvReg) should observe 'connected' - EXPECT_TRUE(recvConn.WaitForStatus(sdv::ipc::EConnectStatus::connected, 1000)) << "Connection receiver didn't see 'connected'."; - EXPECT_TRUE(recvReg.WaitForStatus(sdv::ipc::EConnectStatus::connected, 1000)) << "Registry listener didn't see 'connected'."; + EXPECT_TRUE(recvConn.WaitForState(sdv::ipc::EConnectState::connected, 1000)) << "Connection receiver didn't see 'connected'."; + EXPECT_TRUE(recvReg.WaitForState(sdv::ipc::EConnectState::connected, 1000)) << "Registry listener didn't see 'connected'."; // --------- Unregister the registry listener ---------- - server->UnregisterStatusEventCallback(cookie); + server->UnregisterStateEventCallback(cookie); - // Trigger a disconnect on client to force status transitions + // Trigger a disconnect on client to force state transitions client->Disconnect(); // The connection receiver (recvConn) still sees disconnected (normal path) - EXPECT_TRUE(recvConn.WaitForStatus(sdv::ipc::EConnectStatus::disconnected, 1000)) << "Connection receiver didn't see 'disconnected'."; + EXPECT_TRUE(recvConn.WaitForState(sdv::ipc::EConnectState::disconnected, 1000)) << "Connection receiver didn't see 'disconnected'."; // The registry listener (recvReg) MUST NOT receive 'disconnected' anymore // (wait a short, deterministic interval) - EXPECT_FALSE(recvReg.WaitForStatus(sdv::ipc::EConnectStatus::disconnected, 300)) << "Registry listener received 'disconnected' after UnregisterStatusEventCallback()."; + EXPECT_FALSE(recvReg.WaitForState(sdv::ipc::EConnectState::disconnected, 300)) << "Registry listener received 'disconnected' after UnregisterStateEventCallback()."; // Server cleanup server->Disconnect(); diff --git a/tests/unit_tests/unix_tunnel/CMakeLists.txt b/tests/unit_tests/unix_tunnel/CMakeLists.txt new file mode 100644 index 0000000..2ca7a0d --- /dev/null +++ b/tests/unit_tests/unix_tunnel/CMakeLists.txt @@ -0,0 +1,107 @@ +#******************************************************************************* +# Copyright (c) 2025-2026 ZF Friedrichshafen AG +# +# This program and the accompanying materials are made available under the +# terms of the Apache License Version 2.0 which is available at +# https://www.apache.org/licenses/LICENSE-2.0 +# +# SPDX-License-Identifier: Apache-2.0 +# +# Contributors: +# Denisa Ros - initial API and implementation +#******************************************************************************* +if(UNIX) +cmake_minimum_required(VERSION 3.20) + +project(UnixTunnelCommunicationTests LANGUAGES CXX) + +set(CMAKE_CXX_STANDARD 17) +set(CMAKE_CXX_STANDARD_REQUIRED ON) + +# Executable: GTest for UDS connect +add_executable(UnitTest_UnixTunnelConnectTests + unix_tunnel_connect_tests.cpp +) + +target_include_directories(UnitTest_UnixTunnelConnectTests PUBLIC + ${CMAKE_CURRENT_SOURCE_DIR} +) + +if (CMAKE_CXX_COMPILER_ID STREQUAL "GNU") + target_link_libraries(UnitTest_UnixTunnelConnectTests + GTest::GTest + ${CMAKE_THREAD_LIBS_INIT} + stdc++fs + ${CMAKE_DL_LIBS} + rt + uds_unix_tunnel + uds_unix_sockets + ) +else() + target_link_libraries(UnitTest_UnixTunnelConnectTests + GTest::GTest + Ws2_32 Winmm + Rpcrt4.lib + uds_unix_tunnel + uds_unix_sockets + ) +endif() + +add_test(NAME UnitTest_UnixTunnelConnectTests + COMMAND UnitTest_UnixTunnelConnectTests) + +add_dependencies(UnitTest_UnixTunnelConnectTests dependency_sdv_components) + +if ((NOT CMAKE_CXX_COMPILER_ID STREQUAL "GNU") OR (NOT WIN32)) +add_custom_command(TARGET UnitTest_UnixTunnelConnectTests POST_BUILD + COMMAND ${CMAKE_COMMAND} -E env TEST_EXECUTION_MODE=CMake + "$" + --gtest_output=xml:${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/UnitTest_UnixTunnelConnectTests.xml + VERBATIM +) +endif() + +add_executable(UnitTest_UnixTunnelChannelMgntTests + unix_tunnel_channel_mgnt_tests.cpp +) + +target_include_directories(UnitTest_UnixTunnelChannelMgntTests PUBLIC + ${CMAKE_CURRENT_SOURCE_DIR} +) + +if (CMAKE_CXX_COMPILER_ID STREQUAL "GNU") + target_link_libraries(UnitTest_UnixTunnelChannelMgntTests + GTest::GTest + ${CMAKE_THREAD_LIBS_INIT} + stdc++fs + ${CMAKE_DL_LIBS} + rt + uds_unix_tunnel + uds_unix_sockets + ) +else() + target_link_libraries(UnitTest_UnixTunnelChannelMgntTests + GTest::GTest + Ws2_32 + Winmm + Rpcrt4.lib + uds_unix_tunnel + uds_unix_sockets + ) +endif() + +add_test(NAME UnitTest_UnixTunnelChannelMgntTests + COMMAND UnitTest_UnixTunnelChannelMgntTests) + +add_dependencies(UnitTest_UnixTunnelChannelMgntTests dependency_sdv_components) + +if ((NOT CMAKE_CXX_COMPILER_ID STREQUAL "GNU") OR (NOT WIN32)) +add_custom_command(TARGET UnitTest_UnixTunnelChannelMgntTests POST_BUILD + COMMAND ${CMAKE_COMMAND} -E env TEST_EXECUTION_MODE=CMake + "$" + --gtest_output=xml:${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/UnitTest_UnixTunnelChannelMgntTests.xml + VERBATIM +) +endif() + +endif() \ No newline at end of file diff --git a/tests/unit_tests/unix_tunnel/unix_tunnel_channel_mgnt_tests.cpp b/tests/unit_tests/unix_tunnel/unix_tunnel_channel_mgnt_tests.cpp new file mode 100644 index 0000000..9568e0c --- /dev/null +++ b/tests/unit_tests/unix_tunnel/unix_tunnel_channel_mgnt_tests.cpp @@ -0,0 +1,240 @@ +#if defined(__unix__) + +#include "gtest/gtest.h" + +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#include "../sdv_services/uds_unix_tunnel/channel_mgnt.h" +#include "../sdv_services/uds_unix_tunnel/connection.h" +#include "../sdv_services/uds_unix_sockets/connection.h" + +// Reuse the same CTunnelTestReceiver from unix_tunnel_connect_tests +class CTunnelMgrTestReceiver : + public sdv::IInterfaceAccess, + public sdv::ipc::IDataReceiveCallback, + public sdv::ipc::IConnectEventCallback +{ +public: + BEGIN_SDV_INTERFACE_MAP() + SDV_INTERFACE_ENTRY(sdv::ipc::IDataReceiveCallback) + SDV_INTERFACE_ENTRY(sdv::ipc::IConnectEventCallback) + END_SDV_INTERFACE_MAP() + + void ReceiveData(sdv::sequence>& seqData) override + { + { + std::lock_guard lk(m_mtx); + m_lastData = seqData; + m_received = true; + } + m_cv.notify_all(); + } + + void SetConnectState(sdv::ipc::EConnectState s) override + { + { + std::lock_guard lk(m_mtx); + m_state = s; + } + m_cv.notify_all(); + } + + bool WaitForState(sdv::ipc::EConnectState expected, uint32_t ms = 2000) + { + std::unique_lock lk(m_mtx); + if (m_state == expected) + return true; + auto deadline = std::chrono::steady_clock::now() + std::chrono::milliseconds(ms); + while (m_state != expected) + { + if (m_cv.wait_until(lk, deadline) == std::cv_status::timeout) + return false; + } + return true; + } + + bool WaitForData(uint32_t ms = 2000) + { + std::unique_lock lk(m_mtx); + return m_cv.wait_for(lk, std::chrono::milliseconds(ms), [&]{ return m_received; }); + } + + sdv::sequence> GetLastData() const + { + std::lock_guard lk(m_mtx); + return m_lastData; + } + +private: + mutable std::mutex m_mtx; + std::condition_variable m_cv; + sdv::ipc::EConnectState m_state{ sdv::ipc::EConnectState::uninitialized }; + sdv::sequence> m_lastData; + bool m_received{ false }; +}; + +//Manager instantiate + lifecycle +TEST(UnixTunnelChannelMgnt, InstantiateAndLifecycle) +{ + sdv::app::CAppControl app; + ASSERT_TRUE(app.Startup("")); + CUnixTunnelChannelMgnt mgr; + + EXPECT_NO_THROW(mgr.Initialize("")); + EXPECT_EQ(mgr.GetObjectState(), sdv::EObjectState::initialized); + + EXPECT_NO_THROW(mgr.SetOperationMode(sdv::EOperationMode::configuring)); + EXPECT_EQ(mgr.GetObjectState(), sdv::EObjectState::configuring); + + EXPECT_NO_THROW(mgr.SetOperationMode(sdv::EOperationMode::running)); + EXPECT_EQ(mgr.GetObjectState(), sdv::EObjectState::running); + + EXPECT_NO_THROW(mgr.Shutdown()); + EXPECT_EQ(mgr.GetObjectState(), sdv::EObjectState::destruction_pending); + + app.Shutdown(); +} + +// CreateEndpoint -> Access(server/client) -> AsyncConnect -> Wait -> Disconnect +TEST(UnixTunnelChannelMgnt, BasicConnectDisconnect) +{ + sdv::app::CAppControl app; + ASSERT_TRUE(app.Startup("")); + app.SetRunningMode(); + + CUnixTunnelChannelMgnt mgr; + EXPECT_NO_THROW(mgr.Initialize("")); + EXPECT_NO_THROW(mgr.SetOperationMode(sdv::EOperationMode::running)); + ASSERT_EQ(mgr.GetObjectState(), sdv::EObjectState::running); + + // Create a tunnel endpoint (server) + auto ep = mgr.CreateEndpoint(""); + ASSERT_NE(ep.pConnection, nullptr); + ASSERT_FALSE(ep.ssConnectString.empty()); + + const std::string serverCS = ep.ssConnectString; + // Convert to client by role=client + std::string clientCS = serverCS; + { + const std::string from = "role=server"; + const std::string to = "role=client"; + auto pos = clientCS.find(from); + if (pos != std::string::npos) + clientCS.replace(pos, from.size(), to); + } + + // SERVER + sdv::TObjectPtr serverObj = mgr.Access(serverCS); + ASSERT_TRUE(serverObj); + auto* serverConn = serverObj.GetInterface(); + ASSERT_NE(serverConn, nullptr); + + CTunnelMgrTestReceiver sRcvr; + ASSERT_TRUE(serverConn->AsyncConnect(&sRcvr)); + + // CLIENT (thread) + std::atomic clientResult{0}; + std::thread clientThread([&]{ + sdv::TObjectPtr clientObj = mgr.Access(clientCS); + if (!clientObj) { clientResult = 1; return; } + auto* clientConn = clientObj.GetInterface(); + if (!clientConn) { clientResult = 2; return; } + CTunnelMgrTestReceiver cRcvr; + if (!clientConn->AsyncConnect(&cRcvr)) { clientResult = 3; return; } + if (!clientConn->WaitForConnection(5000)) { clientResult = 4; return; } + if (clientConn->GetConnectState() != sdv::ipc::EConnectState::connected) { clientResult = 5; return; } + clientConn->Disconnect(); + clientResult = 0; + }); + + EXPECT_TRUE(serverConn->WaitForConnection(5000)); + EXPECT_EQ(serverConn->GetConnectState(), sdv::ipc::EConnectState::connected); + + clientThread.join(); + EXPECT_EQ(clientResult.load(), 0); + + serverConn->Disconnect(); + + EXPECT_NO_THROW(mgr.Shutdown()); + app.Shutdown(); +} + +// Data path: "hello" via channel manager (using proto=tunnel) +TEST(UnixTunnelChannelMgnt, DataPath_SimpleHello_ViaManager) +{ + sdv::app::CAppControl app; + ASSERT_TRUE(app.Startup("")); + app.SetRunningMode(); + + CUnixTunnelChannelMgnt mgr; + EXPECT_NO_THROW(mgr.Initialize("")); + EXPECT_NO_THROW(mgr.SetOperationMode(sdv::EOperationMode::running)); + ASSERT_EQ(mgr.GetObjectState(), sdv::EObjectState::running); + + auto ep = mgr.CreateEndpoint(""); + ASSERT_FALSE(ep.ssConnectString.empty()); + const std::string serverCS = ep.ssConnectString; + + std::string clientCS = serverCS; + { + const std::string from = "role=server"; + const std::string to = "role=client"; + auto pos = clientCS.find(from); + if (pos != std::string::npos) + clientCS.replace(pos, from.size(), to); + } + + // Server + sdv::TObjectPtr serverObj = mgr.Access(serverCS); + ASSERT_TRUE(serverObj); + auto* serverConn = serverObj.GetInterface(); + ASSERT_NE(serverConn, nullptr); + CTunnelMgrTestReceiver sRcvr; + ASSERT_TRUE(serverConn->AsyncConnect(&sRcvr)); + + // Client + sdv::TObjectPtr clientObj = mgr.Access(clientCS); + ASSERT_TRUE(clientObj); + auto* clientConn = clientObj.GetInterface(); + ASSERT_NE(clientConn, nullptr); + CTunnelMgrTestReceiver cRcvr; + ASSERT_TRUE(clientConn->AsyncConnect(&cRcvr)); + + EXPECT_TRUE(serverConn->WaitForConnection(5000)); + EXPECT_TRUE(clientConn->WaitForConnection(5000)); + + // Payload "hello" + sdv::pointer p; + p.resize(5); + std::memcpy(p.get(), "hello", 5); + sdv::sequence> seq; + seq.push_back(p); + + auto* pSend = dynamic_cast(clientConn); + ASSERT_NE(pSend, nullptr); + EXPECT_TRUE(pSend->SendData(seq)); + + EXPECT_TRUE(sRcvr.WaitForData(3000)); + sdv::sequence> recv = sRcvr.GetLastData(); + + ASSERT_EQ(recv.size(), 1u); + ASSERT_EQ(recv[0].size(), 5u); + EXPECT_EQ(std::memcmp(recv[0].get(), "hello", 5), 0); + + clientConn->Disconnect(); + serverConn->Disconnect(); + + EXPECT_NO_THROW(mgr.Shutdown()); + app.Shutdown(); +} + +#endif // defined(__unix__) diff --git a/tests/unit_tests/unix_tunnel/unix_tunnel_connect_tests.cpp b/tests/unit_tests/unix_tunnel/unix_tunnel_connect_tests.cpp new file mode 100644 index 0000000..311f787 --- /dev/null +++ b/tests/unit_tests/unix_tunnel/unix_tunnel_connect_tests.cpp @@ -0,0 +1,376 @@ +/******************************************************************************** + * Copyright (c) 2025-2026 ZF Friedrichshafen AG + * + * This program and the accompanying materials are made available under the + * terms of the Apache License Version 2.0 which is available at + * https://www.apache.org/licenses/LICENSE-2.0 + * + * SPDX-License-Identifier: Apache-2.0 + * + * Contributors: + * Denisa Ros - initial API and implementation + ********************************************************************************/ + +#if defined(__unix__) + +#include "gtest/gtest.h" + +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +// Adjust include paths to your tree layout: +#include "../sdv_services/uds_unix_sockets/connection.h" // CUnixSocketConnection +#include "../sdv_services/uds_unix_tunnel/connection.h" // CUnixTunnelConnection + +// ===================== Test helpers ===================== +class CTunnelTestReceiver : + public sdv::IInterfaceAccess, + public sdv::ipc::IDataReceiveCallback, + public sdv::ipc::IConnectEventCallback +{ +public: + BEGIN_SDV_INTERFACE_MAP() + SDV_INTERFACE_ENTRY(sdv::ipc::IDataReceiveCallback) + SDV_INTERFACE_ENTRY(sdv::ipc::IConnectEventCallback) + END_SDV_INTERFACE_MAP() + + // IDataReceiveCallback + void ReceiveData(/*inout*/ sdv::sequence>& seqData) override + { + { + std::lock_guard lk(m_mtx); + m_lastData = seqData; + m_received = true; + } + m_cv.notify_all(); + } + + // IConnectEventCallback + void SetConnectState(sdv::ipc::EConnectState s) override + { + { + std::lock_guard lk(m_mtx); + m_state = s; + } + m_cv.notify_all(); + } + + bool WaitForState(sdv::ipc::EConnectState expected, uint32_t ms = 2000) + { + std::unique_lock lk(m_mtx); + if (m_state == expected) + return true; + + auto deadline = std::chrono::steady_clock::now() + std::chrono::milliseconds(ms); + while (m_state != expected) + { + if (m_cv.wait_until(lk, deadline) == std::cv_status::timeout) + return false; + } + return true; + } + + bool WaitForData(uint32_t ms = 2000) + { + std::unique_lock lk(m_mtx); + return m_cv.wait_for(lk, std::chrono::milliseconds(ms), [&]{ return m_received; }); + } + + sdv::sequence> GetLastData() const + { + std::lock_guard lk(m_mtx); + return m_lastData; + } + + sdv::ipc::EConnectState GetConnectState() const + { + std::lock_guard lk(m_mtx); + return m_state; + } + +private: + mutable std::mutex m_mtx; + std::condition_variable m_cv; + + sdv::ipc::EConnectState m_state { sdv::ipc::EConnectState::uninitialized }; + sdv::sequence> m_lastData; + bool m_received { false }; +}; + +// Small helper similar to MakeRandomSuffix() in uds_connect_tests.cpp +static std::string MakeRandomSuffix() +{ + std::mt19937_64 rng{std::random_device{}()}; + std::uniform_int_distribution dist; + std::ostringstream oss; + oss << std::hex << dist(rng); + return oss.str(); +} + +// ===================== Tests ===================== + +// BASIC: server + client CUnixSocketConnection wrapped by CUnixTunnelConnection. +// Just test connect + disconnect via tunnel. +TEST(UnixTunnelIPC, BasicConnectDisconnectViaTunnel) +{ + const std::string udsPath = std::string("/tmp/sdv_tunnel_") + MakeRandomSuffix() + ".sock"; + + // --- Physical transports (UDS) --- + auto serverTransport = std::make_shared( + -1, /*preconfiguredFd*/ + true, /*acceptConnectionRequired (server)*/ + udsPath); + + auto clientTransport = std::make_shared( + -1, + false, /*client*/ + udsPath); + + // --- Tunnel wrappers --- + auto serverTunnel = std::make_shared( + serverTransport, + /*channelId*/ 1); + + auto clientTunnel = std::make_shared( + clientTransport, + /*channelId*/ 1); + + CTunnelTestReceiver serverRcvr; + CTunnelTestReceiver clientRcvr; + + // Register state callbacks (optional, but useful) + uint64_t srvCookie = serverTunnel->RegisterStateEventCallback(&serverRcvr); + uint64_t cliCookie = clientTunnel->RegisterStateEventCallback(&clientRcvr); + EXPECT_NE(srvCookie, 0u); + EXPECT_NE(cliCookie, 0u); + + // Async connect on both ends + ASSERT_TRUE(serverTunnel->AsyncConnect(&serverRcvr)); + ASSERT_TRUE(clientTunnel->AsyncConnect(&clientRcvr)); + + // Wait for connections + EXPECT_TRUE(serverTunnel->WaitForConnection(5000)); + EXPECT_TRUE(clientTunnel->WaitForConnection(5000)); + + EXPECT_EQ(serverTunnel->GetConnectState(), sdv::ipc::EConnectState::connected); + EXPECT_EQ(clientTunnel->GetConnectState(), sdv::ipc::EConnectState::connected); + + // Disconnect client first + clientTunnel->Disconnect(); + EXPECT_TRUE(clientRcvr.WaitForState(sdv::ipc::EConnectState::disconnected, 2000)); + + // Disconnect server + serverTunnel->Disconnect(); + EXPECT_TRUE(serverRcvr.WaitForState(sdv::ipc::EConnectState::disconnected, 2000)); + + serverTunnel->UnregisterStateEventCallback(srvCookie); + clientTunnel->UnregisterStateEventCallback(cliCookie); +} + +// DATA PATH: send "hello" via tunnel and verify it is received on server side. +// For now the tunnel is "pass-through" (no STunnelHeader yet). +TEST(UnixTunnelIPC, DataPath_SimpleHello_ViaTunnel) +{ + const std::string udsPath = std::string("/tmp/sdv_tunnel_data_") + MakeRandomSuffix() + ".sock"; + + // Physical transports + auto serverTransport = std::make_shared( + -1, true, udsPath); + auto clientTransport = std::make_shared( + -1, false, udsPath); + + // Tunnel wrappers (same logical channel for both ends) + auto serverTunnel = std::make_shared(serverTransport, 1); + auto clientTunnel = std::make_shared(clientTransport, 1); + + CTunnelTestReceiver serverRcvr; + CTunnelTestReceiver clientRcvr; + + ASSERT_TRUE(serverTunnel->AsyncConnect(&serverRcvr)); + ASSERT_TRUE(clientTunnel->AsyncConnect(&clientRcvr)); + + ASSERT_TRUE(serverTunnel->WaitForConnection(5000)); + ASSERT_TRUE(clientTunnel->WaitForConnection(5000)); + + // Build "hello" payload + sdv::sequence> seq; + sdv::pointer p; + const char* msg = "hello"; + const size_t len = std::strlen(msg); + p.resize(len); + std::memcpy(p.get(), msg, len); + seq.push_back(p); + + auto* pSend = dynamic_cast(clientTunnel.get()); + ASSERT_NE(pSend, nullptr); + + EXPECT_TRUE(pSend->SendData(seq)); + + // Wait for server-side data callback + ASSERT_TRUE(serverRcvr.WaitForData(3000)); + sdv::sequence> recv = serverRcvr.GetLastData(); + + ASSERT_EQ(recv.size(), 1u); + ASSERT_EQ(recv[0].size(), len); + EXPECT_EQ(std::memcmp(recv[0].get(), msg, len), 0); + + clientTunnel->Disconnect(); + serverTunnel->Disconnect(); +} + +TEST(UnixTunnelIPC, DataPath_MultiChunk_ViaTunnel) +{ + const std::string udsPath = std::string("/tmp/sdv_tunnel_multi_") + MakeRandomSuffix() + ".sock"; + + auto serverTransport = std::make_shared(-1, true, udsPath); + auto clientTransport = std::make_shared(-1, false, udsPath); + + auto serverTunnel = std::make_shared(serverTransport, 1); + auto clientTunnel = std::make_shared(clientTransport, 1); + + CTunnelTestReceiver serverRcvr; + CTunnelTestReceiver clientRcvr; + + ASSERT_TRUE(serverTunnel->AsyncConnect(&serverRcvr)); + ASSERT_TRUE(clientTunnel->AsyncConnect(&clientRcvr)); + ASSERT_TRUE(serverTunnel->WaitForConnection(5000)); + ASSERT_TRUE(clientTunnel->WaitForConnection(5000)); + + // Two chunks: "sdv" and "framework" + sdv::pointer p1, p2; + p1.resize(3); + std::memcpy(p1.get(), "sdv", 3); + p2.resize(9); + std::memcpy(p2.get(), "framework", 9); + + sdv::sequence> seq; + seq.push_back(p1); + seq.push_back(p2); + + auto* pSend = dynamic_cast(clientTunnel.get()); + ASSERT_NE(pSend, nullptr); + EXPECT_TRUE(pSend->SendData(seq)); + + ASSERT_TRUE(serverRcvr.WaitForData(3000)); + sdv::sequence> recv = serverRcvr.GetLastData(); + + ASSERT_EQ(recv.size(), 2u); + EXPECT_EQ(recv[0].size(), 3u); + EXPECT_EQ(recv[1].size(), 9u); + EXPECT_EQ(std::memcmp(recv[0].get(), "sdv", 3), 0); + EXPECT_EQ(std::memcmp(recv[1].get(), "framework", 9), 0); + + clientTunnel->Disconnect(); + serverTunnel->Disconnect(); +} + +TEST(UnixTunnelIPC, DataPath_LargePayload_Fragmentation_ViaTunnel) +{ + const std::string udsPath = std::string("/tmp/sdv_tunnel_large_") + MakeRandomSuffix() + ".sock"; + + auto serverTransport = std::make_shared(-1, true, udsPath); + auto clientTransport = std::make_shared(-1, false, udsPath); + + auto serverTunnel = std::make_shared(serverTransport, 1); + auto clientTunnel = std::make_shared(clientTransport, 1); + + CTunnelTestReceiver serverRcvr; + CTunnelTestReceiver clientRcvr; + + ASSERT_TRUE(serverTunnel->AsyncConnect(&serverRcvr)); + ASSERT_TRUE(clientTunnel->AsyncConnect(&clientRcvr)); + ASSERT_TRUE(serverTunnel->WaitForConnection(5000)); + ASSERT_TRUE(clientTunnel->WaitForConnection(5000)); + + // Build a large payload (e.g. 256 KiB) + const size_t totalBytes = 256 * 1024; + sdv::pointer big; + big.resize(totalBytes); + for (size_t i = 0; i < totalBytes; ++i) + big.get()[i] = static_cast(i & 0xFF); + + sdv::sequence> seq; + seq.push_back(big); + + auto* pSend = dynamic_cast(clientTunnel.get()); + ASSERT_NE(pSend, nullptr); + EXPECT_TRUE(pSend->SendData(seq)); + + ASSERT_TRUE(serverRcvr.WaitForData(5000)); + sdv::sequence> recv = serverRcvr.GetLastData(); + + ASSERT_EQ(recv.size(), 1u); + ASSERT_EQ(recv[0].size(), totalBytes); + EXPECT_EQ(std::memcmp(recv[0].get(), big.get(), totalBytes), 0); + + clientTunnel->Disconnect(); + serverTunnel->Disconnect(); +} + +// DATA PATH + HEADER: ensure that the upper receiver sees only the original +// payload (no STunnelHeader bytes in front) +TEST(UnixTunnelIPC, DataPath_HeaderStripped_ForUpperReceiver) +{ + const std::string udsPath = + std::string("/tmp/sdv_tunnel_header_") + MakeRandomSuffix() + ".sock"; + + // Physical transports (UDS) + auto serverTransport = std::make_shared( + -1, /*server*/ true, udsPath); + auto clientTransport = std::make_shared( + -1, /*client*/ false, udsPath); + + // Tunnel wrappers + auto serverTunnel = std::make_shared(serverTransport, /*channelId*/ 42); + auto clientTunnel = std::make_shared(clientTransport, /*channelId*/ 42); + + CTunnelTestReceiver serverRcvr; + CTunnelTestReceiver clientRcvr; + + ASSERT_TRUE(serverTunnel->AsyncConnect(&serverRcvr)); + ASSERT_TRUE(clientTunnel->AsyncConnect(&clientRcvr)); + ASSERT_TRUE(serverTunnel->WaitForConnection(5000)); + ASSERT_TRUE(clientTunnel->WaitForConnection(5000)); + + // Build payload "HEADER_TEST" + const char* msg = "HEADER_TEST"; + const size_t len = std::strlen(msg); + + sdv::pointer p; + p.resize(len); + std::memcpy(p.get(), msg, len); + + sdv::sequence> seq; + seq.push_back(p); + + auto* pSend = dynamic_cast(clientTunnel.get()); + ASSERT_NE(pSend, nullptr); + EXPECT_TRUE(pSend->SendData(seq)); + + // Wait for server-side data callback + ASSERT_TRUE(serverRcvr.WaitForData(3000)); + auto recv = serverRcvr.GetLastData(); + + // --- Assertions that header was stripped for upper receiver --- + // Upper receiver must see exactly one chunk with the original size, without extra bytes for STunnelHeader + ASSERT_EQ(recv.size(), 1u) << "Upper receiver must see exactly one payload chunk"; + ASSERT_EQ(recv[0].size(), len) << "Payload size must equal original size (no header)"; + + EXPECT_EQ(std::memcmp(recv[0].get(), msg, len), 0) + << "Payload content must match exactly the original message (no header in front)"; + + clientTunnel->Disconnect(); + serverTunnel->Disconnect(); +} + +#endif // defined(__unix__) \ No newline at end of file diff --git a/tests/unit_tests/win_sockets/CMakeLists.txt b/tests/unit_tests/win_sockets/CMakeLists.txt index 9d64222..4e38239 100644 --- a/tests/unit_tests/win_sockets/CMakeLists.txt +++ b/tests/unit_tests/win_sockets/CMakeLists.txt @@ -7,7 +7,7 @@ # # SPDX-License-Identifier: Apache-2.0 #******************************************************************************* - +if(WIN32) cmake_minimum_required(VERSION 3.20) project(WinSocketCommunicationTests LANGUAGES CXX) @@ -36,6 +36,7 @@ target_include_directories(UnitTest_WinSocketConnectTests Ws2_32 Winmm Rpcrt4 + uds_win_sockets ) add_test(NAME UnitTest_WinSocketConnectTests @@ -50,4 +51,6 @@ add_custom_command(TARGET UnitTest_WinSocketConnectTests POST_BUILD --gtest_output=xml:${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/UnitTest_WinSocketConnectTests.xml VERBATIM ) +endif() + endif() \ No newline at end of file diff --git a/tests/unit_tests/win_sockets/win_connect_tests.cpp b/tests/unit_tests/win_sockets/win_connect_tests.cpp index e6b2446..d9d24d6 100644 --- a/tests/unit_tests/win_sockets/win_connect_tests.cpp +++ b/tests/unit_tests/win_sockets/win_connect_tests.cpp @@ -15,8 +15,8 @@ #include #include -#include "../../../sdv_services/uds_win_sockets/channel_mgnt.cpp" -#include "../../../sdv_services/uds_win_sockets/connection.cpp" +#include "../../../sdv_services/uds_win_sockets/channel_mgnt.h" +#include "../../../sdv_services/uds_win_sockets/connection.h" #include #include @@ -99,7 +99,7 @@ inline void SpinUntilServerArmed(sdv::ipc::IConnect* server, uint32_t maxWaitMs using namespace std::chrono; auto deadline = steady_clock::now() + milliseconds(maxWaitMs); - while (server->GetStatus() == sdv::ipc::EConnectStatus::uninitialized && + while (server->GetConnectState() == sdv::ipc::EConnectState::uninitialized && steady_clock::now() < deadline) { std::this_thread::sleep_for(std::chrono::milliseconds(1)); @@ -116,7 +116,7 @@ inline void SleepTiny(uint32_t ms = 20) } // namespace test_utils -// Unified test receiver (status + data) +// Unified test receiver (state + data) class CTestReceiver : public sdv::IInterfaceAccess, public sdv::ipc::IConnectEventCallback, @@ -128,11 +128,11 @@ public: SDV_INTERFACE_ENTRY(sdv::ipc::IConnectEventCallback) END_SDV_INTERFACE_MAP() - void SetStatus(sdv::ipc::EConnectStatus s) override + void SetConnectState(sdv::ipc::EConnectState s) override { { std::lock_guard lk(m_mtx); - m_status = s; + m_state = s; } m_cv.notify_all(); } @@ -147,11 +147,11 @@ public: m_cv.notify_all(); } - bool WaitForStatus(sdv::ipc::EConnectStatus expected, uint32_t ms = 2000) + bool WaitForState(sdv::ipc::EConnectState expected, uint32_t ms = 2000) { std::unique_lock lk(m_mtx); return m_cv.wait_for(lk, std::chrono::milliseconds(ms), - [&]{ return m_status == expected; }); + [&]{ return m_state == expected; }); } bool WaitForData(uint32_t ms = 2000) @@ -171,7 +171,7 @@ private: mutable std::mutex m_mtx; std::condition_variable m_cv; - sdv::ipc::EConnectStatus m_status{ sdv::ipc::EConnectStatus::uninitialized }; + sdv::ipc::EConnectState m_state{ sdv::ipc::EConnectState::uninitialized }; sdv::sequence> m_data; bool m_hasData{false}; }; @@ -344,7 +344,7 @@ TEST(WindowsAFUnixIPC, ServerDisconnectPropagates) pair.server->Disconnect(); - EXPECT_TRUE(cr.WaitForStatus(sdv::ipc::EConnectStatus::disconnected, 3000)); + EXPECT_TRUE(cr.WaitForState(sdv::ipc::EConnectState::disconnected, 3000)); pair.client->Disconnect(); @@ -713,7 +713,8 @@ TEST(WindowsAFUnixIPC, WaitForConnection_ZeroTimeout_BeforeAndAfter) TEST(WindowsAFUnixIPC, ClientTimeout_NoServer) { sdv::app::CAppControl app; - ASSERT_TRUE(app.Startup("")); + ASSERT_TRUE(app.Startup(R"toml([LogHandler] +ViewFilter = "Fatal")toml")); app.SetRunningMode(); CSocketsChannelMgnt mgr; @@ -742,7 +743,7 @@ TEST(WindowsAFUnixIPC, ClientTimeout_NoServer) EXPECT_FALSE(client->WaitForConnection(1500)); std::this_thread::sleep_for(std::chrono::milliseconds(800)); - EXPECT_EQ(client->GetStatus(), sdv::ipc::EConnectStatus::connection_error); + EXPECT_EQ(client->GetConnectState(), sdv::ipc::EConnectState::connection_error); client->Disconnect(); mgr.Shutdown(); @@ -799,7 +800,7 @@ TEST(WindowsAFUnixIPC, PeerCloseMidTransfer_ClientDetectsDisconnect) t.join(); - EXPECT_TRUE(cr.WaitForStatus(sdv::ipc::EConnectStatus::disconnected, 3000)); + EXPECT_TRUE(cr.WaitForState(sdv::ipc::EConnectState::disconnected, 3000)); pair.client->Disconnect(); mgr.Shutdown(); @@ -810,7 +811,8 @@ TEST(WindowsAFUnixIPC, PeerCloseMidTransfer_ClientDetectsDisconnect) TEST(WindowsAFUnixIPC, ClientCancelConnect_NoServer_Cleanup) { sdv::app::CAppControl app; - ASSERT_TRUE(app.Startup("")); + ASSERT_TRUE(app.Startup(R"toml([LogHandler] +ViewFilter = "Fatal")toml")); app.SetRunningMode(); CSocketsChannelMgnt mgr; @@ -838,7 +840,7 @@ TEST(WindowsAFUnixIPC, ClientCancelConnect_NoServer_Cleanup) std::this_thread::sleep_for(std::chrono::milliseconds(150)); client->Disconnect(); - EXPECT_EQ(client->GetStatus(), sdv::ipc::EConnectStatus::disconnected); + EXPECT_EQ(client->GetConnectState(), sdv::ipc::EConnectState::disconnected); mgr.Shutdown(); app.Shutdown(); @@ -848,7 +850,8 @@ TEST(WindowsAFUnixIPC, ClientCancelConnect_NoServer_Cleanup) TEST(WindowsAFUnixIPC, ServerStartThenImmediateDisconnect_NoClient) { sdv::app::CAppControl app; - ASSERT_TRUE(app.Startup("")); + ASSERT_TRUE(app.Startup(R"toml([LogHandler] +ViewFilter = "Fatal")toml")); app.SetRunningMode(); CSocketsChannelMgnt mgr; @@ -872,14 +875,14 @@ TEST(WindowsAFUnixIPC, ServerStartThenImmediateDisconnect_NoClient) std::this_thread::sleep_for(std::chrono::milliseconds(50)); server->Disconnect(); - EXPECT_EQ(server->GetStatus(), sdv::ipc::EConnectStatus::disconnected); + EXPECT_EQ(server->GetConnectState(), sdv::ipc::EConnectState::disconnected); mgr.Shutdown(); app.Shutdown(); } -// UnregisterStatusEventCallback: ensure single-listener semantics -TEST(WindowsAFUnixIPC, UnregisterStatusEventCallback_SingleListenerSemantics) +// UnregisterStateEventCallback: ensure single-listener semantics +TEST(WindowsAFUnixIPC, UnregisterStateEventCallback_SingleListenerSemantics) { sdv::app::CAppControl app; ASSERT_TRUE(app.Startup("")); @@ -905,7 +908,7 @@ TEST(WindowsAFUnixIPC, UnregisterStatusEventCallback_SingleListenerSemantics) ASSERT_NE(client, nullptr); CTestReceiver regListener; - const uint64_t cookie = server->RegisterStatusEventCallback(®Listener); + const uint64_t cookie = server->RegisterStateEventCallback(®Listener); ASSERT_NE(cookie, 0u); CTestReceiver mainRecv; @@ -917,15 +920,15 @@ TEST(WindowsAFUnixIPC, UnregisterStatusEventCallback_SingleListenerSemantics) EXPECT_TRUE(server->WaitForConnection(5000)); EXPECT_TRUE(client->WaitForConnection(5000)); - EXPECT_TRUE(mainRecv.WaitForStatus(sdv::ipc::EConnectStatus::connected, 1000)); - EXPECT_FALSE(regListener.WaitForStatus(sdv::ipc::EConnectStatus::connected, 300)) << "The registry listener should NOT receive events while main receiver is active."; + EXPECT_TRUE(mainRecv.WaitForState(sdv::ipc::EConnectState::connected, 1000)); + EXPECT_FALSE(regListener.WaitForState(sdv::ipc::EConnectState::connected, 300)) << "The registry listener should NOT receive events while main receiver is active."; - server->UnregisterStatusEventCallback(cookie); + server->UnregisterStateEventCallback(cookie); client->Disconnect(); - EXPECT_TRUE(mainRecv.WaitForStatus(sdv::ipc::EConnectStatus::disconnected, 1500)); + EXPECT_TRUE(mainRecv.WaitForState(sdv::ipc::EConnectState::disconnected, 1500)); - EXPECT_FALSE(regListener.WaitForStatus(sdv::ipc::EConnectStatus::disconnected, 300)); + EXPECT_FALSE(regListener.WaitForState(sdv::ipc::EConnectState::disconnected, 300)); server->Disconnect(); mgr.Shutdown(); diff --git a/tests/unit_tests/win_tunnel/CMakeLists.txt b/tests/unit_tests/win_tunnel/CMakeLists.txt new file mode 100644 index 0000000..1263e5b --- /dev/null +++ b/tests/unit_tests/win_tunnel/CMakeLists.txt @@ -0,0 +1,134 @@ +#******************************************************************************* +# Copyright (c) 2025-2026 ZF Friedrichshafen AG +# +# This program and the accompanying materials are made available under the +# terms of the Apache License Version 2.0 which is available at +# https://www.apache.org/licenses/LICENSE-2.0 +# +# SPDX-License-Identifier: Apache-2.0 +# +# Contributors: +# Denisa Ros - initial API and implementation +#******************************************************************************* +if(WIN32) +cmake_minimum_required(VERSION 3.20) + +project(WinTunnelCommunicationTests LANGUAGES CXX) + +set(CMAKE_CXX_STANDARD 17) +set(CMAKE_CXX_STANDARD_REQUIRED ON) + +# Executable: GTest for UDS connect +add_executable(UnitTest_WinTunnelConnectTests + win_tunnel_connect_tests.cpp +) + +target_include_directories(UnitTest_WinTunnelConnectTests + PUBLIC + ${CMAKE_CURRENT_SOURCE_DIR} + ${PROJECT_SOURCE_DIR}/export + ${PROJECT_SOURCE_DIR}/export/support + ${PROJECT_SOURCE_DIR}/export/interfaces + ${PROJECT_SOURCE_DIR}/sdv_services +) + +target_link_libraries(UnitTest_WinTunnelConnectTests + PRIVATE + GTest::GTest + Ws2_32 + Winmm + Rpcrt4 + uds_win_tunnel + uds_win_sockets +) + +add_test(NAME UnitTest_WinTunnelConnectTests + COMMAND UnitTest_WinTunnelConnectTests) + +add_dependencies(UnitTest_WinTunnelConnectTests dependency_sdv_components) + +if ((NOT CMAKE_CXX_COMPILER_ID STREQUAL "GNU") OR (NOT WIN32)) +add_custom_command(TARGET UnitTest_WinTunnelConnectTests POST_BUILD + COMMAND ${CMAKE_COMMAND} -E env TEST_EXECUTION_MODE=CMake + "$" + --gtest_output=xml:${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/UnitTest_WinTunnelConnectTests.xml + VERBATIM +) +endif() + +add_executable(UnitTest_WinTunnelChannelMgntTests + win_tunnel_channel_mgnt_tests.cpp +) + +target_include_directories(UnitTest_WinTunnelChannelMgntTests + PUBLIC + ${CMAKE_CURRENT_SOURCE_DIR} + ${PROJECT_SOURCE_DIR}/export + ${PROJECT_SOURCE_DIR}/export/support + ${PROJECT_SOURCE_DIR}/export/interfaces + ${PROJECT_SOURCE_DIR}/sdv_services +) + +target_link_libraries(UnitTest_WinTunnelChannelMgntTests + PRIVATE + GTest::GTest + Ws2_32 + Winmm + Rpcrt4 + uds_win_tunnel + uds_win_sockets +) + +add_test(NAME UnitTest_WinTunnelChannelMgntTests + COMMAND UnitTest_WinTunnelChannelMgntTests) + +add_dependencies(UnitTest_WinTunnelChannelMgntTests dependency_sdv_components) + +if ((NOT CMAKE_CXX_COMPILER_ID STREQUAL "GNU") OR (NOT WIN32)) +add_custom_command(TARGET UnitTest_WinTunnelChannelMgntTests POST_BUILD + COMMAND ${CMAKE_COMMAND} -E env TEST_EXECUTION_MODE=CMake + "$" + --gtest_output=xml:${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/UnitTest_WinTunnelChannelMgntTests.xml + VERBATIM +) +endif() + +# Add negative/edge case tests +add_executable(UnitTest_WinTunnelNegativeEdgeTests + win_tunnel_negative_edge_tests.cpp +) + +target_include_directories(UnitTest_WinTunnelNegativeEdgeTests + PUBLIC + ${CMAKE_CURRENT_SOURCE_DIR} + ${PROJECT_SOURCE_DIR}/export + ${PROJECT_SOURCE_DIR}/export/support + ${PROJECT_SOURCE_DIR}/export/interfaces + ${PROJECT_SOURCE_DIR}/sdv_services +) + +target_link_libraries(UnitTest_WinTunnelNegativeEdgeTests + PRIVATE + GTest::GTest + Ws2_32 + Winmm + Rpcrt4 + uds_win_tunnel + uds_win_sockets +) + +add_test(NAME UnitTest_WinTunnelNegativeEdgeTests + COMMAND UnitTest_WinTunnelNegativeEdgeTests) + +add_dependencies(UnitTest_WinTunnelNegativeEdgeTests dependency_sdv_components) + +if ((NOT CMAKE_CXX_COMPILER_ID STREQUAL "GNU") OR (NOT WIN32)) +add_custom_command(TARGET UnitTest_WinTunnelNegativeEdgeTests POST_BUILD + COMMAND ${CMAKE_COMMAND} -E env TEST_EXECUTION_MODE=CMake + "$" + --gtest_output=xml:${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/UnitTest_WinTunnelNegativeEdgeTests.xml + VERBATIM +) +endif() + +endif() diff --git a/tests/unit_tests/win_tunnel/win_tunnel_channel_mgnt_tests.cpp b/tests/unit_tests/win_tunnel/win_tunnel_channel_mgnt_tests.cpp new file mode 100644 index 0000000..6f0c638 --- /dev/null +++ b/tests/unit_tests/win_tunnel/win_tunnel_channel_mgnt_tests.cpp @@ -0,0 +1,442 @@ +/******************************************************************************** +* Copyright (c) 2025-2026 ZF Friedrichshafen AG +* +* This program and the accompanying materials are made available under the +* terms of the Apache License Version 2.0 which is available at +* https://www.apache.org/licenses/LICENSE-2.0 +* +* SPDX-License-Identifier: Apache-2.0 +* +* Contributors: +* Denisa Ros - initial API and implementation +********************************************************************************/ +#include "gtest/gtest.h" + +#include +#include + +#include "../../../sdv_services/uds_win_tunnel/channel_mgnt.h" +#include "../../../sdv_services/uds_win_tunnel/connection.h" + +// Reuse helpers from uds_win_sockets (path normalization, MakeShortUdsPath) +#include "../../../sdv_services/uds_win_sockets/channel_mgnt.h" +#include "../../../sdv_services/uds_win_sockets/connection.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +// Helper namespace +namespace tunnel_utils +{ +inline std::string Expand(const std::string& in) +{ + if (in.find('%') == std::string::npos) + return in; + char buf[4096] = {}; + const DWORD n = ExpandEnvironmentStringsA(in.c_str(), buf, sizeof(buf)); + return (n > 0 && n < sizeof(buf)) ? std::string(buf) : in; +} + +inline void EnsureParentDir(const std::string& full) +{ + auto p = full.find_last_of("\\/"); + if (p == std::string::npos) + return; + std::string dir = full.substr(0, p); + CreateDirectoryA(dir.c_str(), nullptr); +} + +inline std::string MakeShortUdsPath(const char* name) +{ + std::string base = R"(%LOCALAPPDATA%\sdv\)"; + base = Expand(base); + EnsureParentDir(base); + return base + name; +} + +inline std::string RandomHex() +{ + std::mt19937_64 r{std::random_device{}()}; + std::uniform_int_distribution d; + std::ostringstream oss; + oss << std::hex << d(r); + return oss.str(); +} + +inline std::string Unique(const char* prefix) +{ + return MakeShortUdsPath((std::string(prefix) + "_" + RandomHex() + ".sock").c_str()); +} + +inline void SpinUntilServerArmed(sdv::ipc::IConnect* server) +{ + using namespace std::chrono; + const auto deadline = steady_clock::now() + milliseconds(500); + while (server->GetConnectState() == sdv::ipc::EConnectState::uninitialized && + steady_clock::now() < deadline) + { + std::this_thread::sleep_for(milliseconds(2)); + } +} + +} // namespace tunnel_utils + +using namespace tunnel_utils; + +// Unified receiver (state + data) +class CTunnelMgrTestReceiver : + public sdv::IInterfaceAccess, + public sdv::ipc::IDataReceiveCallback, + public sdv::ipc::IConnectEventCallback +{ +public: + BEGIN_SDV_INTERFACE_MAP() + SDV_INTERFACE_ENTRY(sdv::ipc::IDataReceiveCallback) + SDV_INTERFACE_ENTRY(sdv::ipc::IConnectEventCallback) + END_SDV_INTERFACE_MAP() + + void SetConnectState(sdv::ipc::EConnectState s) override + { + { std::lock_guard lk(m_mtx); m_state = s; } + m_cv.notify_all(); + } + + void ReceiveData(sdv::sequence>& seq) override + { + { std::lock_guard lk(m_mtx); + m_last = seq; m_has = true; } + m_cv.notify_all(); + } + + bool WaitForState(sdv::ipc::EConnectState s, uint32_t ms = 2000) + { + std::unique_lock lk(m_mtx); + return m_cv.wait_for(lk, std::chrono::milliseconds(ms), + [&]{ return m_state == s; }); + } + + bool WaitForData(uint32_t ms = 2000) + { + std::unique_lock lk(m_mtx); + return m_cv.wait_for(lk, std::chrono::milliseconds(ms), + [&]{ return m_has; }); + } + + sdv::sequence> Data() + { + std::lock_guard lk(m_mtx); + return m_last; + } + +private: + mutable std::mutex m_mtx; + std::condition_variable m_cv; + sdv::ipc::EConnectState m_state{sdv::ipc::EConnectState::uninitialized}; + sdv::sequence> m_last; + bool m_has{false}; +}; + +// Helper to create server + client +struct TunnelPair +{ + sdv::TObjectPtr serverObj; + sdv::ipc::IConnect* server = nullptr; + + sdv::TObjectPtr clientObj; + sdv::ipc::IConnect* client = nullptr; +}; + +static TunnelPair CreateTunnelPair(CSocketsTunnelChannelMgnt& mgr, + const std::string& cs) +{ + TunnelPair out; + out.serverObj = mgr.Access(cs); + out.server = out.serverObj ? + out.serverObj.GetInterface() : nullptr; + + out.clientObj = mgr.Access(cs); + out.client = out.clientObj ? + out.clientObj.GetInterface() : nullptr; + + return out; +} + +// TESTS +// Manager instantiate + lifecycle +TEST(WinTunnelChannelMgnt, InstantiateAndLifecycle) +{ + sdv::app::CAppControl app; + ASSERT_TRUE(app.Startup("")); + + CSocketsTunnelChannelMgnt mgr; + + EXPECT_NO_THROW(mgr.Initialize("")); + EXPECT_EQ(mgr.GetObjectState(), sdv::EObjectState::initialized); + + mgr.SetOperationMode(sdv::EOperationMode::configuring); + EXPECT_EQ(mgr.GetObjectState(), sdv::EObjectState::configuring); + + mgr.SetOperationMode(sdv::EOperationMode::running); + EXPECT_EQ(mgr.GetObjectState(), sdv::EObjectState::running); + + mgr.Shutdown(); + EXPECT_EQ(mgr.GetObjectState(), sdv::EObjectState::destruction_pending); + + app.Shutdown(); +} + +// Basic connect/disconnect using manager (server + client) +TEST(WinTunnelChannelMgnt, BasicConnectDisconnect) +{ + sdv::app::CAppControl app; + ASSERT_TRUE(app.Startup("")); + + app.SetRunningMode(); + CSocketsTunnelChannelMgnt mgr; + mgr.Initialize(""); + mgr.SetOperationMode(sdv::EOperationMode::running); + + const std::string uds = Unique("tunnel_mgr_basic"); + const std::string cs = "proto=tunnel;path=" + uds + ";"; + + auto ep = mgr.CreateEndpoint(cs); + ASSERT_FALSE(ep.ssConnectString.empty()); + + auto pair = CreateTunnelPair(mgr, ep.ssConnectString); + ASSERT_NE(pair.server, nullptr); + ASSERT_NE(pair.client, nullptr); + + CTunnelMgrTestReceiver sr, cr; + + pair.server->AsyncConnect(&sr); + SpinUntilServerArmed(pair.server); + + pair.client->AsyncConnect(&cr); + + EXPECT_TRUE(pair.server->WaitForConnection(5000)); + EXPECT_TRUE(pair.client->WaitForConnection(5000)); + + pair.client->Disconnect(); + pair.server->Disconnect(); + + mgr.Shutdown(); + app.Shutdown(); +} + +// Simple hello (header stripped) +TEST(WinTunnelChannelMgnt, DataPath_SimpleHello_ViaManager) +{ + sdv::app::CAppControl app; + ASSERT_TRUE(app.Startup("")); + app.SetRunningMode(); + + CSocketsTunnelChannelMgnt mgr; + mgr.Initialize(""); + mgr.SetOperationMode(sdv::EOperationMode::running); + + const std::string cs = "proto=tunnel;path=" + Unique("hello_mgr") + ";"; + + auto ep = mgr.CreateEndpoint(cs); + auto pair = CreateTunnelPair(mgr, ep.ssConnectString); + + ASSERT_NE(pair.server, nullptr); + ASSERT_NE(pair.client, nullptr); + + CTunnelMgrTestReceiver sr, cr; + + pair.server->AsyncConnect(&sr); + SpinUntilServerArmed(pair.server); + + pair.client->AsyncConnect(&cr); + + ASSERT_TRUE(pair.server->WaitForConnection(5000)); + ASSERT_TRUE(pair.client->WaitForConnection(5000)); + + sdv::pointer msg; + msg.resize(5); + memcpy(msg.get(), "hello", 5); + + sdv::sequence> seq; + seq.push_back(msg); + + auto* sender = dynamic_cast(pair.client); + ASSERT_NE(sender, nullptr); + + EXPECT_TRUE(sender->SendData(seq)); + ASSERT_TRUE(sr.WaitForData(3000)); + + auto recv = sr.Data(); + ASSERT_EQ(recv.size(), 1u); + ASSERT_EQ(recv[0].size(), 5u); + EXPECT_EQ(memcmp(recv[0].get(), "hello", 5), 0); + + pair.client->Disconnect(); + pair.server->Disconnect(); + + mgr.Shutdown(); + app.Shutdown(); +} + +// Multi-chunk +TEST(WinTunnelChannelMgnt, DataPath_MultiChunk_ViaManager) +{ + sdv::app::CAppControl app; + ASSERT_TRUE(app.Startup("")); + app.SetRunningMode(); + + CSocketsTunnelChannelMgnt mgr; + mgr.Initialize(""); + mgr.SetOperationMode(sdv::EOperationMode::running); + + const std::string cs = "proto=tunnel;path=" + Unique("mc_mgr") + ";"; + + auto ep = mgr.CreateEndpoint(cs); + auto pair = CreateTunnelPair(mgr, ep.ssConnectString); + + ASSERT_NE(pair.server, nullptr); + ASSERT_NE(pair.client, nullptr); + + CTunnelMgrTestReceiver sr, cr; + + pair.server->AsyncConnect(&sr); + SpinUntilServerArmed(pair.server); + + pair.client->AsyncConnect(&cr); + ASSERT_TRUE(pair.server->WaitForConnection(5000)); + ASSERT_TRUE(pair.client->WaitForConnection(5000)); + + sdv::pointer a, b; + a.resize(3); + memcpy(a.get(), "sdv", 3); + b.resize(9); + memcpy(b.get(), "framework", 9); + + sdv::sequence> seq; + seq.push_back(a); + seq.push_back(b); + + auto* sender = dynamic_cast(pair.client); + ASSERT_NE(sender, nullptr); + + EXPECT_TRUE(sender->SendData(seq)); + ASSERT_TRUE(sr.WaitForData(3000)); + + auto recv = sr.Data(); + ASSERT_EQ(recv.size(), 2u); + EXPECT_EQ(memcmp(recv[0].get(), "sdv", 3), 0); + EXPECT_EQ(memcmp(recv[1].get(), "framework", 9), 0); + + pair.client->Disconnect(); + pair.server->Disconnect(); + mgr.Shutdown(); + app.Shutdown(); +} + +// Header stripping invariant +TEST(WinTunnelChannelMgnt, HeaderStrippedInvariant) +{ + sdv::app::CAppControl app; + ASSERT_TRUE(app.Startup("")); + app.SetRunningMode(); + + CSocketsTunnelChannelMgnt mgr; + mgr.Initialize(""); + mgr.SetOperationMode(sdv::EOperationMode::running); + + const std::string cs ="proto=tunnel;path=" + Unique("hdr_mgr") + ";"; + + auto ep = mgr.CreateEndpoint(cs); + auto pair = CreateTunnelPair(mgr, ep.ssConnectString); + ASSERT_NE(pair.server, nullptr); + ASSERT_NE(pair.client, nullptr); + + CTunnelMgrTestReceiver sr, cr; + + pair.server->AsyncConnect(&sr); + SpinUntilServerArmed(pair.server); + + pair.client->AsyncConnect(&cr); + + ASSERT_TRUE(pair.server->WaitForConnection(5000)); + ASSERT_TRUE(pair.client->WaitForConnection(5000)); + + const char* msg = "HDR_TEST"; + const size_t len = strlen(msg); + + sdv::pointer buf; + buf.resize(len); + memcpy(buf.get(), msg, len); + + sdv::sequence> seq; + seq.push_back(buf); + + auto* sender = dynamic_cast(pair.client); + ASSERT_NE(sender, nullptr); + + EXPECT_TRUE(sender->SendData(seq)); + ASSERT_TRUE(sr.WaitForData(3000)); + + auto recv = sr.Data(); + ASSERT_EQ(recv.size(), 1u); + ASSERT_EQ(recv[0].size(), len); + EXPECT_EQ(memcmp(recv[0].get(), msg, len), 0); + + pair.client->Disconnect(); + pair.server->Disconnect(); + mgr.Shutdown(); + app.Shutdown(); +} + +// Long path normalization +TEST(WinTunnelChannelMgnt, CreateEndpoint_LongPath_Normalized) +{ + sdv::app::CAppControl app; + ASSERT_TRUE(app.Startup("")); + app.SetRunningMode(); + + CSocketsTunnelChannelMgnt mgr; + mgr.Initialize(""); + mgr.SetOperationMode(sdv::EOperationMode::running); + + std::string veryLong(200, 'A'); + const std::string raw ="C:\\Users\\" + veryLong + "\\AppData\\Local\\sdv\\tunnel_long_" + RandomHex() + ".sock"; + const std::string cs = "proto=tunnel;path=" + raw + ";"; + + auto ep = mgr.CreateEndpoint(cs); + ASSERT_FALSE(ep.ssConnectString.empty()); + + // path must be normalized (basename only) + EXPECT_NE(ep.ssConnectString.find("path=tunnel_long_"), std::string::npos); + + sdv::TObjectPtr obj = mgr.Access(ep.ssConnectString); + auto* server = obj.GetInterface(); + ASSERT_NE(server, nullptr); + + CTunnelMgrTestReceiver sr; + server->AsyncConnect(&sr); + SpinUntilServerArmed(server); + + // boot client + sdv::TObjectPtr cobj = mgr.Access(ep.ssConnectString); + auto* client = cobj.GetInterface(); + ASSERT_NE(client, nullptr); + + CTunnelMgrTestReceiver cr; + client->AsyncConnect(&cr); + + EXPECT_TRUE(server->WaitForConnection(5000)); + EXPECT_TRUE(client->WaitForConnection(5000)); + + client->Disconnect(); + server->Disconnect(); + mgr.Shutdown(); + app.Shutdown(); +} \ No newline at end of file diff --git a/tests/unit_tests/win_tunnel/win_tunnel_connect_tests.cpp b/tests/unit_tests/win_tunnel/win_tunnel_connect_tests.cpp new file mode 100644 index 0000000..d646769 --- /dev/null +++ b/tests/unit_tests/win_tunnel/win_tunnel_connect_tests.cpp @@ -0,0 +1,454 @@ +/******************************************************************************** +* Copyright (c) 2025-2026 ZF Friedrichshafen AG +* +* This program and the accompanying materials are made available under the +* terms of the Apache License Version 2.0 which is available at +* https://www.apache.org/licenses/LICENSE-2.0 +* +* SPDX-License-Identifier: Apache-2.0 +* +* Contributors: +* Denisa Ros - initial API and implementation +********************************************************************************/ +#include "gtest/gtest.h" +#include +#include + +// Include transport and tunnel manager +#include "../../../sdv_services/uds_win_tunnel/channel_mgnt.h" +#include "../../../sdv_services/uds_win_tunnel/connection.h" + +// Include UDS Windows helpers for path generation +#include "../../../sdv_services/uds_win_sockets/channel_mgnt.h" // ONLY FOR path helpers (MakeShortUdsPath) +#include "../../../sdv_services/uds_win_sockets/connection.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +// Utility namespace from original tests +namespace test_utils +{ +inline std::string Expand(const std::string& in) +{ + if (in.find('%') == std::string::npos) + return in; + char buf[4096] = {}; + DWORD n = ExpandEnvironmentStringsA(in.c_str(), buf, sizeof(buf)); + return (n > 0 && n < sizeof(buf)) ? std::string(buf) : in; +} + +inline void EnsureParentDir(const std::string& fullPath) +{ + auto pos = fullPath.find_last_of("\\/"); + if (pos == std::string::npos) + return; + std::string dir = fullPath.substr(0, pos); + CreateDirectoryA(dir.c_str(), nullptr); +} + +inline std::string MakeShortUdsPath(const char* name) +{ + std::string base = R"(%LOCALAPPDATA%\sdv\)"; + base = Expand(base); + EnsureParentDir(base); + return base + name; +} + +inline std::string RandomHex() +{ + std::mt19937_64 rng{std::random_device{}()}; + std::uniform_int_distribution dist; + std::ostringstream oss; + oss << std::hex << dist(rng); + return oss.str(); +} + +inline std::string UniqueUds(const char* prefix) +{ + return MakeShortUdsPath((std::string(prefix) + "_" + RandomHex() + ".sock").c_str()); +} + +inline void SpinUntilServerArmed(sdv::ipc::IConnect* server, uint32_t maxWaitMs = 300) +{ + using namespace std::chrono; + auto deadline = steady_clock::now() + milliseconds(maxWaitMs); + while (server->GetConnectState() == sdv::ipc::EConnectState::uninitialized && + steady_clock::now() < deadline) + { + std::this_thread::sleep_for(std::chrono::milliseconds(1)); + } +} + +inline void SleepTiny(uint32_t ms = 20) +{ + std::this_thread::sleep_for(std::chrono::milliseconds(ms)); +} + +} // namespace test_utils + +using namespace test_utils; + +// Unified test receiver (status + payload), identical to UDS tests +class CTunnelTestReceiver : + public sdv::IInterfaceAccess, + public sdv::ipc::IConnectEventCallback, + public sdv::ipc::IDataReceiveCallback +{ +public: + BEGIN_SDV_INTERFACE_MAP() + SDV_INTERFACE_ENTRY(sdv::ipc::IDataReceiveCallback) + SDV_INTERFACE_ENTRY(sdv::ipc::IConnectEventCallback) + END_SDV_INTERFACE_MAP() + + void SetConnectState(sdv::ipc::EConnectState s) override + { + { + std::lock_guard lk(m_mtx); + m_state = s; + } + m_cv.notify_all(); + } + + void ReceiveData(sdv::sequence>& seq) override + { + { + std::lock_guard lk(m_mtx); + m_data = seq; + m_hasData = true; + } + m_cv.notify_all(); + } + + bool WaitForStatus(sdv::ipc::EConnectState expected, uint32_t ms = 2000) + { + std::unique_lock lk(m_mtx); + return m_cv.wait_for(lk, std::chrono::milliseconds(ms), + [&]{ return m_state == expected; }); + } + + bool WaitForData(uint32_t ms = 2000) + { + std::unique_lock lk(m_mtx); + return m_cv.wait_for(lk, std::chrono::milliseconds(ms), + [&]{ return m_hasData; }); + } + + sdv::sequence> Data() + { + std::lock_guard lk(m_mtx); + return m_data; + } + +private: + mutable std::mutex m_mtx; + std::condition_variable m_cv; + sdv::ipc::EConnectState m_state{sdv::ipc::EConnectState::uninitialized}; + sdv::sequence> m_data; + bool m_hasData{false}; +}; + +// Helper to create server + client via manager +struct TunnelPair +{ + sdv::TObjectPtr serverObj; + sdv::ipc::IConnect* server = nullptr; + + sdv::TObjectPtr clientObj; + sdv::ipc::IConnect* client = nullptr; +}; + +static TunnelPair CreateTunnelPair(CSocketsTunnelChannelMgnt& mgr, const std::string& cs) +{ + TunnelPair out; + + out.serverObj = mgr.Access(cs); + out.server = out.serverObj ? out.serverObj.GetInterface() : nullptr; + + out.clientObj = mgr.Access(cs); + out.client = out.clientObj ? out.clientObj.GetInterface() : nullptr; + + return out; +} + +// TEST SUITE START +// Instantiate manager +TEST(WinTunnelIPC, InstantiateManager) +{ + sdv::app::CAppControl app; + ASSERT_TRUE(app.Startup("")); + + CSocketsTunnelChannelMgnt mgr; + EXPECT_NO_THROW(mgr.Initialize("")); + EXPECT_EQ(mgr.GetObjectState(), sdv::EObjectState::initialized); + + EXPECT_NO_THROW(mgr.Shutdown()); + EXPECT_EQ(mgr.GetObjectState(), sdv::EObjectState::destruction_pending); + + app.Shutdown(); +} + +// Basic connect/disconnect +TEST(WinTunnelIPC, BasicConnectDisconnect) +{ + sdv::app::CAppControl app; + ASSERT_TRUE(app.Startup("")); + + app.SetRunningMode(); + CSocketsTunnelChannelMgnt mgr; + mgr.Initialize(""); + mgr.SetOperationMode(sdv::EOperationMode::running); + + const std::string path = UniqueUds("tunnel_basic"); + const std::string cs = "proto=tunnel;path=" + path + ";"; + + auto ep = mgr.CreateEndpoint(cs); + ASSERT_FALSE(ep.ssConnectString.empty()); + + auto pair = CreateTunnelPair(mgr, ep.ssConnectString); + ASSERT_NE(pair.server, nullptr); + ASSERT_NE(pair.client, nullptr); + + CTunnelTestReceiver sr, cr; + pair.server->AsyncConnect(&sr); + SpinUntilServerArmed(pair.server); + + pair.client->AsyncConnect(&cr); + + EXPECT_TRUE(pair.server->WaitForConnection(5000)); + EXPECT_TRUE(pair.client->WaitForConnection(5000)); + + pair.client->Disconnect(); + pair.server->Disconnect(); + + mgr.Shutdown(); + app.Shutdown(); +} + +// Simple data (hello) +TEST(WinTunnelIPC, DataPath_SimpleHello_Tunnel) +{ + sdv::app::CAppControl app; + ASSERT_TRUE(app.Startup("")); + app.SetRunningMode(); + + CSocketsTunnelChannelMgnt mgr; + mgr.Initialize(""); + mgr.SetOperationMode(sdv::EOperationMode::running); + + const std::string cs = "proto=tunnel;path=" + UniqueUds("hello_t") + ";"; + auto ep = mgr.CreateEndpoint(cs); + auto pair = CreateTunnelPair(mgr, ep.ssConnectString); + ASSERT_NE(pair.server, nullptr); + ASSERT_NE(pair.client, nullptr); + + CTunnelTestReceiver sr, cr; + + pair.server->AsyncConnect(&sr); + SpinUntilServerArmed(pair.server); + + pair.client->AsyncConnect(&cr); + ASSERT_TRUE(pair.server->WaitForConnection(5000)); + ASSERT_TRUE(pair.client->WaitForConnection(5000)); + + // Build payload = "hello" + sdv::pointer p; + p.resize(5); + memcpy(p.get(), "hello", 5); + sdv::sequence> seq; + seq.push_back(p); + + auto* sender = dynamic_cast(pair.client); + ASSERT_NE(sender, nullptr); + EXPECT_TRUE(sender->SendData(seq)); + + EXPECT_TRUE(sr.WaitForData(3000)); + auto recv = sr.Data(); + + // header eliminated + ASSERT_EQ(recv.size(), 1u); + ASSERT_EQ(recv[0].size(), 5u); + EXPECT_EQ(memcmp(recv[0].get(), "hello", 5), 0); + + pair.client->Disconnect(); + pair.server->Disconnect(); + + mgr.Shutdown(); + app.Shutdown(); +} + +// Multi-chunk +TEST(WinTunnelIPC, DataPath_MultiChunk_Tunnel) +{ + sdv::app::CAppControl app; + ASSERT_TRUE(app.Startup("")); + app.SetRunningMode(); + + CSocketsTunnelChannelMgnt mgr; + mgr.Initialize(""); + mgr.SetOperationMode(sdv::EOperationMode::running); + + const std::string cs = "proto=tunnel;path=" + UniqueUds("mc_t") + ";"; + + auto ep = mgr.CreateEndpoint(cs); + auto pair = CreateTunnelPair(mgr, ep.ssConnectString); + ASSERT_NE(pair.server, nullptr); + ASSERT_NE(pair.client, nullptr); + + CTunnelTestReceiver sr, cr; + pair.server->AsyncConnect(&sr); + SpinUntilServerArmed(pair.server); + + pair.client->AsyncConnect(&cr); + ASSERT_TRUE(pair.server->WaitForConnection(5000)); + ASSERT_TRUE(pair.client->WaitForConnection(5000)); + + sdv::pointer p1, p2; + p1.resize(3); + memcpy(p1.get(), "sdv", 3); + p2.resize(9); + memcpy(p2.get(), "framework", 9); + + sdv::sequence> seq; + seq.push_back(p1); + seq.push_back(p2); + + auto* sender = dynamic_cast(pair.client); + ASSERT_NE(sender, nullptr); + EXPECT_TRUE(sender->SendData(seq)); + + ASSERT_TRUE(sr.WaitForData(3000)); + auto recv = sr.Data(); + + ASSERT_EQ(recv.size(), 2u); + EXPECT_EQ(memcmp(recv[0].get(), "sdv", 3), 0); + EXPECT_EQ(memcmp(recv[1].get(), "framework", 9), 0); + + pair.client->Disconnect(); + pair.server->Disconnect(); + + mgr.Shutdown(); + app.Shutdown(); +} + +// Large fragmentation +TEST(WinTunnelIPC, DataPath_LargePayload_Tunnel) +{ + sdv::app::CAppControl app; + ASSERT_TRUE(app.Startup("")); + app.SetRunningMode(); + + CSocketsTunnelChannelMgnt mgr; + mgr.Initialize(""); + mgr.SetOperationMode(sdv::EOperationMode::running); + + const std::string cs = "proto=tunnel;path=" + UniqueUds("big_t") + ";"; + auto ep = mgr.CreateEndpoint(cs); + auto pair = CreateTunnelPair(mgr, ep.ssConnectString); + + ASSERT_NE(pair.server, nullptr); + ASSERT_NE(pair.client, nullptr); + + CTunnelTestReceiver sr, cr; + pair.server->AsyncConnect(&sr); + SpinUntilServerArmed(pair.server); + + pair.client->AsyncConnect(&cr); + ASSERT_TRUE(pair.server->WaitForConnection(5000)); + ASSERT_TRUE(pair.client->WaitForConnection(5000)); + + const size_t N = 256 * 1024; + sdv::pointer payload; + payload.resize(N); + + for (size_t i = 0; i < N; ++i) + payload.get()[i] = uint8_t(i & 0xFF); + + sdv::sequence> seq; + seq.push_back(payload); + + auto* sender = dynamic_cast(pair.client); + ASSERT_NE(sender, nullptr); + EXPECT_TRUE(sender->SendData(seq)); + + ASSERT_TRUE(sr.WaitForData(5000)); + auto recv = sr.Data(); + + ASSERT_EQ(recv.size(), 1u); + ASSERT_EQ(recv[0].size(), N); + EXPECT_EQ(memcmp(recv[0].get(), payload.get(), N), 0); + + pair.client->Disconnect(); + pair.server->Disconnect(); + + mgr.Shutdown(); + app.Shutdown(); +} + +// Header stripping test (tunnel feature) +TEST(WinTunnelIPC, DataPath_HeaderStripped_Tunnel) +{ + sdv::app::CAppControl app; + ASSERT_TRUE(app.Startup("")); + + app.SetRunningMode(); + + CSocketsTunnelChannelMgnt mgr; + mgr.Initialize(""); + mgr.SetOperationMode(sdv::EOperationMode::running); + + const std::string cs = + "proto=tunnel;path=" + UniqueUds("headerstrip") + ";"; + + auto ep = mgr.CreateEndpoint(cs); + auto pair = CreateTunnelPair(mgr, ep.ssConnectString); + + ASSERT_NE(pair.server, nullptr); + ASSERT_NE(pair.client, nullptr); + + CTunnelTestReceiver sr, cr; + + pair.server->AsyncConnect(&sr); + SpinUntilServerArmed(pair.server); + + pair.client->AsyncConnect(&cr); + ASSERT_TRUE(pair.server->WaitForConnection(5000)); + ASSERT_TRUE(pair.client->WaitForConnection(5000)); + + const char* msg = "HEADER_TEST"; + const size_t len = strlen(msg); + + sdv::pointer p; + p.resize(len); + memcpy(p.get(), msg, len); + + sdv::sequence> seq; + seq.push_back(p); + + auto* sender = dynamic_cast(pair.client); + ASSERT_NE(sender, nullptr); + + EXPECT_TRUE(sender->SendData(seq)); + ASSERT_TRUE(sr.WaitForData(3000)); + + auto recv = sr.Data(); + + // check header stripped + ASSERT_EQ(recv.size(), 1u); + ASSERT_EQ(recv[0].size(), len); + EXPECT_EQ(memcmp(recv[0].get(), msg, len), 0); + + pair.client->Disconnect(); + pair.server->Disconnect(); + mgr.Shutdown(); + app.Shutdown(); +} + diff --git a/tests/unit_tests/win_tunnel/win_tunnel_negative_edge_tests.cpp b/tests/unit_tests/win_tunnel/win_tunnel_negative_edge_tests.cpp new file mode 100644 index 0000000..ab3f40f --- /dev/null +++ b/tests/unit_tests/win_tunnel/win_tunnel_negative_edge_tests.cpp @@ -0,0 +1,164 @@ +/******************************************************************************** +* Copyright (c) 2025-2026 ZF Friedrichshafen AG +* +* This program and the accompanying materials are made available under the +* terms of the Apache License Version 2.0 which is available at +* https://www.apache.org/licenses/LICENSE-2.0 +* +* SPDX-License-Identifier: Apache-2.0 +* +* Contributors: +* Denisa Ros - initial API and implementation +********************************************************************************/ + +#include "gtest/gtest.h" +#include +#include +#include "../../../sdv_services/uds_win_tunnel/channel_mgnt.h" +#include "../../../sdv_services/uds_win_tunnel/connection.h" + +// Include UDS Windows helpers for path generation +#include "../../../sdv_services/uds_win_sockets/channel_mgnt.h" // ONLY FOR path helpers (MakeShortUdsPath) +#include "../../../sdv_services/uds_win_sockets/connection.h" + +// Helper for unique UDS path +template +static std::string UniqueUds(const char* prefix) { + char buf[64]; + sprintf(buf, "%s_%08x.sock", prefix, rand()); + return std::string("%LOCALAPPDATA%/sdv/") + buf; +} + +// Negative: invalid connect string +TEST(WinTunnelNegative, InvalidConnectString) +{ + sdv::app::CAppControl app; + ASSERT_TRUE(app.Startup(R"toml([LogHandler] +ViewFilter = "Fatal")toml")); + CSocketsTunnelChannelMgnt mgr; + mgr.Initialize(""); + mgr.SetOperationMode(sdv::EOperationMode::running); + // Missing proto, missing path + auto obj = mgr.Access("role=server;"); + EXPECT_EQ(obj, nullptr); + app.Shutdown(); +} + +// Negative: connect to non-existent server +TEST(WinTunnelNegative, ConnectToNonExistentServer) +{ + sdv::app::CAppControl app; + ASSERT_TRUE(app.Startup(R"toml([LogHandler] +ViewFilter = "Fatal")toml")); + CSocketsTunnelChannelMgnt mgr; + mgr.Initialize(""); + mgr.SetOperationMode(sdv::EOperationMode::running); + std::string cs = "proto=tunnel;path=" + UniqueUds("no_server") + ";"; + auto obj = mgr.Access(cs); + if (!obj) { + SUCCEED() << "Client object is nullptr as expected when server does not exist"; + } else { + auto* client = obj->GetInterface(); + ASSERT_NE(client, nullptr); + EXPECT_FALSE(client->WaitForConnection(200)); + } + app.Shutdown(); +} + +// Edge: double disconnect +TEST(WinTunnelEdge, DoubleDisconnect) +{ + sdv::app::CAppControl app; + ASSERT_TRUE(app.Startup("")); + CSocketsTunnelChannelMgnt mgr; + mgr.Initialize(""); + mgr.SetOperationMode(sdv::EOperationMode::running); + std::string cs = "proto=tunnel;path=" + UniqueUds("double_disc") + ";"; + auto ep = mgr.CreateEndpoint(cs); + auto obj = mgr.Access(ep.ssConnectString); + auto* conn = obj->GetInterface(); + ASSERT_NE(conn, nullptr); + conn->Disconnect(); + // Should not crash or throw + EXPECT_NO_THROW(conn->Disconnect()); + app.Shutdown(); +} + +// Edge: repeated connect/disconnect cycles (recreate endpoint each time) +TEST(WinTunnelEdge, RepeatedConnectDisconnect) +{ + sdv::app::CAppControl app; + ASSERT_TRUE(app.Startup(R"toml([LogHandler] +ViewFilter = "Fatal")toml")); + CSocketsTunnelChannelMgnt mgr; + mgr.Initialize(""); + mgr.SetOperationMode(sdv::EOperationMode::running); + std::string cs = "proto=tunnel;path=" + UniqueUds("repeat") + ";"; + for (int i = 0; i < 3; ++i) { + auto ep = mgr.CreateEndpoint(cs); // recreate endpoint every time + sdv::TObjectPtr obj = mgr.Access(ep.ssConnectString); + auto* conn = obj.GetInterface(); + ASSERT_NE(conn, nullptr); + conn->AsyncConnect(nullptr); + conn->WaitForConnection(200); + conn->Disconnect(); + // obj goes out of scope and cleans up + } + app.Shutdown(); +} + +// Edge: simultaneous multiple clients +TEST(WinTunnelEdge, MultipleClients) +{ + sdv::app::CAppControl app; + ASSERT_TRUE(app.Startup("")); + CSocketsTunnelChannelMgnt mgr; + mgr.Initialize(""); + mgr.SetOperationMode(sdv::EOperationMode::running); + std::string cs = "proto=tunnel;path=" + UniqueUds("multi_client") + ";"; + auto ep = mgr.CreateEndpoint(cs); + auto obj1 = mgr.Access(ep.ssConnectString); + auto obj2 = mgr.Access(ep.ssConnectString); + auto* c1 = obj1->GetInterface(); + auto* c2 = obj2->GetInterface(); + ASSERT_NE(c1, nullptr); + ASSERT_NE(c2, nullptr); + c1->AsyncConnect(nullptr); + c2->AsyncConnect(nullptr); + c1->WaitForConnection(500); + c2->WaitForConnection(500); + c1->Disconnect(); + c2->Disconnect(); + app.Shutdown(); +} + +// Edge: callback throws exception (should not crash) +class ThrowingReceiver : public sdv::IInterfaceAccess, public sdv::ipc::IDataReceiveCallback, public sdv::ipc::IConnectEventCallback { +public: + BEGIN_SDV_INTERFACE_MAP() + SDV_INTERFACE_ENTRY(sdv::ipc::IDataReceiveCallback) + SDV_INTERFACE_ENTRY(sdv::ipc::IConnectEventCallback) + END_SDV_INTERFACE_MAP() + void SetConnectState(sdv::ipc::EConnectState) override { throw std::runtime_error("SetConnectState fail"); } + void ReceiveData(sdv::sequence>&) override { throw std::runtime_error("ReceiveData fail"); } +}; + +TEST(WinTunnelEdge, CallbackThrows) +{ + sdv::app::CAppControl app; + ASSERT_TRUE(app.Startup(R"toml([LogHandler] +ViewFilter = "Fatal")toml")); + CSocketsTunnelChannelMgnt mgr; + mgr.Initialize(""); + mgr.SetOperationMode(sdv::EOperationMode::running); + std::string cs = "proto=tunnel;path=" + UniqueUds("cb_throw") + ";"; + auto ep = mgr.CreateEndpoint(cs); + auto obj = mgr.Access(ep.ssConnectString); + auto* conn = obj->GetInterface(); + ASSERT_NE(conn, nullptr); + ThrowingReceiver rcv; + // Should not crash even if callback throws + EXPECT_NO_THROW(conn->AsyncConnect(&rcv)); + conn->Disconnect(); + app.Shutdown(); +}