From d1e5b569e1a153739cfb7a59820eaab8389ffc1d Mon Sep 17 00:00:00 2001 From: paulbourelly999 <77466294+paulbourelly999@users.noreply.github.com> Date: Tue, 6 Jun 2023 18:16:34 -0400 Subject: [PATCH 01/80] Fix other service builds (#332) * Initialize carma-clock at time 0 to avoid wait_for_initialization bug in carma-time-lib. Also added more debug logging for SNMP commands. * Added comment * Added logic for gtest that is compatible with CMake version 3.20 above and below * Fixed Message Services docker build * Fix signal opt service docker build --- intersection_model/Dockerfile | 33 ++++++++++--------- intersection_model/src/server/CMakeLists.txt | 4 +-- kafka_clients/CMakeLists.txt | 8 +++-- message_services/CMakeLists.txt | 5 ++- message_services/Dockerfile | 16 ++++----- signal_opt_service/CMakeLists.txt | 11 ++++--- signal_opt_service/Dockerfile | 12 ++++++- .../streets_desired_phase_plan/CMakeLists.txt | 8 +++-- .../CMakeLists.txt | 6 +++- .../CMakeLists.txt | 6 +++- .../CMakeLists.txt | 8 +++-- .../streets_tsc_configuration/CMakeLists.txt | 8 +++-- .../streets_vehicle_list/CMakeLists.txt | 9 +++-- .../streets_vehicle_scheduler/CMakeLists.txt | 6 +++- 14 files changed, 91 insertions(+), 49 deletions(-) diff --git a/intersection_model/Dockerfile b/intersection_model/Dockerfile index 1b51cb6e5..67704e9ac 100644 --- a/intersection_model/Dockerfile +++ b/intersection_model/Dockerfile @@ -32,11 +32,11 @@ RUN make install # Install librdkafka RUN echo " ------> Install librdkafka..." WORKDIR /home/carma-streets/ext -RUN git clone https://github.com/edenhill/librdkafka +RUN git clone https://github.com/confluentinc/librdkafka.git WORKDIR /home/carma-streets/ext/librdkafka/ -RUN ./configure --prefix=/usr -RUN make -RUN make install +RUN cmake -H. -B_cmake_build +RUN cmake --build _cmake_build +RUN cmake --build _cmake_build --target install # Install rapidjson RUN echo " ------> Install rapidjson..." @@ -119,12 +119,22 @@ RUN source /opt/ros/melodic/setup.bash && \ DEBIAN_FRONTEND=noninteractive apt-get install -y libeigen3-dev && \ ROS_VERSION=1 LANELET2_EXTENSION_LOGGER_TYPE=1 catkin_make install +# Install kafka-clients +RUN echo " ------> Install kafka-clients..." +COPY ./kafka_clients/ /home/carma-streets/kafka_clients +WORKDIR /home/carma-streets/kafka_clients +RUN mkdir build +WORKDIR /home/carma-streets/kafka_clients/build +RUN cmake -DCMAKE_BUILD_TYPE="Debug" .. +RUN make +RUN make install + COPY ./streets_utils/ /home/carma-streets/streets_utils # Install streets_service_base -RUN echo " ------> Install streets service base library from streets_utils..." -WORKDIR /home/carma-streets/streets_utils/streets_service_base +RUN echo " ------> Install streets service configuration library from streets_utils..." +WORKDIR /home/carma-streets/streets_utils/streets_service_configuration RUN mkdir build -WORKDIR /home/carma-streets/streets_utils/streets_service_base/build +WORKDIR /home/carma-streets/streets_utils/streets_service_configuration/build RUN cmake -DCMAKE_BUILD_TYPE="Debug" .. RUN make RUN make install @@ -138,15 +148,6 @@ RUN cmake -DCMAKE_BUILD_TYPE="Debug" .. RUN make RUN make install -# Install kafka-clients -RUN echo " ------> Install kafka-clients..." -COPY ./kafka_clients/ /home/carma-streets/kafka_clients -WORKDIR /home/carma-streets/kafka_clients -RUN mkdir build -WORKDIR /home/carma-streets/kafka_clients/build -RUN cmake -DCMAKE_BUILD_TYPE="Debug" .. -RUN make -RUN make install COPY ./intersection_model/ /home/carma-streets/intersection_model # Install intersection_model diff --git a/intersection_model/src/server/CMakeLists.txt b/intersection_model/src/server/CMakeLists.txt index 28f36e34c..f561008fe 100644 --- a/intersection_model/src/server/CMakeLists.txt +++ b/intersection_model/src/server/CMakeLists.txt @@ -11,7 +11,7 @@ set(CMAKE_AUTOMOC ON) find_package(Qt5Core REQUIRED) find_package(Qt5Network REQUIRED) find_package(qhttpengine REQUIRED) -find_package(streets_service_base_lib REQUIRED) +find_package(streets_service_configuration_lib REQUIRED) file(GLOB SRCS ${CMAKE_CURRENT_SOURCE_DIR}/src/handlers/*.cpp @@ -25,6 +25,6 @@ include_directories( ) add_executable(${PROJECT_NAME} ${SRCS}) -target_link_libraries(${PROJECT_NAME} PUBLIC intersection_model_lib intersection_server_api_lib Qt5Core Qt5Network ssl crypto qhttpengine streets_service_base_lib::streets_service_base_lib spdlog::spdlog) +target_link_libraries(${PROJECT_NAME} PUBLIC intersection_model_lib intersection_server_api_lib Qt5Core Qt5Network ssl crypto qhttpengine streets_service_configuration_lib::streets_service_configuration_lib spdlog::spdlog) set_property(TARGET ${PROJECT_NAME} PROPERTY CXX_STANDARD 14) set_property(TARGET ${PROJECT_NAME} PROPERTY CXX_STANDARD_REQUIRED ON) \ No newline at end of file diff --git a/kafka_clients/CMakeLists.txt b/kafka_clients/CMakeLists.txt index 3f823adc4..2284469d2 100644 --- a/kafka_clients/CMakeLists.txt +++ b/kafka_clients/CMakeLists.txt @@ -79,9 +79,13 @@ target_link_libraries(${BINARY} PUBLIC Boost::filesystem spdlog::spdlog ${PROJECT_NAME}_lib - GTest::gtest - GTest::gmock ) +message(CMAKE_VERSION) +if(${CMAKE_VERSION} VERSION_GREATER_EQUAL "3.20") + target_link_libraries( ${BINARY} PUBLIC GTest::gtest GTest::gmock ) +else() + target_link_libraries( ${BINARY} PUBLIC gtest gmock) +endif() target_include_directories(${BINARY} PUBLIC $ diff --git a/message_services/CMakeLists.txt b/message_services/CMakeLists.txt index 45d8e7e66..28f54bcc5 100644 --- a/message_services/CMakeLists.txt +++ b/message_services/CMakeLists.txt @@ -15,8 +15,7 @@ find_package(catkin COMPONENTS lanelet2_core lanelet2_projection lanelet2_io REQUIRED) -find_package( streets_service_base_lib COMPONENTS streets_service_base_lib REQUIRED) -message(STATUS ${CMAKE_CURRENT_BINARY_DIR}) +find_package( streets_service_configuration_lib COMPONENTS streets_service_configuration_lib REQUIRED) find_package(Boost COMPONENTS thread system REQUIRED) find_package(spdlog REQUIRED) @@ -53,7 +52,7 @@ target_link_libraries(${PROJECT_NAME}_lib PUBLIC "${catkin_LIBRARY_DIRS}/liblanelet2_routing.so" "${catkin_LIBRARY_DIRS}/liblanelet2_extension_lib.so" Eigen3::Eigen - streets_service_base_lib::streets_service_base_lib + streets_service_configuration_lib::streets_service_configuration_lib kafka_clients_lib rdkafka++ gmock diff --git a/message_services/Dockerfile b/message_services/Dockerfile index 9b084604a..30c271410 100644 --- a/message_services/Dockerfile +++ b/message_services/Dockerfile @@ -19,11 +19,11 @@ RUN sudo make install # Install librdkafka RUN echo " ------> Install librdkafka..." WORKDIR /home/carma-streets/ext -RUN git clone https://github.com/edenhill/librdkafka +RUN git clone https://github.com/confluentinc/librdkafka.git WORKDIR /home/carma-streets/ext/librdkafka/ -RUN ./configure --prefix=/usr -RUN make -RUN sudo make install +RUN cmake -H. -B_cmake_build +RUN cmake --build _cmake_build +RUN cmake --build _cmake_build --target install # Install spdlog @@ -109,11 +109,11 @@ RUN source /opt/ros/melodic/setup.bash && \ ROS_VERSION=1 LANELET2_EXTENSION_LOGGER_TYPE=1 catkin_make install COPY ./streets_utils/ /home/carma-streets/streets_utils -# Install streets_service_base -RUN echo " ------> Install streets service base library from streets_utils..." -WORKDIR /home/carma-streets/streets_utils/streets_service_base +# Install streets_service_configuration +RUN echo " ------> Install streets_service_configuration library from streets_utils..." +WORKDIR /home/carma-streets/streets_utils/streets_service_configuration RUN mkdir build -WORKDIR /home/carma-streets/streets_utils/streets_service_base/build +WORKDIR /home/carma-streets/streets_utils/streets_service_configuration/build RUN cmake -DCMAKE_BUILD_TYPE="Debug" .. RUN make RUN make install diff --git a/signal_opt_service/CMakeLists.txt b/signal_opt_service/CMakeLists.txt index dcd6330ff..a76ee77c3 100644 --- a/signal_opt_service/CMakeLists.txt +++ b/signal_opt_service/CMakeLists.txt @@ -14,7 +14,6 @@ add_definitions(-DRAPIDJSON_HAS_STDSTRING=1) find_package(Qt5Core REQUIRED) find_package(Qt5Network REQUIRED) find_package(streets_service_configuration_lib REQUIRED) -find_package(streets_service_base_lib REQUIRED) find_package(streets_vehicle_list_lib REQUIRED) find_package(streets_signal_phase_and_timing_lib REQUIRED) find_package(streets_tsc_configuration_lib REQUIRED) @@ -46,7 +45,6 @@ target_link_libraries(${PROJECT_NAME}_lib streets_desired_phase_plan_lib::streets_desired_phase_plan_lib streets_vehicle_list_lib::streets_vehicle_list_lib streets_vehicle_scheduler_lib::streets_vehicle_scheduler_lib - streets_service_base_lib::streets_service_base_lib streets_tsc_configuration_lib::streets_tsc_configuration_lib streets_signal_optimization_lib::streets_signal_optimization_lib kafka_clients_lib::kafka_clients_lib @@ -82,6 +80,9 @@ target_link_libraries(${BINARY} ${PROJECT_NAME}_lib spdlog::spdlog rapidjson - GTest::gtest - GTest::gmock -) \ No newline at end of file +) +if(${CMAKE_VERSION} VERSION_GREATER_EQUAL "3.20") + target_link_libraries( ${BINARY} PUBLIC GTest::gtest GTest::gmock ) +else() + target_link_libraries( ${BINARY} PUBLIC gtest gmock) +endif() \ No newline at end of file diff --git a/signal_opt_service/Dockerfile b/signal_opt_service/Dockerfile index ea510934d..45fe330c9 100644 --- a/signal_opt_service/Dockerfile +++ b/signal_opt_service/Dockerfile @@ -37,7 +37,17 @@ RUN make RUN make install COPY ./streets_utils/ /home/carma-streets/streets_utils -# Install streets_service_base + +# Install streets_service_configuration lib +RUN echo " ------> Install streets service configuration library from streets_utils..." +WORKDIR /home/carma-streets/streets_utils/streets_service_configuration +RUN mkdir build +WORKDIR /home/carma-streets/streets_utils/streets_service_configuration/build +RUN cmake -DCMAKE_BUILD_TYPE="Debug" .. +RUN make +RUN make install + +# Install streets_service_base lib RUN echo " ------> Install streets service base library from streets_utils..." WORKDIR /home/carma-streets/streets_utils/streets_service_base RUN mkdir build diff --git a/streets_utils/streets_desired_phase_plan/CMakeLists.txt b/streets_utils/streets_desired_phase_plan/CMakeLists.txt index 75a4173f2..1a672d619 100644 --- a/streets_utils/streets_desired_phase_plan/CMakeLists.txt +++ b/streets_utils/streets_desired_phase_plan/CMakeLists.txt @@ -70,5 +70,9 @@ target_include_directories(${BINARY} PUBLIC ${PROJECT_SOURCE_DIR}/include) target_link_libraries(${BINARY} PUBLIC ${PROJECT_NAME}_lib - GTest::gtest -) \ No newline at end of file +) +if(${CMAKE_VERSION} VERSION_GREATER_EQUAL "3.20") + target_link_libraries( ${BINARY} PUBLIC GTest::gtest ) +else() + target_link_libraries( ${BINARY} PUBLIC gtest ) +endif() \ No newline at end of file diff --git a/streets_utils/streets_service_configuration/CMakeLists.txt b/streets_utils/streets_service_configuration/CMakeLists.txt index 230f8ecfc..07f3a770e 100644 --- a/streets_utils/streets_service_configuration/CMakeLists.txt +++ b/streets_utils/streets_service_configuration/CMakeLists.txt @@ -71,8 +71,12 @@ add_test(NAME ${BINARY} COMMAND ${BINARY}) target_link_libraries(${BINARY} PUBLIC ${PROJECT_NAME}_lib - GTest::gtest ) +if(${CMAKE_VERSION} VERSION_GREATER_EQUAL "3.20") + target_link_libraries(${BINARY} PUBLIC GTest::gtest) +else() + target_link_libraries(${BINARY} PUBLIC gtest) +endif() target_include_directories(${BINARY} PUBLIC $ diff --git a/streets_utils/streets_signal_optimization/CMakeLists.txt b/streets_utils/streets_signal_optimization/CMakeLists.txt index a92a936ae..aa9b0892f 100644 --- a/streets_utils/streets_signal_optimization/CMakeLists.txt +++ b/streets_utils/streets_signal_optimization/CMakeLists.txt @@ -86,5 +86,9 @@ target_link_libraries(${BINARY} PUBLIC ${PROJECT_NAME}_lib rapidjson - GTest::gtest ) +if(${CMAKE_VERSION} VERSION_GREATER_EQUAL "3.20") + target_link_libraries( ${BINARY} PUBLIC GTest::gtest ) +else() + target_link_libraries( ${BINARY} PUBLIC gtest ) +endif() \ No newline at end of file diff --git a/streets_utils/streets_signal_phase_and_timing/CMakeLists.txt b/streets_utils/streets_signal_phase_and_timing/CMakeLists.txt index 717f6128a..a36a0e26c 100644 --- a/streets_utils/streets_signal_phase_and_timing/CMakeLists.txt +++ b/streets_utils/streets_signal_phase_and_timing/CMakeLists.txt @@ -81,6 +81,10 @@ target_include_directories(${BINARY} PUBLIC ${PROJECT_SOURCE_DIR}/include) target_link_libraries(${BINARY} PUBLIC ${PROJECT_NAME}_lib - GTest::gtest rapidjson - ) \ No newline at end of file + ) +if(${CMAKE_VERSION} VERSION_GREATER_EQUAL "3.20") + target_link_libraries( ${BINARY} PUBLIC GTest::gtest ) +else() + target_link_libraries( ${BINARY} PUBLIC gtest ) +endif() \ No newline at end of file diff --git a/streets_utils/streets_tsc_configuration/CMakeLists.txt b/streets_utils/streets_tsc_configuration/CMakeLists.txt index 52cc3ed50..2c390deb0 100644 --- a/streets_utils/streets_tsc_configuration/CMakeLists.txt +++ b/streets_utils/streets_tsc_configuration/CMakeLists.txt @@ -70,5 +70,9 @@ target_include_directories(${BINARY} PUBLIC ${PROJECT_SOURCE_DIR}/include) target_link_libraries(${BINARY} PUBLIC ${PROJECT_NAME}_lib - GTest::gtest -) \ No newline at end of file +) +if(${CMAKE_VERSION} VERSION_GREATER_EQUAL "3.20") + target_link_libraries( ${BINARY} PUBLIC GTest::gtest ) +else() + target_link_libraries( ${BINARY} PUBLIC gtest ) +endif() \ No newline at end of file diff --git a/streets_utils/streets_vehicle_list/CMakeLists.txt b/streets_utils/streets_vehicle_list/CMakeLists.txt index 158bcfd12..a7ee23950 100644 --- a/streets_utils/streets_vehicle_list/CMakeLists.txt +++ b/streets_utils/streets_vehicle_list/CMakeLists.txt @@ -77,6 +77,9 @@ add_test(NAME ${BINARY} COMMAND ${BINARY}) target_include_directories(${BINARY} PUBLIC ${PROJECT_SOURCE_DIR}/include) target_link_libraries(${BINARY} PUBLIC spdlog::spdlog - gtest - ${PROJECT_NAME}_lib - ) + ${PROJECT_NAME}_lib) +if(${CMAKE_VERSION} VERSION_GREATER_EQUAL "3.20") + target_link_libraries( ${BINARY} PUBLIC GTest::gtest ) +else() + target_link_libraries( ${BINARY} PUBLIC gtest ) +endif() diff --git a/streets_utils/streets_vehicle_scheduler/CMakeLists.txt b/streets_utils/streets_vehicle_scheduler/CMakeLists.txt index e12f7507b..beaef9b18 100644 --- a/streets_utils/streets_vehicle_scheduler/CMakeLists.txt +++ b/streets_utils/streets_vehicle_scheduler/CMakeLists.txt @@ -89,5 +89,9 @@ target_include_directories(${BINARY} PUBLIC ${PROJECT_SOURCE_DIR}/include) target_link_libraries(${BINARY} PUBLIC ${PROJECT_NAME}_lib - GTest::gtest ) +if(${CMAKE_VERSION} VERSION_GREATER_EQUAL "3.20") + target_link_libraries( ${BINARY} PUBLIC GTest::gtest ) +else() + target_link_libraries( ${BINARY} PUBLIC gtest ) +endif() From bb98a3ab670ee4edd587766e5e29a38309c0c28c Mon Sep 17 00:00:00 2001 From: dan-du-car <62157949+dan-du-car@users.noreply.github.com> Date: Tue, 13 Jun 2023 14:43:57 -0400 Subject: [PATCH 02/80] Issue-331: Add phase control schedule msg (#330) * init * add unit test * update clear * update data type * update service * update * update dockerfile * fix sonarscan * fix sonarscan * update coverage * update coverage * update unit test * update log * address code coverage * update code coverage * update topic * add more coverage * address code smell * add more code coverage * update * address comments * update readme * update code coverage * add coverage * update readme --- .sonarqube/sonar-scanner.properties | 6 + build.sh | 1 + coverage.sh | 7 + docker-compose.yml | 2 +- .../CMakeLists.txt | 79 ++++++++++ .../streets_phase_control_schedule/README.md | 48 ++++++ ..._phase_control_schedule_libConfig.cmake.in | 7 + .../include/streets_phase_control_command.h | 46 ++++++ .../include/streets_phase_control_schedule.h | 43 +++++ ...streets_phase_control_schedule_exception.h | 23 +++ ...reets_phase_control_schedule_exception.cpp | 8 + .../models/streets_phase_control_command.cpp | 74 +++++++++ .../models/streets_phase_control_schedule.cpp | 104 ++++++++++++ .../streets_phase_control_command_test.cpp | 80 ++++++++++ .../streets_phase_control_schedule_test.cpp | 148 ++++++++++++++++++ .../test/test_main.cpp | 6 + tsc_client_service/CMakeLists.txt | 2 + tsc_client_service/Dockerfile | 9 ++ tsc_client_service/README.md | 4 +- .../include/control_tsc_state.h | 8 + tsc_client_service/include/tsc_service.h | 21 +++ tsc_client_service/manifest.json | 12 ++ tsc_client_service/src/control_tsc_state.cpp | 20 +++ tsc_client_service/src/tsc_service.cpp | 54 ++++++- .../test/test_control_tsc_state.cpp | 24 +++ tsc_client_service/test/test_tsc_service.cpp | 1 + 26 files changed, 833 insertions(+), 4 deletions(-) create mode 100644 streets_utils/streets_phase_control_schedule/CMakeLists.txt create mode 100644 streets_utils/streets_phase_control_schedule/README.md create mode 100644 streets_utils/streets_phase_control_schedule/cmake/streets_phase_control_schedule_libConfig.cmake.in create mode 100644 streets_utils/streets_phase_control_schedule/include/streets_phase_control_command.h create mode 100755 streets_utils/streets_phase_control_schedule/include/streets_phase_control_schedule.h create mode 100644 streets_utils/streets_phase_control_schedule/include/streets_phase_control_schedule_exception.h create mode 100644 streets_utils/streets_phase_control_schedule/src/exceptions/streets_phase_control_schedule_exception.cpp create mode 100644 streets_utils/streets_phase_control_schedule/src/models/streets_phase_control_command.cpp create mode 100755 streets_utils/streets_phase_control_schedule/src/models/streets_phase_control_schedule.cpp create mode 100644 streets_utils/streets_phase_control_schedule/test/streets_phase_control_command_test.cpp create mode 100644 streets_utils/streets_phase_control_schedule/test/streets_phase_control_schedule_test.cpp create mode 100644 streets_utils/streets_phase_control_schedule/test/test_main.cpp diff --git a/.sonarqube/sonar-scanner.properties b/.sonarqube/sonar-scanner.properties index c19b7d030..a7e8de2fc 100644 --- a/.sonarqube/sonar-scanner.properties +++ b/.sonarqube/sonar-scanner.properties @@ -32,6 +32,7 @@ sonar.coverageReportPaths= /home/carma-streets/kafka_clients/coverage/coverage.x /home/carma-streets/tsc_client_service/coverage/coverage.xml, \ /home/carma-streets/streets_utils/streets_vehicle_scheduler/coverage/coverage.xml, \ /home/carma-streets/streets_utils/streets_desired_phase_plan/coverage/coverage.xml, \ +/home/carma-streets/streets_utils/streets_phase_control_schedule/coverage/coverage.xml, \ /home/carma-streets/streets_utils/streets_signal_optimization/coverage/coverage.xml # TODO : /home/carma-streets/intersection_model/coverage/coverage.xml, \ @@ -66,6 +67,7 @@ streets_service_configuration, \ streets_service_base, \ streets_vehicle_list, \ streets_signal_phase_and_timing, \ +streets_phase_control_schedule, \ streets_tsc_configuration, \ tsc_client_service, \ streets_vehicle_scheduler, \ @@ -89,6 +91,7 @@ streets_signal_phase_and_timing.sonar.projectBaseDir=/home/carma-streets/streets streets_tsc_configuration.sonar.projectBaseDir=/home/carma-streets/streets_utils/streets_tsc_configuration streets_desired_phase_plan.sonar.projectBaseDir=/home/carma-streets/streets_utils/streets_desired_phase_plan streets_signal_optimization.sonar.projectBaseDir=/home/carma-streets/streets_utils/streets_signal_optimization +streets_phase_control_schedule.sonar.projectBaseDir=/home/carma-streets/streets_utils/streets_phase_control_schedule @@ -122,6 +125,8 @@ streets_desired_phase_plan.sonar.sources =src/,include/ streets_desired_phase_plan.sonar.exclusions =test/** streets_signal_optimization.sonar.sources =src/,include/ streets_signal_optimization.sonar.exclusions =test/** +streets_phase_control_schedule.sonar.sources =src/,include/ +streets_phase_control_schedule.sonar.exclusions =test/** #Tests @@ -140,5 +145,6 @@ streets_signal_phase_and_timing.sonar.tests=test/ streets_tsc_configuration.sonar.tests=test/ streets_desired_phase_plan.sonar.tests=test/ streets_signal_optimization.sonar.tests=test/ +streets_phase_control_schedule.sonar.tests=test/ diff --git a/build.sh b/build.sh index 245b0cab5..70996e296 100755 --- a/build.sh +++ b/build.sh @@ -27,6 +27,7 @@ MAKE_INSTALL_DIRS=( "streets_utils/streets_vehicle_list" "streets_utils/streets_tsc_configuration" "streets_utils/streets_desired_phase_plan" + "streets_utils/streets_phase_control_schedule" "streets_utils/streets_signal_phase_and_timing" "streets_utils/streets_api/intersection_client_api" "streets_utils/streets_vehicle_scheduler" diff --git a/coverage.sh b/coverage.sh index 534b2bab5..1d1ed1fc2 100755 --- a/coverage.sh +++ b/coverage.sh @@ -83,6 +83,13 @@ mkdir coverage cd /home/carma-streets/ gcovr --sonarqube streets_utils/streets_desired_phase_plan/coverage/coverage.xml -s -f streets_utils/streets_desired_phase_plan/ -r . +cd /home/carma-streets/streets_utils/streets_phase_control_schedule/build/ +./streets_phase_control_schedule_test --gtest_output=xml:../../../test_results/ +cd /home/carma-streets/streets_utils/streets_phase_control_schedule +mkdir coverage +cd /home/carma-streets/ +gcovr --sonarqube streets_utils/streets_phase_control_schedule/coverage/coverage.xml -s -f streets_utils/streets_phase_control_schedule/ -r . + cd /home/carma-streets/streets_utils/streets_signal_optimization/build/ ./streets_signal_optimization_test --gtest_output=xml:../../../test_results/ cd /home/carma-streets/streets_utils/streets_signal_optimization diff --git a/docker-compose.yml b/docker-compose.yml index 2f460eeed..341a5ac87 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -19,7 +19,7 @@ services: environment: DOCKER_HOST_IP: ${DOCKER_HOST_IP} KAFKA_ADVERTISED_HOST_NAME: ${DOCKER_HOST_IP} - KAFKA_CREATE_TOPICS: "v2xhub_scheduling_plan_sub:1:1,v2xhub_bsm_in:1:1,v2xhub_mobility_operation_in:1:1,v2xhub_mobility_path_in:1:1,vehicle_status_intent_output:1:1,v2xhub_map_msg_in:1:1,modified_spat:1:1,desired_phase_plan:1:1, tsc_config_state:1:1" + KAFKA_CREATE_TOPICS: "v2xhub_scheduling_plan_sub:1:1,v2xhub_bsm_in:1:1,v2xhub_mobility_operation_in:1:1,v2xhub_mobility_path_in:1:1,vehicle_status_intent_output:1:1,v2xhub_map_msg_in:1:1,modified_spat:1:1,desired_phase_plan:1:1, tsc_config_state:1:1,phase_control_schedule:1:1" KAFKA_ZOOKEEPER_CONNECT: zookeeper:2181 KAFKA_LOG_DIRS: "/kafka/kafka-logs" volumes: diff --git a/streets_utils/streets_phase_control_schedule/CMakeLists.txt b/streets_utils/streets_phase_control_schedule/CMakeLists.txt new file mode 100644 index 000000000..570684b68 --- /dev/null +++ b/streets_utils/streets_phase_control_schedule/CMakeLists.txt @@ -0,0 +1,79 @@ +cmake_minimum_required(VERSION 3.10.2) +project(streets_phase_control_schedule) +######################################################## +# Find Dependent Packages and set configurations +######################################################## +set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fPIC") +find_package(spdlog REQUIRED) +find_package(GTest REQUIRED CONFIG) +add_definitions(-DSPDLOG_ACTIVE_LEVEL=SPDLOG_LEVEL_TRACE) +find_package(RapidJSON REQUIRED) +# Add definition for rapidjson to include std::string +add_definitions(-DRAPIDJSON_HAS_STDSTRING=1) + +######################################################## +# Build Library +######################################################## +file(GLOB SOURCES ${CMAKE_CURRENT_SOURCE_DIR}/src/**/*.cpp) +add_library(${PROJECT_NAME}_lib ${SOURCES} ) +target_include_directories(${PROJECT_NAME}_lib + PUBLIC + $ + $ + PRIVATE + ${CMAKE_CURRENT_SOURCE_DIR}/src +) +target_link_libraries( ${PROJECT_NAME}_lib + PUBLIC + spdlog::spdlog + rapidjson +) + +######################################################## +# Install streets_phase_control_schedule package. +######################################################## +file(GLOB files ${CMAKE_CURRENT_SOURCE_DIR}/include/*.h) +file(GLOB templates ${CMAKE_CURRENT_SOURCE_DIR}/include/internal/*.tpp) +install( + TARGETS ${PROJECT_NAME}_lib + EXPORT ${PROJECT_NAME}_libTargets + LIBRARY DESTINATION lib + INCLUDES DESTINATION include + ARCHIVE DESTINATION lib +) +install( + EXPORT ${PROJECT_NAME}_libTargets + FILE ${PROJECT_NAME}_libTargets.cmake + DESTINATION lib/cmake/${PROJECT_NAME}_lib/ + NAMESPACE ${PROJECT_NAME}_lib:: +) +include(CMakePackageConfigHelpers) +configure_package_config_file( + cmake/${PROJECT_NAME}_libConfig.cmake.in + ${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_NAME}_libConfig.cmake + INSTALL_DESTINATION lib/${PROJECT_NAME}_lib/${PROJECT_NAME}_lib/ ) +install( + FILES ${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_NAME}_libConfig.cmake + DESTINATION lib/cmake/${PROJECT_NAME}_lib/ +) +install(FILES ${files} DESTINATION include) + +######################## +# googletest for unit testing +######################## +set(BINARY ${PROJECT_NAME}_test) +file(GLOB_RECURSE TEST_SOURCES LIST_DIRECTORIES false test/*.h test/*.cpp) +set(SOURCES ${TEST_SOURCES} WORKING_DIRECTORY ${PROJECT_SOURCE_DIR}/test) +add_executable(${BINARY} ${TEST_SOURCES}) +add_test(NAME ${BINARY} COMMAND ${BINARY}) +target_include_directories(${BINARY} PUBLIC ${PROJECT_SOURCE_DIR}/include) +target_link_libraries(${BINARY} + PUBLIC + ${PROJECT_NAME}_lib +) + +if(${CMAKE_VERSION} VERSION_GREATER_EQUAL "3.20") + target_link_libraries( ${BINARY} PUBLIC GTest::gtest ) +else() + target_link_libraries( ${BINARY} PUBLIC gtest ) +endif() \ No newline at end of file diff --git a/streets_utils/streets_phase_control_schedule/README.md b/streets_utils/streets_phase_control_schedule/README.md new file mode 100644 index 000000000..4d452e697 --- /dev/null +++ b/streets_utils/streets_phase_control_schedule/README.md @@ -0,0 +1,48 @@ +# Streets Phase Control Schedule Command library +## Introduction +This CARMA-streets library is meat to handle JSON serialization and deserialization for external phase control schedule message generated by [MMITSS Roadside Processor] (https://github.com/mmitss/mmitss-az), and a sample phase control message refers to https://github.com/mmitss/mmitss-az/tree/master/src/mrp/traffic-controller-interface. + +## Phase Control Schedule +External MMITSS Roadside processor will communicate with CARMA Streets TSC service via Kafka Message Broker, and send the phase control schedule to the TSC service. Thereafter, the TSC service converts the phase control schedule to SNMP commands and communicate with Traffic Signal Controller to update the controller signal phase and schedule. +### Parameter description +| Prameter Name | Data Type | Description | +| ------------- | ------------- | ----------- | +| commandPhase | Integer | Traffic Singal Controller phase | +| commandType | Enumeration | Current supported phase control types: `omit_veh`, `omit_ped`, `hold`, `forceoff`, `call_veh`, `call_ped` | +| commandStartTime | Unsigned integer | The start epoch time in millisecond of the command execution | +| commandEndTime | Unsigned integer | The end epoch time in millisecond of the command execution | + +Note: When a clear schedule is received, the commands are cleared from the phase control schedule. +### Sample +A schedule with commands: +``` +{ + "MsgType": "Schedule", + "Schedule": [ + { + "commandEndTime": 0, + "commandPhase": 2, + "commandStartTime": 0, + "commandType": "hold" + }, + { + "commandEndTime": 69, + "commandPhase": 4, + "commandStartTime": 68, + "commandType": "forceoff" + }] +} +``` +A clear schedule: +``` +{ + "MsgType": "Schedule", + "Schedule": "Clear" +} +``` +## Including library +``` +find_package( streets_phase_control_schedule_lib REQUIRED ) +... +target_link_libraries( PUBLIC streets_phase_control_schedule_lib ) +``` diff --git a/streets_utils/streets_phase_control_schedule/cmake/streets_phase_control_schedule_libConfig.cmake.in b/streets_utils/streets_phase_control_schedule/cmake/streets_phase_control_schedule_libConfig.cmake.in new file mode 100644 index 000000000..206f4cfc1 --- /dev/null +++ b/streets_utils/streets_phase_control_schedule/cmake/streets_phase_control_schedule_libConfig.cmake.in @@ -0,0 +1,7 @@ +@PACKAGE_INIT@ + +include(CMakeFindDependencyMacro) +find_dependency(spdlog REQUIRED) +find_dependency(RapidJSON REQUIRED) + +include("${CMAKE_CURRENT_LIST_DIR}/streets_phase_control_schedule_libTargets.cmake") diff --git a/streets_utils/streets_phase_control_schedule/include/streets_phase_control_command.h b/streets_utils/streets_phase_control_schedule/include/streets_phase_control_command.h new file mode 100644 index 000000000..8cca7b368 --- /dev/null +++ b/streets_utils/streets_phase_control_schedule/include/streets_phase_control_command.h @@ -0,0 +1,46 @@ +#pragma once +#include +#include +#include +#include +#include + +namespace streets_phase_control_schedule +{ + // Define Command Actions (Used in phase controls) + enum class COMMAND_TYPE + { + CALL_VEH_PHASES = 1, // Call a vehicle phase + CALL_PED_PHASES = 2, // Call a pedestrian phase + FORCEOFF_PHASES = 3, // Forceoff a vehicle phase + HOLD_VEH_PHASES = 4, // Hold a vehicle phase + OMIT_VEH_PHASES = 5, // Omit a vehicle phase + OMIT_PED_PHASES = 6, // Omit a pedestrain phase + }; + + /** + * @brief This struct provides command information received from MMITSS. (https://github.com/mmitss/mmitss-az/tree/master/src/mrp/traffic-controller-interface). + * The command will be translated into carma-streets snmp_command structure and sent to the Traffic signal controller. + */ + struct streets_phase_control_command + { + COMMAND_TYPE command_type; // Action + int command_phase = 0; // Affected phase (bitstring to integer) + uint64_t command_start_time = 0; // Start time in epoch unix timestamp in millisecond + uint64_t command_end_time = 0; // End time in epoch unix timestamp in millisecond + streets_phase_control_command() = default; + /*** + * @brief Constructor with arguments to initialize the command object + */ + streets_phase_control_command(const std::string &command_type_str, int command_phase, uint64_t command_start_time, uint64_t command_end_time); + /** + * @brief Set the command type variable + * @param string command type in string format + */ + void set_command_type(const std::string &command_type_str); + ~streets_phase_control_command() = default; + + // Overload operator<< to print command + friend std::ostream &operator<<(std::ostream &os, const streets_phase_control_command& command); + }; +} diff --git a/streets_utils/streets_phase_control_schedule/include/streets_phase_control_schedule.h b/streets_utils/streets_phase_control_schedule/include/streets_phase_control_schedule.h new file mode 100755 index 000000000..85f10ccf7 --- /dev/null +++ b/streets_utils/streets_phase_control_schedule/include/streets_phase_control_schedule.h @@ -0,0 +1,43 @@ +#pragma once +#include "streets_phase_control_schedule_exception.h" +#include +#include +#include +#include +#include +#include +#include +#include "streets_phase_control_command.h" +#include + +namespace streets_phase_control_schedule +{ + + struct streets_phase_control_schedule + { + // When a new schedule with commands is received, the commands list is populated for schedule execution. + std::vector commands; + + /*** + * By default the indicator is set to false assumming that the initial phase control schedule does not have clear execution schedule. + * When a clear execution schedule is received, the indicator should be set to true to indicate clearing all scheduled jobs from the current phase control schedule. + * Note: A clear execution schedule has an empty list of commands. However, A schedule with an empty list of commands does not mean it is a clear schedule. Intially the schedule object has an empty list of commands. + * */ + bool is_clear_current_schedule = false; + + /** + * @brief Deserialize Phase control plan JSON into Phase control plan object. + * + * @param val Phase control plan JSON. + */ + void fromJson(const std::string &json); + /** + * @brief Transform the input value to lower case and trim the leading and tailing spaces + * @param string input value reference + */ + void toLowerCaseAndTrim(std::string &value_str) const; + + // Overload operator<< to print schedule + friend std::ostream &operator<<(std::ostream &os, const streets_phase_control_schedule& schedule); + }; +} \ No newline at end of file diff --git a/streets_utils/streets_phase_control_schedule/include/streets_phase_control_schedule_exception.h b/streets_utils/streets_phase_control_schedule/include/streets_phase_control_schedule_exception.h new file mode 100644 index 000000000..297bd7b0d --- /dev/null +++ b/streets_utils/streets_phase_control_schedule/include/streets_phase_control_schedule_exception.h @@ -0,0 +1,23 @@ +#pragma once + +#include + + + +namespace streets_phase_control_schedule { + /** + * @brief Runtime error related to processing desired phase plan. + */ + class streets_phase_control_schedule_exception : public std::runtime_error{ + public: + /** + * @brief Destructor. + */ + ~streets_phase_control_schedule_exception() override; + /** + * @brief Constructor. + * @param msg String exception message. + */ + explicit streets_phase_control_schedule_exception(const std::string &msg ); + }; +} \ No newline at end of file diff --git a/streets_utils/streets_phase_control_schedule/src/exceptions/streets_phase_control_schedule_exception.cpp b/streets_utils/streets_phase_control_schedule/src/exceptions/streets_phase_control_schedule_exception.cpp new file mode 100644 index 000000000..8e2c68d3c --- /dev/null +++ b/streets_utils/streets_phase_control_schedule/src/exceptions/streets_phase_control_schedule_exception.cpp @@ -0,0 +1,8 @@ +#include "streets_phase_control_schedule_exception.h" + +namespace streets_phase_control_schedule { + + streets_phase_control_schedule_exception::streets_phase_control_schedule_exception(const std::string &msg): std::runtime_error(msg){}; + + streets_phase_control_schedule_exception::~streets_phase_control_schedule_exception() = default; +} \ No newline at end of file diff --git a/streets_utils/streets_phase_control_schedule/src/models/streets_phase_control_command.cpp b/streets_utils/streets_phase_control_schedule/src/models/streets_phase_control_command.cpp new file mode 100644 index 000000000..c4f164013 --- /dev/null +++ b/streets_utils/streets_phase_control_schedule/src/models/streets_phase_control_command.cpp @@ -0,0 +1,74 @@ +#include "streets_phase_control_command.h" +#include "streets_phase_control_schedule_exception.h" + +namespace streets_phase_control_schedule +{ + streets_phase_control_command::streets_phase_control_command(const std::string &command_type_str, int phase, uint64_t start_time, uint64_t end_time) + : command_phase(phase), command_start_time(start_time), command_end_time(end_time) + { + set_command_type(command_type_str); + } + + void streets_phase_control_command::set_command_type(const std::string &command_type_str) + { + if (command_type_str == "call_veh") + { + command_type = COMMAND_TYPE::CALL_VEH_PHASES; + } + else if (command_type_str == "call_ped") + { + command_type = COMMAND_TYPE::CALL_PED_PHASES; + } + else if (command_type_str == "forceoff") + { + command_type = COMMAND_TYPE::FORCEOFF_PHASES; + } + else if (command_type_str == "hold") + { + command_type = COMMAND_TYPE::HOLD_VEH_PHASES; + } + else if (command_type_str == "omit_veh") + { + command_type = COMMAND_TYPE::OMIT_VEH_PHASES; + } + else if (command_type_str == "omit_ped") + { + command_type = COMMAND_TYPE::OMIT_PED_PHASES; + } + else + { + throw streets_phase_control_schedule::streets_phase_control_schedule_exception("Invalid command type"); + } + } + + std::ostream &operator<<(std::ostream &os, const streets_phase_control_command& command) + { + std::string command_type_str; + switch (command.command_type) + { + case COMMAND_TYPE::CALL_VEH_PHASES: + command_type_str = "call_veh"; + break; + case COMMAND_TYPE::CALL_PED_PHASES: + command_type_str = "call_ped"; + break; + case COMMAND_TYPE::FORCEOFF_PHASES: + command_type_str = "forceoff"; + break; + case COMMAND_TYPE::HOLD_VEH_PHASES: + command_type_str = "hold"; + break; + case COMMAND_TYPE::OMIT_VEH_PHASES: + command_type_str = "omit_veh"; + break; + case COMMAND_TYPE::OMIT_PED_PHASES: + command_type_str = "omit_ped"; + break; + default: + break; + } + os << "Command type: " << command_type_str << ", command phase: " << command.command_phase << ", start time: " << command.command_start_time << ", end time: " << std::setprecision(17)<< command.command_end_time; + return os; + } + +} \ No newline at end of file diff --git a/streets_utils/streets_phase_control_schedule/src/models/streets_phase_control_schedule.cpp b/streets_utils/streets_phase_control_schedule/src/models/streets_phase_control_schedule.cpp new file mode 100755 index 000000000..021898eda --- /dev/null +++ b/streets_utils/streets_phase_control_schedule/src/models/streets_phase_control_schedule.cpp @@ -0,0 +1,104 @@ +#include "streets_phase_control_schedule.h" +#include + +namespace streets_phase_control_schedule +{ + void streets_phase_control_schedule::fromJson(const std::string &json) + { + rapidjson::Document doc; + doc.Parse(json); + if (doc.HasParseError()) + { + throw streets_phase_control_schedule_exception("streets_phase_control_schedule message JSON is misformatted. JSON parsing failed!"); + } + + if (doc.HasMember("MsgType") && doc.FindMember("MsgType")->value.IsString()) + { + std::string value = doc["MsgType"].GetString(); + toLowerCaseAndTrim(value); + if (value != "schedule") + { + throw streets_phase_control_schedule_exception("streets_phase_control_schedule message is required MsgType property value (=" + value + ") is not a schedule!"); + } + } + else + { + throw streets_phase_control_schedule_exception("streets_phase_control_schedule message is missing required MsgType property!"); + } + + if (doc.HasMember("Schedule") && doc.FindMember("Schedule")->value.IsArray()) + { + std::vector cmd_v; + // Schedule consists of an array of commands + for (const auto &command_itr : doc["Schedule"].GetArray()) + { + // Each command requires the four properties + if (!command_itr.HasMember("commandType") || !command_itr.HasMember("commandPhase") || !command_itr.HasMember("commandStartTime") || !command_itr.HasMember("commandEndTime")) + { + throw streets_phase_control_schedule_exception("streets_phase_control_schedule message is missing required commandType, commandPhase, commandStartTime or commandEndTime property!"); + } + + // Each command property has to be correct data type + if (!command_itr.FindMember("commandType")->value.IsString() || !command_itr.FindMember("commandPhase")->value.IsInt() || !command_itr.FindMember("commandStartTime")->value.IsUint64() || !command_itr.FindMember("commandEndTime")->value.IsUint64()) + { + throw streets_phase_control_schedule_exception("streets_phase_control_schedule message commandType, commandPhase, commandStartTime or commandEndTime property has incorrect data type."); + } + + std::string command_type_str = command_itr["commandType"].GetString(); + toLowerCaseAndTrim(command_type_str); + streets_phase_control_command command(command_type_str, command_itr["commandPhase"].GetInt(), command_itr["commandStartTime"].GetUint64(), command_itr["commandEndTime"].GetUint64()); + cmd_v.push_back(command); + } + //Display a warning message when receiving two schedules with commands, and there are no clear schedule in between. + if (!commands.empty()) + { + SPDLOG_WARN("Current schedule has commands execution, but new schedule is not a clear schedule!"); + } + // Always replacing the schedule commands with the latest new schedule. + commands = cmd_v; + // Make sure clear schedule indicator is false when there are commands in schedule + is_clear_current_schedule = false; + } + else if (doc.HasMember("Schedule") && doc.FindMember("Schedule")->value.IsString()) + { + std::string value = doc["Schedule"].GetString(); + toLowerCaseAndTrim(value); + if (value == "clear") + { + // Clear the commands from the schedule + commands.clear(); + is_clear_current_schedule = true; + } + else + { + throw streets_phase_control_schedule_exception("streets_phase_control_schedule message is Schedule property has invalid value (=" + value + ") !"); + } + } + else + { + throw streets_phase_control_schedule_exception("streets_phase_control_schedule message is missing required Schedule property, or schedule value is neither string nor array!"); + } + } + + void streets_phase_control_schedule::toLowerCaseAndTrim(std::string &value_str) const + { + boost::to_lower(value_str); + boost::algorithm::trim(value_str); + } + + std::ostream &operator<<(std::ostream &os, const streets_phase_control_schedule& schedule) + { + os << "Clear status: " << (schedule.is_clear_current_schedule ? "True" : "False"); + if (!schedule.is_clear_current_schedule) + { + os << ", commands ["; + for (const auto &command : schedule.commands) + { + os << "{" << command << "},"; + } + os << "]"; + } + return os; + } + +} \ No newline at end of file diff --git a/streets_utils/streets_phase_control_schedule/test/streets_phase_control_command_test.cpp b/streets_utils/streets_phase_control_schedule/test/streets_phase_control_command_test.cpp new file mode 100644 index 000000000..662b1ae1c --- /dev/null +++ b/streets_utils/streets_phase_control_schedule/test/streets_phase_control_command_test.cpp @@ -0,0 +1,80 @@ + +#include "streets_phase_control_command.h" +#include "streets_phase_control_schedule_exception.h" + +#include +#include +#include +#include +#include + +#include +#include +#include + +using namespace streets_phase_control_schedule; + +namespace +{ + + class streets_phase_control_command_test : public ::testing::Test + { + }; +}; + +TEST_F(streets_phase_control_command_test, set_command_type) +{ + streets_phase_control_command command1; + std::stringstream out; + std::string expected_str; + command1.set_command_type("call_veh"); + ASSERT_EQ(COMMAND_TYPE::CALL_VEH_PHASES, command1.command_type); + out.str(""); + out << command1; + expected_str = "Command type: call_veh, command phase: 0, start time: 0, end time: 0"; + ASSERT_EQ(expected_str, out.str()); + + command1.set_command_type("call_ped"); + ASSERT_EQ(COMMAND_TYPE::CALL_PED_PHASES, command1.command_type); + out.str(""); + out << command1; + expected_str = "Command type: call_ped, command phase: 0, start time: 0, end time: 0"; + ASSERT_EQ(expected_str, out.str()); + + command1.set_command_type("forceoff"); + ASSERT_EQ(COMMAND_TYPE::FORCEOFF_PHASES, command1.command_type); + out.str(""); + out << command1; + expected_str = "Command type: forceoff, command phase: 0, start time: 0, end time: 0"; + ASSERT_EQ(expected_str, out.str()); + + command1.set_command_type("hold"); + ASSERT_EQ(COMMAND_TYPE::HOLD_VEH_PHASES, command1.command_type); + out.str(""); + out << command1; + expected_str = "Command type: hold, command phase: 0, start time: 0, end time: 0"; + ASSERT_EQ(expected_str, out.str()); + + command1.set_command_type("omit_veh"); + ASSERT_EQ(COMMAND_TYPE::OMIT_VEH_PHASES, command1.command_type); + out.str(""); + out << command1; + expected_str = "Command type: omit_veh, command phase: 0, start time: 0, end time: 0"; + ASSERT_EQ(expected_str, out.str()); + + command1.set_command_type("omit_ped"); + ASSERT_EQ(COMMAND_TYPE::OMIT_PED_PHASES, command1.command_type); + out.str(""); + out << command1; + expected_str = "Command type: omit_ped, command phase: 0, start time: 0, end time: 0"; + ASSERT_EQ(expected_str, out.str()); + + ASSERT_THROW(command1.set_command_type("unkown_command"), streets_phase_control_schedule_exception); + + streets_phase_control_command command2("call_veh", 2, 0.0, 23.999767065048218); + ASSERT_EQ(COMMAND_TYPE::CALL_VEH_PHASES, command2.command_type); + expected_str = "Command type: call_veh, command phase: 2, start time: 0, end time: 23"; + out.str(""); + out << command2; + ASSERT_EQ(expected_str, out.str()); +} \ No newline at end of file diff --git a/streets_utils/streets_phase_control_schedule/test/streets_phase_control_schedule_test.cpp b/streets_utils/streets_phase_control_schedule/test/streets_phase_control_schedule_test.cpp new file mode 100644 index 000000000..4c982e9ae --- /dev/null +++ b/streets_utils/streets_phase_control_schedule/test/streets_phase_control_schedule_test.cpp @@ -0,0 +1,148 @@ + +#include "streets_phase_control_schedule.h" +#include "streets_phase_control_schedule_exception.h" + +#include +#include +#include +#include +#include + +#include +#include +#include + +using namespace streets_phase_control_schedule; + +namespace +{ + + class streets_phase_control_schedule_test : public ::testing::Test + { + /** + * @brief Test Setup method run before each test. + */ + void SetUp() override + { + } + /** + * @brief Test TearDown method run after each test. + * + */ + void TearDown() override + { + } + }; +}; + +TEST_F(streets_phase_control_schedule_test, fromJson) +{ + std::stringstream out; + std::string expected_str; + streets_phase_control_schedule::streets_phase_control_schedule invalid_schedule; + std::string input_str = "invalid"; + ASSERT_THROW(invalid_schedule.fromJson(input_str), streets_phase_control_schedule_exception); + + input_str = "{\"InvalidKey\":\"Invalid\"}"; + ASSERT_THROW(invalid_schedule.fromJson(input_str), streets_phase_control_schedule_exception); + + input_str = "{\"MsgType\":\"Invalid\",\"Schedule\":\"Clear\"}"; + ASSERT_THROW(invalid_schedule.fromJson(input_str), streets_phase_control_schedule_exception); + + input_str = "{\"MsgType\":\"Schedule\",\"Schedule\":\"invalid\"}"; + ASSERT_THROW(invalid_schedule.fromJson(input_str), streets_phase_control_schedule_exception); + + streets_phase_control_schedule::streets_phase_control_schedule clear_schedule; + ASSERT_FALSE(clear_schedule.is_clear_current_schedule); + input_str = "{\"MsgType\":\"Schedule\",\"Schedule\":\"Clear\"}"; + clear_schedule.fromJson(input_str); + ASSERT_TRUE(clear_schedule.is_clear_current_schedule); + ASSERT_EQ(0, clear_schedule.commands.size()); + ASSERT_TRUE(clear_schedule.is_clear_current_schedule); + + input_str = "{\"MsgType\":\"Schedule\",\"Schedule\": [{\"commandEndTime\":0}]}"; + ASSERT_THROW(invalid_schedule.fromJson(input_str), streets_phase_control_schedule_exception); + + + input_str = "{\"MsgType\":\"Schedule\",\"Schedule\": 0}"; + ASSERT_THROW(invalid_schedule.fromJson(input_str), streets_phase_control_schedule_exception); + + input_str = "{\"MsgType\":\"Schedule\",\"No Schedule\": []}"; + ASSERT_THROW(invalid_schedule.fromJson(input_str), streets_phase_control_schedule_exception); + + streets_phase_control_schedule::streets_phase_control_schedule new_schedule_wrong_data_type; + input_str = "{\"MsgType\":\"Schedule\",\"Schedule\":[{\"commandEndTime\":0,\"commandPhase\":2,\"commandStartTime\":0.0,\"commandType\":\"hold\"}]}"; + ASSERT_THROW(new_schedule_wrong_data_type.fromJson(input_str), streets_phase_control_schedule_exception); + ASSERT_FALSE(new_schedule_wrong_data_type.is_clear_current_schedule); + ASSERT_EQ(0, new_schedule_wrong_data_type.commands.size()); + + streets_phase_control_schedule::streets_phase_control_schedule new_schedule; + ASSERT_FALSE(new_schedule.is_clear_current_schedule); + input_str = "{\"MsgType\":\"Schedule\",\"Schedule\":[{\"commandEndTime\":0,\"commandPhase\":2,\"commandStartTime\":0,\"commandType\":\"hold\"},{\"commandEndTime\":24,\"commandPhase\":4,\"commandStartTime\":5,\"commandType\":\"hold\"},{\"commandEndTime\":44,\"commandPhase\":2,\"commandStartTime\":29,\"commandType\":\"hold\"},{\"commandEndTime\":64,\"commandPhase\":4,\"commandStartTime\":49,\"commandType\":\"hold\"},{\"commandEndTime\":12,\"commandPhase\":2,\"commandStartTime\":11,\"commandType\":\"forceoff\"},{\"commandEndTime\":69,\"commandPhase\":4,\"commandStartTime\":68,\"commandType\":\"forceoff\"},{\"commandEndTime\":109,\"commandPhase\":2,\"commandStartTime\":108,\"commandType\":\"forceoff\"},{\"commandEndTime\":129,\"commandPhase\":4,\"commandStartTime\":128,\"commandType\":\"forceoff\"},{\"commandEndTime\":23,\"commandPhase\":4,\"commandStartTime\":0,\"commandType\":\"call_veh\"},{\"commandEndTime\":133,\"commandPhase\":1,\"commandStartTime\":0,\"commandType\":\"omit_veh\"},{\"commandEndTime\":133,\"commandPhase\":3,\"commandStartTime\":0,\"commandType\":\"omit_veh\"},{\"commandEndTime\":0,\"commandPhase\":6,\"commandStartTime\":0,\"commandType\":\"hold\"},{\"commandEndTime\":24,\"commandPhase\":7,\"commandStartTime\":5,\"commandType\":\"hold\"},{\"commandEndTime\":44,\"commandPhase\":6,\"commandStartTime\":29,\"commandType\":\"hold\"},{\"commandEndTime\":64,\"commandPhase\":7,\"commandStartTime\":49,\"commandType\":\"hold\"},{\"commandEndTime\":12,\"commandPhase\":6,\"commandStartTime\":11,\"commandType\":\"forceoff\"},{\"commandEndTime\":69,\"commandPhase\":7,\"commandStartTime\":68,\"commandType\":\"forceoff\"},{\"commandEndTime\":109,\"commandPhase\":6,\"commandStartTime\":108,\"commandType\":\"forceoff\"},{\"commandEndTime\":129,\"commandPhase\":7,\"commandStartTime\":128,\"commandType\":\"forceoff\"},{\"commandEndTime\":23,\"commandPhase\":7,\"commandStartTime\":0,\"commandType\":\"call_veh\"},{\"commandEndTime\":133,\"commandPhase\":5,\"commandStartTime\":0,\"commandType\":\"omit_veh\"},{\"commandEndTime\":133,\"commandPhase\":8,\"commandStartTime\":0,\"commandType\":\"omit_veh\"}]}"; + new_schedule.fromJson(input_str); + ASSERT_FALSE(new_schedule.is_clear_current_schedule); + ASSERT_EQ(22, new_schedule.commands.size()); + + std::string input_str_wrong_data_type = "{\"MsgType\":\"Schedule\",\"Schedule\":[{\"commandEndTime\":0,\"commandPhase\":2,\"commandStartTime\":0,\"commandType\":\"hold\"},{\"commandEndTime\":0.0,\"commandPhase\":2,\"commandStartTime\":0.0,\"commandType\":\"hold\"}]}"; + ASSERT_THROW(new_schedule.fromJson(input_str_wrong_data_type), streets_phase_control_schedule_exception); + ASSERT_EQ(22, new_schedule.commands.size()); + + input_str = "{\"MsgType\":\"Schedule\",\"Schedule\":[{\"commandEndTime\":0,\"commandPhase\":2,\"commandStartTime\":0,\"commandType\":\"hold\"}]}"; + new_schedule.fromJson(input_str); + ASSERT_EQ(1, new_schedule.commands.size()); + ASSERT_FALSE(new_schedule.is_clear_current_schedule); + expected_str = "Clear status: False, commands [{Command type: hold, command phase: 2, start time: 0, end time: 0},]"; + out.str(""); + out << new_schedule; + ASSERT_EQ(expected_str, out.str()); + + input_str = "{\"MsgType\":\"Schedule\",\"Schedule\":[{\"commandEndTime\":0,\"commandPhase\":2,\"commandStartTime\":0,\"commandType\":\"call_ped\"}]}"; + new_schedule.fromJson(input_str); + ASSERT_EQ(1, new_schedule.commands.size()); + ASSERT_FALSE(new_schedule.is_clear_current_schedule); + expected_str = "Clear status: False, commands [{Command type: call_ped, command phase: 2, start time: 0, end time: 0},]"; + out.str(""); + out << new_schedule; + ASSERT_EQ(expected_str, out.str()); + + input_str = "{\"MsgType\":\"Schedule\",\"Schedule\":[{\"commandEndTime\":0,\"commandPhase\":2,\"commandStartTime\":0,\"commandType\":\"forceoff\"}]}"; + new_schedule.fromJson(input_str); + ASSERT_EQ(1, new_schedule.commands.size()); + ASSERT_FALSE(new_schedule.is_clear_current_schedule); + expected_str = "Clear status: False, commands [{Command type: forceoff, command phase: 2, start time: 0, end time: 0},]"; + out.str(""); + out << new_schedule; + ASSERT_EQ(expected_str, out.str()); + + input_str = "{\"MsgType\":\"Schedule\",\"Schedule\":[{\"commandEndTime\":0,\"commandPhase\":2,\"commandStartTime\":0,\"commandType\":\"omit_veh\"}]}"; + new_schedule.fromJson(input_str); + ASSERT_EQ(1, new_schedule.commands.size()); + ASSERT_FALSE(new_schedule.is_clear_current_schedule); + expected_str = "Clear status: False, commands [{Command type: omit_veh, command phase: 2, start time: 0, end time: 0},]"; + out.str(""); + out << new_schedule; + ASSERT_EQ(expected_str, out.str()); + + input_str = "{\"MsgType\":\"Schedule\",\"Schedule\":[{\"commandEndTime\":0,\"commandPhase\":2,\"commandStartTime\":0,\"commandType\":\"omit_ped\"}]}"; + new_schedule.fromJson(input_str); + ASSERT_EQ(1, new_schedule.commands.size()); + ASSERT_FALSE(new_schedule.is_clear_current_schedule); + expected_str = "Clear status: False, commands [{Command type: omit_ped, command phase: 2, start time: 0, end time: 0},]"; + out.str(""); + out << new_schedule; + ASSERT_EQ(expected_str, out.str()); + + input_str = "{\"MsgType\":\"Schedule\",\"Schedule\":\"Clear\"}"; + new_schedule.fromJson(input_str); + ASSERT_EQ(0, new_schedule.commands.size()); + ASSERT_TRUE(new_schedule.is_clear_current_schedule); + + ASSERT_THROW(new_schedule.fromJson(input_str_wrong_data_type), streets_phase_control_schedule_exception); + ASSERT_EQ(0, new_schedule.commands.size()); + ASSERT_TRUE(new_schedule.is_clear_current_schedule); + + expected_str = "Clear status: True"; + out.str(""); + out << new_schedule; + ASSERT_EQ(expected_str, out.str()); +} \ No newline at end of file diff --git a/streets_utils/streets_phase_control_schedule/test/test_main.cpp b/streets_utils/streets_phase_control_schedule/test/test_main.cpp new file mode 100644 index 000000000..1e925a90b --- /dev/null +++ b/streets_utils/streets_phase_control_schedule/test/test_main.cpp @@ -0,0 +1,6 @@ +#include "gtest/gtest.h" +int main(int argc, char **argv) +{ + ::testing::InitGoogleTest(&argc, argv); + return RUN_ALL_TESTS(); +} \ No newline at end of file diff --git a/tsc_client_service/CMakeLists.txt b/tsc_client_service/CMakeLists.txt index 211a02eeb..43934ed62 100644 --- a/tsc_client_service/CMakeLists.txt +++ b/tsc_client_service/CMakeLists.txt @@ -13,6 +13,7 @@ find_package(streets_service_base_lib REQUIRED) find_package(streets_signal_phase_and_timing_lib REQUIRED) find_package(streets_tsc_configuration_lib REQUIRED) find_package(streets_desired_phase_plan_lib REQUIRED) +find_package(streets_phase_control_schedule_lib REQUIRED) find_library(NETSNMPAGENT "netsnmpagent") find_library(NETSNMPMIBS "netsnmpmibs") find_library(NETSNMP "netsnmp") @@ -49,6 +50,7 @@ target_link_libraries( ${PROJECT_NAME}_lib streets_signal_phase_and_timing_lib streets_tsc_configuration_lib streets_desired_phase_plan_lib + streets_phase_control_schedule_lib intersection_client_api_lib spdlog::spdlog Qt5::Core diff --git a/tsc_client_service/Dockerfile b/tsc_client_service/Dockerfile index 8181cea7d..b55afde16 100644 --- a/tsc_client_service/Dockerfile +++ b/tsc_client_service/Dockerfile @@ -84,6 +84,15 @@ RUN cmake -DCMAKE_BUILD_TYPE="Debug" .. RUN make RUN make install +# Install streets_phase_control_schedule +RUN echo " ------> Install streets phase control schedule from streets_utils..." +WORKDIR /home/carma-streets/streets_utils/streets_phase_control_schedule +RUN mkdir build +WORKDIR /home/carma-streets/streets_utils/streets_phase_control_schedule/build +RUN cmake -DCMAKE_BUILD_TYPE="Debug" .. +RUN make +RUN make install + # Install streets_signal_phase_and_timing RUN echo " ------> Install streets service base library from streets_utils..." WORKDIR /home/carma-streets/streets_utils/streets_signal_phase_and_timing diff --git a/tsc_client_service/README.md b/tsc_client_service/README.md index e64ea1b07..6c41a8630 100644 --- a/tsc_client_service/README.md +++ b/tsc_client_service/README.md @@ -32,6 +32,8 @@ The `intersection_client` is a REST client implemented using the `streets_utils/ The `spat_worker` is a class which encapsulates a UDP socket listener. This socket listener, listens for UDP NTCIP data packets set from the **TSC** at 10 hz that provide traffic signal state information required for populating the **SPaT**. The `spat_worker` contains a method to consume a UDP datapacket and update the `spat` pointer which stores the most up-to-date information of the traffic signal controller state. The `tsc_service` `spat_thread` then continously consumes these messages and publishes the resulting **SPaT** JSON on the CARMA-Streets Kafka broker. - +## tsc_service to MMITSS integration +### Configuration parameter +The `use_mmitss_mrp` parameter in manifest.json file is used to determine whether the TSC service consume [phase control schedule](https://github.com/mmitss/mmitss-az/tree/master/src/mrp/traffic-controller-interface) from external [MMTISS Roadside processor](https://github.com/mmitss/mmitss-az) or not. By default, this parameter is set to false to consume desired phase plan from internal signal optimization service. The MRP and carma-streets signal optimization service shall run exclusively, meaning there is no scenario where both MRP and signal optmization service are trying to manipulate traffic signal controller at the same time. diff --git a/tsc_client_service/include/control_tsc_state.h b/tsc_client_service/include/control_tsc_state.h index 2a7146de3..0e0550623 100644 --- a/tsc_client_service/include/control_tsc_state.h +++ b/tsc_client_service/include/control_tsc_state.h @@ -1,6 +1,7 @@ #pragma once #include "streets_desired_phase_plan.h" +#include "streets_phase_control_schedule.h" #include "snmp_client.h" #include "ntcip_oids.h" #include "monitor_tsc_state.h" @@ -97,6 +98,13 @@ namespace traffic_signal_controller_service void update_tsc_control_queue(std::shared_ptr desired_phase_plan, std::queue& tsc_command_queue) const; + /** + * @brief Method to update the queue of tsc_control + * @param phase_control_schedule Pointer to the phase control schedule. + * @param tsc_command_queue Queue of snmp commands to set HOLD and OMIT on the traffic signal controller + **/ + void update_tsc_control_queue(std::shared_ptr phase_control_schedule, + std::queue& tsc_command_queue) const; /** * @brief Method to create OMIT snmp command for provided signal groups, which will result in the traffic signal controller skipping the specified phases. * This method should be sent before any yellow phase for omission to take effect. diff --git a/tsc_client_service/include/tsc_service.h b/tsc_client_service/include/tsc_service.h index db8625594..45fb8092d 100644 --- a/tsc_client_service/include/tsc_service.h +++ b/tsc_client_service/include/tsc_service.h @@ -40,6 +40,11 @@ namespace traffic_signal_controller_service { */ std::shared_ptr desired_phase_plan_consumer; + /* + * @brief Kafka consumer for consuming Phase Control Schedule JSON + */ + std::shared_ptr phase_control_schedule_consumer; + /** * @brief spat_worker contains udp_socket_listener and consumes UDP data * packets and updates spat accordingly. @@ -66,6 +71,11 @@ namespace traffic_signal_controller_service { * JSON message. */ std::shared_ptr spat_ptr; + + /** + * @brief Point to phase control schedule object which is updated based on the received JSON message from MMITSS Road side processor. + */ + std::shared_ptr phase_control_schedule_ptr; /** * @brief Pointer to tsc_configuration_state object which is traffic signal controller * configuration information obtained from the tsc_state worker @@ -108,6 +118,13 @@ namespace traffic_signal_controller_service { FRIEND_TEST(tsc_service_test,test_produce_tsc_config_json_timeout); FRIEND_TEST(tsc_service_test,test_init_kafka_consumer_producer); + /*** + * Configuration parameter to control different interfaces to schedule the traffic signal controller + * If false, it will use carma-streets internal signal optimization service and its generated desired phase plan to schedule traffic signal controller. + * If true, it will use external MRP (MMITSS Roadside Processor, link: https://github.com/mmitss/mmitss-az) and its generated phase control schedule to schedule the traffic sginal controller. + * **/ + bool use_mmitss_mrp = false; + public: tsc_service() = default; @@ -210,6 +227,10 @@ namespace traffic_signal_controller_service { void produce_tsc_config_json() const; void consume_desired_phase_plan(); + /** + * @brief Thread callback function to run the phase control schedule Kafka consumer to consume phase control schedule JSON message + */ + void consume_phase_control_schedule(); /** * @brief Method to control phases on the Traffic Signal Controller by sending OMIT and HOLD commands constructed to diff --git a/tsc_client_service/manifest.json b/tsc_client_service/manifest.json index f6ead6a43..c2c2c5f17 100644 --- a/tsc_client_service/manifest.json +++ b/tsc_client_service/manifest.json @@ -66,6 +66,12 @@ "description": "Kafka topic for streets internal SPAT messages", "type": "STRING" }, + { + "name": "phase_control_schedule_consumer_topic", + "value": "phase_control_schedule", + "description": "Kafka topic for streets internal phase control schedule messages", + "type": "STRING" + }, { "name": "desired_phase_plan_consumer_topic", "value": "desired_phase_plan", @@ -119,6 +125,12 @@ "value": "snmpcmdLogs", "description": "Filename for snmp commands logging (Note: set enable_snmp_cmd_logging true).", "type": "STRING" + }, + { + "name": "use_mmitss_mrp", + "value": false, + "description": "If false, it will use carma-streets internal signal optimization service and its generated desired phase plan to schedule traffic signal controller. If true, it will use external MRP (MMITSS Roadside Processor, link: https://github.com/mmitss/mmitss-az) and its generated phase control schedule to schedule the traffic sginal controller.", + "type": "BOOL" } ] diff --git a/tsc_client_service/src/control_tsc_state.cpp b/tsc_client_service/src/control_tsc_state.cpp index 11d74ee9d..9c0035177 100644 --- a/tsc_client_service/src/control_tsc_state.cpp +++ b/tsc_client_service/src/control_tsc_state.cpp @@ -85,6 +85,26 @@ namespace traffic_signal_controller_service } + void control_tsc_state::update_tsc_control_queue(std::shared_ptr phase_control_schedule_ptr, + std::queue& tsc_command_queue) const + { + if(!phase_control_schedule_ptr) + { + SPDLOG_DEBUG("Phase control schedule is not initialized."); + return; + } + if(phase_control_schedule_ptr->is_clear_current_schedule) + { + SPDLOG_DEBUG("Clear SNMP command queue!"); + //ToDo: Send clear all scheduled jobs or clear all commands on the update command queue + }else{ + std::stringstream ss; + ss << *phase_control_schedule_ptr; + SPDLOG_DEBUG("Update SNMP command queue with new phase control schedule commands: {0}", ss.str()); + //ToDo: Update command queue with the new phase control schedule commands + } + } + snmp_cmd_struct control_tsc_state::create_omit_command(const std::vector& signal_groups, int64_t start_time, bool is_reset) const { if(!is_reset) diff --git a/tsc_client_service/src/tsc_service.cpp b/tsc_client_service/src/tsc_service.cpp index 55913a854..8b6f2d425 100644 --- a/tsc_client_service/src/tsc_service.cpp +++ b/tsc_client_service/src/tsc_service.cpp @@ -18,6 +18,9 @@ namespace traffic_signal_controller_service { std::string spat_topic_name = streets_configuration::get_string_config("spat_producer_topic"); std::string dpp_consumer_topic = streets_configuration::get_string_config("desired_phase_plan_consumer_topic"); + + //Phase control schedule(PCS) consumer topic + std::string pcs_consumer_topic = streets_configuration::get_string_config("phase_control_schedule_consumer_topic"); if (!spat_producer && !initialize_kafka_producer( spat_topic_name, spat_producer)) { @@ -31,7 +34,13 @@ namespace traffic_signal_controller_service { SPDLOG_ERROR("Failed to initialize kafka desired_phase_plan_consumer!"); return false; - } + } + + if(!phase_control_schedule_consumer && !initialize_kafka_consumer(pcs_consumer_topic, phase_control_schedule_consumer)){ + SPDLOG_ERROR("Failed to initialize kafka phase_control_schedule_consumer!"); + return false; + } + // Initialize SNMP Client std::string target_ip = streets_configuration::get_string_config("target_ip"); int target_port = streets_configuration::get_int_config("target_port"); @@ -63,6 +72,7 @@ namespace traffic_signal_controller_service { int socket_timeout = streets_configuration::get_int_config("socket_timeout"); bool use_msg_timestamp = streets_configuration::get_boolean_config("use_tsc_timestamp"); enable_snmp_cmd_logging_ = streets_configuration::get_boolean_config("enable_snmp_cmd_logging"); + use_mmitss_mrp = streets_configuration::get_boolean_config("use_mmitss_mrp"); if (!initialize_spat_worker(socket_ip, socket_port, socket_timeout, use_msg_timestamp)) { SPDLOG_ERROR("Failed to initialize SPaT Worker"); @@ -87,6 +97,9 @@ namespace traffic_signal_controller_service { // Initialize monitor desired phase plan monitor_dpp_ptr = std::make_shared( snmp_client_ptr ); + //Initialize phase control schedule + phase_control_schedule_ptr = std::make_shared(); + // Initialize control_tsc_state ptr control_tsc_state_ptr_ = std::make_shared(snmp_client_ptr, tsc_state_ptr); @@ -255,6 +268,25 @@ namespace traffic_signal_controller_service { } } + void tsc_service::consume_phase_control_schedule(){ + phase_control_schedule_consumer->subscribe(); + while (phase_control_schedule_consumer->is_running()) + { + const std::string payload = phase_control_schedule_consumer->consume(1000); + if (payload.length() != 0) + { + SPDLOG_DEBUG("Consumed: {0}", payload); + try { + //Update phase control schedule with the latest incoming schedule + phase_control_schedule_ptr->fromJson(payload); + control_tsc_state_ptr_->update_tsc_control_queue(phase_control_schedule_ptr, tsc_set_command_queue_); + } catch(streets_phase_control_schedule::streets_phase_control_schedule_exception &ex){ + SPDLOG_DEBUG("Failed to consume phase control schedule commands: {0}", ex.what()); + } + } + } + } + void tsc_service::control_tsc_phases() { try{ @@ -356,12 +388,25 @@ namespace traffic_signal_controller_service { std::thread desired_phase_plan_t(&tsc_service::consume_desired_phase_plan, this); + std::thread consume_phase_control_schedule_t(&tsc_service::consume_phase_control_schedule, this); + std::thread control_phases_t(&tsc_service::control_tsc_phases, this); // Run threads as joint so that they dont overlap execution tsc_config_thread.join(); spat_t.join(); - desired_phase_plan_t.join(); + + // An indicator to control whether launching a thread to consume carma-streets internal desired phase plan generated by singal optimization (SO) service + // or a thread to consume MMITSS external phase control schedule generated by MRP (https://github.com/mmitss/mmitss-az) + if (use_mmitss_mrp) + { + SPDLOG_DEBUG("Thread joined to consume phase control schedule generated by MRP."); + consume_phase_control_schedule_t.join(); + }else{ + SPDLOG_DEBUG("Thread joined to consume desired phase plan generated by carma-streets SO service."); + desired_phase_plan_t.join(); + } + control_phases_t.join(); } @@ -383,5 +428,10 @@ namespace traffic_signal_controller_service { SPDLOG_WARN("Stopping desired phase plan consumer!"); desired_phase_plan_consumer->stop(); } + + if (phase_control_schedule_consumer) { + SPDLOG_WARN("Stopping phase control consumer!"); + phase_control_schedule_consumer->stop(); + } } } \ No newline at end of file diff --git a/tsc_client_service/test/test_control_tsc_state.cpp b/tsc_client_service/test/test_control_tsc_state.cpp index 8d3984d56..c74223486 100644 --- a/tsc_client_service/test/test_control_tsc_state.cpp +++ b/tsc_client_service/test/test_control_tsc_state.cpp @@ -209,6 +209,30 @@ namespace traffic_signal_controller_service auto dpp_ptr_2 = std::make_shared(desired_phase_plan_2); EXPECT_NO_THROW(worker.update_tsc_control_queue(dpp_ptr_2,control_commands_queue)); + auto pcs_ptr = std::make_shared(); + std::string input_schedule_str = "{\"MsgType\":\"Schedule\",\"Schedule\":[{\"commandEndTime\":0,\"commandPhase\":2,\"commandStartTime\":0,\"commandType\":\"hold\"},{\"commandEndTime\":24,\"commandPhase\":4,\"commandStartTime\":5,\"commandType\":\"hold\"},{\"commandEndTime\":44,\"commandPhase\":2,\"commandStartTime\":29,\"commandType\":\"hold\"},{\"commandEndTime\":64,\"commandPhase\":4,\"commandStartTime\":49,\"commandType\":\"hold\"},{\"commandEndTime\":12,\"commandPhase\":2,\"commandStartTime\":11,\"commandType\":\"forceoff\"},{\"commandEndTime\":69,\"commandPhase\":4,\"commandStartTime\":68,\"commandType\":\"forceoff\"},{\"commandEndTime\":109,\"commandPhase\":2,\"commandStartTime\":108,\"commandType\":\"forceoff\"},{\"commandEndTime\":129,\"commandPhase\":4,\"commandStartTime\":128,\"commandType\":\"forceoff\"},{\"commandEndTime\":23,\"commandPhase\":4,\"commandStartTime\":0,\"commandType\":\"call_veh\"},{\"commandEndTime\":133,\"commandPhase\":1,\"commandStartTime\":0,\"commandType\":\"omit_veh\"},{\"commandEndTime\":133,\"commandPhase\":3,\"commandStartTime\":0,\"commandType\":\"omit_veh\"},{\"commandEndTime\":0,\"commandPhase\":6,\"commandStartTime\":0,\"commandType\":\"hold\"},{\"commandEndTime\":24,\"commandPhase\":7,\"commandStartTime\":5,\"commandType\":\"hold\"},{\"commandEndTime\":44,\"commandPhase\":6,\"commandStartTime\":29,\"commandType\":\"hold\"},{\"commandEndTime\":64,\"commandPhase\":7,\"commandStartTime\":49,\"commandType\":\"hold\"},{\"commandEndTime\":12,\"commandPhase\":6,\"commandStartTime\":11,\"commandType\":\"forceoff\"},{\"commandEndTime\":69,\"commandPhase\":7,\"commandStartTime\":68,\"commandType\":\"forceoff\"},{\"commandEndTime\":109,\"commandPhase\":6,\"commandStartTime\":108,\"commandType\":\"forceoff\"},{\"commandEndTime\":129,\"commandPhase\":7,\"commandStartTime\":128,\"commandType\":\"forceoff\"},{\"commandEndTime\":23,\"commandPhase\":7,\"commandStartTime\":0,\"commandType\":\"call_veh\"},{\"commandEndTime\":133,\"commandPhase\":5,\"commandStartTime\":0,\"commandType\":\"omit_veh\"},{\"commandEndTime\":133,\"commandPhase\":8,\"commandStartTime\":0,\"commandType\":\"omit_veh\"}]}"; + pcs_ptr->fromJson(input_schedule_str); + worker.update_tsc_control_queue(pcs_ptr, control_commands_queue); } + + TEST(traffic_signal_controller_service, update_tsc_control_queue) + { + // Define Worker + auto mock_client = std::make_shared(); + auto _tsc_state = std::make_shared(mock_client); + _tsc_state->initialize(); + traffic_signal_controller_service::control_tsc_state worker(mock_client, _tsc_state); + auto pcs_ptr = std::make_shared(); + std::string input_schedule_str = "{\"MsgType\":\"Schedule\",\"Schedule\":[{\"commandEndTime\":0,\"commandPhase\":2,\"commandStartTime\":0,\"commandType\":\"hold\"},{\"commandEndTime\":24,\"commandPhase\":4,\"commandStartTime\":5,\"commandType\":\"hold\"},{\"commandEndTime\":44,\"commandPhase\":2,\"commandStartTime\":29,\"commandType\":\"hold\"},{\"commandEndTime\":64,\"commandPhase\":4,\"commandStartTime\":49,\"commandType\":\"hold\"},{\"commandEndTime\":12,\"commandPhase\":2,\"commandStartTime\":11,\"commandType\":\"forceoff\"},{\"commandEndTime\":69,\"commandPhase\":4,\"commandStartTime\":68,\"commandType\":\"forceoff\"},{\"commandEndTime\":109,\"commandPhase\":2,\"commandStartTime\":108,\"commandType\":\"forceoff\"},{\"commandEndTime\":129,\"commandPhase\":4,\"commandStartTime\":128,\"commandType\":\"forceoff\"},{\"commandEndTime\":23,\"commandPhase\":4,\"commandStartTime\":0,\"commandType\":\"call_veh\"},{\"commandEndTime\":133,\"commandPhase\":1,\"commandStartTime\":0,\"commandType\":\"omit_veh\"},{\"commandEndTime\":133,\"commandPhase\":3,\"commandStartTime\":0,\"commandType\":\"omit_veh\"},{\"commandEndTime\":0,\"commandPhase\":6,\"commandStartTime\":0,\"commandType\":\"hold\"},{\"commandEndTime\":24,\"commandPhase\":7,\"commandStartTime\":5,\"commandType\":\"hold\"},{\"commandEndTime\":44,\"commandPhase\":6,\"commandStartTime\":29,\"commandType\":\"hold\"},{\"commandEndTime\":64,\"commandPhase\":7,\"commandStartTime\":49,\"commandType\":\"hold\"},{\"commandEndTime\":12,\"commandPhase\":6,\"commandStartTime\":11,\"commandType\":\"forceoff\"},{\"commandEndTime\":69,\"commandPhase\":7,\"commandStartTime\":68,\"commandType\":\"forceoff\"},{\"commandEndTime\":109,\"commandPhase\":6,\"commandStartTime\":108,\"commandType\":\"forceoff\"},{\"commandEndTime\":129,\"commandPhase\":7,\"commandStartTime\":128,\"commandType\":\"forceoff\"},{\"commandEndTime\":23,\"commandPhase\":7,\"commandStartTime\":0,\"commandType\":\"call_veh\"},{\"commandEndTime\":133,\"commandPhase\":5,\"commandStartTime\":0,\"commandType\":\"omit_veh\"},{\"commandEndTime\":133,\"commandPhase\":8,\"commandStartTime\":0,\"commandType\":\"omit_veh\"}]}"; + pcs_ptr->fromJson(input_schedule_str); + // Test update queue + std::queue control_commands_queue; + worker.update_tsc_control_queue(pcs_ptr, control_commands_queue); + input_schedule_str = "{\"MsgType\":\"Schedule\",\"Schedule\":\"Clear\"}"; + pcs_ptr->fromJson(input_schedule_str); + worker.update_tsc_control_queue(pcs_ptr, control_commands_queue); + std::shared_ptr pcs_ptr_null; + worker.update_tsc_control_queue(pcs_ptr_null, control_commands_queue); + } } \ No newline at end of file diff --git a/tsc_client_service/test/test_tsc_service.cpp b/tsc_client_service/test/test_tsc_service.cpp index 9247042cd..f1212fe9c 100644 --- a/tsc_client_service/test/test_tsc_service.cpp +++ b/tsc_client_service/test/test_tsc_service.cpp @@ -31,6 +31,7 @@ namespace traffic_signal_controller_service service.spat_producer = spat_producer; service.tsc_config_producer = tsc_config_producer; service.desired_phase_plan_consumer = dpp_consumer; + service.phase_control_schedule_consumer = std::make_shared(); service.snmp_client_ptr = mock_snmp; setenv("SIMULATION_MODE", "FALSE", 1); setenv("CONFIG_FILE_PATH", "../manifest.json", 1); From c5791491c37448a5ae877a054eb5ffb39c656d46 Mon Sep 17 00:00:00 2001 From: dan-du-car <62157949+dan-du-car@users.noreply.github.com> Date: Mon, 26 Jun 2023 09:03:45 -0400 Subject: [PATCH 03/80] CARMA Streets can execute MMITSS phase control schedule (#335) * init * update lib * update * update tsc service * update queue * update build * update unit test * update tsc service * update coverage * update * update * update * update code coverage and refactor create omit cmd func * update code coverage * add test script * fix concurrent queue update * update * address comments * address comments * add readme * address comments * address comments * address comments * add break * update * update * update * add more comments --- .sonarqube/sonar-scanner.properties | 10 +- build.sh | 1 + coverage.sh | 7 + scripts/phase_control_schedule.json | 137 +++++ scripts/phase_control_schedule_clear.json | 4 + scripts/phase_control_schedule_simple.json | 131 +++++ scripts/simulate_phase_control_schedule.py | 74 +++ .../streets_phase_control_schedule/README.md | 4 +- .../include/streets_phase_control_command.h | 12 +- .../models/streets_phase_control_command.cpp | 31 +- .../models/streets_phase_control_schedule.cpp | 4 + .../streets_phase_control_command_test.cpp | 31 +- .../streets_phase_control_schedule_test.cpp | 10 + streets_utils/streets_snmp_cmd/CMakeLists.txt | 81 +++ streets_utils/streets_snmp_cmd/README.md | 29 + .../cmake/streets_snmp_cmd_libConfig.cmake.in | 7 + .../include/streets_snmp_cmd.h | 74 +++ .../include/streets_snmp_cmd_converter.h | 85 +++ .../include/streets_snmp_cmd_exception.h | 20 + .../exceptions/streets_snmp_cmd_exception.cpp | 8 + .../src/models/streets_snmp_cmd.cpp | 39 ++ .../src/models/streets_snmp_cmd_converter.cpp | 400 ++++++++++++++ .../test/streets_snmp_cmd_converter_test.cpp | 507 ++++++++++++++++++ .../test/streets_snmp_cmd_test.cpp | 58 ++ .../streets_snmp_cmd/test/test_main.cpp | 6 + tsc_client_service/CMakeLists.txt | 4 +- tsc_client_service/Dockerfile | 8 + .../include/control_tsc_state.h | 96 +--- tsc_client_service/include/mock_snmp_client.h | 2 +- .../include/monitor_tsc_state.h | 10 +- tsc_client_service/include/ntcip_oids.h | 36 +- tsc_client_service/include/snmp_client.h | 38 +- tsc_client_service/include/tsc_service.h | 8 +- tsc_client_service/src/control_tsc_state.cpp | 219 ++++---- .../src/monitor_desired_phase_plan.cpp | 6 +- tsc_client_service/src/monitor_tsc_state.cpp | 65 +-- tsc_client_service/src/snmp_client.cpp | 28 +- tsc_client_service/src/tsc_service.cpp | 100 +++- .../test/test_control_tsc_state.cpp | 68 ++- .../test/test_monitor_desired_phase_plan.cpp | 70 +-- .../test/test_monitor_state.cpp | 86 +-- tsc_client_service/test/test_snmp_client.cpp | 18 +- tsc_client_service/test/test_tsc_service.cpp | 78 +-- 43 files changed, 2261 insertions(+), 449 deletions(-) create mode 100644 scripts/phase_control_schedule.json create mode 100644 scripts/phase_control_schedule_clear.json create mode 100644 scripts/phase_control_schedule_simple.json create mode 100644 scripts/simulate_phase_control_schedule.py create mode 100644 streets_utils/streets_snmp_cmd/CMakeLists.txt create mode 100644 streets_utils/streets_snmp_cmd/README.md create mode 100644 streets_utils/streets_snmp_cmd/cmake/streets_snmp_cmd_libConfig.cmake.in create mode 100644 streets_utils/streets_snmp_cmd/include/streets_snmp_cmd.h create mode 100644 streets_utils/streets_snmp_cmd/include/streets_snmp_cmd_converter.h create mode 100644 streets_utils/streets_snmp_cmd/include/streets_snmp_cmd_exception.h create mode 100644 streets_utils/streets_snmp_cmd/src/exceptions/streets_snmp_cmd_exception.cpp create mode 100644 streets_utils/streets_snmp_cmd/src/models/streets_snmp_cmd.cpp create mode 100644 streets_utils/streets_snmp_cmd/src/models/streets_snmp_cmd_converter.cpp create mode 100644 streets_utils/streets_snmp_cmd/test/streets_snmp_cmd_converter_test.cpp create mode 100644 streets_utils/streets_snmp_cmd/test/streets_snmp_cmd_test.cpp create mode 100644 streets_utils/streets_snmp_cmd/test/test_main.cpp diff --git a/.sonarqube/sonar-scanner.properties b/.sonarqube/sonar-scanner.properties index a7e8de2fc..f3a9debc8 100644 --- a/.sonarqube/sonar-scanner.properties +++ b/.sonarqube/sonar-scanner.properties @@ -33,7 +33,8 @@ sonar.coverageReportPaths= /home/carma-streets/kafka_clients/coverage/coverage.x /home/carma-streets/streets_utils/streets_vehicle_scheduler/coverage/coverage.xml, \ /home/carma-streets/streets_utils/streets_desired_phase_plan/coverage/coverage.xml, \ /home/carma-streets/streets_utils/streets_phase_control_schedule/coverage/coverage.xml, \ -/home/carma-streets/streets_utils/streets_signal_optimization/coverage/coverage.xml +/home/carma-streets/streets_utils/streets_signal_optimization/coverage/coverage.xml, \ +/home/carma-streets/streets_utils/streets_snmp_cmd/coverage/coverage.xml # TODO : /home/carma-streets/intersection_model/coverage/coverage.xml, \ # TODO: /home/carma-streets/message_services/coverage/coverage.xml, \ @@ -72,7 +73,8 @@ streets_tsc_configuration, \ tsc_client_service, \ streets_vehicle_scheduler, \ streets_desired_phase_plan, \ -streets_signal_optimization +streets_signal_optimization, \ +streets_snmp_cmd # TODO message_services # TODO intersection_model @@ -92,6 +94,7 @@ streets_tsc_configuration.sonar.projectBaseDir=/home/carma-streets/streets_utils streets_desired_phase_plan.sonar.projectBaseDir=/home/carma-streets/streets_utils/streets_desired_phase_plan streets_signal_optimization.sonar.projectBaseDir=/home/carma-streets/streets_utils/streets_signal_optimization streets_phase_control_schedule.sonar.projectBaseDir=/home/carma-streets/streets_utils/streets_phase_control_schedule +streets_snmp_cmd.sonar.projectBaseDir=/home/carma-streets/streets_utils/streets_snmp_cmd @@ -127,6 +130,8 @@ streets_signal_optimization.sonar.sources =src/,include/ streets_signal_optimization.sonar.exclusions =test/** streets_phase_control_schedule.sonar.sources =src/,include/ streets_phase_control_schedule.sonar.exclusions =test/** +streets_snmp_cmd.sonar.sources =src/,include/ +streets_snmp_cmd.sonar.exclusions =test/** #Tests @@ -146,5 +151,6 @@ streets_tsc_configuration.sonar.tests=test/ streets_desired_phase_plan.sonar.tests=test/ streets_signal_optimization.sonar.tests=test/ streets_phase_control_schedule.sonar.tests=test/ +streets_snmp_cmd.sonar.tests=test/ diff --git a/build.sh b/build.sh index 70996e296..9f0f95f19 100755 --- a/build.sh +++ b/build.sh @@ -33,6 +33,7 @@ MAKE_INSTALL_DIRS=( "streets_utils/streets_vehicle_scheduler" "streets_utils/streets_api/intersection_server_api" "streets_utils/streets_signal_optimization" + "streets_utils/streets_snmp_cmd" ) # only make for these subdirectories diff --git a/coverage.sh b/coverage.sh index 1d1ed1fc2..1f6ca372b 100755 --- a/coverage.sh +++ b/coverage.sh @@ -97,6 +97,13 @@ mkdir coverage cd /home/carma-streets/ gcovr --sonarqube streets_utils/streets_signal_optimization/coverage/coverage.xml -s -f streets_utils/streets_signal_optimization/ -r . +cd /home/carma-streets/streets_utils/streets_snmp_cmd/build/ +./streets_snmp_cmd_test --gtest_output=xml:../../../test_results/ +cd /home/carma-streets/streets_utils/streets_snmp_cmd +mkdir coverage +cd /home/carma-streets/ +gcovr --sonarqube streets_utils/streets_snmp_cmd/coverage/coverage.xml -s -f streets_utils/streets_snmp_cmd/ -r . + # cd /home/carma-streets/message_services/build/ # # Currently only running a subset of message_services tests. TODO: Fix the remaining test cases. # ./message_services_test --gtest_output=xml:../../test_results/ diff --git a/scripts/phase_control_schedule.json b/scripts/phase_control_schedule.json new file mode 100644 index 000000000..168f687d1 --- /dev/null +++ b/scripts/phase_control_schedule.json @@ -0,0 +1,137 @@ +{ + "MsgType": "Schedule", + "Schedule": [ + { + "commandEndTime": 0, + "commandPhase": 2, + "commandStartTime": 0, + "commandType": "hold" + }, + { + "commandEndTime": 24, + "commandPhase": 4, + "commandStartTime": 5, + "commandType": "hold" + }, + { + "commandEndTime": 44, + "commandPhase": 2, + "commandStartTime": 29, + "commandType": "hold" + }, + { + "commandEndTime": 64, + "commandPhase": 4, + "commandStartTime": 49, + "commandType": "hold" + }, + { + "commandEndTime": 12, + "commandPhase": 2, + "commandStartTime": 11, + "commandType": "forceoff" + }, + { + "commandEndTime": 69, + "commandPhase": 4, + "commandStartTime": 68, + "commandType": "forceoff" + }, + { + "commandEndTime": 109, + "commandPhase": 2, + "commandStartTime": 108, + "commandType": "forceoff" + }, + { + "commandEndTime": 129, + "commandPhase": 4, + "commandStartTime": 128, + "commandType": "forceoff" + }, + { + "commandEndTime": 23, + "commandPhase": 4, + "commandStartTime": 0, + "commandType": "call_veh" + }, + { + "commandEndTime": 133, + "commandPhase": 1, + "commandStartTime": 0, + "commandType": "omit_veh" + }, + { + "commandEndTime": 133, + "commandPhase": 3, + "commandStartTime": 0, + "commandType": "omit_veh" + }, + { + "commandEndTime": 0, + "commandPhase": 6, + "commandStartTime": 0, + "commandType": "hold" + }, + { + "commandEndTime": 24, + "commandPhase": 7, + "commandStartTime": 5, + "commandType": "hold" + }, + { + "commandEndTime": 44, + "commandPhase": 6, + "commandStartTime": 29, + "commandType": "hold" + }, + { + "commandEndTime": 64, + "commandPhase": 7, + "commandStartTime": 49, + "commandType": "hold" + }, + { + "commandEndTime": 12, + "commandPhase": 6, + "commandStartTime": 11, + "commandType": "forceoff" + }, + { + "commandEndTime": 69, + "commandPhase": 7, + "commandStartTime": 68, + "commandType": "forceoff" + }, + { + "commandEndTime": 109, + "commandPhase": 6, + "commandStartTime": 108, + "commandType": "forceoff" + }, + { + "commandEndTime": 129, + "commandPhase": 7, + "commandStartTime": 128, + "commandType": "forceoff" + }, + { + "commandEndTime": 23, + "commandPhase": 7, + "commandStartTime": 0, + "commandType": "call_veh" + }, + { + "commandEndTime": 133, + "commandPhase": 5, + "commandStartTime": 0, + "commandType": "omit_veh" + }, + { + "commandEndTime": 133, + "commandPhase": 8, + "commandStartTime": 0, + "commandType": "omit_veh" + } + ] + } \ No newline at end of file diff --git a/scripts/phase_control_schedule_clear.json b/scripts/phase_control_schedule_clear.json new file mode 100644 index 000000000..629e2ff4a --- /dev/null +++ b/scripts/phase_control_schedule_clear.json @@ -0,0 +1,4 @@ +{ + "MsgType": "Schedule", + "Schedule": "Clear" + } \ No newline at end of file diff --git a/scripts/phase_control_schedule_simple.json b/scripts/phase_control_schedule_simple.json new file mode 100644 index 000000000..6875345c4 --- /dev/null +++ b/scripts/phase_control_schedule_simple.json @@ -0,0 +1,131 @@ +{ + "MsgType": "Schedule", + "Schedule": [ + { + "commandStartTime": 5000, + "commandPhase": 1, + "commandEndTime": 10000, + "commandType": "omit_ped" + }, + { + "commandStartTime": 5000, + "commandPhase": 2, + "commandEndTime": 10000, + "commandType": "omit_ped" + }, + { + "commandStartTime": 5000, + "commandPhase": 3, + "commandEndTime": 10000, + "commandType": "omit_ped" + }, + { + "commandStartTime": 5000, + "commandPhase": 4, + "commandEndTime": 10000, + "commandType": "omit_ped" + }, + { + "commandStartTime": 5000, + "commandPhase": 5, + "commandEndTime": 10000, + "commandType": "omit_ped" + }, + { + "commandStartTime": 5000, + "commandPhase": 6, + "commandEndTime": 10000, + "commandType": "omit_ped" + }, + { + "commandStartTime": 5000, + "commandPhase": 7, + "commandEndTime": 10000, + "commandType": "omit_ped" + }, + { + "commandStartTime": 5000, + "commandPhase": 8, + "commandEndTime": 10000, + "commandType": "omit_ped" + }, + { + "commandStartTime": 15000, + "commandPhase": 1, + "commandEndTime": 20000, + "commandType": "call_veh" + }, + { + "commandStartTime": 15000, + "commandPhase": 2, + "commandEndTime": 20000, + "commandType": "call_veh" + }, + { + "commandStartTime": 15000, + "commandPhase": 3, + "commandEndTime": 20000, + "commandType": "call_veh" + }, + { + "commandStartTime": 15000, + "commandPhase": 4, + "commandEndTime": 20000, + "commandType": "call_veh" + }, + { + "commandStartTime": 15000, + "commandPhase": 5, + "commandEndTime": 20000, + "commandType": "call_veh" + }, + { + "commandStartTime": 15000, + "commandPhase": 6, + "commandEndTime": 20000, + "commandType": "call_veh" + }, + { + "commandStartTime": 15000, + "commandPhase": 7, + "commandEndTime": 20000, + "commandType": "call_veh" + }, + { + "commandStartTime": 15000, + "commandPhase": 8, + "commandEndTime": 20000, + "commandType": "call_veh" + }, + { + "commandStartTime": 25000, + "commandPhase": 1, + "commandEndTime": 30000, + "commandType": "call_ped" + }, + { + "commandStartTime": 25000, + "commandPhase": 4, + "commandEndTime": 30000, + "commandType": "call_ped" + }, + { + "commandStartTime": 25000, + "commandPhase": 6, + "commandEndTime": 30000, + "commandType": "call_ped" + }, + { + "commandStartTime": 25000, + "commandPhase": 8, + "commandEndTime": 45000, + "commandType": "call_ped" + }, + { + "commandStartTime": 35000, + "commandPhase": 8, + "commandEndTime": 40000, + "commandType": "forceoff" + } + ] +} \ No newline at end of file diff --git a/scripts/simulate_phase_control_schedule.py b/scripts/simulate_phase_control_schedule.py new file mode 100644 index 000000000..f1001a444 --- /dev/null +++ b/scripts/simulate_phase_control_schedule.py @@ -0,0 +1,74 @@ +from os import read +from kafka import KafkaProducer +import time +from time import sleep +import json + + +def read_json(json_name): + data = {} + with open(json_name, "r") as json_file: + data = json.load(json_file) + return data + + +if __name__ == "__main__": + producer = KafkaProducer(bootstrap_servers=["127.0.0.1:9092"], + value_serializer=lambda x: json.dumps(x).encode('utf-8')) + obj = time.gmtime(0) + epoch = time.asctime(obj) + print("The epoch is:",epoch) + + # Send a new schedule + curr_time = round(time.time()*1000) + data = read_json('phase_control_schedule_simple.json') + i = 0 + for schedule in data["Schedule"]: + #update start time with the current time + start time from the json file + data["Schedule"][i]["commandStartTime"] = curr_time + data["Schedule"][i]["commandStartTime"] + #update end time with the current time + end time from the json file + data["Schedule"][i]["commandEndTime"] = curr_time + data["Schedule"][i]["commandEndTime"] + i+=1 + producer.send('phase_control_schedule', value=data) + print(f'Sent a phase control schedule.{data}') + producer.flush() + + # Send a clear schedule + print(f'Sleep 10 seconds...') + sleep(10) # sleep seconds + data = read_json('phase_control_schedule_clear.json') + producer.send('phase_control_schedule', value=data) + print('Sent a phase control schedule clear.') + producer.flush() + + # Send a new schedule to replace the clear schedule + print(f'Sleep 5 seconds...') + sleep(5) # sleep seconds + curr_time = round(time.time()*1000) + data = read_json('phase_control_schedule_simple.json') + i = 0 + for schedule in data["Schedule"]: + #update start time with the current time + start time from the json file + data["Schedule"][i]["commandStartTime"] = curr_time + data["Schedule"][i]["commandStartTime"] + #update end time with the current time + end time from the json file + data["Schedule"][i]["commandEndTime"] = curr_time + data["Schedule"][i]["commandEndTime"] + i+=1 + producer.send('phase_control_schedule', value=data) + print(f'Sent a phase control schedule.{data}') + producer.flush() + + # Send a new schedule to replace the old schedule + print(f'Sleep 5 seconds...') + sleep(5) # sleep seconds + curr_time = round(time.time()*1000) + data = read_json('phase_control_schedule_simple.json') + i = 0 + for schedule in data["Schedule"]: + #update start time with the current time + start time from the json file + data["Schedule"][i]["commandStartTime"] = curr_time + data["Schedule"][i]["commandStartTime"] + #update end time with the current time + end time from the json file + data["Schedule"][i]["commandEndTime"] = curr_time + data["Schedule"][i]["commandEndTime"] + i+=1 + producer.send('phase_control_schedule', value=data) + print(f'Sent a phase control schedule.{data}') + producer.flush() diff --git a/streets_utils/streets_phase_control_schedule/README.md b/streets_utils/streets_phase_control_schedule/README.md index 4d452e697..d6711e202 100644 --- a/streets_utils/streets_phase_control_schedule/README.md +++ b/streets_utils/streets_phase_control_schedule/README.md @@ -1,6 +1,6 @@ # Streets Phase Control Schedule Command library ## Introduction -This CARMA-streets library is meat to handle JSON serialization and deserialization for external phase control schedule message generated by [MMITSS Roadside Processor] (https://github.com/mmitss/mmitss-az), and a sample phase control message refers to https://github.com/mmitss/mmitss-az/tree/master/src/mrp/traffic-controller-interface. +This CARMA-streets library is meant to handle JSON deserialization for external phase control schedule message generated by [MMITSS Roadside Processor] (https://github.com/mmitss/mmitss-az), and a sample phase control message refers to https://github.com/mmitss/mmitss-az/tree/master/src/mrp/traffic-controller-interface. ## Phase Control Schedule External MMITSS Roadside processor will communicate with CARMA Streets TSC service via Kafka Message Broker, and send the phase control schedule to the TSC service. Thereafter, the TSC service converts the phase control schedule to SNMP commands and communicate with Traffic Signal Controller to update the controller signal phase and schedule. @@ -44,5 +44,5 @@ A clear schedule: ``` find_package( streets_phase_control_schedule_lib REQUIRED ) ... -target_link_libraries( PUBLIC streets_phase_control_schedule_lib ) +target_link_libraries( PUBLIC streets_phase_control_schedule_lib::streets_phase_control_schedule_lib ) ``` diff --git a/streets_utils/streets_phase_control_schedule/include/streets_phase_control_command.h b/streets_utils/streets_phase_control_schedule/include/streets_phase_control_command.h index 8cca7b368..a768a035f 100644 --- a/streets_utils/streets_phase_control_schedule/include/streets_phase_control_command.h +++ b/streets_utils/streets_phase_control_schedule/include/streets_phase_control_command.h @@ -24,10 +24,10 @@ namespace streets_phase_control_schedule */ struct streets_phase_control_command { - COMMAND_TYPE command_type; // Action - int command_phase = 0; // Affected phase (bitstring to integer) - uint64_t command_start_time = 0; // Start time in epoch unix timestamp in millisecond - uint64_t command_end_time = 0; // End time in epoch unix timestamp in millisecond + COMMAND_TYPE command_type; // Action + int command_phase = 0; // Affected phase (bitstring to integer) + uint64_t command_start_time = 0; // Start time in epoch unix timestamp in millisecond + uint64_t command_end_time = 0; // End time in epoch unix timestamp in millisecond streets_phase_control_command() = default; /*** * @brief Constructor with arguments to initialize the command object @@ -41,6 +41,8 @@ namespace streets_phase_control_schedule ~streets_phase_control_command() = default; // Overload operator<< to print command - friend std::ostream &operator<<(std::ostream &os, const streets_phase_control_command& command); + friend std::ostream &operator<<(std::ostream &os, const streets_phase_control_command &command); + + std::string COMMAND_TYPE_to_string(COMMAND_TYPE command_type) noexcept; }; } diff --git a/streets_utils/streets_phase_control_schedule/src/models/streets_phase_control_command.cpp b/streets_utils/streets_phase_control_schedule/src/models/streets_phase_control_command.cpp index c4f164013..46966d12a 100644 --- a/streets_utils/streets_phase_control_schedule/src/models/streets_phase_control_command.cpp +++ b/streets_utils/streets_phase_control_schedule/src/models/streets_phase_control_command.cpp @@ -41,7 +41,7 @@ namespace streets_phase_control_schedule } } - std::ostream &operator<<(std::ostream &os, const streets_phase_control_command& command) + std::ostream &operator<<(std::ostream &os, const streets_phase_control_command &command) { std::string command_type_str; switch (command.command_type) @@ -67,8 +67,35 @@ namespace streets_phase_control_schedule default: break; } - os << "Command type: " << command_type_str << ", command phase: " << command.command_phase << ", start time: " << command.command_start_time << ", end time: " << std::setprecision(17)<< command.command_end_time; + os << "Command type: " << command_type_str << ", command phase: " << command.command_phase << ", start time: " << command.command_start_time << ", end time: " << std::setprecision(17) << command.command_end_time; return os; } + std::string streets_phase_control_command::COMMAND_TYPE_to_string( COMMAND_TYPE command_type) noexcept + { + std::string command_type_str; + switch (command_type) + { + case streets_phase_control_schedule::COMMAND_TYPE::CALL_VEH_PHASES: + command_type_str = "call_veh"; + break; + case streets_phase_control_schedule::COMMAND_TYPE::CALL_PED_PHASES: + command_type_str = "call_ped"; + break; + case streets_phase_control_schedule::COMMAND_TYPE::FORCEOFF_PHASES: + command_type_str = "forceoff"; + break; + case streets_phase_control_schedule::COMMAND_TYPE::HOLD_VEH_PHASES: + command_type_str = "hold"; + break; + case streets_phase_control_schedule::COMMAND_TYPE::OMIT_VEH_PHASES: + command_type_str = "omit_veh"; + break; + case streets_phase_control_schedule::COMMAND_TYPE::OMIT_PED_PHASES: + command_type_str = "omit_ped"; + break; + } + return command_type_str; + } + } \ No newline at end of file diff --git a/streets_utils/streets_phase_control_schedule/src/models/streets_phase_control_schedule.cpp b/streets_utils/streets_phase_control_schedule/src/models/streets_phase_control_schedule.cpp index 021898eda..b9e12bd38 100755 --- a/streets_utils/streets_phase_control_schedule/src/models/streets_phase_control_schedule.cpp +++ b/streets_utils/streets_phase_control_schedule/src/models/streets_phase_control_schedule.cpp @@ -46,6 +46,10 @@ namespace streets_phase_control_schedule std::string command_type_str = command_itr["commandType"].GetString(); toLowerCaseAndTrim(command_type_str); + if(command_itr["commandStartTime"].GetUint64() > command_itr["commandEndTime"].GetUint64()) + { + throw streets_phase_control_schedule_exception("streets_phase_control_schedule message commandStartTime value cannot be greater than commandEndTime value."); + } streets_phase_control_command command(command_type_str, command_itr["commandPhase"].GetInt(), command_itr["commandStartTime"].GetUint64(), command_itr["commandEndTime"].GetUint64()); cmd_v.push_back(command); } diff --git a/streets_utils/streets_phase_control_schedule/test/streets_phase_control_command_test.cpp b/streets_utils/streets_phase_control_schedule/test/streets_phase_control_command_test.cpp index 662b1ae1c..ae6017a6e 100644 --- a/streets_utils/streets_phase_control_schedule/test/streets_phase_control_command_test.cpp +++ b/streets_utils/streets_phase_control_schedule/test/streets_phase_control_command_test.cpp @@ -25,7 +25,7 @@ namespace TEST_F(streets_phase_control_command_test, set_command_type) { streets_phase_control_command command1; - std::stringstream out; + std::stringstream out; std::string expected_str; command1.set_command_type("call_veh"); ASSERT_EQ(COMMAND_TYPE::CALL_VEH_PHASES, command1.command_type); @@ -61,14 +61,14 @@ TEST_F(streets_phase_control_command_test, set_command_type) out << command1; expected_str = "Command type: omit_veh, command phase: 0, start time: 0, end time: 0"; ASSERT_EQ(expected_str, out.str()); - + command1.set_command_type("omit_ped"); ASSERT_EQ(COMMAND_TYPE::OMIT_PED_PHASES, command1.command_type); out.str(""); out << command1; expected_str = "Command type: omit_ped, command phase: 0, start time: 0, end time: 0"; ASSERT_EQ(expected_str, out.str()); - + ASSERT_THROW(command1.set_command_type("unkown_command"), streets_phase_control_schedule_exception); streets_phase_control_command command2("call_veh", 2, 0.0, 23.999767065048218); @@ -77,4 +77,29 @@ TEST_F(streets_phase_control_command_test, set_command_type) out.str(""); out << command2; ASSERT_EQ(expected_str, out.str()); +} + +TEST_F(streets_phase_control_command_test, COMMAND_TYPE_to_string) +{ + streets_phase_control_command command1; + std::string expected_str = "call_veh"; + ASSERT_EQ(expected_str, command1.COMMAND_TYPE_to_string(COMMAND_TYPE::CALL_VEH_PHASES)); + + expected_str = "call_ped"; + ASSERT_EQ(expected_str, command1.COMMAND_TYPE_to_string(COMMAND_TYPE::CALL_PED_PHASES)); + + expected_str = "forceoff"; + ASSERT_EQ(expected_str, command1.COMMAND_TYPE_to_string(COMMAND_TYPE::FORCEOFF_PHASES)); + + expected_str = "hold"; + ASSERT_EQ(expected_str, command1.COMMAND_TYPE_to_string(COMMAND_TYPE::HOLD_VEH_PHASES)); + + expected_str = "omit_veh"; + ASSERT_EQ(expected_str, command1.COMMAND_TYPE_to_string(COMMAND_TYPE::OMIT_VEH_PHASES)); + + expected_str = "omit_ped"; + ASSERT_EQ(expected_str, command1.COMMAND_TYPE_to_string(COMMAND_TYPE::OMIT_PED_PHASES)); + + expected_str = "call_veh"; + ASSERT_EQ(expected_str, command1.COMMAND_TYPE_to_string(COMMAND_TYPE::CALL_VEH_PHASES)); } \ No newline at end of file diff --git a/streets_utils/streets_phase_control_schedule/test/streets_phase_control_schedule_test.cpp b/streets_utils/streets_phase_control_schedule/test/streets_phase_control_schedule_test.cpp index 4c982e9ae..42b8075cf 100644 --- a/streets_utils/streets_phase_control_schedule/test/streets_phase_control_schedule_test.cpp +++ b/streets_utils/streets_phase_control_schedule/test/streets_phase_control_schedule_test.cpp @@ -132,6 +132,16 @@ TEST_F(streets_phase_control_schedule_test, fromJson) out << new_schedule; ASSERT_EQ(expected_str, out.str()); + input_str = "{\"MsgType\":\"Schedule\",\"Schedule\":[{\"commandEndTime\":0,\"commandPhase\":2,\"commandStartTime\":1,\"commandType\":\"omit_ped\"}]}"; + ASSERT_THROW(new_schedule.fromJson(input_str), streets_phase_control_schedule_exception); + //If exception throw, the schedule commands are not updated. + ASSERT_EQ(1, new_schedule.commands.size()); + ASSERT_FALSE(new_schedule.is_clear_current_schedule); + expected_str = "Clear status: False, commands [{Command type: omit_ped, command phase: 2, start time: 0, end time: 0},]"; + out.str(""); + out << new_schedule; + ASSERT_EQ(expected_str, out.str()); + input_str = "{\"MsgType\":\"Schedule\",\"Schedule\":\"Clear\"}"; new_schedule.fromJson(input_str); ASSERT_EQ(0, new_schedule.commands.size()); diff --git a/streets_utils/streets_snmp_cmd/CMakeLists.txt b/streets_utils/streets_snmp_cmd/CMakeLists.txt new file mode 100644 index 000000000..68609de13 --- /dev/null +++ b/streets_utils/streets_snmp_cmd/CMakeLists.txt @@ -0,0 +1,81 @@ +cmake_minimum_required(VERSION 3.10.2) +project(streets_snmp_cmd) +######################################################## +# Find Dependent Packages and set configurations +######################################################## +set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fPIC") +find_package(spdlog REQUIRED) +find_package(GTest REQUIRED CONFIG) +add_definitions(-DSPDLOG_ACTIVE_LEVEL=SPDLOG_LEVEL_TRACE) +find_package(RapidJSON REQUIRED) +find_package(streets_phase_control_schedule_lib REQUIRED) +# Add definition for rapidjson to include std::string +add_definitions(-DRAPIDJSON_HAS_STDSTRING=1) + +######################################################## +# Build Library +######################################################## +file(GLOB SOURCES ${CMAKE_CURRENT_SOURCE_DIR}/src/**/*.cpp) +add_library(${PROJECT_NAME}_lib ${SOURCES} ) +target_include_directories(${PROJECT_NAME}_lib + PUBLIC + $ + $ + PRIVATE + ${CMAKE_CURRENT_SOURCE_DIR}/src +) +target_link_libraries( ${PROJECT_NAME}_lib + PUBLIC + spdlog::spdlog + rapidjson + streets_phase_control_schedule_lib::streets_phase_control_schedule_lib +) + +######################################################## +# Install streets_snmp_cmd package. +######################################################## +file(GLOB files ${CMAKE_CURRENT_SOURCE_DIR}/include/*.h) +file(GLOB templates ${CMAKE_CURRENT_SOURCE_DIR}/include/internal/*.tpp) +install( + TARGETS ${PROJECT_NAME}_lib + EXPORT ${PROJECT_NAME}_libTargets + LIBRARY DESTINATION lib + INCLUDES DESTINATION include + ARCHIVE DESTINATION lib +) +install( + EXPORT ${PROJECT_NAME}_libTargets + FILE ${PROJECT_NAME}_libTargets.cmake + DESTINATION lib/cmake/${PROJECT_NAME}_lib/ + NAMESPACE ${PROJECT_NAME}_lib:: +) +include(CMakePackageConfigHelpers) +configure_package_config_file( + cmake/${PROJECT_NAME}_libConfig.cmake.in + ${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_NAME}_libConfig.cmake + INSTALL_DESTINATION lib/${PROJECT_NAME}_lib/${PROJECT_NAME}_lib/ ) +install( + FILES ${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_NAME}_libConfig.cmake + DESTINATION lib/cmake/${PROJECT_NAME}_lib/ +) +install(FILES ${files} DESTINATION include) + +######################## +# googletest for unit testing +######################## +set(BINARY ${PROJECT_NAME}_test) +file(GLOB_RECURSE TEST_SOURCES LIST_DIRECTORIES false test/*.h test/*.cpp) +set(SOURCES ${TEST_SOURCES} WORKING_DIRECTORY ${PROJECT_SOURCE_DIR}/test) +add_executable(${BINARY} ${TEST_SOURCES}) +add_test(NAME ${BINARY} COMMAND ${BINARY}) +target_include_directories(${BINARY} PUBLIC ${PROJECT_SOURCE_DIR}/include) +target_link_libraries(${BINARY} + PUBLIC + ${PROJECT_NAME}_lib +) + +if(${CMAKE_VERSION} VERSION_GREATER_EQUAL "3.20") + target_link_libraries( ${BINARY} PUBLIC GTest::gtest ) +else() + target_link_libraries( ${BINARY} PUBLIC gtest ) +endif() \ No newline at end of file diff --git a/streets_utils/streets_snmp_cmd/README.md b/streets_utils/streets_snmp_cmd/README.md new file mode 100644 index 000000000..ad8069348 --- /dev/null +++ b/streets_utils/streets_snmp_cmd/README.md @@ -0,0 +1,29 @@ +# Streets SNMP Command library +## Introduction +This CARMA-streets library is meant to define the streets SNMP command and converts external schedule into SNMP commands. + +## Streets SNMP command structure +External MMITSS Roadside processor will communicate with CARMA Streets TSC service via Kafka Message Broker, and send the phase control schedule to the TSC service. Thereafter, the TSC service converts the phase control schedule to Streets SNMP commands and communicate with Traffic Signal Controller to update the controller signal phase and schedule. +The Streets SNMP command definition: +### Parameter description +#### snmp_cmd_struct definition +| Prameter Name | Data Type | Description | +| ------------- | ------------- | ----------- | +| set_val_ | snmp_response_obj | SNMP response object populated by the SNMP response.| +| start_time_ | uint64_t | Start execution time of the SNMP command. | +| control_type_ | PHASE_CONTROL_TYPE | The type of SNMP command control sent to TSC. | + +#### snmp_response_obj definition +| Prameter Name | Data Type | Description | +| ------------- | ------------- | ----------- | +| val_int | int64_t | Integer value sent to TSC via SNMP command.| +| val_string | std::vector | String value sent to TSC via SNMP command. | +| type | RESPONSE_TYPE | The type of value being requested or set, on the TSC | + + +## Including library +``` +find_package( streets_snmp_cmd_lib REQUIRED ) +... +target_link_libraries( PUBLIC streets_snmp_cmd_lib::streets_snmp_cmd_lib ) +``` diff --git a/streets_utils/streets_snmp_cmd/cmake/streets_snmp_cmd_libConfig.cmake.in b/streets_utils/streets_snmp_cmd/cmake/streets_snmp_cmd_libConfig.cmake.in new file mode 100644 index 000000000..85e205314 --- /dev/null +++ b/streets_utils/streets_snmp_cmd/cmake/streets_snmp_cmd_libConfig.cmake.in @@ -0,0 +1,7 @@ +@PACKAGE_INIT@ + +include(CMakeFindDependencyMacro) +find_dependency(spdlog REQUIRED) +find_dependency(RapidJSON REQUIRED) + +include("${CMAKE_CURRENT_LIST_DIR}/streets_snmp_cmd_libTargets.cmake") diff --git a/streets_utils/streets_snmp_cmd/include/streets_snmp_cmd.h b/streets_utils/streets_snmp_cmd/include/streets_snmp_cmd.h new file mode 100644 index 000000000..91cd7285e --- /dev/null +++ b/streets_utils/streets_snmp_cmd/include/streets_snmp_cmd.h @@ -0,0 +1,74 @@ +#pragma once +#include +#include +#include + +namespace streets_snmp_cmd +{ + enum class REQUEST_TYPE + { + GET, + SET, + OTHER // Processing this request type is not a defined behavior, included for testing only + }; + + /** @brief The type of control being set on the TSC */ + enum class PHASE_CONTROL_TYPE + { + CALL_VEH_PHASES = 1, // Call a vehicle phase + CALL_PED_PHASES = 2, // Call a pedestrian phase + FORCEOFF_PHASES = 3, // Forceoff a vehicle phase + HOLD_VEH_PHASES = 4, // Hold a vehicle phase + OMIT_VEH_PHASES = 5, // Omit a vehicle phase + OMIT_PED_PHASES = 6, // Omit a pedestrain phase + }; + + /** @brief The type of value being requested or set, on the TSC */ + enum class RESPONSE_TYPE + { + INTEGER, + STRING + }; + + /** @brief A struct to hold the value being sent to the TSC, can be integer or string. Type needs to be defined*/ + struct snmp_response_obj + { + // snmp response values can be any asn.1 supported types. + // Integer and string values can be processed here + int64_t val_int = 0; + std::vector val_string; + RESPONSE_TYPE type; + + inline bool operator==(const snmp_response_obj &obj2) const + { + return val_int == obj2.val_int && val_string == obj2.val_string && type == obj2.type; + } + }; + + /** @brief Object to store snmp control commands. Contructed with an initialized snmp_client_worker_ this object stores SNMP HOLD and OMIT commands + to be executed at specified time */ + struct snmp_cmd_struct + { + + /*Value to be set for Hold/Omit*/ + snmp_response_obj set_val_; + /*Time at which the snmp set command should be executed*/ + uint64_t start_time_; + /*Type of the snmp set command this object creates- Hold or Omit*/ + PHASE_CONTROL_TYPE control_type_; + + snmp_cmd_struct(uint64_t start_time, PHASE_CONTROL_TYPE type, int64_t val) + : start_time_(start_time), control_type_(type) + { + set_val_.type = RESPONSE_TYPE::INTEGER; + set_val_.val_int = val; + } + + /** + * @brief Method to return information about the snmp set command + * @return Returns string formatted as "control_cmd_type:;execution_start_time:;signal_groups_set:". + * */ + std::string get_cmd_info() const; + }; + +} // namespace streets_snmp_cmd diff --git a/streets_utils/streets_snmp_cmd/include/streets_snmp_cmd_converter.h b/streets_utils/streets_snmp_cmd/include/streets_snmp_cmd_converter.h new file mode 100644 index 000000000..12b3e7faa --- /dev/null +++ b/streets_utils/streets_snmp_cmd/include/streets_snmp_cmd_converter.h @@ -0,0 +1,85 @@ +#pragma once +#include "streets_snmp_cmd.h" +#include "streets_phase_control_schedule.h" +#include "streets_snmp_cmd_exception.h" +#include +#include + +namespace streets_snmp_cmd +{ + class streets_snmp_cmd_converter + { + private: + /*** + * @brief Private method to support the phase control schedule conversion to snmp_cmd_struct. It takes each individual streets_phase_control_command, and populate a two dimension map with start time, command type and a vector of phases. + * Sort the streets_phase_control_command using start time, and categorize the phases based on the phase control schedule command types. + * @param streets_phase_control_command a structure of phase control command. + * @param time_cmd_m Two dimension map with key of snmp_start_time, and value of inner map. Inner map is with key of phase control schedule command type, and value of a vector of phases numbers. + * @param pcs_cmd phase control command structure that has the command start and end time, command phase and type + */ + void add_phase_control_schedule_command_to_two_dimension_map(uint64_t start_time, streets_phase_control_schedule::streets_phase_control_command pcs_cmd, std::map>> &time_cmd_m) const; + /*** + * @brief Private method to print the two dimension map with command start time, command type and a vector of phases. + * @param time_cmd_m a map of command type and the phases the command is applied to. + * @param is_cmd_start indicator whether the map of command type and phases is command sent at start time or end time from the phase control schedule. + */ + void print_two_dimension_map(std::map>> &time_cmd_m, bool is_cmd_start = true) const; + /** + * @brief Private method to perform bitwise shift or operation on input value and left shift the value by the phases number of positions. + * @param val The value sent by the SNMP command to traffic signal controller. + * @param phases The numbers of left shift positions applied to the value. + */ + uint8_t bitwise_or_phases(uint8_t val, const std::vector& phases) const; + /** + * @brief Private method to perform bitwise shift xor operation on input value and left shift the value by the phases number of positions. + * @param val The value sent by the SNMP command to traffic signal controller. + * @param phases The numbers of left shift positions applied to the value. + */ + uint8_t bitwise_xor_phases(uint8_t val, const std::vector& phases) const; + /** + * @brief Private method to update the queue with the input SNMP command. + * @param cmds_queue A queue of SNMP commands to be updated. + * @param start_time The start execution time of the command. + * @param command_type The type of command. + * @param val The value of the command to be sent to TSC. + */ + void push_snmp_command_to_queue(std::queue& cmds_queue, uint64_t start_time, PHASE_CONTROL_TYPE command_type, int64_t val) const; + + public: + streets_snmp_cmd_converter() = default; + ~streets_snmp_cmd_converter() = default; + /** + * @brief Method to create SNMP commands for provided phases and a given phase control type. + * @param phases A list of phases the SNMP command applies to. + * @param phase_control_type The type of phase control of the SNMP command. + * @param start_time Time at which the snmp command needs to be sent. + * **/ + snmp_cmd_struct create_snmp_command_by_phases(const std::vector &phases, PHASE_CONTROL_TYPE phase_control_type, int64_t start_time) const; + /*** + * @brief Method to create SNMP reset commands for all phases and a given phase control type. * + * @param phase_control_type The type of phase control of the SNMP command. + * @param start_time Time at which the snmp command needs to be sent. + */ + snmp_cmd_struct create_snmp_reset_command(PHASE_CONTROL_TYPE phase_control_type, int64_t start_time) const; + /** + * @brief Method to create SNMP commands for provided phase control schedule. + * @param phase_control_schedule The phase control schedule has a list of commands (commandPhase, commandType, commandStartTime, and commandEndTime). + */ + std::queue create_snmp_cmds_by_phase_control_schedule(const std::shared_ptr phase_control_schedule) const; + + /** + * @brief Conversion between phase control schedule command type and the SNMP command phase control type in the streets SNMP command structure. + * @param command_type an enum of command types defined in the streets phase control schedule. + */ + PHASE_CONTROL_TYPE to_phase_control_type(streets_phase_control_schedule::COMMAND_TYPE command_type) const; + + /** + * @brief Create a vector of SNMP command structures of all current supported phase control types: CALL_VEH_PHASES, CALL_PED_PHASES, FORCEOFF_PHASES, HOLD_VEH_PHASES, OMIT_VEH_PHASES, OMIT_PED_PHASES. + * All phase control OIDs are set to 0, and the commands are used to clear all the phases controls from the traffic signal. + * @param execution_time when the SNMP commands are executed. + * @return A vector of SNMP commands structures. + */ + std::vector create_clear_all_snmp_commands(uint64_t execution_time) const; + }; + +} // namespace streets_snmp_cmd diff --git a/streets_utils/streets_snmp_cmd/include/streets_snmp_cmd_exception.h b/streets_utils/streets_snmp_cmd/include/streets_snmp_cmd_exception.h new file mode 100644 index 000000000..baa8177db --- /dev/null +++ b/streets_utils/streets_snmp_cmd/include/streets_snmp_cmd_exception.h @@ -0,0 +1,20 @@ +#pragma once + +#include +namespace streets_snmp_cmd { + /** + * @brief Runtime error related to processing streets snmp cmd. + */ + class streets_snmp_cmd_exception : public std::runtime_error{ + public: + /** + * @brief Destructor. + */ + ~streets_snmp_cmd_exception() override; + /** + * @brief Constructor. + * @param msg String exception message. + */ + explicit streets_snmp_cmd_exception(const std::string &msg ); + }; +} \ No newline at end of file diff --git a/streets_utils/streets_snmp_cmd/src/exceptions/streets_snmp_cmd_exception.cpp b/streets_utils/streets_snmp_cmd/src/exceptions/streets_snmp_cmd_exception.cpp new file mode 100644 index 000000000..2c19b4188 --- /dev/null +++ b/streets_utils/streets_snmp_cmd/src/exceptions/streets_snmp_cmd_exception.cpp @@ -0,0 +1,8 @@ +#include "streets_snmp_cmd_exception.h" + +namespace streets_snmp_cmd { + + streets_snmp_cmd_exception::streets_snmp_cmd_exception(const std::string &msg): std::runtime_error(msg){}; + + streets_snmp_cmd_exception::~streets_snmp_cmd_exception() = default; +} \ No newline at end of file diff --git a/streets_utils/streets_snmp_cmd/src/models/streets_snmp_cmd.cpp b/streets_utils/streets_snmp_cmd/src/models/streets_snmp_cmd.cpp new file mode 100644 index 000000000..2868d6e97 --- /dev/null +++ b/streets_utils/streets_snmp_cmd/src/models/streets_snmp_cmd.cpp @@ -0,0 +1,39 @@ +#include "streets_snmp_cmd.h" + +namespace streets_snmp_cmd +{ + std::string snmp_cmd_struct::get_cmd_info() const + { + + std::string control_type_str; + switch (control_type_) + { + case PHASE_CONTROL_TYPE::HOLD_VEH_PHASES: + control_type_str = "Vehicle Hold"; + break; + case PHASE_CONTROL_TYPE::FORCEOFF_PHASES: + control_type_str = "Vehicle Forceoff"; + break; + case PHASE_CONTROL_TYPE::CALL_PED_PHASES: + control_type_str = "Pedestrian Call"; + break; + case PHASE_CONTROL_TYPE::CALL_VEH_PHASES: + control_type_str = "Vehicle Call"; + break; + case PHASE_CONTROL_TYPE::OMIT_PED_PHASES: + control_type_str = "Pedestrian Omit"; + break; + case PHASE_CONTROL_TYPE::OMIT_VEH_PHASES: + control_type_str = "Vehicle Omit"; + break; + default: + break; + } + std::string execution_start_time = std::to_string(start_time_); + + // Convert value set to phases nums + std::string value_set = std::to_string(set_val_.val_int); + + return "control_cmd_type:" + control_type_str + "; execution_start_time:" + execution_start_time + "; value_set:" + value_set; + } +} \ No newline at end of file diff --git a/streets_utils/streets_snmp_cmd/src/models/streets_snmp_cmd_converter.cpp b/streets_utils/streets_snmp_cmd/src/models/streets_snmp_cmd_converter.cpp new file mode 100644 index 000000000..33595236a --- /dev/null +++ b/streets_utils/streets_snmp_cmd/src/models/streets_snmp_cmd_converter.cpp @@ -0,0 +1,400 @@ +#include "streets_snmp_cmd_converter.h" +#include + +namespace streets_snmp_cmd +{ + snmp_cmd_struct streets_snmp_cmd_converter::create_snmp_command_by_phases(const std::vector &phases, PHASE_CONTROL_TYPE phase_control_type, int64_t start_time) const + { + uint8_t set_val = 0; // Initialize to 00000000 + set_val = bitwise_or_phases(set_val, phases); + snmp_cmd_struct command(start_time, phase_control_type, static_cast(set_val)); + return command; + } + + snmp_cmd_struct streets_snmp_cmd_converter::create_snmp_reset_command(PHASE_CONTROL_TYPE phase_control_type, int64_t start_time) const + { + snmp_cmd_struct command(start_time, phase_control_type, static_cast(0)); + return command; + } + + std::queue streets_snmp_cmd_converter::create_snmp_cmds_by_phase_control_schedule(const std::shared_ptr phase_control_schedule) const + { + // A queue of SNMP commands to return after processing the input phase control schedule. + std::queue cmds_result; + std::map>> start_time_cmd_m; + std::map>> end_time_cmd_m; + for (auto &pcs_cmd : phase_control_schedule->commands) + { + // Start time commands + add_phase_control_schedule_command_to_two_dimension_map(pcs_cmd.command_start_time, pcs_cmd, start_time_cmd_m); + // End time commands + add_phase_control_schedule_command_to_two_dimension_map(pcs_cmd.command_end_time, pcs_cmd, end_time_cmd_m); + } + print_two_dimension_map(start_time_cmd_m); + print_two_dimension_map(end_time_cmd_m, false); + + // Keep track of the SNMP command start time. It is used by the snmp_cmd_struct to create a streets defined SNMP command. + uint64_t snmp_cmd_start_time = 0; + // Keep track of the previous SNMP command start time. It is used to determine whether to create a separate SNMP command based on end time. + uint64_t prev_snmp_start_time = 0; + // Below values are affected by the whole phase control schedule, and it is increased or decreased by all phases in the vector of phase control schedule commands. + uint8_t set_val_hold = 0; // Initialize hold value to 00000000 + uint8_t set_val_forceoff = 0; // Initialize forceoff value to 00000000 + uint8_t set_val_call_veh = 0; // Initialize call vehicle value to 00000000 + uint8_t set_val_call_ped = 0; // Initialize call pedestrian value to 00000000 + uint8_t set_val_omit_veh = 0; // Initialize omit vehicle value to 00000000 + uint8_t set_val_omit_ped = 0; // Initialize omit pedestrian value to 00000000 + /*** + * There are two maps (start_time_cmd_m, end_time_cmd_m) to work with in the following loops. The start time commands map contains all the commands at start time. + * The end time commands map contains all the commands at end time. + * 1. Interate the start time commands map. + * ** 1.1 Iterate the end time commands map, checking if the end time command is executed before the current start time command, and after the previous start time command. + * (Note: This usually applies to second and forward commands from the start time commands map) + * ** 1.2 If yes, create new SNMP commands using the end time. + * ** Iterate the nested map of command type and phases, perform the bitwise or operation on the values using the phases.Create SNMP command for each command type and phases, and add the SNMP command to the queue. + * + * 2. Iterate the nested map of command type and phases. At each start time, multiple command types are expected. Each command type has multiple phases. + * It needs to create streets SNMP command for each command type. The boolean indicators (is_forceoff, is_hold, is_call_veh, is_call_ped, is_omit_veh, is_omit_ped) are used to indicate + * which types of commands should be executed at the current start time. The set_val_hold, set_val_forceoff, set_val_call_veh, set_val_call_ped, set_val_omit_ped, set_val_omit_veh + * values are used to accumulate the phases associated to each command type at the current start time. + * Once finished interating the nested map of command types and phases. It will create the streets SNMP command based on the boolean indicator to corresponding create SNMP command and values for each command type. + * 3. Iterate the end time commands map in case some commands ends the same time as the start time commands. + * 4. Check the boolean indicators and create corresponding SNMP commands. Add the SNMP commands to the queue. + * + * 5. Iterate the end time commands map in case some commands ends after finishing all commands at the start time. + * ** Checking the snmp_cmd_start_time and the end time from the end time command maps. If there are any commands end time that is greater than snmp_cmd_start_time, create streets + * ** SNMP commands for that end time command, and add the SNMP commands to the queue. (Note: At the end of the step 1 iteration, the snmp_cmd_start_time is updated with the maximum start time.) + * */ + //Step 1: Loop through start time commands map + for (auto start_time_cmd_itr = start_time_cmd_m.begin(); start_time_cmd_itr != start_time_cmd_m.end(); start_time_cmd_itr++) + { + // Indicator to note which phase control types are received at a the start time + bool is_hold = false; + bool is_forceoff = false; + bool is_call_veh = false; + bool is_call_ped = false; + bool is_omit_veh = false; + bool is_omit_ped = false; + snmp_cmd_start_time = start_time_cmd_itr->first; + + // Step 1.1 & 1.2: Checking end time for commands and make sure to reset the phases if end time is earlier than start time and is older then previous start time + for (auto end_time_cmd_itr = end_time_cmd_m.begin(); end_time_cmd_itr != end_time_cmd_m.end(); end_time_cmd_itr++) + { + // Command end time is earlier than current command start time and the end time is older than previous start time + if (end_time_cmd_itr->first < start_time_cmd_itr->first && end_time_cmd_itr->first > prev_snmp_start_time) + { + // Loop through each control type and find all phases belong to the control type. Bitwise xor operation on the phases for the end time phases and previous start time phases + for (auto inner_itr = end_time_cmd_itr->second.begin(); inner_itr != end_time_cmd_itr->second.end(); inner_itr++) + { + // Phases at the end time + auto phases = inner_itr->second; + auto phase_control_type = to_phase_control_type(inner_itr->first); + if (phase_control_type == PHASE_CONTROL_TYPE::CALL_PED_PHASES) + { + set_val_call_ped = bitwise_xor_phases(set_val_call_ped, phases); + push_snmp_command_to_queue(cmds_result, end_time_cmd_itr->first, PHASE_CONTROL_TYPE::CALL_PED_PHASES, static_cast(set_val_call_ped)); + } + else if (phase_control_type == PHASE_CONTROL_TYPE::CALL_VEH_PHASES) + { + set_val_call_veh = bitwise_xor_phases(set_val_call_veh, phases); + push_snmp_command_to_queue(cmds_result, end_time_cmd_itr->first, PHASE_CONTROL_TYPE::CALL_VEH_PHASES, static_cast(set_val_call_veh)); + } + else if (phase_control_type == PHASE_CONTROL_TYPE::OMIT_PED_PHASES) + { + set_val_omit_ped = bitwise_xor_phases(set_val_omit_ped, phases); + push_snmp_command_to_queue(cmds_result, end_time_cmd_itr->first, PHASE_CONTROL_TYPE::OMIT_PED_PHASES, static_cast(set_val_omit_ped)); + } + else if (phase_control_type == PHASE_CONTROL_TYPE::OMIT_VEH_PHASES) + { + set_val_omit_veh = bitwise_xor_phases(set_val_omit_veh, phases); + push_snmp_command_to_queue(cmds_result, end_time_cmd_itr->first, PHASE_CONTROL_TYPE::OMIT_VEH_PHASES, static_cast(set_val_omit_veh)); + } + else if (phase_control_type == PHASE_CONTROL_TYPE::FORCEOFF_PHASES) + { + set_val_forceoff = bitwise_xor_phases(set_val_forceoff, phases); + push_snmp_command_to_queue(cmds_result, end_time_cmd_itr->first, PHASE_CONTROL_TYPE::FORCEOFF_PHASES, static_cast(set_val_forceoff)); + } + else if (phase_control_type == PHASE_CONTROL_TYPE::HOLD_VEH_PHASES) + { + set_val_hold = bitwise_xor_phases(set_val_hold, phases); + push_snmp_command_to_queue(cmds_result, end_time_cmd_itr->first, PHASE_CONTROL_TYPE::HOLD_VEH_PHASES, static_cast(set_val_hold)); + } + } + } + } + + // Step 2: Check start time commands, loop through each control type to find the phases. Bitwise or operation on the phases for the current start time and current control type + for (auto inner_itr = start_time_cmd_itr->second.begin(); inner_itr != start_time_cmd_itr->second.end(); inner_itr++) + { + auto phases = inner_itr->second; + auto phase_control_type = to_phase_control_type(inner_itr->first); + if (phase_control_type == PHASE_CONTROL_TYPE::CALL_PED_PHASES) + { + set_val_call_ped = bitwise_or_phases(set_val_call_ped, phases); + is_call_ped = true; + } + else if (phase_control_type == PHASE_CONTROL_TYPE::CALL_VEH_PHASES) + { + set_val_call_veh = bitwise_or_phases(set_val_call_veh, phases); + is_call_veh = true; + } + else if (phase_control_type == PHASE_CONTROL_TYPE::OMIT_PED_PHASES) + { + set_val_omit_ped = bitwise_or_phases(set_val_omit_ped, phases); + is_omit_ped = true; + } + else if (phase_control_type == PHASE_CONTROL_TYPE::OMIT_VEH_PHASES) + { + set_val_omit_veh = bitwise_or_phases(set_val_omit_veh, phases); + is_omit_veh = true; + } + else if (phase_control_type == PHASE_CONTROL_TYPE::FORCEOFF_PHASES) + { + set_val_forceoff = bitwise_or_phases(set_val_forceoff, phases); + is_forceoff = true; + } + else if (phase_control_type == PHASE_CONTROL_TYPE::HOLD_VEH_PHASES) + { + set_val_hold = bitwise_or_phases(set_val_hold, phases); + is_hold = true; + } + } + + // Step 3: Checking end time for commands and make sure to reset the phases if end time equals to start time + for (auto end_time_cmd_itr = end_time_cmd_m.begin(); end_time_cmd_itr != end_time_cmd_m.end(); end_time_cmd_itr++) + { + // Command end time equals to current command start time + if (end_time_cmd_itr->first == start_time_cmd_itr->first) + { + for (auto inner_itr = end_time_cmd_itr->second.begin(); inner_itr != end_time_cmd_itr->second.end(); inner_itr++) + { + auto phase_control_type = to_phase_control_type(inner_itr->first); + if (phase_control_type == PHASE_CONTROL_TYPE::CALL_PED_PHASES) + { + set_val_call_ped = bitwise_xor_phases(set_val_call_ped, inner_itr->second); + } + else if (phase_control_type == PHASE_CONTROL_TYPE::CALL_VEH_PHASES) + { + set_val_call_veh = bitwise_xor_phases(set_val_call_veh, inner_itr->second); + } + else if (phase_control_type == PHASE_CONTROL_TYPE::OMIT_PED_PHASES) + { + set_val_omit_ped = bitwise_xor_phases(set_val_omit_ped, inner_itr->second); + } + else if (phase_control_type == PHASE_CONTROL_TYPE::OMIT_VEH_PHASES) + { + set_val_omit_veh = bitwise_xor_phases(set_val_omit_veh, inner_itr->second); + } + else if (phase_control_type == PHASE_CONTROL_TYPE::FORCEOFF_PHASES) + { + set_val_forceoff = bitwise_xor_phases(set_val_forceoff, inner_itr->second); + } + else if (phase_control_type == PHASE_CONTROL_TYPE::HOLD_VEH_PHASES) + { + set_val_hold = bitwise_xor_phases(set_val_hold, inner_itr->second); + } + } + } + } + + //Step 4: + if(is_hold) + { + push_snmp_command_to_queue(cmds_result, snmp_cmd_start_time, PHASE_CONTROL_TYPE::HOLD_VEH_PHASES, static_cast(set_val_hold)); + } + if(is_forceoff) + { + push_snmp_command_to_queue(cmds_result, snmp_cmd_start_time, PHASE_CONTROL_TYPE::FORCEOFF_PHASES, static_cast(set_val_forceoff)); + } + if(is_omit_veh) + { + push_snmp_command_to_queue(cmds_result, snmp_cmd_start_time, PHASE_CONTROL_TYPE::OMIT_VEH_PHASES, static_cast(set_val_omit_veh)); + } + if(is_omit_ped) + { + push_snmp_command_to_queue(cmds_result, snmp_cmd_start_time, PHASE_CONTROL_TYPE::OMIT_PED_PHASES, static_cast(set_val_omit_ped)); + } + if(is_call_ped) + { + push_snmp_command_to_queue(cmds_result, snmp_cmd_start_time, PHASE_CONTROL_TYPE::CALL_PED_PHASES, static_cast(set_val_call_ped)); + } + if(is_call_veh) + { + push_snmp_command_to_queue(cmds_result, snmp_cmd_start_time, PHASE_CONTROL_TYPE::CALL_VEH_PHASES, static_cast(set_val_call_veh)); + } + // Update previous start time with current start time + prev_snmp_start_time = snmp_cmd_start_time; + } // END Start Time commands + + // Step 5: Checking end time for commands and make sure to reset the phases if end time greater than the maximum (last) start time + for (auto end_time_cmd_itr = end_time_cmd_m.begin(); end_time_cmd_itr != end_time_cmd_m.end(); end_time_cmd_itr++) + { + if (end_time_cmd_itr->first > snmp_cmd_start_time) + { + // Check end time commands, loop through each control type to find the phases. Bitwise xor operation on the phases for the end time (which is greater than maximum start time) and current control type + for (auto inner_itr = end_time_cmd_itr->second.begin(); inner_itr != end_time_cmd_itr->second.end(); inner_itr++) + { + auto phase_control_type = to_phase_control_type(inner_itr->first); + if (phase_control_type == PHASE_CONTROL_TYPE::CALL_PED_PHASES) + { + set_val_call_ped = bitwise_xor_phases(set_val_call_ped, inner_itr->second); + push_snmp_command_to_queue(cmds_result, end_time_cmd_itr->first, PHASE_CONTROL_TYPE::CALL_PED_PHASES, static_cast(set_val_call_ped)); + } + else if (phase_control_type == PHASE_CONTROL_TYPE::CALL_VEH_PHASES) + { + set_val_call_veh = bitwise_xor_phases(set_val_call_veh, inner_itr->second); + push_snmp_command_to_queue(cmds_result, end_time_cmd_itr->first, PHASE_CONTROL_TYPE::CALL_VEH_PHASES, static_cast(set_val_call_veh)); + } + else if (phase_control_type == PHASE_CONTROL_TYPE::OMIT_PED_PHASES) + { + set_val_omit_ped = bitwise_xor_phases(set_val_omit_ped, inner_itr->second); + push_snmp_command_to_queue(cmds_result, end_time_cmd_itr->first, PHASE_CONTROL_TYPE::OMIT_PED_PHASES, static_cast(set_val_omit_ped)); + } + else if (phase_control_type == PHASE_CONTROL_TYPE::OMIT_VEH_PHASES) + { + set_val_omit_veh = bitwise_xor_phases(set_val_omit_veh, inner_itr->second); + push_snmp_command_to_queue(cmds_result, end_time_cmd_itr->first, PHASE_CONTROL_TYPE::OMIT_VEH_PHASES, static_cast(set_val_omit_veh)); + } + else if (phase_control_type == PHASE_CONTROL_TYPE::FORCEOFF_PHASES) + { + set_val_forceoff = bitwise_xor_phases(set_val_forceoff, inner_itr->second); + push_snmp_command_to_queue(cmds_result, end_time_cmd_itr->first, PHASE_CONTROL_TYPE::FORCEOFF_PHASES, static_cast(set_val_forceoff)); + } + else if (phase_control_type == PHASE_CONTROL_TYPE::HOLD_VEH_PHASES) + { + set_val_hold = bitwise_xor_phases(set_val_hold, inner_itr->second); + push_snmp_command_to_queue(cmds_result, end_time_cmd_itr->first, PHASE_CONTROL_TYPE::HOLD_VEH_PHASES, static_cast(set_val_hold)); + } + } + } + }//END end time commands map + + return cmds_result; + } + + uint8_t streets_snmp_cmd_converter::bitwise_or_phases(uint8_t val, const std::vector &phases) const + { + for (auto phase : phases) + { + val |= (1 << (phase - 1)); + } + return val; + } + + uint8_t streets_snmp_cmd_converter::bitwise_xor_phases(uint8_t val, const std::vector &phases) const + { + for (auto phase : phases) + { + val ^= (1 << (phase - 1)); + } + return val; + } + + void streets_snmp_cmd_converter::add_phase_control_schedule_command_to_two_dimension_map(uint64_t start_time, streets_phase_control_schedule::streets_phase_control_command pcs_cmd, std::map>> &time_cmd_m) const + { + // Checking start time, if the start time as key does not exist in the map, add the start time, the phase and control type as new record in the map. + if (time_cmd_m.find(start_time) == time_cmd_m.end()) + { + std::map> cmd_type_phase_m; + std::vector phases; + phases.push_back(pcs_cmd.command_phase); + cmd_type_phase_m.try_emplace(pcs_cmd.command_type, phases); + time_cmd_m.try_emplace(start_time, cmd_type_phase_m); + } + else + { + /*** + * Checking start time, if the start time already exist, then check the command type. + * If it is a new command type, add the phase abd control type as new record in the nest map. + * If the command type already exists, add the phase to the existing phases list. + * ***/ + auto tmp_cmd_phase_m = time_cmd_m.find(start_time)->second; + // Different command types at the same start time + if (tmp_cmd_phase_m.find(pcs_cmd.command_type) == tmp_cmd_phase_m.end()) + { + std::map> cmd_type_phase_m; + std::vector phases; + phases.push_back(pcs_cmd.command_phase); + time_cmd_m.find(start_time)->second.try_emplace(pcs_cmd.command_type, phases); + } + else + { + // Same command type at the same start time + time_cmd_m.find(start_time)->second.find(pcs_cmd.command_type)->second.push_back(pcs_cmd.command_phase); + } + } + } + + void streets_snmp_cmd_converter::print_two_dimension_map(std::map>> &time_cmd_m, bool is_cmd_start) const + { + SPDLOG_DEBUG("---------------Printing Two Dimension Command Map of Phase Control Schedule Command and Phases---------"); + for (auto &cmd : time_cmd_m) + { + // Start/End time + is_cmd_start ? SPDLOG_DEBUG("Start Timestamp: {0}", cmd.first) : SPDLOG_DEBUG("End Timestamp: {0}", cmd.first); + for (auto &cmd_inner : cmd.second) + { + // Command type + streets_phase_control_schedule::streets_phase_control_command cmd_tmp; + std::string command_type_str = cmd_tmp.COMMAND_TYPE_to_string(cmd_inner.first); + SPDLOG_DEBUG("\tCommand Type: {0}", command_type_str); + // Phases + std::string cmd_inner_inner_str = ""; + for (auto &cmd_inner_inner : cmd_inner.second) + { + cmd_inner_inner_str += std::to_string(cmd_inner_inner) + ","; + } + SPDLOG_DEBUG("\t\tPhases: {0}", cmd_inner_inner_str); + } + SPDLOG_DEBUG("\n"); + } + } + + PHASE_CONTROL_TYPE streets_snmp_cmd_converter::to_phase_control_type(streets_phase_control_schedule::COMMAND_TYPE command_type) const + { + PHASE_CONTROL_TYPE result; + switch (command_type) + { + case streets_phase_control_schedule::COMMAND_TYPE::CALL_PED_PHASES: + result = PHASE_CONTROL_TYPE::CALL_PED_PHASES; + break; + case streets_phase_control_schedule::COMMAND_TYPE::CALL_VEH_PHASES: + result = PHASE_CONTROL_TYPE::CALL_VEH_PHASES; + break; + case streets_phase_control_schedule::COMMAND_TYPE::FORCEOFF_PHASES: + result = PHASE_CONTROL_TYPE::FORCEOFF_PHASES; + break; + case streets_phase_control_schedule::COMMAND_TYPE::HOLD_VEH_PHASES: + result = PHASE_CONTROL_TYPE::HOLD_VEH_PHASES; + break; + case streets_phase_control_schedule::COMMAND_TYPE::OMIT_PED_PHASES: + result = PHASE_CONTROL_TYPE::OMIT_PED_PHASES; + break; + case streets_phase_control_schedule::COMMAND_TYPE::OMIT_VEH_PHASES: + result = PHASE_CONTROL_TYPE::OMIT_VEH_PHASES; + break; + default: + throw streets_snmp_cmd_exception("Phase control schedule command type does not have a mapping SNMP phase control type."); + break; + } + return result; + } + + std::vector streets_snmp_cmd_converter::create_clear_all_snmp_commands(uint64_t execution_time) const + { + std::vector snmp_cmds_result; + snmp_cmds_result.push_back(create_snmp_reset_command(streets_snmp_cmd::PHASE_CONTROL_TYPE::CALL_PED_PHASES, execution_time)); + snmp_cmds_result.push_back(create_snmp_reset_command(streets_snmp_cmd::PHASE_CONTROL_TYPE::CALL_VEH_PHASES, execution_time)); + snmp_cmds_result.push_back(create_snmp_reset_command(streets_snmp_cmd::PHASE_CONTROL_TYPE::OMIT_PED_PHASES, execution_time)); + snmp_cmds_result.push_back(create_snmp_reset_command(streets_snmp_cmd::PHASE_CONTROL_TYPE::OMIT_VEH_PHASES, execution_time)); + snmp_cmds_result.push_back(create_snmp_reset_command(streets_snmp_cmd::PHASE_CONTROL_TYPE::FORCEOFF_PHASES, execution_time)); + snmp_cmds_result.push_back(create_snmp_reset_command(streets_snmp_cmd::PHASE_CONTROL_TYPE::HOLD_VEH_PHASES, execution_time)); + return snmp_cmds_result; + } + + void streets_snmp_cmd_converter::push_snmp_command_to_queue(std::queue &cmds_queue, uint64_t start_time, PHASE_CONTROL_TYPE command_type, int64_t val) const + { + snmp_cmd_struct command(start_time, command_type, val); + cmds_queue.push(command); + } +} // namespace streets_snmp_cmd diff --git a/streets_utils/streets_snmp_cmd/test/streets_snmp_cmd_converter_test.cpp b/streets_utils/streets_snmp_cmd/test/streets_snmp_cmd_converter_test.cpp new file mode 100644 index 000000000..62458f5e0 --- /dev/null +++ b/streets_utils/streets_snmp_cmd/test/streets_snmp_cmd_converter_test.cpp @@ -0,0 +1,507 @@ + +#include "streets_snmp_cmd_converter.h" +#include "streets_snmp_cmd_exception.h" + +#include +#include +#include +#include +#include + +#include +#include +#include + +using namespace streets_snmp_cmd; + +namespace +{ + class streets_snmp_cmd_converter_test : public ::testing::Test + { + public: + streets_snmp_cmd_converter converter; + }; + + TEST_F(streets_snmp_cmd_converter_test, create_snmp_command_by_phases) + { + std::vector phases; + phases.push_back(7); + uint64_t start_time = 1686751639; + PHASE_CONTROL_TYPE control_type; + auto command1 = converter.create_snmp_command_by_phases(phases, control_type, start_time); + std::string expected_str = "control_cmd_type:; execution_start_time:1686751639; value_set:64"; + ASSERT_EQ(expected_str, command1.get_cmd_info()); + + phases.push_back(1); + auto command2 = converter.create_snmp_command_by_phases(phases, control_type, start_time); + expected_str = "control_cmd_type:; execution_start_time:1686751639; value_set:65"; + ASSERT_EQ(expected_str, command2.get_cmd_info()); + + phases.push_back(3); + auto command3 = converter.create_snmp_command_by_phases(phases, control_type, start_time); + expected_str = "control_cmd_type:; execution_start_time:1686751639; value_set:69"; + ASSERT_EQ(expected_str, command3.get_cmd_info()); + + phases.push_back(8); + auto command4 = converter.create_snmp_command_by_phases(phases, control_type, start_time); + expected_str = "control_cmd_type:; execution_start_time:1686751639; value_set:197"; + ASSERT_EQ(expected_str, command4.get_cmd_info()); + }; + + TEST_F(streets_snmp_cmd_converter_test, create_snmp_reset_command) + { + uint64_t start_time = 1686751639; + PHASE_CONTROL_TYPE control_type; + auto command = converter.create_snmp_reset_command(control_type, start_time); + std::string expected_str = "control_cmd_type:; execution_start_time:1686751639; value_set:0"; + ASSERT_EQ(expected_str, command.get_cmd_info()); + }; + + TEST_F(streets_snmp_cmd_converter_test, create_snmp_cmds_by_phase_control_schedule) + { + // Different commandType, same phase and start/end time the same + auto pcs = std::make_shared(); + streets_phase_control_schedule::streets_phase_control_command psc_cmd0("hold", 2, 0, 0); + streets_phase_control_schedule::streets_phase_control_command psc_cmd01("omit_ped", 2, 0, 0); + pcs->commands.push_back(psc_cmd0); + pcs->commands.push_back(psc_cmd01); + + // Same commandType, two different phases and different start time, but end time is the same + streets_phase_control_schedule::streets_phase_control_command psc_cmd2("hold", 4, 6, 24); + streets_phase_control_schedule::streets_phase_control_command psc_cmd3("hold", 2, 5, 24); + pcs->commands.push_back(psc_cmd2); + pcs->commands.push_back(psc_cmd3); + + // Same commandType, two different phases and same start time, and end time + streets_phase_control_schedule::streets_phase_control_command psc_cmd4("hold", 4, 25, 28); + streets_phase_control_schedule::streets_phase_control_command psc_cmd5("hold", 2, 25, 28); + pcs->commands.push_back(psc_cmd4); + pcs->commands.push_back(psc_cmd5); + + // Same commandType, two different phases and different start/end time + streets_phase_control_schedule::streets_phase_control_command psc_cmd6("hold", 2, 48, 65); + streets_phase_control_schedule::streets_phase_control_command psc_cmd7("hold", 4, 49, 64); + pcs->commands.push_back(psc_cmd6); + pcs->commands.push_back(psc_cmd7); + + streets_phase_control_schedule::streets_phase_control_command psc_cmd8("forceoff", 4, 49, 69); + pcs->commands.push_back(psc_cmd8); + + // Same commandType, two difference phases and start/end time are the same. + streets_phase_control_schedule::streets_phase_control_command psc_cmd9("omit_veh", 5, 0, 135); + streets_phase_control_schedule::streets_phase_control_command psc_cmd10("omit_veh", 8, 0, 135); + pcs->commands.push_back(psc_cmd9); + pcs->commands.push_back(psc_cmd10); + + // Different commandType and phases, start and end time are the same + streets_phase_control_schedule::streets_phase_control_command psc_cmd13("call_ped", 8, 158, 159); + streets_phase_control_schedule::streets_phase_control_command psc_cmd14("call_veh", 5, 158, 159); + pcs->commands.push_back(psc_cmd13); + pcs->commands.push_back(psc_cmd14); + + streets_phase_control_schedule::streets_phase_control_command psc_cmd15("hold", 1, 160, 200); + streets_phase_control_schedule::streets_phase_control_command psc_cmd16("hold", 2, 161, 199); + streets_phase_control_schedule::streets_phase_control_command psc_cmd17("hold", 3, 162, 198); + streets_phase_control_schedule::streets_phase_control_command psc_cmd18("hold", 4, 163, 197); + streets_phase_control_schedule::streets_phase_control_command psc_cmd19("hold", 5, 164, 196); + streets_phase_control_schedule::streets_phase_control_command psc_cmd20("hold", 6, 165, 195); + streets_phase_control_schedule::streets_phase_control_command psc_cmd21("hold", 7, 166, 194); + streets_phase_control_schedule::streets_phase_control_command psc_cmd22("hold", 8, 167, 193); + pcs->commands.push_back(psc_cmd15); + pcs->commands.push_back(psc_cmd16); + pcs->commands.push_back(psc_cmd17); + pcs->commands.push_back(psc_cmd18); + pcs->commands.push_back(psc_cmd19); + pcs->commands.push_back(psc_cmd20); + pcs->commands.push_back(psc_cmd21); + + auto cmds_result = converter.create_snmp_cmds_by_phase_control_schedule(pcs); + std::string expected_str = ""; + while (!cmds_result.empty()) + { + auto snmp_cmd = cmds_result.front(); + cmds_result.pop(); + SPDLOG_INFO("{0}", snmp_cmd.get_cmd_info()); + if (snmp_cmd.start_time_ == 0) + { + if (snmp_cmd.control_type_ == PHASE_CONTROL_TYPE::HOLD_VEH_PHASES) + { + expected_str = "control_cmd_type:Vehicle Hold; execution_start_time:0; value_set:0"; + ASSERT_EQ(expected_str, snmp_cmd.get_cmd_info()); + } + else if (snmp_cmd.control_type_ == PHASE_CONTROL_TYPE::OMIT_VEH_PHASES) + { + expected_str = "control_cmd_type:Vehicle Omit; execution_start_time:0; value_set:144"; + ASSERT_EQ(expected_str, snmp_cmd.get_cmd_info()); + } + else if (snmp_cmd.control_type_ == PHASE_CONTROL_TYPE::OMIT_PED_PHASES) + { + expected_str = "control_cmd_type:Pedestrian Omit; execution_start_time:0; value_set:0"; + ASSERT_EQ(expected_str, snmp_cmd.get_cmd_info()); + } + } + else if (snmp_cmd.start_time_ == 5) + { + if (snmp_cmd.control_type_ == PHASE_CONTROL_TYPE::HOLD_VEH_PHASES) + { + expected_str = "control_cmd_type:Vehicle Hold; execution_start_time:5; value_set:2"; + ASSERT_EQ(expected_str, snmp_cmd.get_cmd_info()); + } + } + else if (snmp_cmd.start_time_ == 6) + { + if (snmp_cmd.control_type_ == PHASE_CONTROL_TYPE::HOLD_VEH_PHASES) + { + expected_str = "control_cmd_type:Vehicle Hold; execution_start_time:6; value_set:10"; + ASSERT_EQ(expected_str, snmp_cmd.get_cmd_info()); + } + } + else if (snmp_cmd.start_time_ == 24) + { + if (snmp_cmd.control_type_ == PHASE_CONTROL_TYPE::HOLD_VEH_PHASES) + { + expected_str = "control_cmd_type:Vehicle Hold; execution_start_time:24; value_set:0"; + ASSERT_EQ(expected_str, snmp_cmd.get_cmd_info()); + } + } + else if (snmp_cmd.start_time_ == 25) + { + if (snmp_cmd.control_type_ == PHASE_CONTROL_TYPE::HOLD_VEH_PHASES) + { + expected_str = "control_cmd_type:Vehicle Hold; execution_start_time:25; value_set:10"; + ASSERT_EQ(expected_str, snmp_cmd.get_cmd_info()); + } + } + else if (snmp_cmd.start_time_ == 28) + { + if (snmp_cmd.control_type_ == PHASE_CONTROL_TYPE::HOLD_VEH_PHASES) + { + expected_str = "control_cmd_type:Vehicle Hold; execution_start_time:28; value_set:0"; + ASSERT_EQ(expected_str, snmp_cmd.get_cmd_info()); + } + } + else if (snmp_cmd.start_time_ == 48) + { + if (snmp_cmd.control_type_ == PHASE_CONTROL_TYPE::HOLD_VEH_PHASES) + { + expected_str = "control_cmd_type:Vehicle Hold; execution_start_time:48; value_set:2"; + ASSERT_EQ(expected_str, snmp_cmd.get_cmd_info()); + } + } + else if (snmp_cmd.start_time_ == 49) + { + if (snmp_cmd.control_type_ == PHASE_CONTROL_TYPE::HOLD_VEH_PHASES) + { + expected_str = "control_cmd_type:Vehicle Hold; execution_start_time:49; value_set:10"; + ASSERT_EQ(expected_str, snmp_cmd.get_cmd_info()); + } + else if (snmp_cmd.control_type_ == PHASE_CONTROL_TYPE::FORCEOFF_PHASES) + { + expected_str = "control_cmd_type:Vehicle Forceoff; execution_start_time:49; value_set:8"; + ASSERT_EQ(expected_str, snmp_cmd.get_cmd_info()); + } + } + else if (snmp_cmd.start_time_ == 64) + { + if (snmp_cmd.control_type_ == PHASE_CONTROL_TYPE::HOLD_VEH_PHASES) + { + expected_str = "control_cmd_type:Vehicle Hold; execution_start_time:64; value_set:2"; + ASSERT_EQ(expected_str, snmp_cmd.get_cmd_info()); + } + } + else if (snmp_cmd.start_time_ == 65) + { + if (snmp_cmd.control_type_ == PHASE_CONTROL_TYPE::HOLD_VEH_PHASES) + { + expected_str = "control_cmd_type:Vehicle Hold; execution_start_time:65; value_set:0"; + ASSERT_EQ(expected_str, snmp_cmd.get_cmd_info()); + } + } + else if (snmp_cmd.start_time_ == 69) + { + if (snmp_cmd.control_type_ == PHASE_CONTROL_TYPE::FORCEOFF_PHASES) + { + expected_str = "control_cmd_type:Vehicle Forceoff; execution_start_time:69; value_set:0"; + ASSERT_EQ(expected_str, snmp_cmd.get_cmd_info()); + } + } + else if (snmp_cmd.start_time_ == 135) + { + if (snmp_cmd.control_type_ == PHASE_CONTROL_TYPE::OMIT_VEH_PHASES) + { + expected_str = "control_cmd_type:Vehicle Omit; execution_start_time:135; value_set:0"; + ASSERT_EQ(expected_str, snmp_cmd.get_cmd_info()); + } + } + else if (snmp_cmd.start_time_ == 158) + { + if (snmp_cmd.control_type_ == PHASE_CONTROL_TYPE::CALL_PED_PHASES) + { + expected_str = "control_cmd_type:Pedestrian Call; execution_start_time:158; value_set:128"; + ASSERT_EQ(expected_str, snmp_cmd.get_cmd_info()); + } + else if (snmp_cmd.control_type_ == PHASE_CONTROL_TYPE::CALL_VEH_PHASES) + { + expected_str = "control_cmd_type:Vehicle Call; execution_start_time:158; value_set:16"; + ASSERT_EQ(expected_str, snmp_cmd.get_cmd_info()); + } + } + else if (snmp_cmd.start_time_ == 159) + { + if (snmp_cmd.control_type_ == PHASE_CONTROL_TYPE::CALL_VEH_PHASES) + { + expected_str = "control_cmd_type:Vehicle Call; execution_start_time:159; value_set:0"; + ASSERT_EQ(expected_str, snmp_cmd.get_cmd_info()); + } + else if (snmp_cmd.control_type_ == PHASE_CONTROL_TYPE::OMIT_VEH_PHASES) + { + expected_str = "control_cmd_type:Vehicle Omit; execution_start_time:159; value_set:0"; + ASSERT_EQ(expected_str, snmp_cmd.get_cmd_info()); + } + } + else if (snmp_cmd.start_time_ == 160) + { + if (snmp_cmd.control_type_ == PHASE_CONTROL_TYPE::HOLD_VEH_PHASES) + { + expected_str = "control_cmd_type:Vehicle Hold; execution_start_time:160; value_set:1"; + ASSERT_EQ(expected_str, snmp_cmd.get_cmd_info()); + } + } + else if (snmp_cmd.start_time_ == 161) + { + if (snmp_cmd.control_type_ == PHASE_CONTROL_TYPE::HOLD_VEH_PHASES) + { + expected_str = "control_cmd_type:Vehicle Hold; execution_start_time:161; value_set:3"; + ASSERT_EQ(expected_str, snmp_cmd.get_cmd_info()); + } + } + else if (snmp_cmd.start_time_ == 162) + { + if (snmp_cmd.control_type_ == PHASE_CONTROL_TYPE::HOLD_VEH_PHASES) + { + expected_str = "control_cmd_type:Vehicle Hold; execution_start_time:162; value_set:7"; + ASSERT_EQ(expected_str, snmp_cmd.get_cmd_info()); + } + } + else if (snmp_cmd.start_time_ == 163) + { + if (snmp_cmd.control_type_ == PHASE_CONTROL_TYPE::HOLD_VEH_PHASES) + { + expected_str = "control_cmd_type:Vehicle Hold; execution_start_time:163; value_set:15"; + ASSERT_EQ(expected_str, snmp_cmd.get_cmd_info()); + } + } + else if (snmp_cmd.start_time_ == 164) + { + if (snmp_cmd.control_type_ == PHASE_CONTROL_TYPE::HOLD_VEH_PHASES) + { + expected_str = "control_cmd_type:Vehicle Hold; execution_start_time:164; value_set:31"; + ASSERT_EQ(expected_str, snmp_cmd.get_cmd_info()); + } + } + else if (snmp_cmd.start_time_ == 165) + { + if (snmp_cmd.control_type_ == PHASE_CONTROL_TYPE::HOLD_VEH_PHASES) + { + expected_str = "control_cmd_type:Vehicle Hold; execution_start_time:165; value_set:63"; + ASSERT_EQ(expected_str, snmp_cmd.get_cmd_info()); + } + } + else if (snmp_cmd.start_time_ == 166) + { + if (snmp_cmd.control_type_ == PHASE_CONTROL_TYPE::HOLD_VEH_PHASES) + { + expected_str = "control_cmd_type:Vehicle Hold; execution_start_time:166; value_set:127"; + ASSERT_EQ(expected_str, snmp_cmd.get_cmd_info()); + } + } + else if (snmp_cmd.start_time_ == 194) + { + if (snmp_cmd.control_type_ == PHASE_CONTROL_TYPE::HOLD_VEH_PHASES) + { + expected_str = "control_cmd_type:Vehicle Hold; execution_start_time:194; value_set:63"; + ASSERT_EQ(expected_str, snmp_cmd.get_cmd_info()); + } + } + else if (snmp_cmd.start_time_ == 195) + { + if (snmp_cmd.control_type_ == PHASE_CONTROL_TYPE::HOLD_VEH_PHASES) + { + expected_str = "control_cmd_type:Vehicle Hold; execution_start_time:195; value_set:31"; + ASSERT_EQ(expected_str, snmp_cmd.get_cmd_info()); + } + } + else if (snmp_cmd.start_time_ == 196) + { + if (snmp_cmd.control_type_ == PHASE_CONTROL_TYPE::HOLD_VEH_PHASES) + { + expected_str = "control_cmd_type:Vehicle Hold; execution_start_time:196; value_set:15"; + ASSERT_EQ(expected_str, snmp_cmd.get_cmd_info()); + } + } + else if (snmp_cmd.start_time_ == 197) + { + if (snmp_cmd.control_type_ == PHASE_CONTROL_TYPE::HOLD_VEH_PHASES) + { + expected_str = "control_cmd_type:Vehicle Hold; execution_start_time:197; value_set:7"; + ASSERT_EQ(expected_str, snmp_cmd.get_cmd_info()); + } + } + else if (snmp_cmd.start_time_ == 198) + { + if (snmp_cmd.control_type_ == PHASE_CONTROL_TYPE::HOLD_VEH_PHASES) + { + expected_str = "control_cmd_type:Vehicle Hold; execution_start_time:198; value_set:3"; + ASSERT_EQ(expected_str, snmp_cmd.get_cmd_info()); + } + } + else if (snmp_cmd.start_time_ == 199) + { + if (snmp_cmd.control_type_ == PHASE_CONTROL_TYPE::HOLD_VEH_PHASES) + { + expected_str = "control_cmd_type:Vehicle Hold; execution_start_time:199; value_set:1"; + ASSERT_EQ(expected_str, snmp_cmd.get_cmd_info()); + } + } + else if (snmp_cmd.start_time_ == 200) + { + if (snmp_cmd.control_type_ == PHASE_CONTROL_TYPE::HOLD_VEH_PHASES) + { + expected_str = "control_cmd_type:Vehicle Hold; execution_start_time:200; value_set:0"; + ASSERT_EQ(expected_str, snmp_cmd.get_cmd_info()); + } + } + } + } + + TEST_F(streets_snmp_cmd_converter_test, create_snmp_cmds_by_phase_control_schedule_2) + { + // Different commandType, different phases, same start time and different end time + auto pcs = std::make_shared(); + streets_phase_control_schedule::streets_phase_control_command psc_cmd0("hold", 1, 0, 10); + streets_phase_control_schedule::streets_phase_control_command psc_cmd1("omit_ped", 2, 0, 12); + streets_phase_control_schedule::streets_phase_control_command psc_cmd2("omit_veh", 3, 0, 13); + streets_phase_control_schedule::streets_phase_control_command psc_cmd3("call_veh", 4, 0, 14); + streets_phase_control_schedule::streets_phase_control_command psc_cmd4("call_ped", 5, 0, 15); + streets_phase_control_schedule::streets_phase_control_command psc_cmd5("forceoff", 6, 0, 16); + pcs->commands.push_back(psc_cmd0); + pcs->commands.push_back(psc_cmd1); + pcs->commands.push_back(psc_cmd2); + pcs->commands.push_back(psc_cmd3); + pcs->commands.push_back(psc_cmd4); + pcs->commands.push_back(psc_cmd5); + auto cmds_result = converter.create_snmp_cmds_by_phase_control_schedule(pcs); + std::string expected_str = ""; + while (!cmds_result.empty()) + { + auto snmp_cmd = cmds_result.front(); + cmds_result.pop(); + SPDLOG_INFO("{0}", snmp_cmd.get_cmd_info()); + if (snmp_cmd.start_time_ == 0) + { + switch (snmp_cmd.control_type_) + { + case PHASE_CONTROL_TYPE::HOLD_VEH_PHASES: + expected_str = "control_cmd_type:Vehicle Hold; execution_start_time:0; value_set:1"; + ASSERT_EQ(expected_str, snmp_cmd.get_cmd_info()); + break; + case PHASE_CONTROL_TYPE::FORCEOFF_PHASES: + expected_str = "control_cmd_type:Vehicle Forceoff; execution_start_time:0; value_set:32"; + ASSERT_EQ(expected_str, snmp_cmd.get_cmd_info()); + break; + case PHASE_CONTROL_TYPE::OMIT_VEH_PHASES: + expected_str = "control_cmd_type:Vehicle Omit; execution_start_time:0; value_set:4"; + ASSERT_EQ(expected_str, snmp_cmd.get_cmd_info()); + break; + case PHASE_CONTROL_TYPE::OMIT_PED_PHASES: + expected_str = "control_cmd_type:Pedestrian Omit; execution_start_time:0; value_set:2"; + ASSERT_EQ(expected_str, snmp_cmd.get_cmd_info()); + break; + case PHASE_CONTROL_TYPE::CALL_PED_PHASES: + expected_str = "control_cmd_type:Pedestrian Call; execution_start_time:0; value_set:16"; + ASSERT_EQ(expected_str, snmp_cmd.get_cmd_info()); + break; + case PHASE_CONTROL_TYPE::CALL_VEH_PHASES: + expected_str = "control_cmd_type:Vehicle Call; execution_start_time:0; value_set:8"; + ASSERT_EQ(expected_str, snmp_cmd.get_cmd_info()); + break; + } + } + else if (snmp_cmd.start_time_ == 10) + { + switch (snmp_cmd.control_type_) + { + case PHASE_CONTROL_TYPE::HOLD_VEH_PHASES: + expected_str = "control_cmd_type:Vehicle Hold; execution_start_time:10; value_set:0"; + ASSERT_EQ(expected_str, snmp_cmd.get_cmd_info()); + break; + } + } + else if (snmp_cmd.start_time_ == 12) + { + switch (snmp_cmd.control_type_) + { + case PHASE_CONTROL_TYPE::OMIT_PED_PHASES: + expected_str = "control_cmd_type:Pedestrian Omit; execution_start_time:12; value_set:0"; + ASSERT_EQ(expected_str, snmp_cmd.get_cmd_info()); + break; + } + } + else if (snmp_cmd.start_time_ == 13) + { + switch (snmp_cmd.control_type_) + { + case PHASE_CONTROL_TYPE::OMIT_VEH_PHASES: + expected_str = "control_cmd_type:Vehicle Omit; execution_start_time:13; value_set:0"; + ASSERT_EQ(expected_str, snmp_cmd.get_cmd_info()); + break; + } + } + else if (snmp_cmd.start_time_ == 14) + { + switch (snmp_cmd.control_type_) + { + case PHASE_CONTROL_TYPE::CALL_VEH_PHASES: + expected_str = "control_cmd_type:Vehicle Call; execution_start_time:14; value_set:0"; + ASSERT_EQ(expected_str, snmp_cmd.get_cmd_info()); + break; + } + } + else if (snmp_cmd.start_time_ == 15) + { + switch (snmp_cmd.control_type_) + { + case PHASE_CONTROL_TYPE::CALL_PED_PHASES: + expected_str = "control_cmd_type:Pedestrian Call; execution_start_time:15; value_set:0"; + ASSERT_EQ(expected_str, snmp_cmd.get_cmd_info()); + break; + } + } + else if (snmp_cmd.start_time_ == 16) + { + switch (snmp_cmd.control_type_) + { + case PHASE_CONTROL_TYPE::FORCEOFF_PHASES: + expected_str = "control_cmd_type:Vehicle Forceoff; execution_start_time:16; value_set:0"; + ASSERT_EQ(expected_str, snmp_cmd.get_cmd_info()); + break; + } + } + } + } + + TEST_F(streets_snmp_cmd_converter_test, to_phase_control_type) + { + ASSERT_EQ(PHASE_CONTROL_TYPE::CALL_PED_PHASES, converter.to_phase_control_type(streets_phase_control_schedule::COMMAND_TYPE::CALL_PED_PHASES)); + ASSERT_EQ(PHASE_CONTROL_TYPE::CALL_VEH_PHASES, converter.to_phase_control_type(streets_phase_control_schedule::COMMAND_TYPE::CALL_VEH_PHASES)); + ASSERT_EQ(PHASE_CONTROL_TYPE::OMIT_PED_PHASES, converter.to_phase_control_type(streets_phase_control_schedule::COMMAND_TYPE::OMIT_PED_PHASES)); + ASSERT_EQ(PHASE_CONTROL_TYPE::OMIT_VEH_PHASES, converter.to_phase_control_type(streets_phase_control_schedule::COMMAND_TYPE::OMIT_VEH_PHASES)); + ASSERT_EQ(PHASE_CONTROL_TYPE::FORCEOFF_PHASES, converter.to_phase_control_type(streets_phase_control_schedule::COMMAND_TYPE::FORCEOFF_PHASES)); + ASSERT_EQ(PHASE_CONTROL_TYPE::HOLD_VEH_PHASES, converter.to_phase_control_type(streets_phase_control_schedule::COMMAND_TYPE::HOLD_VEH_PHASES)); + } + + TEST_F(streets_snmp_cmd_converter_test, create_clear_all_snmp_commands) + { + ASSERT_EQ(6, converter.create_clear_all_snmp_commands(0).size()); + } +}; diff --git a/streets_utils/streets_snmp_cmd/test/streets_snmp_cmd_test.cpp b/streets_utils/streets_snmp_cmd/test/streets_snmp_cmd_test.cpp new file mode 100644 index 000000000..238214646 --- /dev/null +++ b/streets_utils/streets_snmp_cmd/test/streets_snmp_cmd_test.cpp @@ -0,0 +1,58 @@ + +#include "streets_snmp_cmd.h" +#include "streets_snmp_cmd_exception.h" + +#include +#include +#include +#include +#include + +#include +#include +#include + +using namespace streets_snmp_cmd; + +namespace +{ + + class streets_snmp_cmd_test : public ::testing::Test + { + }; + + TEST_F(streets_snmp_cmd_test, get_cmd_info) + { + uint64_t start_time = 1686683893; + PHASE_CONTROL_TYPE type = PHASE_CONTROL_TYPE::HOLD_VEH_PHASES; + int64_t val = 2; + snmp_cmd_struct snmp_cmd1(start_time, type, val); + std::string expected_str = "control_cmd_type:Vehicle Hold; execution_start_time:1686683893; value_set:2"; + ASSERT_EQ(expected_str, snmp_cmd1.get_cmd_info()); + + type = PHASE_CONTROL_TYPE::CALL_PED_PHASES; + snmp_cmd_struct snmp_cmd2(start_time, type, val); + expected_str = "control_cmd_type:Pedestrian Call; execution_start_time:1686683893; value_set:2"; + ASSERT_EQ(expected_str, snmp_cmd2.get_cmd_info()); + + type = PHASE_CONTROL_TYPE::CALL_VEH_PHASES; + snmp_cmd_struct snmp_cmd3(start_time, type, val); + expected_str = "control_cmd_type:Vehicle Call; execution_start_time:1686683893; value_set:2"; + ASSERT_EQ(expected_str, snmp_cmd3.get_cmd_info()); + + type = PHASE_CONTROL_TYPE::OMIT_PED_PHASES; + snmp_cmd_struct snmp_cmd4(start_time, type, val); + expected_str = "control_cmd_type:Pedestrian Omit; execution_start_time:1686683893; value_set:2"; + ASSERT_EQ(expected_str, snmp_cmd4.get_cmd_info()); + + type = PHASE_CONTROL_TYPE::OMIT_VEH_PHASES; + snmp_cmd_struct snmp_cmd5(start_time, type, val); + expected_str = "control_cmd_type:Vehicle Omit; execution_start_time:1686683893; value_set:2"; + ASSERT_EQ(expected_str, snmp_cmd5.get_cmd_info()); + + type = PHASE_CONTROL_TYPE::FORCEOFF_PHASES; + snmp_cmd_struct snmp_cmd6(start_time, type, val); + expected_str = "control_cmd_type:Vehicle Forceoff; execution_start_time:1686683893; value_set:2"; + ASSERT_EQ(expected_str, snmp_cmd6.get_cmd_info()); + } +}; \ No newline at end of file diff --git a/streets_utils/streets_snmp_cmd/test/test_main.cpp b/streets_utils/streets_snmp_cmd/test/test_main.cpp new file mode 100644 index 000000000..1e925a90b --- /dev/null +++ b/streets_utils/streets_snmp_cmd/test/test_main.cpp @@ -0,0 +1,6 @@ +#include "gtest/gtest.h" +int main(int argc, char **argv) +{ + ::testing::InitGoogleTest(&argc, argv); + return RUN_ALL_TESTS(); +} \ No newline at end of file diff --git a/tsc_client_service/CMakeLists.txt b/tsc_client_service/CMakeLists.txt index 43934ed62..638476c49 100644 --- a/tsc_client_service/CMakeLists.txt +++ b/tsc_client_service/CMakeLists.txt @@ -14,6 +14,7 @@ find_package(streets_signal_phase_and_timing_lib REQUIRED) find_package(streets_tsc_configuration_lib REQUIRED) find_package(streets_desired_phase_plan_lib REQUIRED) find_package(streets_phase_control_schedule_lib REQUIRED) +find_package(streets_snmp_cmd_lib REQUIRED) find_library(NETSNMPAGENT "netsnmpagent") find_library(NETSNMPMIBS "netsnmpmibs") find_library(NETSNMP "netsnmp") @@ -50,7 +51,8 @@ target_link_libraries( ${PROJECT_NAME}_lib streets_signal_phase_and_timing_lib streets_tsc_configuration_lib streets_desired_phase_plan_lib - streets_phase_control_schedule_lib + streets_phase_control_schedule_lib::streets_phase_control_schedule_lib + streets_snmp_cmd_lib::streets_snmp_cmd_lib intersection_client_api_lib spdlog::spdlog Qt5::Core diff --git a/tsc_client_service/Dockerfile b/tsc_client_service/Dockerfile index b55afde16..d6981a3ca 100644 --- a/tsc_client_service/Dockerfile +++ b/tsc_client_service/Dockerfile @@ -102,6 +102,14 @@ RUN cmake -DCMAKE_BUILD_TYPE="Debug" .. RUN make RUN make install +# Install streets_snmp_cmd +RUN echo " ------> Install streets snmp command from streets_utils..." +WORKDIR /home/carma-streets/streets_utils/streets_snmp_cmd +RUN mkdir build +WORKDIR /home/carma-streets/streets_utils/streets_snmp_cmd/build +RUN cmake -DCMAKE_BUILD_TYPE="Debug" .. +RUN make +RUN make install # Install net-snmp WORKDIR /home/carma-streets/ext/ diff --git a/tsc_client_service/include/control_tsc_state.h b/tsc_client_service/include/control_tsc_state.h index 0e0550623..3e656aa4a 100644 --- a/tsc_client_service/include/control_tsc_state.h +++ b/tsc_client_service/include/control_tsc_state.h @@ -2,6 +2,7 @@ #include "streets_desired_phase_plan.h" #include "streets_phase_control_schedule.h" +#include "streets_snmp_cmd_converter.h" #include "snmp_client.h" #include "ntcip_oids.h" #include "monitor_tsc_state.h" @@ -10,66 +11,6 @@ namespace traffic_signal_controller_service { - - /** @brief Object to store snmp control commands. Contructed with an initialized snmp_client_worker_ this object stores SNMP HOLD and OMIT commands - to be executed at specified time */ - struct snmp_cmd_struct - { - /** @brief The type of control being set on the TSC */ - enum class control_type{ - Hold, - Omit - }; - /* Pointer to a snmp client object which can execute snmp commands*/ - std::shared_ptr snmp_client_worker_; - /*Value to be set for Hold/Omit*/ - snmp_response_obj set_val_; - /*Time at which the snmp set command should be executed*/ - uint64_t start_time_; - /*Type of the snmp set command this object creates- Hold or Omit*/ - control_type control_type_; - - snmp_cmd_struct(std::shared_ptr snmp_client_worker, int64_t start_time, control_type type, int64_t val) - : snmp_client_worker_(snmp_client_worker), start_time_(start_time), control_type_(type) - { - set_val_.type = snmp_response_obj::response_type::INTEGER; - set_val_.val_int = val; - } - - /** - * @brief Method to call the snmp command. Object type determines what SET command is sent. - * Types are Omit and Hold. - * @return True if SET commands are successful. False if command fails. - * */ - bool run() - { - /*Type of request to be sent to the TSC, within this context it is always SET*/ - request_type type = request_type::SET; - - if(control_type_ == control_type::Omit) - { - if(!snmp_client_worker_->process_snmp_request(ntcip_oids::PHASE_OMIT_CONTROL, type, set_val_)){ - return false; - } - } - else - { - if(!snmp_client_worker_->process_snmp_request(ntcip_oids::PHASE_HOLD_CONTROL, type, set_val_)){ - return false; - } - } - - return true; - - } - - /** - * @brief Method to return information about the snmp set command - * @return Returns string formatted as "control_cmd_type:;execution_start_time:;signal_groups_set:". - * */ - std::string get_cmd_info() const; - }; - class control_tsc_state { // This class aims to control the phases on the traffic signal controller using Omit and Hold SNMP commands @@ -96,33 +37,32 @@ namespace traffic_signal_controller_service * @param tsc_command_queue Queue of snmp commands to set HOLD and OMIT on the traffic signal controller **/ void update_tsc_control_queue(std::shared_ptr desired_phase_plan, - std::queue& tsc_command_queue) const; + std::queue& tsc_command_queue) const; /** * @brief Method to update the queue of tsc_control * @param phase_control_schedule Pointer to the phase control schedule. - * @param tsc_command_queue Queue of snmp commands to set HOLD and OMIT on the traffic signal controller + * @param tsc_command_queue Queue of snmp commands to set HOLD and OMIT on the traffic signal controller.This queue is a managed by the tsc_service and passed by reference for update by incoming phase control schedule changes. **/ void update_tsc_control_queue(std::shared_ptr phase_control_schedule, - std::queue& tsc_command_queue) const; - /** - * @brief Method to create OMIT snmp command for provided signal groups, which will result in the traffic signal controller skipping the specified phases. - * This method should be sent before any yellow phase for omission to take effect. - * @param signal_groups A list of signal groups NOT to be omitted. Omit command will aim to omit everything besides signal groups specified here. - * @param start_time Time at which the snmp command needs to be sent - * @param is_reset if true, omit command is reset on the traffic signal controller to 0. - * If false will calculate the omit value required to reach given signal groups - **/ - snmp_cmd_struct create_omit_command(const std::vector& signal_groups, int64_t start_time, bool is_reset = false) const; + std::queue& tsc_command_queue) const; /** - * @brief Method to create command for Hold for provided signal groups, - * which will result in the traffic signal controller "holding" the specified phases till a change in the Hold command. + * @brief Method to create command for provided signal groups. Calculate the value required to control (hold, forceoff, omit, call) given signal groups. * @param signal_groups A list of signal groups NOT to be omitted. Hold command will aim to hold the signal groups specified here. - * @param start_time Time at which the snmp command needs to be sent - * @param is_reset if true, hold command is reset on the traffic signal controller to 0. - * If false will calculate the value required to hold given signal groups + * @param start_time Time at which the snmp command needs to be sent. **/ - snmp_cmd_struct create_hold_command(const std::vector& signal_groups, int64_t start_time, bool is_reset = false) const; + streets_snmp_cmd::snmp_cmd_struct create_snmp_command_by_signal_groups(const std::vector& signal_groups, streets_snmp_cmd::PHASE_CONTROL_TYPE phase_control_type, int64_t start_time) const; + /** + * @brief Method to call the snmp command. Object type determines what SET command is sent. + * Types are Omit, Forceoff, Call and Hold. + * @return True if SET commands are successful. False if command fails. + * */ + bool run_snmp_cmd_set_request(streets_snmp_cmd::snmp_cmd_struct& snmp_cmd) const; + /** + * @brief Method to call the vector of snmp commands. Object type determines what SET command is sent. + * Types are Omit, Forceoff, Call and Hold. + * */ + void run_clear_all_snmp_commands() const; }; } \ No newline at end of file diff --git a/tsc_client_service/include/mock_snmp_client.h b/tsc_client_service/include/mock_snmp_client.h index b37562f04..643ae0778 100644 --- a/tsc_client_service/include/mock_snmp_client.h +++ b/tsc_client_service/include/mock_snmp_client.h @@ -20,7 +20,7 @@ namespace traffic_signal_controller_service { */ mock_snmp_client(const std::string& ip = "", const int &port = 0 ) : snmp_client(ip, port){}; ~mock_snmp_client() = default; - MOCK_METHOD(bool, process_snmp_request, (const std::string &input_oid, const request_type &request_type, snmp_response_obj &val), (override)); + MOCK_METHOD(bool, process_snmp_request, (const std::string &input_oid, const streets_snmp_cmd::REQUEST_TYPE &request_type, streets_snmp_cmd::snmp_response_obj &val), (override)); }; } \ No newline at end of file diff --git a/tsc_client_service/include/monitor_tsc_state.h b/tsc_client_service/include/monitor_tsc_state.h index a8ea722e1..70e5ed7b2 100644 --- a/tsc_client_service/include/monitor_tsc_state.h +++ b/tsc_client_service/include/monitor_tsc_state.h @@ -212,11 +212,17 @@ namespace traffic_signal_controller_service const std::unordered_map & get_ped_phase_map(); /** - * @brief Returns a map of pedestrian phases to signal group ids - * @return a map of pedestrian phases to signal group ids + * @brief Returns a map of vehicle phases to signal group ids + * @return a map of vehicle phases to signal group ids **/ const std::unordered_map& get_vehicle_phase_map(); + /** + * @brief Return a map of signal group ids to phases map. + * @return a map of signal group ids to vehicle phases map. + */ + const std::unordered_map & get_signal_group_map(); + /** * @brief Get the phase number using signal group id. * diff --git a/tsc_client_service/include/ntcip_oids.h b/tsc_client_service/include/ntcip_oids.h index 3fe87bfaf..a5d29444f 100644 --- a/tsc_client_service/include/ntcip_oids.h +++ b/tsc_client_service/include/ntcip_oids.h @@ -77,7 +77,7 @@ namespace ntcip_oids { static const std::string PHASE_CONCURRENCY = "1.3.6.1.4.1.1206.4.2.1.1.2.1.23"; /** - * @brief Phase Omit Control object is used to allow omission of pedestrian phases from being serviced in the device. + * @brief Phase Omit Control object is used to allow omission of vehicle phases from being serviced in the device. * It takes in a 8 bit argument, one for each phase. When a bit=1 the Traffic Signal Controller will omit * that phase and keep omitting till the bit is set to 0. * Values range from 0-255. @@ -86,7 +86,16 @@ namespace ntcip_oids { static const std::string PHASE_OMIT_CONTROL = "1.3.6.1.4.1.1206.4.2.1.1.5.1.2.1"; /** - * @brief Phase Hold Control object is used to Hold pedestrian phases. + * @brief Phase Omit Control object is used to allow omission of pedestrian phases from being serviced in the device. + * It takes in a 8 bit argument, one for each phase. When a bit=1 the Traffic Signal Controller will omit + * that phase and keep omitting till the bit is set to 0. + * Values range from 0-255. + * .1 is needed at the end to set the phase control. NTCIP documentation is unclear about why its required or what it represents + */ + static const std::string PHASE_PEDESTRIAN_OMIT_CONTROL = "1.3.6.1.4.1.1206.4.2.1.1.5.1.3.1"; + + /** + * @brief Phase Hold Control object is used to Hold Vehicle phases. * It takes in a 8 bit argument, one for each phase. When a bit=1 the Traffic Signal Controller will hold * that phase when it turns green and keep holding till the bit is set to 0. * Values range from 0-255. @@ -104,4 +113,27 @@ namespace ntcip_oids { */ static const std::string PHASE_STATUS_GROUP_PHASE_NEXT = "1.3.6.1.4.1.1206.4.2.1.1.4.1.11.1"; + /** + * @brief Phase FORCEOFF Control object is used to allow apply force offs on a per phase basis. + * It takes in a 8 bit argument, one for each phase. When a bit=1 the Traffic Signal Controller will force off + * that phase, and should not activate the system phase force off control for that phase when the bit is set to 0. + * Values range from 0-255. When the phase green terminates, the associated bit shall be reset to 0. + * .1 is needed at the end to set the phase control. NTCIP documentation is unclear about why its required or what it represents + */ + static const std::string PHASE_FORCEOFF_CONTROL = "1.3.6.1.4.1.1206.4.2.1.1.5.1.5.1"; + + /** + * @brief Phase vehicle call control object is used to allow a remote entity to place calls for vehicle service in the service. + * When a bit = 1, the device shall place a call for vehicle service on that phase. + * When a bit = 0, the device shall not place a call for vehicle service on that phase. + * .1 is needed at the end to set the phase control. NTCIP documentation is unclear about why its required or what it represents + */ + static const std::string PHASE_VEHICLE_CALL_CONTROL = "1.3.6.1.4.1.1206.4.2.1.1.5.1.6.1"; + + /** + * @brief The phase pedestrian call control is used to allow a remote entity to place calls for ped service in the device. + * When a bit = 1, the device shall place a call for ped service on that phase. + * When a bit = 0, the device shall not place a call for ped service on that phase. + */ + static const std::string PHASE_PEDESTRIAN_CALL_CONTROL = "1.3.6.1.4.1.1206.4.2.1.1.5.1.7.1"; } \ No newline at end of file diff --git a/tsc_client_service/include/snmp_client.h b/tsc_client_service/include/snmp_client.h index f11d09845..89215d614 100644 --- a/tsc_client_service/include/snmp_client.h +++ b/tsc_client_service/include/snmp_client.h @@ -10,40 +10,12 @@ #include "ntcip_oids.h" #include "snmp_client_exception.h" +#include "streets_snmp_cmd.h" namespace traffic_signal_controller_service { -enum class request_type -{ - GET, - SET, - OTHER //Processing this request type is not a defined behavior, included for testing only -}; - -/** @brief A struct to hold the value being sent to the TSC, can be integer or string. Type needs to be defined*/ -struct snmp_response_obj -{ - /** @brief The type of value being requested or set, on the TSC */ - enum class response_type - { - INTEGER, - STRING - }; - - //snmp response values can be any asn.1 supported types. - //Integer and string values can be processed here - int64_t val_int = 0; - std::vector val_string; - response_type type; - - inline bool operator==(const snmp_response_obj& obj2) const - { - return val_int == obj2.val_int && val_string == obj2.val_string && type == obj2.type; - } -}; - class snmp_client { private: @@ -92,19 +64,19 @@ class snmp_client /** @brief Returns true or false depending on whether the request could be processed for given input OID at the Traffic Signal Controller. * @param input_oid The OID to request information for. - * @param request_type The request type for which the error is being logged. Accepted values are "GET" and "SET" only. + * @param streets_snmp_cmd::REQUEST_TYPE The request type for which the error is being logged. Accepted values are "GET" and "SET" only. * @param value_int The integer value for the object returned by reference. For "SET" it is the value to be set. * For "GET", it is the value returned for the returned object by reference. * This is an optional argument, if not provided, defaults to 0. * @param value_str String value for the object, returned by reference. Optional argument, if not provided the value is set as an empty string * @return Integer value at the oid, returns false if value cannot be set/requested or oid doesn't have an integer value to return.*/ - virtual bool process_snmp_request(const std::string& input_oid, const request_type& request_type, snmp_response_obj& val); + virtual bool process_snmp_request(const std::string& input_oid, const streets_snmp_cmd::REQUEST_TYPE& request_type, streets_snmp_cmd::snmp_response_obj& val); /** @brief Finds error type from status and logs an error. * @param status The integer value corresponding to net-snmp defined errors. macros considered are STAT_SUCCESS(0) and STAT_TIMEOUT(2) - * @param request_type The request type for which the error is being logged (GET/SET). + * @param streets_snmp_cmd::REQUEST_TYPE The request type for which the error is being logged (GET/SET). * @param response The snmp_pdu struct */ - void log_error(const int& status, const request_type& request_type, snmp_pdu *response) const; + void log_error(const int& status, const streets_snmp_cmd::REQUEST_TYPE& request_type, snmp_pdu *response) const; /** @brief Destructor for client. Closes the snmp session**/ virtual ~snmp_client(); diff --git a/tsc_client_service/include/tsc_service.h b/tsc_client_service/include/tsc_service.h index 45fb8092d..4329532eb 100644 --- a/tsc_client_service/include/tsc_service.h +++ b/tsc_client_service/include/tsc_service.h @@ -104,7 +104,7 @@ namespace traffic_signal_controller_service { bool use_desired_phase_plan_update_ = false; // Queue to store snmp_cmd_structs which are objects used to run snmp HOLD and OMIT commands - std::queue tsc_set_command_queue_; + std::queue tsc_set_command_queue_; // Configurable sleep duration for control_tsc_state thread in milliseconds. This sleep is required to allow some time between checking queue for control commands int control_tsc_state_sleep_dur_ = 0; @@ -234,14 +234,14 @@ namespace traffic_signal_controller_service { /** * @brief Method to control phases on the Traffic Signal Controller by sending OMIT and HOLD commands constructed to - * follow the desired phase plan. Calls set_tsc_hold_and_omit + * follow the desired phase plan. Calls set_tsc_hold_and_omit_forceoff_call **/ void control_tsc_phases(); /** - * @brief Method to set HOLD and OMIT on the Traffic signal controller accorording to the desired phase plan. + * @brief Method to set HOLD and OMIT, CALL, FORCEOFF on the Traffic signal controller accorording to the desired phase plan/phase control schedule. **/ - void set_tsc_hold_and_omit(); + void set_tsc_hold_and_omit_forceoff_call(); /** * @brief Method to configure spdlog::logger for logging snmp control commands into daily rotating csv file. diff --git a/tsc_client_service/src/control_tsc_state.cpp b/tsc_client_service/src/control_tsc_state.cpp index 9c0035177..c495fe772 100644 --- a/tsc_client_service/src/control_tsc_state.cpp +++ b/tsc_client_service/src/control_tsc_state.cpp @@ -3,46 +3,48 @@ namespace traffic_signal_controller_service { - control_tsc_state::control_tsc_state(std::shared_ptr snmp_client, - std::shared_ptr _tsc_state) - : snmp_client_worker_(snmp_client), _tsc_state(_tsc_state) + control_tsc_state::control_tsc_state(std::shared_ptr snmp_client, + std::shared_ptr _tsc_state) + : snmp_client_worker_(snmp_client), _tsc_state(_tsc_state) { - } void control_tsc_state::update_tsc_control_queue(std::shared_ptr desired_phase_plan, - std::queue& tsc_command_queue) const + std::queue &tsc_command_queue) const { - if(desired_phase_plan->desired_phase_plan.empty()){ + if (desired_phase_plan->desired_phase_plan.empty()) + { SPDLOG_DEBUG("No events in desired phase plan"); return; } - if(desired_phase_plan->desired_phase_plan.size() == 1){ + if (desired_phase_plan->desired_phase_plan.size() == 1) + { SPDLOG_DEBUG("TSC service assumes first event is already set, no update to queue required"); return; } + auto signal_groups_2phase_map = _tsc_state->get_signal_group_map(); // Check if desired phase plan is valid // Check no repeated signal groups in adjacent events - for(int i = 0; i < desired_phase_plan->desired_phase_plan.size() - 1; ++i){ - + for (int i = 0; i < desired_phase_plan->desired_phase_plan.size() - 1; ++i) + { + auto event = desired_phase_plan->desired_phase_plan[0]; auto next_event = desired_phase_plan->desired_phase_plan[1]; - for(auto signal_group : event.signal_groups) + for (auto signal_group : event.signal_groups) { auto it = std::find(next_event.signal_groups.begin(), next_event.signal_groups.end(), signal_group); - if(it != next_event.signal_groups.end()) + if (it != next_event.signal_groups.end()) { SPDLOG_ERROR("TSC Service assumes adjacent events dont have the same signal_group. Given Desired phase plan fails this assumption"); throw control_tsc_state_exception("Repeating signal group found in adjacent events. Desired phase plan cannot be set"); } } } - - //Reset queue - tsc_command_queue = std::queue(); + // Reset queue + tsc_command_queue = std::queue(); // add Omit and Hold commands auto first_event = desired_phase_plan->desired_phase_plan[0]; @@ -50,129 +52,166 @@ namespace traffic_signal_controller_service auto current_time = streets_service::streets_clock_singleton::time_in_ms(); // Assuming the first event start doesn't need to be planned for, we execute omit and hold for the next event. Resetting Hold ends the first event - int64_t omit_execution_time = current_time + (first_event.end_time - current_time)/2; - tsc_command_queue.push(create_omit_command(second_event.signal_groups, omit_execution_time)); + int64_t omit_execution_time = current_time + (first_event.end_time - current_time) / 2; + // Omit all others signal groups other than the second_event event signal_groups + std::vector second_event_signal_groups_to_omit; + for (auto &[sg, phase] : signal_groups_2phase_map) + { + // If the signal group is not in the second_event signal groups, add the signal group to the list of omit signal groups + if (std::find(second_event.signal_groups.begin(), second_event.signal_groups.end(), sg) == second_event.signal_groups.end()) + { + second_event_signal_groups_to_omit.push_back(sg); + } + } + tsc_command_queue.push(create_snmp_command_by_signal_groups(second_event_signal_groups_to_omit, streets_snmp_cmd::PHASE_CONTROL_TYPE::OMIT_VEH_PHASES, omit_execution_time)); int64_t hold_execution_time = first_event.end_time; - tsc_command_queue.push(create_hold_command(second_event.signal_groups, hold_execution_time)); - + tsc_command_queue.push(create_snmp_command_by_signal_groups(second_event.signal_groups, streets_snmp_cmd::PHASE_CONTROL_TYPE::HOLD_VEH_PHASES, hold_execution_time)); int event_itr = 1; - - while(event_itr < desired_phase_plan->desired_phase_plan.size() - 1) + while (event_itr < desired_phase_plan->desired_phase_plan.size() - 1) { - auto current_event = desired_phase_plan->desired_phase_plan[event_itr]; + auto current_event = desired_phase_plan->desired_phase_plan[event_itr]; auto next_event = desired_phase_plan->desired_phase_plan[event_itr + 1]; - omit_execution_time = current_event.start_time + (current_event.end_time - current_event.start_time)/2; - tsc_command_queue.push(create_omit_command(next_event.signal_groups, omit_execution_time)); + omit_execution_time = current_event.start_time + (current_event.end_time - current_event.start_time) / 2; + // Omit all others signal groups other than the next_event event signal_groups + std::vector next_event_signal_groups_to_omit; + for (auto &[sg, phase] : signal_groups_2phase_map) + { + // If the signal group is not in the next_event signal groups, add the signal group to the list of omit signal groups + if (std::find(next_event.signal_groups.begin(), next_event.signal_groups.end(), sg) == next_event.signal_groups.end()) + { + next_event_signal_groups_to_omit.push_back(sg); + } + } + tsc_command_queue.push(create_snmp_command_by_signal_groups(next_event_signal_groups_to_omit, streets_snmp_cmd::PHASE_CONTROL_TYPE::OMIT_VEH_PHASES, omit_execution_time)); hold_execution_time = current_event.end_time; - tsc_command_queue.push(create_hold_command(next_event.signal_groups, hold_execution_time)); + tsc_command_queue.push(create_snmp_command_by_signal_groups(next_event.signal_groups, streets_snmp_cmd::PHASE_CONTROL_TYPE::HOLD_VEH_PHASES, hold_execution_time)); event_itr++; } // Reset Hold and Omit for last event auto last_event = desired_phase_plan->desired_phase_plan.back(); - omit_execution_time = last_event.start_time + (last_event.end_time - last_event.start_time)/2; - std::vector empty_group = {}; - tsc_command_queue.push(create_omit_command(empty_group, omit_execution_time, true)); + omit_execution_time = last_event.start_time + (last_event.end_time - last_event.start_time) / 2; + streets_snmp_cmd::streets_snmp_cmd_converter converter; + tsc_command_queue.push(converter.create_snmp_reset_command(streets_snmp_cmd::PHASE_CONTROL_TYPE::OMIT_VEH_PHASES, omit_execution_time)); hold_execution_time = last_event.end_time; - tsc_command_queue.push(create_hold_command(empty_group, hold_execution_time, true)); + tsc_command_queue.push(converter.create_snmp_reset_command(streets_snmp_cmd::PHASE_CONTROL_TYPE::HOLD_VEH_PHASES, hold_execution_time)); SPDLOG_DEBUG("Updated queue with front command {0}!", tsc_command_queue.front().get_cmd_info()); - } void control_tsc_state::update_tsc_control_queue(std::shared_ptr phase_control_schedule_ptr, - std::queue& tsc_command_queue) const + std::queue &tsc_command_queue) const { - if(!phase_control_schedule_ptr) + if (!phase_control_schedule_ptr) { - SPDLOG_DEBUG("Phase control schedule is not initialized."); + SPDLOG_ERROR("Phase control schedule is not initialized."); return; } - if(phase_control_schedule_ptr->is_clear_current_schedule) + + if (phase_control_schedule_ptr->is_clear_current_schedule) + { + //Clear all phase controls from the traffic signal controller + SPDLOG_DEBUG("Clear all phase controls from TSC!"); + run_clear_all_snmp_commands(); + + SPDLOG_INFO("Clear SNMP command queue!"); + // Clear all commands on the update command queue. + std::queue empty_queue; + std::swap(tsc_command_queue, empty_queue); + } + else { - SPDLOG_DEBUG("Clear SNMP command queue!"); - //ToDo: Send clear all scheduled jobs or clear all commands on the update command queue - }else{ std::stringstream ss; - ss << *phase_control_schedule_ptr; - SPDLOG_DEBUG("Update SNMP command queue with new phase control schedule commands: {0}", ss.str()); - //ToDo: Update command queue with the new phase control schedule commands + ss << *phase_control_schedule_ptr; + SPDLOG_DEBUG("Update SNMP command queue with new phase control schedule commands: {0}", ss.str()); + // Update command queue with the new phase control schedule commands. + streets_snmp_cmd::streets_snmp_cmd_converter converter; + auto snmp_cmd_queue = converter.create_snmp_cmds_by_phase_control_schedule(phase_control_schedule_ptr); + std::swap(tsc_command_queue, snmp_cmd_queue); } + SPDLOG_INFO("Updated Queue Size: {0}", tsc_command_queue.size()); } - snmp_cmd_struct control_tsc_state::create_omit_command(const std::vector& signal_groups, int64_t start_time, bool is_reset) const + streets_snmp_cmd::snmp_cmd_struct control_tsc_state::create_snmp_command_by_signal_groups(const std::vector &signal_groups, streets_snmp_cmd::PHASE_CONTROL_TYPE phase_control_type, int64_t start_time) const { - if(!is_reset) + streets_snmp_cmd::streets_snmp_cmd_converter converter; + std::vector phases; + for (auto signal_group : signal_groups) { - uint8_t omit_val = 255; //Initialize to 11111111 + int phase = _tsc_state->get_phase_number(signal_group); + phases.push_back(phase); + } + auto command = converter.create_snmp_command_by_phases(phases, phase_control_type, start_time); + return command; + } - for(auto signal_group : signal_groups) + bool control_tsc_state::run_snmp_cmd_set_request(streets_snmp_cmd::snmp_cmd_struct &snmp_cmd) const + { + if (!snmp_client_worker_) + { + throw control_tsc_state_exception("SNMP Client worker is not initialized."); + } + /*Type of request to be sent to the TSC, within this context it is always SET*/ + auto type = streets_snmp_cmd::REQUEST_TYPE::SET; + + if (snmp_cmd.control_type_ == streets_snmp_cmd::PHASE_CONTROL_TYPE::OMIT_VEH_PHASES) + { + if (!snmp_client_worker_->process_snmp_request(ntcip_oids::PHASE_OMIT_CONTROL, type, snmp_cmd.set_val_)) { - int phase = _tsc_state->get_phase_number(signal_group); - // Omit all phases except the ones in the given movement group - // For Omit only given phase bits are 0. Subtract 1 since phases range from 1-8. - omit_val &= ~(1 << (phase - 1)); - + return false; } - - snmp_cmd_struct command(snmp_client_worker_, start_time, snmp_cmd_struct::control_type::Omit, static_cast(omit_val)); - return command; } - else + else if (snmp_cmd.control_type_ == streets_snmp_cmd::PHASE_CONTROL_TYPE::HOLD_VEH_PHASES) { - snmp_cmd_struct command(snmp_client_worker_, start_time, snmp_cmd_struct::control_type::Omit, static_cast(0)); - return command; + if (!snmp_client_worker_->process_snmp_request(ntcip_oids::PHASE_HOLD_CONTROL, type, snmp_cmd.set_val_)) + { + return false; + } } - - } - - snmp_cmd_struct control_tsc_state::create_hold_command(const std::vector& signal_groups, int64_t start_time, bool is_reset) const - { - if(!is_reset) + else if (snmp_cmd.control_type_ == streets_snmp_cmd::PHASE_CONTROL_TYPE::OMIT_PED_PHASES) { - uint8_t hold_val = 0; //Initialize to 00000000 - - for(auto signal_group : signal_groups) + if (!snmp_client_worker_->process_snmp_request(ntcip_oids::PHASE_PEDESTRIAN_OMIT_CONTROL, type, snmp_cmd.set_val_)) { - int phase = _tsc_state->get_phase_number(signal_group); - // Hold phases in the given movement group - //For Hold only given phase bits are 1. Subtract 1 since phases range from 1-8. - hold_val |= (1 << ( phase - 1)); - + return false; } - - snmp_cmd_struct command(snmp_client_worker_, start_time, snmp_cmd_struct::control_type::Hold, static_cast(hold_val)); - return command; } - else + else if (snmp_cmd.control_type_ == streets_snmp_cmd::PHASE_CONTROL_TYPE::FORCEOFF_PHASES) { - snmp_cmd_struct command(snmp_client_worker_, start_time, snmp_cmd_struct::control_type::Hold, static_cast(0)); - return command; + if (!snmp_client_worker_->process_snmp_request(ntcip_oids::PHASE_FORCEOFF_CONTROL, type, snmp_cmd.set_val_)) + { + return false; + } } - } - - std::string snmp_cmd_struct::get_cmd_info() const{ - - std::string command_type; - if(control_type_ == control_type::Hold){ - command_type = "Hold"; + else if (snmp_cmd.control_type_ == streets_snmp_cmd::PHASE_CONTROL_TYPE::CALL_PED_PHASES) + { + if (!snmp_client_worker_->process_snmp_request(ntcip_oids::PHASE_PEDESTRIAN_CALL_CONTROL, type, snmp_cmd.set_val_)) + { + return false; + } } - else{ - command_type = "Omit"; + else if (snmp_cmd.control_type_ == streets_snmp_cmd::PHASE_CONTROL_TYPE::CALL_VEH_PHASES) + { + if (!snmp_client_worker_->process_snmp_request(ntcip_oids::PHASE_VEHICLE_CALL_CONTROL, type, snmp_cmd.set_val_)) + { + return false; + } } - std::string execution_start_time = std::to_string(start_time_); - // Convert value set to phases nums - std::string value_set = std::to_string(set_val_.val_int); - - return "control_cmd_type:" + command_type + ";execution_start_time:" + execution_start_time + ";value_set:" + value_set; - + return true; } - + void control_tsc_state::run_clear_all_snmp_commands() const + { + streets_snmp_cmd::streets_snmp_cmd_converter converter; + auto snmp_cmds = converter.create_clear_all_snmp_commands(0);//Set default execution time 0 because it is executed immediately at following line + for(auto& snmp_cmd: snmp_cmds) + { + run_snmp_cmd_set_request(snmp_cmd); + } + } } \ No newline at end of file diff --git a/tsc_client_service/src/monitor_desired_phase_plan.cpp b/tsc_client_service/src/monitor_desired_phase_plan.cpp index 04c929f4f..1ff5700f7 100644 --- a/tsc_client_service/src/monitor_desired_phase_plan.cpp +++ b/tsc_client_service/src/monitor_desired_phase_plan.cpp @@ -142,9 +142,9 @@ namespace traffic_signal_controller_service } } // Once start time is determine. We must determine next phase. - snmp_response_obj response; - response.type = snmp_response_obj::response_type::INTEGER; - _snmp_client->process_snmp_request(ntcip_oids::PHASE_STATUS_GROUP_PHASE_NEXT,traffic_signal_controller_service::request_type::GET, response ); + streets_snmp_cmd::snmp_response_obj response; + response.type = streets_snmp_cmd::RESPONSE_TYPE::INTEGER; + _snmp_client->process_snmp_request(ntcip_oids::PHASE_STATUS_GROUP_PHASE_NEXT, streets_snmp_cmd::REQUEST_TYPE::GET, response ); // Generate Desired Phase Plan with next green fixed. streets_desired_phase_plan::streets_desired_phase_plan one_fixed_green; diff --git a/tsc_client_service/src/monitor_tsc_state.cpp b/tsc_client_service/src/monitor_tsc_state.cpp index 6f989adde..1fc90e14e 100644 --- a/tsc_client_service/src/monitor_tsc_state.cpp +++ b/tsc_client_service/src/monitor_tsc_state.cpp @@ -273,12 +273,12 @@ namespace traffic_signal_controller_service std::unordered_map tsc_state::get_phases_associated_with_channel(const std::vector& signal_group_ids) const { std::unordered_map phases_to_signal_group; - request_type request_type = request_type::GET; + auto request_type= streets_snmp_cmd::REQUEST_TYPE::GET; for(int signal_group : signal_group_ids) { - snmp_response_obj phase_num; - phase_num.type = snmp_response_obj::response_type::INTEGER; + streets_snmp_cmd::snmp_response_obj phase_num; + phase_num.type = streets_snmp_cmd::RESPONSE_TYPE::INTEGER; // Control source returns the phase associated with the signal group. Channel and signal group id is synonymous according to the NTCIP documentation std::string control_source_parameter_oid = ntcip_oids::CHANNEL_CONTROL_SOURCE_PARAMETER + "." + std::to_string(signal_group); snmp_client_worker_->process_snmp_request(control_source_parameter_oid, request_type, phase_num); @@ -302,10 +302,10 @@ namespace traffic_signal_controller_service void tsc_state::get_channels(int max_channels, std::vector& vehicle_channels, std::vector& ped_channels) const{ // Loop through channel control types and add channels with vehicle phase to list - snmp_response_obj control_type; - control_type.type = snmp_response_obj::response_type::INTEGER; + streets_snmp_cmd::snmp_response_obj control_type; + control_type.type = streets_snmp_cmd::RESPONSE_TYPE::INTEGER; - request_type request_type = request_type::GET; + auto request_type = streets_snmp_cmd::REQUEST_TYPE::GET; for(int channel_num = 1; channel_num <= max_channels; ++channel_num) { std::string control_type_parameter_oid = ntcip_oids::CHANNEL_CONTROL_TYPE_PARAMETER + "." + std::to_string(channel_num); @@ -344,7 +344,7 @@ namespace traffic_signal_controller_service std::vector> active_ring_sequences; // Read sequence data for rings - request_type request_type = request_type::GET; + auto request_type= streets_snmp_cmd::REQUEST_TYPE::GET; for(int i = 1 ; i <= max_rings; ++i){ int ring_num = i; @@ -352,8 +352,8 @@ namespace traffic_signal_controller_service std::string phase_seq_oid= ntcip_oids::SEQUENCE_DATA + "." + std::to_string(sequence) + "." + std::to_string(ring_num); - snmp_response_obj seq_data; - seq_data.type = snmp_response_obj::response_type::STRING; + streets_snmp_cmd::snmp_response_obj seq_data; + seq_data.type = streets_snmp_cmd::RESPONSE_TYPE::STRING; snmp_client_worker_->process_snmp_request(phase_seq_oid, request_type, seq_data); //extract phase numbers from strings @@ -378,9 +378,9 @@ namespace traffic_signal_controller_service } int tsc_state::get_max_rings() const { - request_type request_type = request_type::GET; - snmp_response_obj max_rings_in_tsc; - max_rings_in_tsc.type = snmp_response_obj::response_type::INTEGER; + auto request_type= streets_snmp_cmd::REQUEST_TYPE::GET; + streets_snmp_cmd::snmp_response_obj max_rings_in_tsc; + max_rings_in_tsc.type = streets_snmp_cmd::RESPONSE_TYPE::INTEGER; if(!snmp_client_worker_->process_snmp_request(ntcip_oids::MAX_RINGS, request_type, max_rings_in_tsc)) { @@ -391,9 +391,9 @@ namespace traffic_signal_controller_service } int tsc_state::get_max_channels() const { - request_type request_type = request_type::GET; - snmp_response_obj max_channels_in_tsc; - max_channels_in_tsc.type = snmp_response_obj::response_type::INTEGER; + auto request_type= streets_snmp_cmd::REQUEST_TYPE::GET; + streets_snmp_cmd::snmp_response_obj max_channels_in_tsc; + max_channels_in_tsc.type = streets_snmp_cmd::RESPONSE_TYPE::INTEGER; if(!snmp_client_worker_->process_snmp_request(ntcip_oids::MAX_CHANNELS, request_type, max_channels_in_tsc)) { @@ -433,11 +433,11 @@ namespace traffic_signal_controller_service int tsc_state::get_min_green(int phase_num) const { - request_type request_type = request_type::GET; + auto request_type = streets_snmp_cmd::REQUEST_TYPE::GET; std::string min_green_parameter_oid = ntcip_oids::MINIMUM_GREEN + "." + std::to_string(phase_num); - snmp_response_obj min_green; - min_green.type = snmp_response_obj::response_type::INTEGER; + streets_snmp_cmd::snmp_response_obj min_green; + min_green.type = streets_snmp_cmd::RESPONSE_TYPE::INTEGER; snmp_client_worker_->process_snmp_request(min_green_parameter_oid, request_type, min_green); @@ -446,11 +446,11 @@ namespace traffic_signal_controller_service int tsc_state::get_max_green(int phase_num) const { - request_type request_type = request_type::GET; + auto request_type= streets_snmp_cmd::REQUEST_TYPE::GET; std::string max_green_parameter_oid = ntcip_oids::MAXIMUM_GREEN + "." + std::to_string(phase_num); - snmp_response_obj max_green; - max_green.type = snmp_response_obj::response_type::INTEGER; + streets_snmp_cmd::snmp_response_obj max_green; + max_green.type = streets_snmp_cmd::RESPONSE_TYPE::INTEGER; snmp_client_worker_->process_snmp_request(max_green_parameter_oid, request_type, max_green); @@ -459,11 +459,11 @@ namespace traffic_signal_controller_service int tsc_state::get_yellow_duration(int phase_num) const { - request_type request_type = request_type::GET; + auto request_type = streets_snmp_cmd::REQUEST_TYPE::GET; std::string yellow_duration_oid = ntcip_oids::YELLOW_CHANGE_PARAMETER + "." + std::to_string(phase_num); - snmp_response_obj yellow_duration; - yellow_duration.type = snmp_response_obj::response_type::INTEGER; + streets_snmp_cmd::snmp_response_obj yellow_duration; + yellow_duration.type = streets_snmp_cmd::RESPONSE_TYPE::INTEGER; snmp_client_worker_ ->process_snmp_request(yellow_duration_oid, request_type, yellow_duration); @@ -472,11 +472,11 @@ namespace traffic_signal_controller_service int tsc_state::get_red_clearance(int phase_num) const { - request_type get_request = request_type::GET; + auto get_request = streets_snmp_cmd::REQUEST_TYPE::GET; std::string red_clearance_oid = ntcip_oids::RED_CLEAR_PARAMETER + "." + std::to_string(phase_num); - snmp_response_obj red_clearance; - red_clearance.type = snmp_response_obj::response_type::INTEGER; + streets_snmp_cmd::snmp_response_obj red_clearance; + red_clearance.type = streets_snmp_cmd::RESPONSE_TYPE::INTEGER; snmp_client_worker_->process_snmp_request(red_clearance_oid, get_request, red_clearance); @@ -518,9 +518,9 @@ namespace traffic_signal_controller_service std::vector concurrent_signal_groups; std::string concurrent_phases_oid = ntcip_oids::PHASE_CONCURRENCY + "." + std::to_string(phase_num); - snmp_response_obj concurrent_phase_data; - request_type request_type = request_type::GET; - concurrent_phase_data.type = snmp_response_obj::response_type::STRING; + streets_snmp_cmd::snmp_response_obj concurrent_phase_data; + auto request_type = streets_snmp_cmd::REQUEST_TYPE::GET; + concurrent_phase_data.type = streets_snmp_cmd::RESPONSE_TYPE::STRING; snmp_client_worker_->process_snmp_request(concurrent_phases_oid, request_type, concurrent_phase_data); //extract phase numbers from strings @@ -548,6 +548,11 @@ namespace traffic_signal_controller_service return vehicle_phase_2signalgroup_map_; } + const std::unordered_map & tsc_state::get_signal_group_map() + { + return signal_group_2vehiclephase_map_; + } + std::unordered_map& tsc_state::get_signal_group_state_map() { return signal_group_2tsc_state_map_; diff --git a/tsc_client_service/src/snmp_client.cpp b/tsc_client_service/src/snmp_client.cpp index 9a009ce69..daf5f856c 100644 --- a/tsc_client_service/src/snmp_client.cpp +++ b/tsc_client_service/src/snmp_client.cpp @@ -53,18 +53,18 @@ namespace traffic_signal_controller_service } - bool snmp_client::process_snmp_request(const std::string& input_oid, const request_type& request_type, snmp_response_obj& val){ + bool snmp_client::process_snmp_request(const std::string& input_oid, const streets_snmp_cmd::REQUEST_TYPE& request_type, streets_snmp_cmd::snmp_response_obj& val){ /*Structure to hold response from the remote host*/ snmp_pdu *response; // Create pdu for the data - if (request_type == request_type::GET) + if (request_type == streets_snmp_cmd::REQUEST_TYPE::GET) { SPDLOG_DEBUG("Attemping to GET value for: {0}", input_oid); pdu = snmp_pdu_create(SNMP_MSG_GET); } - else if (request_type == request_type::SET) + else if (request_type == streets_snmp_cmd::REQUEST_TYPE::SET) { SPDLOG_DEBUG("Attemping to SET value for {0}", input_oid, " to {1}", val.val_int); pdu = snmp_pdu_create(SNMP_MSG_SET); @@ -85,17 +85,17 @@ namespace traffic_signal_controller_service } else{ - if(request_type == request_type::GET) + if(request_type == streets_snmp_cmd::REQUEST_TYPE::GET) { // Add OID to pdu for get request snmp_add_null_var(pdu, OID, OID_len); } - else if(request_type == request_type::SET) + else if(request_type == streets_snmp_cmd::REQUEST_TYPE::SET) { - if(val.type == snmp_response_obj::response_type::INTEGER){ + if(val.type == streets_snmp_cmd::RESPONSE_TYPE::INTEGER){ snmp_add_var(pdu, OID, OID_len, 'i', (std::to_string(val.val_int)).c_str()); } - else if(val.type == snmp_response_obj::response_type::STRING){ + else if(val.type == streets_snmp_cmd::RESPONSE_TYPE::STRING){ SPDLOG_ERROR("Setting string value is currently not supported"); return false; } @@ -112,7 +112,7 @@ namespace traffic_signal_controller_service SPDLOG_DEBUG("STAT_SUCCESS, received a response"); - if(request_type == request_type::GET){ + if(request_type == streets_snmp_cmd::REQUEST_TYPE::GET){ for(auto vars = response->variables; vars; vars = vars->next_variable){ // Get value of variable depending on ASN.1 type // Variable could be a integer, string, bitstring, ojbid, counter : defined here https://github.com/net-snmp/net-snmp/blob/master/include/net-snmp/types.h @@ -148,13 +148,13 @@ namespace traffic_signal_controller_service } } } - else if(request_type == request_type::SET){ + else if(request_type == streets_snmp_cmd::REQUEST_TYPE::SET){ - if(val.type == snmp_response_obj::response_type::INTEGER){ + if(val.type == streets_snmp_cmd::RESPONSE_TYPE::INTEGER){ SPDLOG_DEBUG("Success in SET for OID: {0} Value: {1}", input_oid ,val.val_int); } - else if(val.type == snmp_response_obj::response_type::STRING){ + else if(val.type == streets_snmp_cmd::RESPONSE_TYPE::STRING){ SPDLOG_DEBUG("Success in SET for OID: {0} Value:", input_oid); for(auto data : val.val_string){ SPDLOG_DEBUG("{0}", data); @@ -178,7 +178,7 @@ namespace traffic_signal_controller_service } - void snmp_client::log_error(const int& status, const request_type& request_type, snmp_pdu *response) const + void snmp_client::log_error(const int& status, const streets_snmp_cmd::REQUEST_TYPE& request_type, snmp_pdu *response) const { if (status == STAT_SUCCESS) @@ -191,10 +191,10 @@ namespace traffic_signal_controller_service SPDLOG_ERROR("Timeout, no response from server"); } else{ - if(request_type == request_type::GET){ + if(request_type == streets_snmp_cmd::REQUEST_TYPE::GET){ SPDLOG_ERROR("Unknown SNMP Error for {0}", "GET"); } - else if(request_type == request_type::SET){ + else if(request_type == streets_snmp_cmd::REQUEST_TYPE::SET){ SPDLOG_ERROR("Unknown SNMP Error for {0}", "SET"); } } diff --git a/tsc_client_service/src/tsc_service.cpp b/tsc_client_service/src/tsc_service.cpp index 8b6f2d425..cd63d9d0c 100644 --- a/tsc_client_service/src/tsc_service.cpp +++ b/tsc_client_service/src/tsc_service.cpp @@ -3,6 +3,7 @@ namespace traffic_signal_controller_service { std::mutex dpp_mtx; + std::mutex snmp_cmd_queue_mtx; using namespace streets_service; bool tsc_service::initialize() { if (!streets_service::initialize()) { @@ -13,7 +14,9 @@ namespace traffic_signal_controller_service { // Temporary fix for bug in CarmaClock::wait_for_initialization(). No mechanism to support notifying multiple threads // of initialization. This fix avoids any threads waiting on initialization. // TODO: Remove initialization and fix issue in carma-time-lib (CarmaClock class) - streets_clock_singleton::update(0); + if ( is_simulation_mode() ) { + streets_clock_singleton::update(0); + } // Intialize spat kafka producer std::string spat_topic_name = streets_configuration::get_string_config("spat_producer_topic"); @@ -145,10 +148,10 @@ namespace traffic_signal_controller_service { } bool tsc_service::enable_spat() const{ // Enable SPaT - snmp_response_obj enable_spat; - enable_spat.type = snmp_response_obj::response_type::INTEGER; + streets_snmp_cmd::snmp_response_obj enable_spat; + enable_spat.type = streets_snmp_cmd::RESPONSE_TYPE::INTEGER; enable_spat.val_int = 2; - if (!snmp_client_ptr->process_snmp_request(ntcip_oids::ENABLE_SPAT_OID, request_type::SET, enable_spat)){ + if (!snmp_client_ptr->process_snmp_request(ntcip_oids::ENABLE_SPAT_OID, streets_snmp_cmd::REQUEST_TYPE::SET, enable_spat)){ SPDLOG_ERROR("Failed to enable SPaT broadcasting on Traffic Signal Controller!"); return false; } @@ -260,6 +263,7 @@ namespace traffic_signal_controller_service { // update command queue if(monitor_dpp_ptr->get_desired_phase_plan_ptr()){ // Send desired phase plan to control_tsc_state + std::scoped_lock snmp_cmd_lck(snmp_cmd_queue_mtx); control_tsc_state_ptr_->update_tsc_control_queue(monitor_dpp_ptr->get_desired_phase_plan_ptr(), tsc_set_command_queue_); } @@ -279,6 +283,8 @@ namespace traffic_signal_controller_service { try { //Update phase control schedule with the latest incoming schedule phase_control_schedule_ptr->fromJson(payload); + //Update command queue + std::scoped_lock snmp_cmd_lck(snmp_cmd_queue_mtx); control_tsc_state_ptr_->update_tsc_control_queue(phase_control_schedule_ptr, tsc_set_command_queue_); } catch(streets_phase_control_schedule::streets_phase_control_schedule_exception &ex){ SPDLOG_DEBUG("Failed to consume phase control schedule commands: {0}", ex.what()); @@ -289,55 +295,93 @@ namespace traffic_signal_controller_service { void tsc_service::control_tsc_phases() { - try{ - SPDLOG_INFO("Starting TSC Control Phases"); + SPDLOG_INFO("Starting TSC Control Phases"); + try + { while(true) { SPDLOG_INFO("Iterate TSC Control Phases for time {0}", streets_clock_singleton::time_in_ms()); - set_tsc_hold_and_omit(); + set_tsc_hold_and_omit_forceoff_call(); streets_clock_singleton::sleep_for(control_tsc_state_sleep_dur_); } } catch(const control_tsc_state_exception &e){ SPDLOG_ERROR("Encountered exception : \n {0}", e.what()); - SPDLOG_ERROR("Removing front command from queue : {0}", tsc_set_command_queue_.front().get_cmd_info()); - tsc_set_command_queue_.pop(); - + if(!tsc_set_command_queue_.empty()) + { + SPDLOG_ERROR("Removing front command from queue : {0}", tsc_set_command_queue_.front().get_cmd_info()); + std::scoped_lock snmp_cmd_lck(snmp_cmd_queue_mtx); + tsc_set_command_queue_.pop(); + } } - } - void tsc_service::set_tsc_hold_and_omit() + void tsc_service::set_tsc_hold_and_omit_forceoff_call() { - - while(!tsc_set_command_queue_.empty()) + if(control_tsc_state_ptr_ && !tsc_set_command_queue_.empty()) { - SPDLOG_DEBUG("Checking if front command {0} is expired", tsc_set_command_queue_.front().get_cmd_info()); + //Clear all phase controls from the traffic signal controller + SPDLOG_DEBUG("Clear all phase controls from TSC!"); + control_tsc_state_ptr_->run_clear_all_snmp_commands(); + } + while(!tsc_set_command_queue_.empty()) + { + auto current_command = tsc_set_command_queue_.front(); + SPDLOG_DEBUG("Checking if front command {0} is expired", current_command.get_cmd_info()); //Check if event is expired - long duration = tsc_set_command_queue_.front().start_time_ - streets_clock_singleton::time_in_ms(); + long duration = current_command.start_time_ - streets_clock_singleton::time_in_ms(); if(duration < 0){ throw control_tsc_state_exception("SNMP set command is expired! Start time was " - + std::to_string(tsc_set_command_queue_.front().start_time_) + " and current time is " + + std::to_string(current_command.start_time_) + " and current time is " + std::to_string(streets_clock_singleton::time_in_ms() ) + "."); - } + } SPDLOG_DEBUG("Sleeping for {0}ms.", duration); streets_clock_singleton::sleep_for(duration); + + //After sleep duration, check the snmp queue again. If empty, stop the current command execution + if(tsc_set_command_queue_.empty()) + { + SPDLOG_DEBUG("SNMP command queue is empty, stop the current command execution: {0}", current_command.get_cmd_info()); + break; + } + //Check if queue commands has been updated + else if (tsc_set_command_queue_.front().get_cmd_info() != current_command.get_cmd_info()) + { + SPDLOG_DEBUG("SNMP command queue is updated with new schedule, stop the current command execution: {0}", current_command.get_cmd_info()); + break; + } - if(!(tsc_set_command_queue_.front()).run()) + if(!control_tsc_state_ptr_) { - throw control_tsc_state_exception("Could not set state for movement group in desired phase plan"); + throw control_tsc_state_exception("Control TSC state is not initialized."); } - // Log command info sent - SPDLOG_INFO(tsc_set_command_queue_.front().get_cmd_info()); - - if ( enable_snmp_cmd_logging_ ){ - if(auto logger = spdlog::get("snmp_cmd_logger"); logger != nullptr ){ - logger->info( tsc_set_command_queue_.front().get_cmd_info()); + //Checking all the commands from the queue. If there are any commands executed at the same time as the current_command, and execute these commands now. + while (!tsc_set_command_queue_.empty()) + { + auto command_to_execute = tsc_set_command_queue_.front(); + if(command_to_execute.start_time_ == current_command.start_time_) + { + if(control_tsc_state_ptr_ && !control_tsc_state_ptr_->run_snmp_cmd_set_request(command_to_execute)) + { + throw control_tsc_state_exception("Could not set state for movement group in desired phase plan/phase control schedule."); + } + // Log command info sent + SPDLOG_INFO(command_to_execute.get_cmd_info()); + if ( enable_snmp_cmd_logging_ ){ + if(auto logger = spdlog::get("snmp_cmd_logger"); logger != nullptr ){ + logger->info( command_to_execute.get_cmd_info()); + } + } + //SNMP command Queue has no update nor has been cleared, pop the existing front command. + std::scoped_lock snmp_cmd_lck(snmp_cmd_queue_mtx); + tsc_set_command_queue_.pop(); } + else + { + break; + } } - - tsc_set_command_queue_.pop(); } } diff --git a/tsc_client_service/test/test_control_tsc_state.cpp b/tsc_client_service/test/test_control_tsc_state.cpp index c74223486..c187e2ca9 100644 --- a/tsc_client_service/test/test_control_tsc_state.cpp +++ b/tsc_client_service/test/test_control_tsc_state.cpp @@ -26,25 +26,25 @@ namespace traffic_signal_controller_service int phase_num = 0; const std::string&input_oid = ""; - request_type request_type = request_type::GET; + auto request_type= streets_snmp_cmd::REQUEST_TYPE::GET; // Test get max channels - snmp_response_obj max_channels_in_tsc; + streets_snmp_cmd::snmp_response_obj max_channels_in_tsc; max_channels_in_tsc.val_int =8; - max_channels_in_tsc.type = snmp_response_obj::response_type::INTEGER; + max_channels_in_tsc.type = streets_snmp_cmd::RESPONSE_TYPE::INTEGER; EXPECT_CALL( *mock_client, process_snmp_request(ntcip_oids::MAX_CHANNELS, request_type , _) ).Times(1).WillRepeatedly(testing::DoAll( SetArgReferee<2>(max_channels_in_tsc), Return(true))); - snmp_response_obj max_rings_in_tsc; + streets_snmp_cmd::snmp_response_obj max_rings_in_tsc; max_rings_in_tsc.val_int = 4; - max_rings_in_tsc.type = snmp_response_obj::response_type::INTEGER; + max_rings_in_tsc.type = streets_snmp_cmd::RESPONSE_TYPE::INTEGER; EXPECT_CALL( *mock_client, process_snmp_request(ntcip_oids::MAX_RINGS, request_type , _) ).Times(1).WillRepeatedly(testing::DoAll( SetArgReferee<2>(max_rings_in_tsc), Return(true))); // Define Sequence Data - snmp_response_obj seq_data; + streets_snmp_cmd::snmp_response_obj seq_data; seq_data.val_string = {char(1),char(2), char(3),char(4)}; std::string seq_data_ring1_oid = ntcip_oids::SEQUENCE_DATA + "." + "1" + "." + std::to_string(1); @@ -73,7 +73,7 @@ namespace traffic_signal_controller_service for(int i = 1; i <= max_channels_in_tsc.val_int; ++i){ // Define Control Type - snmp_response_obj channel_control_resp; + streets_snmp_cmd::snmp_response_obj channel_control_resp; channel_control_resp.val_int = 2; std::string channel_control_oid = ntcip_oids::CHANNEL_CONTROL_TYPE_PARAMETER + "." + std::to_string(i); EXPECT_CALL(*mock_client, process_snmp_request(channel_control_oid, request_type , _) ).Times(1).WillRepeatedly(testing::DoAll( @@ -81,7 +81,7 @@ namespace traffic_signal_controller_service testing::Return(true))); // Define Control Source - snmp_response_obj control_source_resp; + streets_snmp_cmd::snmp_response_obj control_source_resp; control_source_resp.val_int = i; std::string control_source_oid = ntcip_oids::CHANNEL_CONTROL_SOURCE_PARAMETER + "." + std::to_string(i); EXPECT_CALL(*mock_client, process_snmp_request(control_source_oid, request_type , _) ).Times(1).WillRepeatedly(testing::DoAll( @@ -92,7 +92,7 @@ namespace traffic_signal_controller_service // Define get min green std::string min_green_oid = ntcip_oids::MINIMUM_GREEN + "." + std::to_string(i); - snmp_response_obj min_green; + streets_snmp_cmd::snmp_response_obj min_green; min_green.val_int = 20; EXPECT_CALL(*mock_client, process_snmp_request(min_green_oid , request_type , _) ).Times(1).WillRepeatedly(testing::DoAll( SetArgReferee<2>(min_green), @@ -101,7 +101,7 @@ namespace traffic_signal_controller_service // Define get max green std::string max_green_oid = ntcip_oids::MAXIMUM_GREEN + "." + std::to_string(i); - snmp_response_obj max_green; + streets_snmp_cmd::snmp_response_obj max_green; max_green.val_int = 30; EXPECT_CALL(*mock_client, process_snmp_request(max_green_oid , request_type , _) ).Times(1).WillRepeatedly(testing::DoAll( SetArgReferee<2>(max_green), @@ -110,7 +110,7 @@ namespace traffic_signal_controller_service // Define get yellow Duration std::string yellow_oid = ntcip_oids::YELLOW_CHANGE_PARAMETER + "." + std::to_string(i); - snmp_response_obj yellow_duration; + streets_snmp_cmd::snmp_response_obj yellow_duration; yellow_duration.val_int = 40; EXPECT_CALL(*mock_client, process_snmp_request(yellow_oid , request_type , _) ).Times(1).WillRepeatedly(testing::DoAll( SetArgReferee<2>(yellow_duration), @@ -120,7 +120,7 @@ namespace traffic_signal_controller_service // Define red clearance std::string red_clearance_oid = ntcip_oids::RED_CLEAR_PARAMETER + "." + std::to_string(i); - snmp_response_obj red_clearance_duration; + streets_snmp_cmd::snmp_response_obj red_clearance_duration; red_clearance_duration.val_int = 10; EXPECT_CALL(*mock_client, process_snmp_request(red_clearance_oid , request_type , _) ).Times(1).WillRepeatedly(testing::DoAll( SetArgReferee<2>(red_clearance_duration), @@ -128,7 +128,7 @@ namespace traffic_signal_controller_service //Define get concurrent phases std::string concurrent_phase_oid = ntcip_oids::PHASE_CONCURRENCY + "." + std::to_string(i); - snmp_response_obj concurrent_phase_resp; + streets_snmp_cmd::snmp_response_obj concurrent_phase_resp; if ( i == 1 || i == 2) { concurrent_phase_resp.val_string = {char(5), char(6)}; } @@ -150,11 +150,11 @@ namespace traffic_signal_controller_service } // Define Control Type - snmp_response_obj hold_control; + streets_snmp_cmd::snmp_response_obj hold_control; hold_control.val_int = 255; - hold_control.type = snmp_response_obj::response_type::INTEGER; + hold_control.type = streets_snmp_cmd::RESPONSE_TYPE::INTEGER; - EXPECT_CALL(*mock_client, process_snmp_request(_, request_type::SET , _) ) + EXPECT_CALL(*mock_client, process_snmp_request(_, streets_snmp_cmd::REQUEST_TYPE::SET , _) ) .WillRepeatedly(testing::DoAll(testing::Return(true))); @@ -190,19 +190,32 @@ namespace traffic_signal_controller_service // Test update queue - std::queue control_commands_queue; + std::queue control_commands_queue; EXPECT_NO_THROW(worker.update_tsc_control_queue(desired_phase_plan_ptr,control_commands_queue)); EXPECT_NO_THROW(worker.update_tsc_control_queue(desired_phase_plan_ptr_2,control_commands_queue)); desired_phase_plan_ptr->desired_phase_plan.back().signal_groups = {1,6}; EXPECT_THROW(worker.update_tsc_control_queue(desired_phase_plan_ptr,control_commands_queue), control_tsc_state_exception); - // Test snmp_cmd_struct - snmp_cmd_struct test_control_obj(mock_client, event1.start_time,snmp_cmd_struct::control_type::Hold, 0); - EXPECT_TRUE(test_control_obj.run()); + // Test streets_snmp_cmd::snmp_cmd_struct + streets_snmp_cmd::snmp_cmd_struct test_control_obj(event1.start_time,streets_snmp_cmd::PHASE_CONTROL_TYPE::HOLD_VEH_PHASES, 0); + EXPECT_TRUE(worker.run_snmp_cmd_set_request(test_control_obj)); - snmp_cmd_struct test_control_obj_2(mock_client, event1.start_time,snmp_cmd_struct::control_type::Omit, 0); - EXPECT_TRUE(test_control_obj_2.run()); + streets_snmp_cmd::snmp_cmd_struct test_control_obj_2( event1.start_time,streets_snmp_cmd::PHASE_CONTROL_TYPE::OMIT_VEH_PHASES, 0); + EXPECT_TRUE(worker.run_snmp_cmd_set_request(test_control_obj_2)); + + streets_snmp_cmd::snmp_cmd_struct test_control_obj_3( event1.start_time,streets_snmp_cmd::PHASE_CONTROL_TYPE::OMIT_PED_PHASES, 0); + EXPECT_TRUE(worker.run_snmp_cmd_set_request(test_control_obj_3)); + + streets_snmp_cmd::snmp_cmd_struct test_control_obj_4( event1.start_time,streets_snmp_cmd::PHASE_CONTROL_TYPE::FORCEOFF_PHASES, 0); + EXPECT_TRUE(worker.run_snmp_cmd_set_request(test_control_obj_4)); + + streets_snmp_cmd::snmp_cmd_struct test_control_obj_5( event1.start_time,streets_snmp_cmd::PHASE_CONTROL_TYPE::CALL_PED_PHASES, 0); + EXPECT_TRUE(worker.run_snmp_cmd_set_request(test_control_obj_5)); + + streets_snmp_cmd::snmp_cmd_struct test_control_obj_6( event1.start_time,streets_snmp_cmd::PHASE_CONTROL_TYPE::CALL_VEH_PHASES, 0); + EXPECT_TRUE(worker.run_snmp_cmd_set_request(test_control_obj_6)); + EXPECT_NO_THROW(worker.run_clear_all_snmp_commands()); // Test empty desired phase plan streets_desired_phase_plan::streets_desired_phase_plan desired_phase_plan_2; @@ -216,23 +229,28 @@ namespace traffic_signal_controller_service } - TEST(traffic_signal_controller_service, update_tsc_control_queue) + TEST(traffic_signal_controller_service, update_tsc_control_queue_by_phase_control_schedule) { // Define Worker auto mock_client = std::make_shared(); auto _tsc_state = std::make_shared(mock_client); _tsc_state->initialize(); traffic_signal_controller_service::control_tsc_state worker(mock_client, _tsc_state); + + //Update queue with new schedule that has commands auto pcs_ptr = std::make_shared(); std::string input_schedule_str = "{\"MsgType\":\"Schedule\",\"Schedule\":[{\"commandEndTime\":0,\"commandPhase\":2,\"commandStartTime\":0,\"commandType\":\"hold\"},{\"commandEndTime\":24,\"commandPhase\":4,\"commandStartTime\":5,\"commandType\":\"hold\"},{\"commandEndTime\":44,\"commandPhase\":2,\"commandStartTime\":29,\"commandType\":\"hold\"},{\"commandEndTime\":64,\"commandPhase\":4,\"commandStartTime\":49,\"commandType\":\"hold\"},{\"commandEndTime\":12,\"commandPhase\":2,\"commandStartTime\":11,\"commandType\":\"forceoff\"},{\"commandEndTime\":69,\"commandPhase\":4,\"commandStartTime\":68,\"commandType\":\"forceoff\"},{\"commandEndTime\":109,\"commandPhase\":2,\"commandStartTime\":108,\"commandType\":\"forceoff\"},{\"commandEndTime\":129,\"commandPhase\":4,\"commandStartTime\":128,\"commandType\":\"forceoff\"},{\"commandEndTime\":23,\"commandPhase\":4,\"commandStartTime\":0,\"commandType\":\"call_veh\"},{\"commandEndTime\":133,\"commandPhase\":1,\"commandStartTime\":0,\"commandType\":\"omit_veh\"},{\"commandEndTime\":133,\"commandPhase\":3,\"commandStartTime\":0,\"commandType\":\"omit_veh\"},{\"commandEndTime\":0,\"commandPhase\":6,\"commandStartTime\":0,\"commandType\":\"hold\"},{\"commandEndTime\":24,\"commandPhase\":7,\"commandStartTime\":5,\"commandType\":\"hold\"},{\"commandEndTime\":44,\"commandPhase\":6,\"commandStartTime\":29,\"commandType\":\"hold\"},{\"commandEndTime\":64,\"commandPhase\":7,\"commandStartTime\":49,\"commandType\":\"hold\"},{\"commandEndTime\":12,\"commandPhase\":6,\"commandStartTime\":11,\"commandType\":\"forceoff\"},{\"commandEndTime\":69,\"commandPhase\":7,\"commandStartTime\":68,\"commandType\":\"forceoff\"},{\"commandEndTime\":109,\"commandPhase\":6,\"commandStartTime\":108,\"commandType\":\"forceoff\"},{\"commandEndTime\":129,\"commandPhase\":7,\"commandStartTime\":128,\"commandType\":\"forceoff\"},{\"commandEndTime\":23,\"commandPhase\":7,\"commandStartTime\":0,\"commandType\":\"call_veh\"},{\"commandEndTime\":133,\"commandPhase\":5,\"commandStartTime\":0,\"commandType\":\"omit_veh\"},{\"commandEndTime\":133,\"commandPhase\":8,\"commandStartTime\":0,\"commandType\":\"omit_veh\"}]}"; pcs_ptr->fromJson(input_schedule_str); - // Test update queue - std::queue control_commands_queue; + + // Test update queue with clear schedule + std::queue control_commands_queue; worker.update_tsc_control_queue(pcs_ptr, control_commands_queue); + ASSERT_EQ(19, control_commands_queue.size()); input_schedule_str = "{\"MsgType\":\"Schedule\",\"Schedule\":\"Clear\"}"; pcs_ptr->fromJson(input_schedule_str); worker.update_tsc_control_queue(pcs_ptr, control_commands_queue); std::shared_ptr pcs_ptr_null; worker.update_tsc_control_queue(pcs_ptr_null, control_commands_queue); + ASSERT_EQ(0, control_commands_queue.size()); } } \ No newline at end of file diff --git a/tsc_client_service/test/test_monitor_desired_phase_plan.cpp b/tsc_client_service/test/test_monitor_desired_phase_plan.cpp index e7d924753..ac8444218 100644 --- a/tsc_client_service/test/test_monitor_desired_phase_plan.cpp +++ b/tsc_client_service/test/test_monitor_desired_phase_plan.cpp @@ -241,24 +241,24 @@ namespace traffic_signal_controller_service void mock_tsc_ntcip() { // gmock SNMP response --------------------------------------------------------------------------------------------------------------------- // Test get max channels - snmp_response_obj max_channels_in_tsc; + streets_snmp_cmd::snmp_response_obj max_channels_in_tsc; max_channels_in_tsc.val_int = 16; - max_channels_in_tsc.type = snmp_response_obj::response_type::INTEGER; + max_channels_in_tsc.type = streets_snmp_cmd::RESPONSE_TYPE::INTEGER; - request_type request_type = request_type::GET; + auto request_type= streets_snmp_cmd::REQUEST_TYPE::GET; EXPECT_CALL( *mock_snmp, process_snmp_request(ntcip_oids::MAX_CHANNELS, request_type , _) ).Times(1).WillRepeatedly(testing::DoAll( SetArgReferee<2>(max_channels_in_tsc), Return(true))); - snmp_response_obj max_rings_in_tsc; + streets_snmp_cmd::snmp_response_obj max_rings_in_tsc; max_rings_in_tsc.val_int = 4; - max_rings_in_tsc.type = snmp_response_obj::response_type::INTEGER; + max_rings_in_tsc.type = streets_snmp_cmd::RESPONSE_TYPE::INTEGER; EXPECT_CALL( *mock_snmp, process_snmp_request(ntcip_oids::MAX_RINGS, request_type , _) ).Times(1).WillRepeatedly(testing::DoAll( SetArgReferee<2>(max_rings_in_tsc), Return(true))); // Define Sequence Data - snmp_response_obj seq_data; + streets_snmp_cmd::snmp_response_obj seq_data; seq_data.val_string = {char(1),char(2), char(3), char(4)}; std::string seq_data_ring1_oid = ntcip_oids::SEQUENCE_DATA + "." + "1" + "." + std::to_string(1); @@ -293,7 +293,7 @@ namespace traffic_signal_controller_service // switch(i) { case 1: { - snmp_response_obj channel_control_resp; + streets_snmp_cmd::snmp_response_obj channel_control_resp; channel_control_resp.val_int = 2; std::string channel_control_oid = ntcip_oids::CHANNEL_CONTROL_TYPE_PARAMETER + "." + std::to_string(i); EXPECT_CALL(*mock_snmp, process_snmp_request(channel_control_oid, request_type , _) ).Times(1).WillRepeatedly(testing::DoAll( @@ -301,7 +301,7 @@ namespace traffic_signal_controller_service Return(true))); // Define Control Source - snmp_response_obj control_source_resp; + streets_snmp_cmd::snmp_response_obj control_source_resp; control_source_resp.val_int = 1; std::string control_source_oid = ntcip_oids::CHANNEL_CONTROL_SOURCE_PARAMETER + "." + std::to_string(i); EXPECT_CALL(*mock_snmp, process_snmp_request(control_source_oid, request_type , _) ).Times(1).WillRepeatedly(testing::DoAll( @@ -310,7 +310,7 @@ namespace traffic_signal_controller_service break; } case 2: { - snmp_response_obj channel_control_resp; + streets_snmp_cmd::snmp_response_obj channel_control_resp; channel_control_resp.val_int = 2; std::string channel_control_oid = ntcip_oids::CHANNEL_CONTROL_TYPE_PARAMETER + "." + std::to_string(i); EXPECT_CALL(*mock_snmp, process_snmp_request(channel_control_oid, request_type , _) ).Times(1).WillRepeatedly(testing::DoAll( @@ -318,7 +318,7 @@ namespace traffic_signal_controller_service Return(true))); // Define Control Source - snmp_response_obj control_source_resp; + streets_snmp_cmd::snmp_response_obj control_source_resp; control_source_resp.val_int = 2; std::string control_source_oid = ntcip_oids::CHANNEL_CONTROL_SOURCE_PARAMETER + "." + std::to_string(i); EXPECT_CALL(*mock_snmp, process_snmp_request(control_source_oid, request_type , _) ).Times(1).WillRepeatedly(testing::DoAll( @@ -327,7 +327,7 @@ namespace traffic_signal_controller_service break; } case 3: { - snmp_response_obj channel_control_resp; + streets_snmp_cmd::snmp_response_obj channel_control_resp; channel_control_resp.val_int = 2; std::string channel_control_oid = ntcip_oids::CHANNEL_CONTROL_TYPE_PARAMETER + "." + std::to_string(i); EXPECT_CALL(*mock_snmp, process_snmp_request(channel_control_oid, request_type , _) ).Times(1).WillRepeatedly(testing::DoAll( @@ -335,7 +335,7 @@ namespace traffic_signal_controller_service Return(true))); // Define Control Source - snmp_response_obj control_source_resp; + streets_snmp_cmd::snmp_response_obj control_source_resp; control_source_resp.val_int = 3; std::string control_source_oid = ntcip_oids::CHANNEL_CONTROL_SOURCE_PARAMETER + "." + std::to_string(i); EXPECT_CALL(*mock_snmp, process_snmp_request(control_source_oid, request_type , _) ).Times(1).WillRepeatedly(testing::DoAll( @@ -344,7 +344,7 @@ namespace traffic_signal_controller_service break; } case 4: { - snmp_response_obj channel_control_resp; + streets_snmp_cmd::snmp_response_obj channel_control_resp; channel_control_resp.val_int = 2; std::string channel_control_oid = ntcip_oids::CHANNEL_CONTROL_TYPE_PARAMETER + "." + std::to_string(i); EXPECT_CALL(*mock_snmp, process_snmp_request(channel_control_oid, request_type , _) ).Times(1).WillRepeatedly(testing::DoAll( @@ -352,7 +352,7 @@ namespace traffic_signal_controller_service Return(true))); // Define Control Source - snmp_response_obj control_source_resp; + streets_snmp_cmd::snmp_response_obj control_source_resp; control_source_resp.val_int = 4; std::string control_source_oid = ntcip_oids::CHANNEL_CONTROL_SOURCE_PARAMETER + "." + std::to_string(i); EXPECT_CALL(*mock_snmp, process_snmp_request(control_source_oid, request_type , _) ).Times(1).WillRepeatedly(testing::DoAll( @@ -361,7 +361,7 @@ namespace traffic_signal_controller_service break; } case 5: { - snmp_response_obj channel_control_resp; + streets_snmp_cmd::snmp_response_obj channel_control_resp; channel_control_resp.val_int = 2; std::string channel_control_oid = ntcip_oids::CHANNEL_CONTROL_TYPE_PARAMETER + "." + std::to_string(i); EXPECT_CALL(*mock_snmp, process_snmp_request(channel_control_oid, request_type , _) ).Times(1).WillRepeatedly(testing::DoAll( @@ -369,7 +369,7 @@ namespace traffic_signal_controller_service Return(true))); // Define Control Source - snmp_response_obj control_source_resp; + streets_snmp_cmd::snmp_response_obj control_source_resp; control_source_resp.val_int = 5; std::string control_source_oid = ntcip_oids::CHANNEL_CONTROL_SOURCE_PARAMETER + "." + std::to_string(i); EXPECT_CALL(*mock_snmp, process_snmp_request(control_source_oid, request_type , _) ).Times(1).WillRepeatedly(testing::DoAll( @@ -378,7 +378,7 @@ namespace traffic_signal_controller_service break; } case 6: { - snmp_response_obj channel_control_resp; + streets_snmp_cmd::snmp_response_obj channel_control_resp; channel_control_resp.val_int = 2; std::string channel_control_oid = ntcip_oids::CHANNEL_CONTROL_TYPE_PARAMETER + "." + std::to_string(i); EXPECT_CALL(*mock_snmp, process_snmp_request(channel_control_oid, request_type , _) ).Times(1).WillRepeatedly(testing::DoAll( @@ -386,7 +386,7 @@ namespace traffic_signal_controller_service Return(true))); // Define Control Source - snmp_response_obj control_source_resp; + streets_snmp_cmd::snmp_response_obj control_source_resp; control_source_resp.val_int = 6; std::string control_source_oid = ntcip_oids::CHANNEL_CONTROL_SOURCE_PARAMETER + "." + std::to_string(i); EXPECT_CALL(*mock_snmp, process_snmp_request(control_source_oid, request_type , _) ).Times(1).WillRepeatedly(testing::DoAll( @@ -395,7 +395,7 @@ namespace traffic_signal_controller_service break; } case 7: { - snmp_response_obj channel_control_resp; + streets_snmp_cmd::snmp_response_obj channel_control_resp; channel_control_resp.val_int = 2; std::string channel_control_oid = ntcip_oids::CHANNEL_CONTROL_TYPE_PARAMETER + "." + std::to_string(i); EXPECT_CALL(*mock_snmp, process_snmp_request(channel_control_oid, request_type , _) ).Times(1).WillRepeatedly(testing::DoAll( @@ -403,7 +403,7 @@ namespace traffic_signal_controller_service Return(true))); // Define Control Source - snmp_response_obj control_source_resp; + streets_snmp_cmd::snmp_response_obj control_source_resp; control_source_resp.val_int = 7; std::string control_source_oid = ntcip_oids::CHANNEL_CONTROL_SOURCE_PARAMETER + "." + std::to_string(i); EXPECT_CALL(*mock_snmp, process_snmp_request(control_source_oid, request_type , _) ).Times(1).WillRepeatedly(testing::DoAll( @@ -412,7 +412,7 @@ namespace traffic_signal_controller_service break; } case 8: { - snmp_response_obj channel_control_resp; + streets_snmp_cmd::snmp_response_obj channel_control_resp; channel_control_resp.val_int = 2; std::string channel_control_oid = ntcip_oids::CHANNEL_CONTROL_TYPE_PARAMETER + "." + std::to_string(i); EXPECT_CALL(*mock_snmp, process_snmp_request(channel_control_oid, request_type , _) ).Times(1).WillRepeatedly(testing::DoAll( @@ -420,7 +420,7 @@ namespace traffic_signal_controller_service Return(true))); // Define Control Source - snmp_response_obj control_source_resp; + streets_snmp_cmd::snmp_response_obj control_source_resp; control_source_resp.val_int = 8; std::string control_source_oid = ntcip_oids::CHANNEL_CONTROL_SOURCE_PARAMETER + "." + std::to_string(i); EXPECT_CALL(*mock_snmp, process_snmp_request(control_source_oid, request_type , _) ).Times(1).WillRepeatedly(testing::DoAll( @@ -429,7 +429,7 @@ namespace traffic_signal_controller_service break; } default: { - snmp_response_obj channel_control_resp; + streets_snmp_cmd::snmp_response_obj channel_control_resp; channel_control_resp.val_int = 0; std::string channel_control_oid = ntcip_oids::CHANNEL_CONTROL_TYPE_PARAMETER + "." + std::to_string(i); EXPECT_CALL(*mock_snmp, process_snmp_request(channel_control_oid, request_type , _) ).Times(1).WillRepeatedly(testing::DoAll( @@ -439,7 +439,7 @@ namespace traffic_signal_controller_service // Define Control Source // Note this OID is not actually called for any non vehicle/pedestrian phases which is why the Times() assertion // is not included - snmp_response_obj control_source_resp; + streets_snmp_cmd::snmp_response_obj control_source_resp; control_source_resp.val_int = 0; std::string control_source_oid = ntcip_oids::CHANNEL_CONTROL_SOURCE_PARAMETER + "." + std::to_string(i); EXPECT_CALL(*mock_snmp, process_snmp_request(control_source_oid, request_type , _) ).WillRepeatedly(testing::DoAll( @@ -454,7 +454,7 @@ namespace traffic_signal_controller_service // Define get min green std::string min_green_oid = ntcip_oids::MINIMUM_GREEN + "." + std::to_string(i); - snmp_response_obj min_green; + streets_snmp_cmd::snmp_response_obj min_green; min_green.val_int = 5; EXPECT_CALL(*mock_snmp, process_snmp_request(min_green_oid , request_type , _) ).Times(1).WillRepeatedly(testing::DoAll( SetArgReferee<2>(min_green), @@ -463,7 +463,7 @@ namespace traffic_signal_controller_service // Define get max green std::string max_green_oid = ntcip_oids::MAXIMUM_GREEN + "." + std::to_string(i); - snmp_response_obj max_green; + streets_snmp_cmd::snmp_response_obj max_green; max_green.val_int = 300; EXPECT_CALL(*mock_snmp, process_snmp_request(max_green_oid , request_type , _) ).Times(1).WillRepeatedly(testing::DoAll( SetArgReferee<2>(max_green), @@ -472,7 +472,7 @@ namespace traffic_signal_controller_service // Define get yellow Duration std::string yellow_oid = ntcip_oids::YELLOW_CHANGE_PARAMETER + "." + std::to_string(i); - snmp_response_obj yellow_duration; + streets_snmp_cmd::snmp_response_obj yellow_duration; yellow_duration.val_int = 10; EXPECT_CALL(*mock_snmp, process_snmp_request(yellow_oid , request_type , _) ).Times(1).WillRepeatedly(testing::DoAll( SetArgReferee<2>(yellow_duration), @@ -482,7 +482,7 @@ namespace traffic_signal_controller_service // Define red clearance std::string red_clearance_oid = ntcip_oids::RED_CLEAR_PARAMETER + "." + std::to_string(i); - snmp_response_obj red_clearance_duration; + streets_snmp_cmd::snmp_response_obj red_clearance_duration; red_clearance_duration.val_int = 15; EXPECT_CALL(*mock_snmp, process_snmp_request(red_clearance_oid , request_type , _) ).Times(1).WillRepeatedly(testing::DoAll( SetArgReferee<2>(red_clearance_duration), @@ -491,7 +491,7 @@ namespace traffic_signal_controller_service //Define get concurrent phases std::string concurrent_phase_oid = ntcip_oids::PHASE_CONCURRENCY + "." + std::to_string(i); - snmp_response_obj concurrent_phase_resp; + streets_snmp_cmd::snmp_response_obj concurrent_phase_resp; if ( i == 1 || i == 2) { concurrent_phase_resp.val_string = {char(5), char(6)}; } @@ -1534,11 +1534,11 @@ namespace traffic_signal_controller_service last_green_served.push_back(five); monitor_dpp_ptr->last_green_served = last_green_served ; // mock next phase NTCIP OID - snmp_response_obj next_phase; + streets_snmp_cmd::snmp_response_obj next_phase; next_phase.val_int = 68; // Binary 01000100 -> phase 3 and phase 7 next green std::string next_phase_oid = ntcip_oids::PHASE_STATUS_GROUP_PHASE_NEXT; - EXPECT_CALL(*mock_snmp, process_snmp_request(next_phase_oid, request_type::GET , _) ).Times(1). + EXPECT_CALL(*mock_snmp, process_snmp_request(next_phase_oid, streets_snmp_cmd::REQUEST_TYPE::GET , _) ).Times(1). WillRepeatedly( testing::DoAll( SetArgReferee<2>(next_phase), @@ -1621,11 +1621,11 @@ namespace traffic_signal_controller_service monitor_dpp_ptr->last_green_served = last_green_served ; // mock next phase NTCIP OID - snmp_response_obj next_phase; + streets_snmp_cmd::snmp_response_obj next_phase; next_phase.val_int = 68; // Binary 01000100 -> phase 3 and phase 7 next green std::string next_phase_oid = ntcip_oids::PHASE_STATUS_GROUP_PHASE_NEXT; - EXPECT_CALL(*mock_snmp, process_snmp_request(next_phase_oid, request_type::GET , _) ).Times(1). + EXPECT_CALL(*mock_snmp, process_snmp_request(next_phase_oid, streets_snmp_cmd::REQUEST_TYPE::GET , _) ).Times(1). WillRepeatedly( testing::DoAll( SetArgReferee<2>(next_phase), @@ -1729,11 +1729,11 @@ namespace traffic_signal_controller_service two.state_time_speed.push_back(green); // mock next phase NTCIP OID - snmp_response_obj next_phase; + streets_snmp_cmd::snmp_response_obj next_phase; next_phase.val_int = 68; // Binary 01000100 -> phase 3 and phase 7 next green std::string next_phase_oid = ntcip_oids::PHASE_STATUS_GROUP_PHASE_NEXT; - EXPECT_CALL(*mock_snmp, process_snmp_request(next_phase_oid, request_type::GET , _) ).Times(1). + EXPECT_CALL(*mock_snmp, process_snmp_request(next_phase_oid, streets_snmp_cmd::REQUEST_TYPE::GET , _) ).Times(1). WillRepeatedly( testing::DoAll( SetArgReferee<2>(next_phase), diff --git a/tsc_client_service/test/test_monitor_state.cpp b/tsc_client_service/test/test_monitor_state.cpp index 818c9f79e..9f394efca 100644 --- a/tsc_client_service/test/test_monitor_state.cpp +++ b/tsc_client_service/test/test_monitor_state.cpp @@ -21,25 +21,25 @@ namespace traffic_signal_controller_service int phase_num = 0; const std::string&input_oid = ""; - request_type request_type = request_type::GET; + auto request_type= streets_snmp_cmd::REQUEST_TYPE::GET; // Test get max channels - snmp_response_obj max_channels_in_tsc; + streets_snmp_cmd::snmp_response_obj max_channels_in_tsc; max_channels_in_tsc.val_int = 4; - max_channels_in_tsc.type = snmp_response_obj::response_type::INTEGER; + max_channels_in_tsc.type = streets_snmp_cmd::RESPONSE_TYPE::INTEGER; EXPECT_CALL( *mock_client, process_snmp_request(ntcip_oids::MAX_CHANNELS, request_type , _) ).Times(1).WillRepeatedly(testing::DoAll( SetArgReferee<2>(max_channels_in_tsc), Return(true))); - snmp_response_obj max_rings_in_tsc; + streets_snmp_cmd::snmp_response_obj max_rings_in_tsc; max_rings_in_tsc.val_int = 4; - max_rings_in_tsc.type = snmp_response_obj::response_type::INTEGER; + max_rings_in_tsc.type = streets_snmp_cmd::RESPONSE_TYPE::INTEGER; EXPECT_CALL( *mock_client, process_snmp_request(ntcip_oids::MAX_RINGS, request_type , _) ).Times(1).WillRepeatedly(testing::DoAll( SetArgReferee<2>(max_rings_in_tsc), Return(true))); // Define Sequence Data - snmp_response_obj seq_data; + streets_snmp_cmd::snmp_response_obj seq_data; seq_data.val_string = {char(1),char(2)}; std::string seq_data_ring1_oid = ntcip_oids::SEQUENCE_DATA + "." + "1" + "." + std::to_string(1); @@ -68,7 +68,7 @@ namespace traffic_signal_controller_service for(int i = 1; i <= max_channels_in_tsc.val_int; ++i){ // Define Control Type - snmp_response_obj channel_control_resp; + streets_snmp_cmd::snmp_response_obj channel_control_resp; channel_control_resp.val_int = 2; std::string channel_control_oid = ntcip_oids::CHANNEL_CONTROL_TYPE_PARAMETER + "." + std::to_string(i); EXPECT_CALL(*mock_client, process_snmp_request(channel_control_oid, request_type , _) ).Times(1).WillRepeatedly(testing::DoAll( @@ -76,7 +76,7 @@ namespace traffic_signal_controller_service testing::Return(true))); // Define Control Source - snmp_response_obj control_source_resp; + streets_snmp_cmd::snmp_response_obj control_source_resp; control_source_resp.val_int = i; std::string control_source_oid = ntcip_oids::CHANNEL_CONTROL_SOURCE_PARAMETER + "." + std::to_string(i); EXPECT_CALL(*mock_client, process_snmp_request(control_source_oid, request_type , _) ).Times(1).WillRepeatedly(testing::DoAll( @@ -87,7 +87,7 @@ namespace traffic_signal_controller_service // Define get min green std::string min_green_oid = ntcip_oids::MINIMUM_GREEN + "." + std::to_string(i); - snmp_response_obj min_green; + streets_snmp_cmd::snmp_response_obj min_green; min_green.val_int = 20; EXPECT_CALL(*mock_client, process_snmp_request(min_green_oid , request_type , _) ).Times(1).WillRepeatedly(testing::DoAll( SetArgReferee<2>(min_green), @@ -96,7 +96,7 @@ namespace traffic_signal_controller_service // Define get max green std::string max_green_oid = ntcip_oids::MAXIMUM_GREEN + "." + std::to_string(i); - snmp_response_obj max_green; + streets_snmp_cmd::snmp_response_obj max_green; max_green.val_int = 30; EXPECT_CALL(*mock_client, process_snmp_request(max_green_oid , request_type , _) ).Times(1).WillRepeatedly(testing::DoAll( SetArgReferee<2>(max_green), @@ -105,7 +105,7 @@ namespace traffic_signal_controller_service // Define get yellow Duration std::string yellow_oid = ntcip_oids::YELLOW_CHANGE_PARAMETER + "." + std::to_string(i); - snmp_response_obj yellow_duration; + streets_snmp_cmd::snmp_response_obj yellow_duration; yellow_duration.val_int = 40; EXPECT_CALL(*mock_client, process_snmp_request(yellow_oid , request_type , _) ).Times(1).WillRepeatedly(testing::DoAll( SetArgReferee<2>(yellow_duration), @@ -115,7 +115,7 @@ namespace traffic_signal_controller_service // Define red clearance std::string red_clearance_oid = ntcip_oids::RED_CLEAR_PARAMETER + "." + std::to_string(i); - snmp_response_obj red_clearance_duration; + streets_snmp_cmd::snmp_response_obj red_clearance_duration; red_clearance_duration.val_int = 10; EXPECT_CALL(*mock_client, process_snmp_request(red_clearance_oid , request_type , _) ).Times(1).WillRepeatedly(testing::DoAll( SetArgReferee<2>(red_clearance_duration), @@ -123,7 +123,7 @@ namespace traffic_signal_controller_service //Define get concurrent phases std::string concurrent_phase_oid = ntcip_oids::PHASE_CONCURRENCY + "." + std::to_string(i); - snmp_response_obj concurrent_phase_resp; + streets_snmp_cmd::snmp_response_obj concurrent_phase_resp; if ( i == 1 || i == 2) { concurrent_phase_resp.val_string = {char(1), char(2)}; } @@ -154,6 +154,8 @@ namespace traffic_signal_controller_service EXPECT_TRUE(ped_phase_map.empty()); std::unordered_map vehicle_phase_map = worker.get_vehicle_phase_map(); EXPECT_FALSE(vehicle_phase_map.empty()); + std::unordered_map signal_group_map = worker.get_signal_group_map(); + EXPECT_FALSE(signal_group_map.empty()); //Test tsc_config_state std::shared_ptr tsc_config_state = worker.get_tsc_config_state(); @@ -276,24 +278,24 @@ namespace traffic_signal_controller_service // When phase to signal group map is empty, ASSERT_THROW(state.get_phase_number(1), monitor_states_exception); // Test get max channels - snmp_response_obj max_channels_in_tsc; + streets_snmp_cmd::snmp_response_obj max_channels_in_tsc; max_channels_in_tsc.val_int = 16; - max_channels_in_tsc.type = snmp_response_obj::response_type::INTEGER; + max_channels_in_tsc.type = streets_snmp_cmd::RESPONSE_TYPE::INTEGER; - request_type request_type = request_type::GET; + auto request_type= streets_snmp_cmd::REQUEST_TYPE::GET; EXPECT_CALL( *mock_snmp, process_snmp_request(ntcip_oids::MAX_CHANNELS, request_type , _) ).Times(1).WillRepeatedly(testing::DoAll( SetArgReferee<2>(max_channels_in_tsc), Return(true))); - snmp_response_obj max_rings_in_tsc; + streets_snmp_cmd::snmp_response_obj max_rings_in_tsc; max_rings_in_tsc.val_int = 4; - max_rings_in_tsc.type = snmp_response_obj::response_type::INTEGER; + max_rings_in_tsc.type = streets_snmp_cmd::RESPONSE_TYPE::INTEGER; EXPECT_CALL( *mock_snmp, process_snmp_request(ntcip_oids::MAX_RINGS, request_type , _) ).Times(1).WillRepeatedly(testing::DoAll( SetArgReferee<2>(max_rings_in_tsc), Return(true))); // Define Sequence Data - snmp_response_obj seq_data; + streets_snmp_cmd::snmp_response_obj seq_data; seq_data.val_string = {char(1),char(2), char(3), char(4)}; std::string seq_data_ring1_oid = ntcip_oids::SEQUENCE_DATA + "." + "1" + "." + std::to_string(1); @@ -328,7 +330,7 @@ namespace traffic_signal_controller_service // switch(i) { case 3: { - snmp_response_obj channel_control_resp; + streets_snmp_cmd::snmp_response_obj channel_control_resp; channel_control_resp.val_int = 2; std::string channel_control_oid = ntcip_oids::CHANNEL_CONTROL_TYPE_PARAMETER + "." + std::to_string(i); EXPECT_CALL(*mock_snmp, process_snmp_request(channel_control_oid, request_type , _) ).Times(1).WillRepeatedly(testing::DoAll( @@ -336,7 +338,7 @@ namespace traffic_signal_controller_service Return(true))); // Define Control Source - snmp_response_obj control_source_resp; + streets_snmp_cmd::snmp_response_obj control_source_resp; control_source_resp.val_int = 1; std::string control_source_oid = ntcip_oids::CHANNEL_CONTROL_SOURCE_PARAMETER + "." + std::to_string(i); EXPECT_CALL(*mock_snmp, process_snmp_request(control_source_oid, request_type , _) ).Times(1).WillRepeatedly(testing::DoAll( @@ -345,7 +347,7 @@ namespace traffic_signal_controller_service break; } case 7: { - snmp_response_obj channel_control_resp; + streets_snmp_cmd::snmp_response_obj channel_control_resp; channel_control_resp.val_int = 2; std::string channel_control_oid = ntcip_oids::CHANNEL_CONTROL_TYPE_PARAMETER + "." + std::to_string(i); EXPECT_CALL(*mock_snmp, process_snmp_request(channel_control_oid, request_type , _) ).Times(1).WillRepeatedly(testing::DoAll( @@ -353,7 +355,7 @@ namespace traffic_signal_controller_service Return(true))); // Define Control Source - snmp_response_obj control_source_resp; + streets_snmp_cmd::snmp_response_obj control_source_resp; control_source_resp.val_int = 2; std::string control_source_oid = ntcip_oids::CHANNEL_CONTROL_SOURCE_PARAMETER + "." + std::to_string(i); EXPECT_CALL(*mock_snmp, process_snmp_request(control_source_oid, request_type , _) ).Times(1).WillRepeatedly(testing::DoAll( @@ -362,7 +364,7 @@ namespace traffic_signal_controller_service break; } case 9: { - snmp_response_obj channel_control_resp; + streets_snmp_cmd::snmp_response_obj channel_control_resp; channel_control_resp.val_int = 2; std::string channel_control_oid = ntcip_oids::CHANNEL_CONTROL_TYPE_PARAMETER + "." + std::to_string(i); EXPECT_CALL(*mock_snmp, process_snmp_request(channel_control_oid, request_type , _) ).Times(1).WillRepeatedly(testing::DoAll( @@ -370,7 +372,7 @@ namespace traffic_signal_controller_service Return(true))); // Define Control Source - snmp_response_obj control_source_resp; + streets_snmp_cmd::snmp_response_obj control_source_resp; control_source_resp.val_int = 3; std::string control_source_oid = ntcip_oids::CHANNEL_CONTROL_SOURCE_PARAMETER + "." + std::to_string(i); EXPECT_CALL(*mock_snmp, process_snmp_request(control_source_oid, request_type , _) ).Times(1).WillRepeatedly(testing::DoAll( @@ -379,7 +381,7 @@ namespace traffic_signal_controller_service break; } case 14: { - snmp_response_obj channel_control_resp; + streets_snmp_cmd::snmp_response_obj channel_control_resp; channel_control_resp.val_int = 2; std::string channel_control_oid = ntcip_oids::CHANNEL_CONTROL_TYPE_PARAMETER + "." + std::to_string(i); EXPECT_CALL(*mock_snmp, process_snmp_request(channel_control_oid, request_type , _) ).Times(1).WillRepeatedly(testing::DoAll( @@ -387,7 +389,7 @@ namespace traffic_signal_controller_service Return(true))); // Define Control Source - snmp_response_obj control_source_resp; + streets_snmp_cmd::snmp_response_obj control_source_resp; control_source_resp.val_int = 4; std::string control_source_oid = ntcip_oids::CHANNEL_CONTROL_SOURCE_PARAMETER + "." + std::to_string(i); EXPECT_CALL(*mock_snmp, process_snmp_request(control_source_oid, request_type , _) ).Times(1).WillRepeatedly(testing::DoAll( @@ -396,7 +398,7 @@ namespace traffic_signal_controller_service break; } case 11: { - snmp_response_obj channel_control_resp; + streets_snmp_cmd::snmp_response_obj channel_control_resp; channel_control_resp.val_int = 2; std::string channel_control_oid = ntcip_oids::CHANNEL_CONTROL_TYPE_PARAMETER + "." + std::to_string(i); EXPECT_CALL(*mock_snmp, process_snmp_request(channel_control_oid, request_type , _) ).Times(1).WillRepeatedly(testing::DoAll( @@ -404,7 +406,7 @@ namespace traffic_signal_controller_service Return(true))); // Define Control Source - snmp_response_obj control_source_resp; + streets_snmp_cmd::snmp_response_obj control_source_resp; control_source_resp.val_int = 5; std::string control_source_oid = ntcip_oids::CHANNEL_CONTROL_SOURCE_PARAMETER + "." + std::to_string(i); EXPECT_CALL(*mock_snmp, process_snmp_request(control_source_oid, request_type , _) ).Times(1).WillRepeatedly(testing::DoAll( @@ -413,7 +415,7 @@ namespace traffic_signal_controller_service break; } case 6: { - snmp_response_obj channel_control_resp; + streets_snmp_cmd::snmp_response_obj channel_control_resp; channel_control_resp.val_int = 2; std::string channel_control_oid = ntcip_oids::CHANNEL_CONTROL_TYPE_PARAMETER + "." + std::to_string(i); EXPECT_CALL(*mock_snmp, process_snmp_request(channel_control_oid, request_type , _) ).Times(1).WillRepeatedly(testing::DoAll( @@ -421,7 +423,7 @@ namespace traffic_signal_controller_service Return(true))); // Define Control Source - snmp_response_obj control_source_resp; + streets_snmp_cmd::snmp_response_obj control_source_resp; control_source_resp.val_int = 6; std::string control_source_oid = ntcip_oids::CHANNEL_CONTROL_SOURCE_PARAMETER + "." + std::to_string(i); EXPECT_CALL(*mock_snmp, process_snmp_request(control_source_oid, request_type , _) ).Times(1).WillRepeatedly(testing::DoAll( @@ -430,7 +432,7 @@ namespace traffic_signal_controller_service break; } case 15: { - snmp_response_obj channel_control_resp; + streets_snmp_cmd::snmp_response_obj channel_control_resp; channel_control_resp.val_int = 2; std::string channel_control_oid = ntcip_oids::CHANNEL_CONTROL_TYPE_PARAMETER + "." + std::to_string(i); EXPECT_CALL(*mock_snmp, process_snmp_request(channel_control_oid, request_type , _) ).Times(1).WillRepeatedly(testing::DoAll( @@ -438,7 +440,7 @@ namespace traffic_signal_controller_service Return(true))); // Define Control Source - snmp_response_obj control_source_resp; + streets_snmp_cmd::snmp_response_obj control_source_resp; control_source_resp.val_int = 7; std::string control_source_oid = ntcip_oids::CHANNEL_CONTROL_SOURCE_PARAMETER + "." + std::to_string(i); EXPECT_CALL(*mock_snmp, process_snmp_request(control_source_oid, request_type , _) ).Times(1).WillRepeatedly(testing::DoAll( @@ -447,7 +449,7 @@ namespace traffic_signal_controller_service break; } case 1: { - snmp_response_obj channel_control_resp; + streets_snmp_cmd::snmp_response_obj channel_control_resp; channel_control_resp.val_int = 2; std::string channel_control_oid = ntcip_oids::CHANNEL_CONTROL_TYPE_PARAMETER + "." + std::to_string(i); EXPECT_CALL(*mock_snmp, process_snmp_request(channel_control_oid, request_type , _) ).Times(1).WillRepeatedly(testing::DoAll( @@ -455,7 +457,7 @@ namespace traffic_signal_controller_service Return(true))); // Define Control Source - snmp_response_obj control_source_resp; + streets_snmp_cmd::snmp_response_obj control_source_resp; control_source_resp.val_int = 8; std::string control_source_oid = ntcip_oids::CHANNEL_CONTROL_SOURCE_PARAMETER + "." + std::to_string(i); EXPECT_CALL(*mock_snmp, process_snmp_request(control_source_oid, request_type , _) ).Times(1).WillRepeatedly(testing::DoAll( @@ -464,7 +466,7 @@ namespace traffic_signal_controller_service break; } default: { - snmp_response_obj channel_control_resp; + streets_snmp_cmd::snmp_response_obj channel_control_resp; channel_control_resp.val_int = 0; std::string channel_control_oid = ntcip_oids::CHANNEL_CONTROL_TYPE_PARAMETER + "." + std::to_string(i); EXPECT_CALL(*mock_snmp, process_snmp_request(channel_control_oid, request_type , _) ).Times(1).WillRepeatedly(testing::DoAll( @@ -474,7 +476,7 @@ namespace traffic_signal_controller_service // Define Control Source // Note this OID is not actually called for any non vehicle/pedestrian phases which is why the Times() assertion // is not included - snmp_response_obj control_source_resp; + streets_snmp_cmd::snmp_response_obj control_source_resp; control_source_resp.val_int = 0; std::string control_source_oid = ntcip_oids::CHANNEL_CONTROL_SOURCE_PARAMETER + "." + std::to_string(i); EXPECT_CALL(*mock_snmp, process_snmp_request(control_source_oid, request_type , _) ).WillRepeatedly(testing::DoAll( @@ -489,7 +491,7 @@ namespace traffic_signal_controller_service // Define get min green std::string min_green_oid = ntcip_oids::MINIMUM_GREEN + "." + std::to_string(i); - snmp_response_obj min_green; + streets_snmp_cmd::snmp_response_obj min_green; min_green.val_int = 20; EXPECT_CALL(*mock_snmp, process_snmp_request(min_green_oid , request_type , _) ).Times(1).WillRepeatedly(testing::DoAll( SetArgReferee<2>(min_green), @@ -498,7 +500,7 @@ namespace traffic_signal_controller_service // Define get max green std::string max_green_oid = ntcip_oids::MAXIMUM_GREEN + "." + std::to_string(i); - snmp_response_obj max_green; + streets_snmp_cmd::snmp_response_obj max_green; max_green.val_int = 30; EXPECT_CALL(*mock_snmp, process_snmp_request(max_green_oid , request_type , _) ).Times(1).WillRepeatedly(testing::DoAll( SetArgReferee<2>(max_green), @@ -507,7 +509,7 @@ namespace traffic_signal_controller_service // Define get yellow Duration std::string yellow_oid = ntcip_oids::YELLOW_CHANGE_PARAMETER + "." + std::to_string(i); - snmp_response_obj yellow_duration; + streets_snmp_cmd::snmp_response_obj yellow_duration; yellow_duration.val_int = 40; EXPECT_CALL(*mock_snmp, process_snmp_request(yellow_oid , request_type , _) ).Times(1).WillRepeatedly(testing::DoAll( SetArgReferee<2>(yellow_duration), @@ -517,7 +519,7 @@ namespace traffic_signal_controller_service // Define red clearance std::string red_clearance_oid = ntcip_oids::RED_CLEAR_PARAMETER + "." + std::to_string(i); - snmp_response_obj red_clearance_duration; + streets_snmp_cmd::snmp_response_obj red_clearance_duration; red_clearance_duration.val_int = 10; EXPECT_CALL(*mock_snmp, process_snmp_request(red_clearance_oid , request_type , _) ).Times(1).WillRepeatedly(testing::DoAll( SetArgReferee<2>(red_clearance_duration), @@ -526,7 +528,7 @@ namespace traffic_signal_controller_service //Define get concurrent phases std::string concurrent_phase_oid = ntcip_oids::PHASE_CONCURRENCY + "." + std::to_string(i); - snmp_response_obj concurrent_phase_resp; + streets_snmp_cmd::snmp_response_obj concurrent_phase_resp; if ( i == 1 || i == 2) { concurrent_phase_resp.val_string = {char(5), char(6)}; } diff --git a/tsc_client_service/test/test_snmp_client.cpp b/tsc_client_service/test/test_snmp_client.cpp index 31d0d1967..847489f2f 100644 --- a/tsc_client_service/test/test_snmp_client.cpp +++ b/tsc_client_service/test/test_snmp_client.cpp @@ -13,9 +13,9 @@ TEST(test_snmp_client, test_process_snmp_request) snmp_client worker(dummy_ip, dummy_port); // Test GET - request_type request_type = request_type::GET; - snmp_response_obj int_response; - int_response.type = snmp_response_obj::response_type::INTEGER; + auto request_type= streets_snmp_cmd::REQUEST_TYPE::GET; + streets_snmp_cmd::snmp_response_obj int_response; + int_response.type = streets_snmp_cmd::RESPONSE_TYPE::INTEGER; int_response.val_int = 0; // Expect get call to fail since we're communicating with invalid host EXPECT_FALSE(worker.process_snmp_request(test_oid, request_type, int_response)); @@ -26,7 +26,7 @@ TEST(test_snmp_client, test_process_snmp_request) EXPECT_FALSE(worker.process_snmp_request(test_oid, request_type, int_response)); // Test log_error - request_type = request_type::GET; + request_type = streets_snmp_cmd::REQUEST_TYPE::GET; snmp_pdu *response = nullptr; int status = STAT_TIMEOUT; worker.log_error(status, request_type, response); @@ -35,22 +35,22 @@ TEST(test_snmp_client, test_process_snmp_request) worker.log_error(status, request_type, response); // Test SET - request_type = request_type::SET; - snmp_response_obj set_value; - int_response.type = snmp_response_obj::response_type::INTEGER; + request_type = streets_snmp_cmd::REQUEST_TYPE::SET; + streets_snmp_cmd::snmp_response_obj set_value; + int_response.type = streets_snmp_cmd::RESPONSE_TYPE::INTEGER; int_response.val_int = 10; // Expect set call to fail since we're communicating with invalid host EXPECT_FALSE(worker.process_snmp_request(test_oid, request_type, set_value)); // Test log_error status = STAT_TIMEOUT; - request_type = request_type::SET; + request_type = streets_snmp_cmd::REQUEST_TYPE::SET; status = -7; //Random error value worker.log_error(status, request_type,response); // Invalid Request type - request_type = request_type::OTHER; + request_type = streets_snmp_cmd::REQUEST_TYPE::OTHER; EXPECT_FALSE(worker.process_snmp_request(test_oid, request_type, set_value)); } diff --git a/tsc_client_service/test/test_tsc_service.cpp b/tsc_client_service/test/test_tsc_service.cpp index f1212fe9c..776423f04 100644 --- a/tsc_client_service/test/test_tsc_service.cpp +++ b/tsc_client_service/test/test_tsc_service.cpp @@ -41,24 +41,24 @@ namespace traffic_signal_controller_service void mock_tsc_ntcip() { // gmock SNMP response --------------------------------------------------------------------------------------------------------------------- // Test get max channels - snmp_response_obj max_channels_in_tsc; + streets_snmp_cmd::snmp_response_obj max_channels_in_tsc; max_channels_in_tsc.val_int = 16; - max_channels_in_tsc.type = snmp_response_obj::response_type::INTEGER; + max_channels_in_tsc.type = streets_snmp_cmd::RESPONSE_TYPE::INTEGER; - request_type request_type = request_type::GET; + auto request_type= streets_snmp_cmd::REQUEST_TYPE::GET; EXPECT_CALL( *mock_snmp, process_snmp_request(ntcip_oids::MAX_CHANNELS, request_type , _) ).WillRepeatedly(testing::DoAll( SetArgReferee<2>(max_channels_in_tsc), Return(true))); - snmp_response_obj max_rings_in_tsc; + streets_snmp_cmd::snmp_response_obj max_rings_in_tsc; max_rings_in_tsc.val_int = 4; - max_rings_in_tsc.type = snmp_response_obj::response_type::INTEGER; + max_rings_in_tsc.type = streets_snmp_cmd::RESPONSE_TYPE::INTEGER; EXPECT_CALL( *mock_snmp, process_snmp_request(ntcip_oids::MAX_RINGS, request_type , _) ).WillRepeatedly(testing::DoAll( SetArgReferee<2>(max_rings_in_tsc), Return(true))); // Define Sequence Data - snmp_response_obj seq_data; + streets_snmp_cmd::snmp_response_obj seq_data; seq_data.val_string = {char(1),char(2), char(3), char(4)}; std::string seq_data_ring1_oid = ntcip_oids::SEQUENCE_DATA + "." + "1" + "." + std::to_string(1); @@ -93,7 +93,7 @@ namespace traffic_signal_controller_service // switch(i) { case 1: { - snmp_response_obj channel_control_resp; + streets_snmp_cmd::snmp_response_obj channel_control_resp; channel_control_resp.val_int = 2; std::string channel_control_oid = ntcip_oids::CHANNEL_CONTROL_TYPE_PARAMETER + "." + std::to_string(i); EXPECT_CALL(*mock_snmp, process_snmp_request(channel_control_oid, request_type , _) ).WillRepeatedly(testing::DoAll( @@ -101,7 +101,7 @@ namespace traffic_signal_controller_service Return(true))); // Define Control Source - snmp_response_obj control_source_resp; + streets_snmp_cmd::snmp_response_obj control_source_resp; control_source_resp.val_int = 1; std::string control_source_oid = ntcip_oids::CHANNEL_CONTROL_SOURCE_PARAMETER + "." + std::to_string(i); EXPECT_CALL(*mock_snmp, process_snmp_request(control_source_oid, request_type , _) ).WillRepeatedly(testing::DoAll( @@ -110,7 +110,7 @@ namespace traffic_signal_controller_service break; } case 2: { - snmp_response_obj channel_control_resp; + streets_snmp_cmd::snmp_response_obj channel_control_resp; channel_control_resp.val_int = 2; std::string channel_control_oid = ntcip_oids::CHANNEL_CONTROL_TYPE_PARAMETER + "." + std::to_string(i); EXPECT_CALL(*mock_snmp, process_snmp_request(channel_control_oid, request_type , _) ).WillRepeatedly(testing::DoAll( @@ -118,7 +118,7 @@ namespace traffic_signal_controller_service Return(true))); // Define Control Source - snmp_response_obj control_source_resp; + streets_snmp_cmd::snmp_response_obj control_source_resp; control_source_resp.val_int = 2; std::string control_source_oid = ntcip_oids::CHANNEL_CONTROL_SOURCE_PARAMETER + "." + std::to_string(i); EXPECT_CALL(*mock_snmp, process_snmp_request(control_source_oid, request_type , _) ).WillRepeatedly(testing::DoAll( @@ -127,7 +127,7 @@ namespace traffic_signal_controller_service break; } case 3: { - snmp_response_obj channel_control_resp; + streets_snmp_cmd::snmp_response_obj channel_control_resp; channel_control_resp.val_int = 2; std::string channel_control_oid = ntcip_oids::CHANNEL_CONTROL_TYPE_PARAMETER + "." + std::to_string(i); EXPECT_CALL(*mock_snmp, process_snmp_request(channel_control_oid, request_type , _) ).WillRepeatedly(testing::DoAll( @@ -135,7 +135,7 @@ namespace traffic_signal_controller_service Return(true))); // Define Control Source - snmp_response_obj control_source_resp; + streets_snmp_cmd::snmp_response_obj control_source_resp; control_source_resp.val_int = 3; std::string control_source_oid = ntcip_oids::CHANNEL_CONTROL_SOURCE_PARAMETER + "." + std::to_string(i); EXPECT_CALL(*mock_snmp, process_snmp_request(control_source_oid, request_type , _) ).WillRepeatedly(testing::DoAll( @@ -144,7 +144,7 @@ namespace traffic_signal_controller_service break; } case 4: { - snmp_response_obj channel_control_resp; + streets_snmp_cmd::snmp_response_obj channel_control_resp; channel_control_resp.val_int = 2; std::string channel_control_oid = ntcip_oids::CHANNEL_CONTROL_TYPE_PARAMETER + "." + std::to_string(i); EXPECT_CALL(*mock_snmp, process_snmp_request(channel_control_oid, request_type , _) ).WillRepeatedly(testing::DoAll( @@ -152,7 +152,7 @@ namespace traffic_signal_controller_service Return(true))); // Define Control Source - snmp_response_obj control_source_resp; + streets_snmp_cmd::snmp_response_obj control_source_resp; control_source_resp.val_int = 4; std::string control_source_oid = ntcip_oids::CHANNEL_CONTROL_SOURCE_PARAMETER + "." + std::to_string(i); EXPECT_CALL(*mock_snmp, process_snmp_request(control_source_oid, request_type , _) ).WillRepeatedly(testing::DoAll( @@ -161,7 +161,7 @@ namespace traffic_signal_controller_service break; } case 5: { - snmp_response_obj channel_control_resp; + streets_snmp_cmd::snmp_response_obj channel_control_resp; channel_control_resp.val_int = 2; std::string channel_control_oid = ntcip_oids::CHANNEL_CONTROL_TYPE_PARAMETER + "." + std::to_string(i); EXPECT_CALL(*mock_snmp, process_snmp_request(channel_control_oid, request_type , _) ).WillRepeatedly(testing::DoAll( @@ -169,7 +169,7 @@ namespace traffic_signal_controller_service Return(true))); // Define Control Source - snmp_response_obj control_source_resp; + streets_snmp_cmd::snmp_response_obj control_source_resp; control_source_resp.val_int = 5; std::string control_source_oid = ntcip_oids::CHANNEL_CONTROL_SOURCE_PARAMETER + "." + std::to_string(i); EXPECT_CALL(*mock_snmp, process_snmp_request(control_source_oid, request_type , _) ).WillRepeatedly(testing::DoAll( @@ -178,7 +178,7 @@ namespace traffic_signal_controller_service break; } case 6: { - snmp_response_obj channel_control_resp; + streets_snmp_cmd::snmp_response_obj channel_control_resp; channel_control_resp.val_int = 2; std::string channel_control_oid = ntcip_oids::CHANNEL_CONTROL_TYPE_PARAMETER + "." + std::to_string(i); EXPECT_CALL(*mock_snmp, process_snmp_request(channel_control_oid, request_type , _) ).WillRepeatedly(testing::DoAll( @@ -186,7 +186,7 @@ namespace traffic_signal_controller_service Return(true))); // Define Control Source - snmp_response_obj control_source_resp; + streets_snmp_cmd::snmp_response_obj control_source_resp; control_source_resp.val_int = 6; std::string control_source_oid = ntcip_oids::CHANNEL_CONTROL_SOURCE_PARAMETER + "." + std::to_string(i); EXPECT_CALL(*mock_snmp, process_snmp_request(control_source_oid, request_type , _) ).WillRepeatedly(testing::DoAll( @@ -195,7 +195,7 @@ namespace traffic_signal_controller_service break; } case 7: { - snmp_response_obj channel_control_resp; + streets_snmp_cmd::snmp_response_obj channel_control_resp; channel_control_resp.val_int = 2; std::string channel_control_oid = ntcip_oids::CHANNEL_CONTROL_TYPE_PARAMETER + "." + std::to_string(i); EXPECT_CALL(*mock_snmp, process_snmp_request(channel_control_oid, request_type , _) ).WillRepeatedly(testing::DoAll( @@ -203,7 +203,7 @@ namespace traffic_signal_controller_service Return(true))); // Define Control Source - snmp_response_obj control_source_resp; + streets_snmp_cmd::snmp_response_obj control_source_resp; control_source_resp.val_int = 7; std::string control_source_oid = ntcip_oids::CHANNEL_CONTROL_SOURCE_PARAMETER + "." + std::to_string(i); EXPECT_CALL(*mock_snmp, process_snmp_request(control_source_oid, request_type , _) ).WillRepeatedly(testing::DoAll( @@ -212,7 +212,7 @@ namespace traffic_signal_controller_service break; } case 8: { - snmp_response_obj channel_control_resp; + streets_snmp_cmd::snmp_response_obj channel_control_resp; channel_control_resp.val_int = 2; std::string channel_control_oid = ntcip_oids::CHANNEL_CONTROL_TYPE_PARAMETER + "." + std::to_string(i); EXPECT_CALL(*mock_snmp, process_snmp_request(channel_control_oid, request_type , _) ).WillRepeatedly(testing::DoAll( @@ -220,7 +220,7 @@ namespace traffic_signal_controller_service Return(true))); // Define Control Source - snmp_response_obj control_source_resp; + streets_snmp_cmd::snmp_response_obj control_source_resp; control_source_resp.val_int = 8; std::string control_source_oid = ntcip_oids::CHANNEL_CONTROL_SOURCE_PARAMETER + "." + std::to_string(i); EXPECT_CALL(*mock_snmp, process_snmp_request(control_source_oid, request_type , _) ).WillRepeatedly(testing::DoAll( @@ -229,7 +229,7 @@ namespace traffic_signal_controller_service break; } default: { - snmp_response_obj channel_control_resp; + streets_snmp_cmd::snmp_response_obj channel_control_resp; channel_control_resp.val_int = 0; std::string channel_control_oid = ntcip_oids::CHANNEL_CONTROL_TYPE_PARAMETER + "." + std::to_string(i); EXPECT_CALL(*mock_snmp, process_snmp_request(channel_control_oid, request_type , _) ).WillRepeatedly(testing::DoAll( @@ -239,7 +239,7 @@ namespace traffic_signal_controller_service // Define Control Source // Note this OID is not actually called for any non vehicle/pedestrian phases which is why the Times() assertion // is not included - snmp_response_obj control_source_resp; + streets_snmp_cmd::snmp_response_obj control_source_resp; control_source_resp.val_int = 0; std::string control_source_oid = ntcip_oids::CHANNEL_CONTROL_SOURCE_PARAMETER + "." + std::to_string(i); EXPECT_CALL(*mock_snmp, process_snmp_request(control_source_oid, request_type , _) ).WillRepeatedly(testing::DoAll( @@ -254,7 +254,7 @@ namespace traffic_signal_controller_service // Define get min green std::string min_green_oid = ntcip_oids::MINIMUM_GREEN + "." + std::to_string(i); - snmp_response_obj min_green; + streets_snmp_cmd::snmp_response_obj min_green; min_green.val_int = 5; EXPECT_CALL(*mock_snmp, process_snmp_request(min_green_oid , request_type , _) ).WillRepeatedly(testing::DoAll( SetArgReferee<2>(min_green), @@ -263,7 +263,7 @@ namespace traffic_signal_controller_service // Define get max green std::string max_green_oid = ntcip_oids::MAXIMUM_GREEN + "." + std::to_string(i); - snmp_response_obj max_green; + streets_snmp_cmd::snmp_response_obj max_green; max_green.val_int = 300; EXPECT_CALL(*mock_snmp, process_snmp_request(max_green_oid , request_type , _) ).WillRepeatedly(testing::DoAll( SetArgReferee<2>(max_green), @@ -272,7 +272,7 @@ namespace traffic_signal_controller_service // Define get yellow Duration std::string yellow_oid = ntcip_oids::YELLOW_CHANGE_PARAMETER + "." + std::to_string(i); - snmp_response_obj yellow_duration; + streets_snmp_cmd::snmp_response_obj yellow_duration; yellow_duration.val_int = 10; EXPECT_CALL(*mock_snmp, process_snmp_request(yellow_oid , request_type , _) ).WillRepeatedly(testing::DoAll( SetArgReferee<2>(yellow_duration), @@ -282,7 +282,7 @@ namespace traffic_signal_controller_service // Define red clearance std::string red_clearance_oid = ntcip_oids::RED_CLEAR_PARAMETER + "." + std::to_string(i); - snmp_response_obj red_clearance_duration; + streets_snmp_cmd::snmp_response_obj red_clearance_duration; red_clearance_duration.val_int = 15; EXPECT_CALL(*mock_snmp, process_snmp_request(red_clearance_oid , request_type , _) ).WillRepeatedly(testing::DoAll( SetArgReferee<2>(red_clearance_duration), @@ -291,7 +291,7 @@ namespace traffic_signal_controller_service //Define get concurrent phases std::string concurrent_phase_oid = ntcip_oids::PHASE_CONCURRENCY + "." + std::to_string(i); - snmp_response_obj concurrent_phase_resp; + streets_snmp_cmd::snmp_response_obj concurrent_phase_resp; if ( i == 1 || i == 2) { concurrent_phase_resp.val_string = {char(5), char(6)}; } @@ -398,29 +398,31 @@ namespace traffic_signal_controller_service .WillRepeatedly(testing::DoAll(testing::Return(true))); // Define command - snmp_response_obj set_val; - set_val.type = snmp_response_obj::response_type::INTEGER; + streets_snmp_cmd::snmp_response_obj set_val; + set_val.type = streets_snmp_cmd::RESPONSE_TYPE::INTEGER; uint64_t start_time = 0; - snmp_cmd_struct::control_type control_type = snmp_cmd_struct::control_type::Hold; - snmp_cmd_struct hold_command(mock_snmp, start_time, control_type, 0); - std::queue tsc_set_command_queue; + streets_snmp_cmd::PHASE_CONTROL_TYPE control_type = streets_snmp_cmd::PHASE_CONTROL_TYPE::HOLD_VEH_PHASES; + streets_snmp_cmd::snmp_cmd_struct hold_command(start_time, control_type, 0); + std::queue tsc_set_command_queue; tsc_set_command_queue.push(hold_command); service.tsc_set_command_queue_ = tsc_set_command_queue; - EXPECT_THROW(service.set_tsc_hold_and_omit(), control_tsc_state_exception); + auto tsc_state_ptr = std::make_shared(service.snmp_client_ptr ); + service.control_tsc_state_ptr_ = std::make_shared(service.snmp_client_ptr, tsc_state_ptr); + EXPECT_THROW(service.set_tsc_hold_and_omit_forceoff_call(), control_tsc_state_exception); ASSERT_EQ(1, service.tsc_set_command_queue_.size()); // Test control_tsc_phases service.control_tsc_phases(); ASSERT_EQ(0, service.tsc_set_command_queue_.size()); start_time = std::chrono::duration_cast(std::chrono::system_clock::now().time_since_epoch()).count() + 100; - snmp_cmd_struct hold_command_2(mock_snmp, start_time, control_type, 0); - std::queue tsc_set_command_queue_2; + streets_snmp_cmd::snmp_cmd_struct hold_command_2(start_time, control_type, 0); + std::queue tsc_set_command_queue_2; tsc_set_command_queue_2.push(hold_command_2); service.tsc_set_command_queue_ = tsc_set_command_queue_2; ASSERT_EQ(1, service.tsc_set_command_queue_.size()); - EXPECT_NO_THROW(service.set_tsc_hold_and_omit()); + EXPECT_NO_THROW(service.set_tsc_hold_and_omit_forceoff_call()); ASSERT_EQ(0, service.tsc_set_command_queue_.size()); } From 8b6c0e58946c9e4210ff61eb85e36a1c4eab346e Mon Sep 17 00:00:00 2001 From: dan-du-car <62157949+dan-du-car@users.noreply.github.com> Date: Tue, 27 Jun 2023 12:18:21 -0400 Subject: [PATCH 04/80] Implement TimingPlanResponse Object and JSON processing (#338) * init * update coverage * update * update * update unit test * correct readme * correct readme * add more unit test --- .sonarqube/sonar-scanner.properties | 7 + build.sh | 1 + coverage.sh | 7 + .../streets_timing_plan/CMakeLists.txt | 79 ++++++ streets_utils/streets_timing_plan/README.md | 110 ++++++++ .../streets_timing_plan_libConfig.cmake.in | 7 + .../include/streets_timing_plan.h | 42 +++ .../include/streets_timing_plan_exception.h | 23 ++ .../streets_timing_plan_exception.cpp | 8 + .../src/models/streets_timing_plan.cpp | 262 ++++++++++++++++++ .../test/streets_timing_plan_test.cpp | 195 +++++++++++++ .../streets_timing_plan/test/test_main.cpp | 6 + 12 files changed, 747 insertions(+) create mode 100644 streets_utils/streets_timing_plan/CMakeLists.txt create mode 100644 streets_utils/streets_timing_plan/README.md create mode 100644 streets_utils/streets_timing_plan/cmake/streets_timing_plan_libConfig.cmake.in create mode 100755 streets_utils/streets_timing_plan/include/streets_timing_plan.h create mode 100644 streets_utils/streets_timing_plan/include/streets_timing_plan_exception.h create mode 100644 streets_utils/streets_timing_plan/src/exceptions/streets_timing_plan_exception.cpp create mode 100755 streets_utils/streets_timing_plan/src/models/streets_timing_plan.cpp create mode 100644 streets_utils/streets_timing_plan/test/streets_timing_plan_test.cpp create mode 100644 streets_utils/streets_timing_plan/test/test_main.cpp diff --git a/.sonarqube/sonar-scanner.properties b/.sonarqube/sonar-scanner.properties index f3a9debc8..992c78576 100644 --- a/.sonarqube/sonar-scanner.properties +++ b/.sonarqube/sonar-scanner.properties @@ -33,6 +33,8 @@ sonar.coverageReportPaths= /home/carma-streets/kafka_clients/coverage/coverage.x /home/carma-streets/streets_utils/streets_vehicle_scheduler/coverage/coverage.xml, \ /home/carma-streets/streets_utils/streets_desired_phase_plan/coverage/coverage.xml, \ /home/carma-streets/streets_utils/streets_phase_control_schedule/coverage/coverage.xml, \ +/home/carma-streets/streets_utils/streets_timing_plan/coverage/coverage.xml, \ +/home/carma-streets/streets_utils/streets_signal_optimization/coverage/coverage.xml /home/carma-streets/streets_utils/streets_signal_optimization/coverage/coverage.xml, \ /home/carma-streets/streets_utils/streets_snmp_cmd/coverage/coverage.xml @@ -69,6 +71,7 @@ streets_service_base, \ streets_vehicle_list, \ streets_signal_phase_and_timing, \ streets_phase_control_schedule, \ +streets_timing_plan, \ streets_tsc_configuration, \ tsc_client_service, \ streets_vehicle_scheduler, \ @@ -94,6 +97,7 @@ streets_tsc_configuration.sonar.projectBaseDir=/home/carma-streets/streets_utils streets_desired_phase_plan.sonar.projectBaseDir=/home/carma-streets/streets_utils/streets_desired_phase_plan streets_signal_optimization.sonar.projectBaseDir=/home/carma-streets/streets_utils/streets_signal_optimization streets_phase_control_schedule.sonar.projectBaseDir=/home/carma-streets/streets_utils/streets_phase_control_schedule +streets_timing_plan.sonar.projectBaseDir=/home/carma-streets/streets_utils/streets_timing_plan streets_snmp_cmd.sonar.projectBaseDir=/home/carma-streets/streets_utils/streets_snmp_cmd @@ -130,6 +134,8 @@ streets_signal_optimization.sonar.sources =src/,include/ streets_signal_optimization.sonar.exclusions =test/** streets_phase_control_schedule.sonar.sources =src/,include/ streets_phase_control_schedule.sonar.exclusions =test/** +streets_timing_plan.sonar.sources =src/,include/ +streets_timing_plan.sonar.exclusions =test/** streets_snmp_cmd.sonar.sources =src/,include/ streets_snmp_cmd.sonar.exclusions =test/** @@ -151,6 +157,7 @@ streets_tsc_configuration.sonar.tests=test/ streets_desired_phase_plan.sonar.tests=test/ streets_signal_optimization.sonar.tests=test/ streets_phase_control_schedule.sonar.tests=test/ +streets_timing_plan.sonar.tests=test/ streets_snmp_cmd.sonar.tests=test/ diff --git a/build.sh b/build.sh index 9f0f95f19..bde47325d 100755 --- a/build.sh +++ b/build.sh @@ -28,6 +28,7 @@ MAKE_INSTALL_DIRS=( "streets_utils/streets_tsc_configuration" "streets_utils/streets_desired_phase_plan" "streets_utils/streets_phase_control_schedule" + "streets_utils/streets_timing_plan" "streets_utils/streets_signal_phase_and_timing" "streets_utils/streets_api/intersection_client_api" "streets_utils/streets_vehicle_scheduler" diff --git a/coverage.sh b/coverage.sh index 1f6ca372b..37937ca33 100755 --- a/coverage.sh +++ b/coverage.sh @@ -90,6 +90,13 @@ mkdir coverage cd /home/carma-streets/ gcovr --sonarqube streets_utils/streets_phase_control_schedule/coverage/coverage.xml -s -f streets_utils/streets_phase_control_schedule/ -r . +cd /home/carma-streets/streets_utils/streets_timing_plan/build/ +./streets_timing_plan_test --gtest_output=xml:../../../test_results/ +cd /home/carma-streets/streets_utils/streets_timing_plan +mkdir coverage +cd /home/carma-streets/ +gcovr --sonarqube streets_utils/streets_timing_plan/coverage/coverage.xml -s -f streets_utils/streets_timing_plan/ -r . + cd /home/carma-streets/streets_utils/streets_signal_optimization/build/ ./streets_signal_optimization_test --gtest_output=xml:../../../test_results/ cd /home/carma-streets/streets_utils/streets_signal_optimization diff --git a/streets_utils/streets_timing_plan/CMakeLists.txt b/streets_utils/streets_timing_plan/CMakeLists.txt new file mode 100644 index 000000000..d8c60ab02 --- /dev/null +++ b/streets_utils/streets_timing_plan/CMakeLists.txt @@ -0,0 +1,79 @@ +cmake_minimum_required(VERSION 3.10.2) +project(streets_timing_plan) +######################################################## +# Find Dependent Packages and set configurations +######################################################## +set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fPIC") +find_package(spdlog REQUIRED) +find_package(GTest REQUIRED CONFIG) +add_definitions(-DSPDLOG_ACTIVE_LEVEL=SPDLOG_LEVEL_TRACE) +find_package(RapidJSON REQUIRED) +# Add definition for rapidjson to include std::string +add_definitions(-DRAPIDJSON_HAS_STDSTRING=1) + +######################################################## +# Build Library +######################################################## +file(GLOB SOURCES ${CMAKE_CURRENT_SOURCE_DIR}/src/**/*.cpp) +add_library(${PROJECT_NAME}_lib ${SOURCES} ) +target_include_directories(${PROJECT_NAME}_lib + PUBLIC + $ + $ + PRIVATE + ${CMAKE_CURRENT_SOURCE_DIR}/src +) +target_link_libraries( ${PROJECT_NAME}_lib + PUBLIC + spdlog::spdlog + rapidjson +) + +######################################################## +# Install streets_timing_plan package. +######################################################## +file(GLOB files ${CMAKE_CURRENT_SOURCE_DIR}/include/*.h) +file(GLOB templates ${CMAKE_CURRENT_SOURCE_DIR}/include/internal/*.tpp) +install( + TARGETS ${PROJECT_NAME}_lib + EXPORT ${PROJECT_NAME}_libTargets + LIBRARY DESTINATION lib + INCLUDES DESTINATION include + ARCHIVE DESTINATION lib +) +install( + EXPORT ${PROJECT_NAME}_libTargets + FILE ${PROJECT_NAME}_libTargets.cmake + DESTINATION lib/cmake/${PROJECT_NAME}_lib/ + NAMESPACE ${PROJECT_NAME}_lib:: +) +include(CMakePackageConfigHelpers) +configure_package_config_file( + cmake/${PROJECT_NAME}_libConfig.cmake.in + ${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_NAME}_libConfig.cmake + INSTALL_DESTINATION lib/${PROJECT_NAME}_lib/${PROJECT_NAME}_lib/ ) +install( + FILES ${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_NAME}_libConfig.cmake + DESTINATION lib/cmake/${PROJECT_NAME}_lib/ +) +install(FILES ${files} DESTINATION include) + +######################## +# googletest for unit testing +######################## +set(BINARY ${PROJECT_NAME}_test) +file(GLOB_RECURSE TEST_SOURCES LIST_DIRECTORIES false test/*.h test/*.cpp) +set(SOURCES ${TEST_SOURCES} WORKING_DIRECTORY ${PROJECT_SOURCE_DIR}/test) +add_executable(${BINARY} ${TEST_SOURCES}) +add_test(NAME ${BINARY} COMMAND ${BINARY}) +target_include_directories(${BINARY} PUBLIC ${PROJECT_SOURCE_DIR}/include) +target_link_libraries(${BINARY} + PUBLIC + ${PROJECT_NAME}_lib +) + +if(${CMAKE_VERSION} VERSION_GREATER_EQUAL "3.20") + target_link_libraries( ${BINARY} PUBLIC GTest::gtest ) +else() + target_link_libraries( ${BINARY} PUBLIC gtest ) +endif() \ No newline at end of file diff --git a/streets_utils/streets_timing_plan/README.md b/streets_utils/streets_timing_plan/README.md new file mode 100644 index 000000000..a3f62251b --- /dev/null +++ b/streets_utils/streets_timing_plan/README.md @@ -0,0 +1,110 @@ +# Streets Timing Plan Command library +## Introduction +This CARMA-streets library is meant to handle JSON deserialization and serialization for external signal timing plan message generated by [MMITSS Roadside Processor] (https://github.com/mmitss/mmitss-az), and a sample signal timing plan message refers to https://github.com/mmitss/mmitss-az/blob/master/src/mrp/priority-request-solver/test/tci-msg-sender/signalPlan.json. + +### Sample +A sample timing plan: +``` +{ + "MsgType": "ActiveTimingPlan", + "TimingPlan": { + "NoOfPhase": 8, + "PhaseNumber": [ + 1, + 2, + 3, + 4, + 5, + 6, + 7, + 8 + ], + "PedWalk": [ + 0, + 7, + 0, + 7, + 0, + 7, + 0, + 7 + ], + "PedClear": [ + 0, + 33, + 0, + 43, + 0, + 33, + 0, + 33 + ], + "MinGreen": [ + 4, + 15, + 4, + 15, + 4, + 15, + 4, + 15 + ], + "Passage": [ + 2.0, + 5.0, + 2.0, + 5.0, + 2.0, + 5.0, + 2.0, + 5.0 + ], + "MaxGreen": [ + 37, + 35, + 19, + 40, + 32, + 40, + 19, + 29 + ], + "YellowChange": [ + 3.0, + 4.0, + 3.0, + 3.6, + 3.0, + 4.0, + 3.0, + 3.6 + ], + "RedClear": [ + 1.0, + 2.5, + 1.0, + 3.4000000000000004, + 1.0, + 2.5, + 1.0, + 3.4000000000000004 + ], + "PhaseRing": [ + 1, + 1, + 1, + 1, + 2, + 2, + 2, + 2 + ] + } +} +``` +## Including library +``` +find_package( streets_timing_plan_lib REQUIRED ) +... +target_link_libraries( PUBLIC streets_timing_plan_lib::streets_timing_plan_lib ) +``` diff --git a/streets_utils/streets_timing_plan/cmake/streets_timing_plan_libConfig.cmake.in b/streets_utils/streets_timing_plan/cmake/streets_timing_plan_libConfig.cmake.in new file mode 100644 index 000000000..990da0c79 --- /dev/null +++ b/streets_utils/streets_timing_plan/cmake/streets_timing_plan_libConfig.cmake.in @@ -0,0 +1,7 @@ +@PACKAGE_INIT@ + +include(CMakeFindDependencyMacro) +find_dependency(spdlog REQUIRED) +find_dependency(RapidJSON REQUIRED) + +include("${CMAKE_CURRENT_LIST_DIR}/streets_timing_plan_libTargets.cmake") diff --git a/streets_utils/streets_timing_plan/include/streets_timing_plan.h b/streets_utils/streets_timing_plan/include/streets_timing_plan.h new file mode 100755 index 000000000..ac7e178d3 --- /dev/null +++ b/streets_utils/streets_timing_plan/include/streets_timing_plan.h @@ -0,0 +1,42 @@ +#pragma once +#include "streets_timing_plan_exception.h" +#include +#include +#include +#include +#include +#include +#include +#include "streets_phase_control_command.h" +#include + +namespace streets_timing_plan +{ + + struct streets_timing_plan + { + const std::string TIMING_PLAN_MSG_TYPE = "ActiveTimingPlan"; + std::string msg_type; + int number_of_phase = 0; + std::vector phase_number_v; + std::vector pedestrian_walk_v; + std::vector pedestrian_clear_v; + std::vector min_green_v; + std::vector passage_v; + std::vector max_green_v; + std::vector yellow_change_v; + std::vector red_clear_v; + std::vector phase_ring_v; + + /** + * @brief Deserialize Timing Plan JSON into Timing Plan object. + * + * @param valTiming Plan JSON. + */ + void fromJson(const std::string &json); + /** + * @brief Serialize Timing Plan into JSON String + */ + rapidjson::Document toJson() const; + }; +} \ No newline at end of file diff --git a/streets_utils/streets_timing_plan/include/streets_timing_plan_exception.h b/streets_utils/streets_timing_plan/include/streets_timing_plan_exception.h new file mode 100644 index 000000000..60ddddc25 --- /dev/null +++ b/streets_utils/streets_timing_plan/include/streets_timing_plan_exception.h @@ -0,0 +1,23 @@ +#pragma once + +#include + + + +namespace streets_timing_plan { + /** + * @brief Runtime error related to processing streets_timing_plan. + */ + class streets_timing_plan_exception : public std::runtime_error{ + public: + /** + * @brief Destructor. + */ + ~streets_timing_plan_exception() override; + /** + * @brief Constructor. + * @param msg String exception message. + */ + explicit streets_timing_plan_exception(const std::string &msg ); + }; +} \ No newline at end of file diff --git a/streets_utils/streets_timing_plan/src/exceptions/streets_timing_plan_exception.cpp b/streets_utils/streets_timing_plan/src/exceptions/streets_timing_plan_exception.cpp new file mode 100644 index 000000000..7cbbff354 --- /dev/null +++ b/streets_utils/streets_timing_plan/src/exceptions/streets_timing_plan_exception.cpp @@ -0,0 +1,8 @@ +#include "streets_timing_plan_exception.h" + +namespace streets_timing_plan { + + streets_timing_plan_exception::streets_timing_plan_exception(const std::string &msg): std::runtime_error(msg){}; + + streets_timing_plan_exception::~streets_timing_plan_exception() = default; +} \ No newline at end of file diff --git a/streets_utils/streets_timing_plan/src/models/streets_timing_plan.cpp b/streets_utils/streets_timing_plan/src/models/streets_timing_plan.cpp new file mode 100755 index 000000000..8f1840c92 --- /dev/null +++ b/streets_utils/streets_timing_plan/src/models/streets_timing_plan.cpp @@ -0,0 +1,262 @@ +#include "streets_timing_plan.h" +#include + +namespace streets_timing_plan +{ + void streets_timing_plan::fromJson(const std::string &json) + { + rapidjson::Document doc; + doc.Parse(json); + if (doc.HasParseError()) + { + throw streets_timing_plan_exception("streets_timing_plan message JSON is misformatted."); + } + + if (doc.HasMember("MsgType") && doc.FindMember("MsgType")->value.IsString()) + { + std::string value = doc.FindMember("MsgType")->value.GetString(); + if (value != TIMING_PLAN_MSG_TYPE) + { + throw streets_timing_plan_exception("streets_timing_plan requires MsgType property value (= " + TIMING_PLAN_MSG_TYPE + "), but received " + value + " instead."); + } + msg_type = value; + } + else + { + throw streets_timing_plan_exception("streets_timing_plan is missing required MsgType property."); + } + + if (!doc.HasMember("TimingPlan") || !doc.FindMember("TimingPlan")->value.IsObject()) + { + throw streets_timing_plan_exception("streets_timing_plan is missing required TimingPlan property."); + } + + auto timing_plan_value = doc.FindMember("TimingPlan")->value.GetObject(); + if (timing_plan_value.HasMember("NoOfPhase") && timing_plan_value.FindMember("NoOfPhase")->value.IsUint64()) + { + number_of_phase = timing_plan_value.FindMember("NoOfPhase")->value.GetUint(); + } + else + { + throw streets_timing_plan_exception("streets_timing_plan is missing required NoOfPhase property."); + } + + if (timing_plan_value.HasMember("PhaseNumber") && timing_plan_value.FindMember("PhaseNumber")->value.IsArray()) + { + for (const auto &itr : timing_plan_value.FindMember("PhaseNumber")->value.GetArray()) + { + phase_number_v.push_back(itr.GetInt()); + } + } + else + { + throw streets_timing_plan_exception("streets_timing_plan is missing required PhaseNumber property."); + } + + if (timing_plan_value.HasMember("PedWalk") && timing_plan_value.FindMember("PedWalk")->value.IsArray()) + { + for (const auto &itr : timing_plan_value.FindMember("PedWalk")->value.GetArray()) + { + pedestrian_walk_v.push_back(itr.GetInt()); + } + } + else + { + throw streets_timing_plan_exception("streets_timing_plan is missing required PedWalk property."); + } + + if (timing_plan_value.HasMember("PedClear") && timing_plan_value.FindMember("PedClear")->value.IsArray()) + { + for (const auto &itr : timing_plan_value.FindMember("PedClear")->value.GetArray()) + { + pedestrian_clear_v.push_back(itr.GetInt()); + } + } + else + { + throw streets_timing_plan_exception("streets_timing_plan is missing required PedClear property."); + } + + if (timing_plan_value.HasMember("MinGreen") && timing_plan_value.FindMember("MinGreen")->value.IsArray()) + { + for (const auto &itr : timing_plan_value.FindMember("MinGreen")->value.GetArray()) + { + min_green_v.push_back(itr.GetInt()); + } + } + else + { + throw streets_timing_plan_exception("streets_timing_plan is missing required MinGreen property."); + } + + if (timing_plan_value.HasMember("Passage") && timing_plan_value.FindMember("Passage")->value.IsArray()) + { + for (const auto &itr : timing_plan_value.FindMember("Passage")->value.GetArray()) + { + passage_v.push_back(itr.GetDouble()); + } + } + else + { + throw streets_timing_plan_exception("streets_timing_plan is missing required Passage property."); + } + + if (timing_plan_value.HasMember("MaxGreen") && timing_plan_value.FindMember("MaxGreen")->value.IsArray()) + { + for (const auto &itr : timing_plan_value.FindMember("MaxGreen")->value.GetArray()) + { + max_green_v.push_back(itr.GetInt()); + } + } + else + { + throw streets_timing_plan_exception("streets_timing_plan is missing required MaxGreen property."); + } + + if (timing_plan_value.HasMember("YellowChange") && timing_plan_value.FindMember("YellowChange")->value.IsArray()) + { + for (const auto &itr : timing_plan_value.FindMember("YellowChange")->value.GetArray()) + { + yellow_change_v.push_back(itr.GetDouble()); + } + } + else + { + throw streets_timing_plan_exception("streets_timing_plan is missing required YellowChange property."); + } + + if (timing_plan_value.HasMember("RedClear") && timing_plan_value.FindMember("RedClear")->value.IsArray()) + { + for (const auto &itr : timing_plan_value.FindMember("RedClear")->value.GetArray()) + { + red_clear_v.push_back(itr.GetDouble()); + } + } + else + { + throw streets_timing_plan_exception("streets_timing_plan is missing required RedClear property."); + } + + if (timing_plan_value.HasMember("PhaseRing") && timing_plan_value.FindMember("PhaseRing")->value.IsArray()) + { + for (const auto &itr : timing_plan_value.FindMember("PhaseRing")->value.GetArray()) + { + phase_ring_v.push_back(itr.GetInt()); + } + } + else + { + throw streets_timing_plan_exception("streets_timing_plan is missing required PhaseRing property."); + } + } + + rapidjson::Document streets_timing_plan::toJson() const + { + // document is the root of a JSON message + rapidjson::Document document; + + // Define the document as an object + document.SetObject(); + + // Must pass an allocator when the object may need to allocate memory + rapidjson::Document::AllocatorType &allocator = document.GetAllocator(); + + document.AddMember("MsgType", TIMING_PLAN_MSG_TYPE, allocator); + + // Create a rapidjson timing plan object ytpe + rapidjson::Value timing_plan_obj_value(rapidjson::kObjectType); + timing_plan_obj_value.AddMember("NoOfPhase", number_of_phase, allocator); + + // Create a rapidjson array type for number of phases + rapidjson::Value phase_number_array_value(rapidjson::kArrayType); + + // Add an array of phase number to JSON array + for (const auto &phase_num : phase_number_v) + { + phase_number_array_value.PushBack(phase_num, allocator); + } + timing_plan_obj_value.AddMember("PhaseNumber", phase_number_array_value, allocator); + + // Create a rapidjson array type for pedestrian walk + rapidjson::Value pede_walk_array_value(rapidjson::kArrayType); + + // Add an array of pedestrian walk to rapidjson array + for (const auto &ped_walk : pedestrian_walk_v) + { + pede_walk_array_value.PushBack(ped_walk, allocator); + } + timing_plan_obj_value.AddMember("PedWalk", pede_walk_array_value, allocator); + + // Create a rapidjson array type for pedestrian clear + rapidjson::Value pede_clear_array_value(rapidjson::kArrayType); + + // Add an array of pedestrian clear to rapidjson array + for (const auto &pede_clear : pedestrian_clear_v) + { + pede_clear_array_value.PushBack(pede_clear, allocator); + } + timing_plan_obj_value.AddMember("PedClear", pede_clear_array_value, allocator); + + // Create a rapidjson array type for minimum green + rapidjson::Value min_green_array_value(rapidjson::kArrayType); + + // Add an array of pedestrian clear to rapidjson array + for (const auto &min_green : min_green_v) + { + min_green_array_value.PushBack(min_green, allocator); + } + timing_plan_obj_value.AddMember("MinGreen", min_green_array_value, allocator); + + // Create a rapidjson array type for passage + rapidjson::Value passage_array_value(rapidjson::kArrayType); + + // Add an array of pedestrian clear to rapidjson array + for (const auto &passage : passage_v) + { + passage_array_value.PushBack(passage, allocator); + } + timing_plan_obj_value.AddMember("Passage", passage_array_value, allocator); + + // Create a rapidjson array type for maximum green + rapidjson::Value max_green_array_value(rapidjson::kArrayType); + + // Add an array of pedestrian clear to rapidjson array + for (const auto &max_green : max_green_v) + { + max_green_array_value.PushBack(max_green, allocator); + } + timing_plan_obj_value.AddMember("MaxGreen", max_green_array_value, allocator); + + // Create a rapidjson array type for yellow change + rapidjson::Value yellow_change_array_value(rapidjson::kArrayType); + + // Add an array of pedestrian clear to rapidjson array + for (const auto &yellow_change : yellow_change_v) + { + yellow_change_array_value.PushBack(yellow_change, allocator); + } + timing_plan_obj_value.AddMember("YellowChange", yellow_change_array_value, allocator); + + // Create a rapidjson array type for red clear + rapidjson::Value red_clear_array_value(rapidjson::kArrayType); + + // Add an array of pedestrian clear to rapidjson array + for (const auto &red_clear : red_clear_v) + { + red_clear_array_value.PushBack(red_clear, allocator); + } + timing_plan_obj_value.AddMember("RedClear", red_clear_array_value, allocator); + + // Create a rapidjson array type for phase ring + rapidjson::Value phase_ring_array_value(rapidjson::kArrayType); + + // Add an array of pedestrian clear to rapidjson array + for (const auto &phase_ring : phase_ring_v) + { + phase_ring_array_value.PushBack(phase_ring, allocator); + } + timing_plan_obj_value.AddMember("PhaseRing", phase_ring_array_value, allocator); + document.AddMember("TimingPlan", timing_plan_obj_value, allocator); + return document; + } +} \ No newline at end of file diff --git a/streets_utils/streets_timing_plan/test/streets_timing_plan_test.cpp b/streets_utils/streets_timing_plan/test/streets_timing_plan_test.cpp new file mode 100644 index 000000000..f28456377 --- /dev/null +++ b/streets_utils/streets_timing_plan/test/streets_timing_plan_test.cpp @@ -0,0 +1,195 @@ + +#include "streets_timing_plan.h" +#include "streets_timing_plan_exception.h" + +#include +#include +#include +#include +#include + +#include +#include +#include + +using namespace streets_timing_plan; + +namespace +{ + + class streets_timing_plan_test : public ::testing::Test + { + /** + * @brief Test Setup method run before each test. + */ + void SetUp() override + { + } + /** + * @brief Test TearDown method run after each test. + */ + void TearDown() override + { + } + }; +}; + +TEST_F(streets_timing_plan_test, toJson) +{ + streets_timing_plan::streets_timing_plan timing_plan; + timing_plan.number_of_phase = 8; + std::vector phase_numbers_v{1, 2, 3, 4, 5, 6, 7, 8}; + std::swap(timing_plan.phase_number_v, phase_numbers_v); + std::vector pede_walk_v{0, 7, 0, 7, 0, 7, 0, 7}; + std::swap(timing_plan.pedestrian_walk_v, pede_walk_v); + std::vector pede_clear_v{0, 33, 1, 43, 0, 33, 0, 33}; + std::swap(timing_plan.pedestrian_clear_v, pede_clear_v); + std::vector min_green_v{4, 15, 4, 15, 4, 15, 4, 15}; + std::swap(timing_plan.min_green_v, min_green_v); + std::vector passage_v{2.0, 5.0, 2.0, 5.0, 2.0, 5.0, 2.0, 5.0}; + std::swap(timing_plan.passage_v, passage_v); + std::vector max_green_v{37, 35, 19, 40, 32, 19, 29}; + std::swap(timing_plan.max_green_v, max_green_v); + std::vector yellow_change_v{3.0, 4.0, 3.0, 3.6, 4.0, 3.0, 3.6}; + std::swap(timing_plan.yellow_change_v, yellow_change_v); + std::vector red_clear_v{1.0, 2.5, 1.0, 3.40000000000004, 1.0, 2.5, 1.0, 3.4000000000000000000004}; + std::swap(timing_plan.red_clear_v, red_clear_v); + std::vector phase_ring_v{1, 1, 1, 1, 1, 2, 2, 2, 2}; + std::swap(timing_plan.phase_ring_v, phase_ring_v); + + auto timing_plan_json_value = timing_plan.toJson(); + rapidjson::StringBuffer strbuf; + rapidjson::Writer writer(strbuf); + timing_plan_json_value.Accept(writer); + std::string expected_str = "{\"MsgType\":\"ActiveTimingPlan\",\"TimingPlan\":{\"NoOfPhase\":8,\"PhaseNumber\":[1,2,3,4,5,6,7,8],\"PedWalk\":[0,7,0,7,0,7,0,7],\"PedClear\":[0,33,1,43,0,33,0,33],\"MinGreen\":[4,15,4,15,4,15,4,15],\"Passage\":[2.0,5.0,2.0,5.0,2.0,5.0,2.0,5.0],\"MaxGreen\":[37,35,19,40,32,19,29],\"YellowChange\":[3.0,4.0,3.0,3.6,4.0,3.0,3.6],\"RedClear\":[1.0,2.5,1.0,3.40000000000004,1.0,2.5,1.0,3.4],\"PhaseRing\":[1,1,1,1,1,2,2,2,2]}}"; + ASSERT_EQ(expected_str, strbuf.GetString()); +} + +TEST_F(streets_timing_plan_test, fromJson) +{ + streets_timing_plan::streets_timing_plan timing_plan; + // Valid JSON + std::string expected_str = "{\"MsgType\":\"ActiveTimingPlan\",\"TimingPlan\":{\"NoOfPhase\":8,\"PhaseNumber\":[1,2,3,4,5,6,7,8],\"PedWalk\":[0,7,0,7,0,7,0,7],\"PedClear\":[0,33,1,43,0,33,0,33],\"MinGreen\":[4,15,4,15,4,15,4,15],\"Passage\":[2.1,5.0,2.0,5.0,2.0,5.0,2.0,5.0],\"MaxGreen\":[37,35,19,40,32,19,29],\"YellowChange\":[3.0,4.0,3.0,3.6,4.0,3.0,3.6],\"RedClear\":[1.0,2.5,1.0,3.40000000000004,1.0,2.5,1.0,3.4],\"PhaseRing\":[1,1,1,1,1,2,2,2,2]}}"; + timing_plan.fromJson(expected_str); + ASSERT_EQ(8, timing_plan.number_of_phase); + ASSERT_EQ(timing_plan.TIMING_PLAN_MSG_TYPE, timing_plan.msg_type); + ASSERT_EQ(8, timing_plan.phase_number_v.size()); + std::stringstream ss; + for (auto phase_num : timing_plan.phase_number_v) + { + ss << phase_num; + } + ASSERT_EQ("12345678", ss.str()); + + ss.str(""); + for (auto ped_walk : timing_plan.pedestrian_walk_v) + { + ss << ped_walk; + } + ASSERT_EQ("07070707", ss.str()); + + ss.str(""); + for (auto ped_clear : timing_plan.pedestrian_clear_v) + { + ss << ped_clear; + } + ASSERT_EQ("033143033033", ss.str()); + + ss.str(""); + for (auto min_green : timing_plan.min_green_v) + { + ss << min_green; + } + ASSERT_EQ("415415415415", ss.str()); + + ss.str(""); + for (auto passage : timing_plan.passage_v) + { + ss << passage << ","; + } + ASSERT_EQ("2.1,5,2,5,2,5,2,5,", ss.str()); + + ss.str(""); + for (auto max_green : timing_plan.max_green_v) + { + ss << max_green; + } + ASSERT_EQ("37351940321929", ss.str()); + + ss.str(""); + for (auto yellow_change : timing_plan.yellow_change_v) + { + ss << yellow_change << ","; + } + ASSERT_EQ("3,4,3,3.6,4,3,3.6,", ss.str()); + + ss.str(""); + for (auto red_clear : timing_plan.red_clear_v) + { + ss << red_clear << ","; + } + ASSERT_EQ("1,2.5,1,3.4,1,2.5,1,3.4,", ss.str()); + + ss.str(""); + for (auto phase_ring : timing_plan.phase_ring_v) + { + ss << phase_ring << ","; + } + ASSERT_EQ("1,1,1,1,1,2,2,2,2,", ss.str()); + + // Malformatted JSON + std::string invalid_json = "{invalid}"; + ASSERT_THROW(timing_plan.fromJson(invalid_json), streets_timing_plan_exception); + + // Missing MsgType + std::string input_json = "{\"key\":\"value\"}"; + ASSERT_THROW(timing_plan.fromJson(input_json), streets_timing_plan_exception); + + // Incorrect MsgType + input_json = "{\"MsgType\":\"Missing\"}"; + ASSERT_THROW(timing_plan.fromJson(input_json), streets_timing_plan_exception); + + // Missing TimingPlan property + input_json = "{\"MsgType\":\"ActiveTimingPlan\"}"; + ASSERT_THROW(timing_plan.fromJson(input_json), streets_timing_plan_exception); + + // Missing NoOfPhase property + input_json = "{\"MsgType\":\"ActiveTimingPlan\", \"TimingPlan\": {\"invalid\": \"invalid\"}}"; + ASSERT_THROW(timing_plan.fromJson(input_json), streets_timing_plan_exception); + + // Missing PhaseNumber property + input_json = "{\"MsgType\":\"ActiveTimingPlan\", \"TimingPlan\": {\"NoOfPhase\": 8}}"; + ASSERT_THROW(timing_plan.fromJson(input_json), streets_timing_plan_exception); + + // Missing PedWalk property + input_json = "{\"MsgType\":\"ActiveTimingPlan\", \"TimingPlan\": {\"NoOfPhase\": 8,\"PhaseNumber\":[1,2,3,4,5,6,7,8]}}"; + ASSERT_THROW(timing_plan.fromJson(input_json), streets_timing_plan_exception); + + // Missing PedClear property + input_json = "{\"MsgType\":\"ActiveTimingPlan\", \"TimingPlan\": {\"NoOfPhase\": 8,\"PhaseNumber\":[1,2,3,4,5,6,7,8],\"PedWalk\":[0,7,0,7,0,7,0,7]}}"; + ASSERT_THROW(timing_plan.fromJson(input_json), streets_timing_plan_exception); + + // Missing MinGreen property + input_json = "{\"MsgType\":\"ActiveTimingPlan\", \"TimingPlan\": {\"NoOfPhase\": 8,\"PhaseNumber\":[1,2,3,4,5,6,7,8],\"PedWalk\":[0,7,0,7,0,7,0,7],\"PedClear\":[0,33,1,43,0,33,0,33]}}"; + ASSERT_THROW(timing_plan.fromJson(input_json), streets_timing_plan_exception); + + // Missing Passage property + input_json = "{\"MsgType\":\"ActiveTimingPlan\", \"TimingPlan\": {\"NoOfPhase\": 8,\"PhaseNumber\":[1,2,3,4,5,6,7,8],\"PedWalk\":[0,7,0,7,0,7,0,7],\"PedClear\":[0,33,1,43,0,33,0,33],\"MinGreen\":[4,15,4,15,4,15,4,15]}}"; + ASSERT_THROW(timing_plan.fromJson(input_json), streets_timing_plan_exception); + + // Missing MaxGreen property + input_json = "{\"MsgType\":\"ActiveTimingPlan\", \"TimingPlan\": {\"NoOfPhase\": 8,\"PhaseNumber\":[1,2,3,4,5,6,7,8],\"PedWalk\":[0,7,0,7,0,7,0,7],\"PedClear\":[0,33,1,43,0,33,0,33],\"MinGreen\":[4,15,4,15,4,15,4,15],\"Passage\":[2.0,5.0,2.0,5.0,2.0,5.0,2.0,5.0]}}"; + ASSERT_THROW(timing_plan.fromJson(input_json), streets_timing_plan_exception); + + // Missing YellowChange property + input_json = "{\"MsgType\":\"ActiveTimingPlan\", \"TimingPlan\": {\"NoOfPhase\": 8,\"PhaseNumber\":[1,2,3,4,5,6,7,8],\"PedWalk\":[0,7,0,7,0,7,0,7],\"PedClear\":[0,33,1,43,0,33,0,33],\"MinGreen\":[4,15,4,15,4,15,4,15],\"Passage\":[2.0,5.0,2.0,5.0,2.0,5.0,2.0,5.0],\"MaxGreen\":[37,35,19,40,32,19,29]}}"; + ASSERT_THROW(timing_plan.fromJson(input_json), streets_timing_plan_exception); + + // Missing RedClear property + input_json = "{\"MsgType\":\"ActiveTimingPlan\", \"TimingPlan\": {\"NoOfPhase\": 8,\"PhaseNumber\":[1,2,3,4,5,6,7,8],\"PedWalk\":[0,7,0,7,0,7,0,7],\"PedClear\":[0,33,1,43,0,33,0,33],\"MinGreen\":[4,15,4,15,4,15,4,15],\"Passage\":[2.0,5.0,2.0,5.0,2.0,5.0,2.0,5.0],\"MaxGreen\":[37,35,19,40,32,19,29],\"YellowChange\":[3.0,4.0,3.0,3.6,4.0,3.0,3.6]}}"; + ASSERT_THROW(timing_plan.fromJson(input_json), streets_timing_plan_exception); + + // Missing PhaseRing property + input_json = "{\"MsgType\":\"ActiveTimingPlan\", \"TimingPlan\": {\"NoOfPhase\": 8,\"PhaseNumber\":[1,2,3,4,5,6,7,8],\"PedWalk\":[0,7,0,7,0,7,0,7],\"PedClear\":[0,33,1,43,0,33,0,33],\"MinGreen\":[4,15,4,15,4,15,4,15],\"Passage\":[2.0,5.0,2.0,5.0,2.0,5.0,2.0,5.0],\"MaxGreen\":[37,35,19,40,32,19,29],\"YellowChange\":[3.0,4.0,3.0,3.6,4.0,3.0,3.6],\"RedClear\":[1.0,2.5,1.0,3.40000000000004,1.0,2.5,1.0,3.4]}}"; + ASSERT_THROW(timing_plan.fromJson(input_json), streets_timing_plan_exception); +} \ No newline at end of file diff --git a/streets_utils/streets_timing_plan/test/test_main.cpp b/streets_utils/streets_timing_plan/test/test_main.cpp new file mode 100644 index 000000000..1e925a90b --- /dev/null +++ b/streets_utils/streets_timing_plan/test/test_main.cpp @@ -0,0 +1,6 @@ +#include "gtest/gtest.h" +int main(int argc, char **argv) +{ + ::testing::InitGoogleTest(&argc, argv); + return RUN_ALL_TESTS(); +} \ No newline at end of file From 30251d93066422be8ec9bccfa496e2244fc89e13 Mon Sep 17 00:00:00 2001 From: Saikrishna Bairamoni <84093461+SaikrishnaBairamoni@users.noreply.github.com> Date: Tue, 18 Jul 2023 16:15:32 -0400 Subject: [PATCH 05/80] update dockerfiles to point dev images --- docker-compose.yml | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/docker-compose.yml b/docker-compose.yml index 03274f17f..03a9f29a7 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -60,7 +60,7 @@ services: - ./mysql/localhost.sql:/docker-entrypoint-initdb.d/localhost.sql - mysql-datavolume:/var/lib/mysql php: - image: usdotfhwaops/php:7.5.1 + image: usdotfhwaops/php:latest container_name: php network_mode: host depends_on: @@ -69,7 +69,7 @@ services: tty: true v2xhub: - image: usdotfhwaops/v2xhubamd:7.5.1 + image: usdotfhwaops/v2xhubamd:latest container_name: v2xhub network_mode: host restart: always @@ -95,7 +95,7 @@ services: - /etc/localtime:/etc/localtime:ro - /etc/timezone:/etc/timezone:ro scheduling_service: - image: usdotfhwastolcandidate/scheduling_service:carma-system-4.4.3 + image: usdotfhwastolcandidate/scheduling_service:develop command: sh -c "/wait && /home/carma-streets/scheduling_service/build/scheduling_service" build: context: . @@ -120,7 +120,7 @@ services: - /etc/timezone:/etc/timezone:ro message_services: - image: usdotfhwastolcandidate/message_services:carma-system-4.4.3 + image: usdotfhwastolcandidate/message_services:develop build: context: . dockerfile: message_services/Dockerfile @@ -138,7 +138,7 @@ services: - /etc/localtime:/etc/localtime:ro - /etc/timezone:/etc/timezone:ro intersection_model: - image: usdotfhwastolcandidate/intersection_model:carma-system-4.4.3 + image: usdotfhwastolcandidate/intersection_model:develop build: context: . dockerfile: intersection_model/Dockerfile @@ -156,7 +156,7 @@ services: - /etc/localtime:/etc/localtime:ro - /etc/timezone:/etc/timezone:ro signal_opt_service: - image: usdotfhwastolcandidate/signal_opt_service:carma-system-4.4.3 + image: usdotfhwastolcandidate/signal_opt_service:develop command: sh -c "/wait && /home/carma-streets/signal_opt_service/build/signal_opt_service" build: context: . @@ -182,7 +182,7 @@ services: - /etc/localtime:/etc/localtime:ro - /etc/timezone:/etc/timezone:ro tsc_service: - image: usdotfhwastolcandidate/tsc_service:carma-system-4.4.3 + image: usdotfhwastolcandidate/tsc_service:develop command: sh -c "/wait && /home/carma-streets/tsc_client_service/build/traffic_signal_controller_service" build: context: . From 0e65c45090bdbf001426ee165782e68e1471c5ef Mon Sep 17 00:00:00 2001 From: Saikrishna Bairamoni <84093461+SaikrishnaBairamoni@users.noreply.github.com> Date: Wed, 19 Jul 2023 13:36:25 -0400 Subject: [PATCH 06/80] update docker compose files to point dev images --- docker-compose.yml | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/docker-compose.yml b/docker-compose.yml index 03a9f29a7..2f460eeed 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -95,7 +95,7 @@ services: - /etc/localtime:/etc/localtime:ro - /etc/timezone:/etc/timezone:ro scheduling_service: - image: usdotfhwastolcandidate/scheduling_service:develop + image: usdotfhwastoldev/scheduling_service:develop command: sh -c "/wait && /home/carma-streets/scheduling_service/build/scheduling_service" build: context: . @@ -120,7 +120,7 @@ services: - /etc/timezone:/etc/timezone:ro message_services: - image: usdotfhwastolcandidate/message_services:develop + image: usdotfhwastoldev/message_services:develop build: context: . dockerfile: message_services/Dockerfile @@ -138,7 +138,7 @@ services: - /etc/localtime:/etc/localtime:ro - /etc/timezone:/etc/timezone:ro intersection_model: - image: usdotfhwastolcandidate/intersection_model:develop + image: usdotfhwastoldev/intersection_model:develop build: context: . dockerfile: intersection_model/Dockerfile @@ -156,7 +156,7 @@ services: - /etc/localtime:/etc/localtime:ro - /etc/timezone:/etc/timezone:ro signal_opt_service: - image: usdotfhwastolcandidate/signal_opt_service:develop + image: usdotfhwastoldev/signal_opt_service:develop command: sh -c "/wait && /home/carma-streets/signal_opt_service/build/signal_opt_service" build: context: . @@ -182,7 +182,7 @@ services: - /etc/localtime:/etc/localtime:ro - /etc/timezone:/etc/timezone:ro tsc_service: - image: usdotfhwastolcandidate/tsc_service:develop + image: usdotfhwastoldev/tsc_service:develop command: sh -c "/wait && /home/carma-streets/tsc_client_service/build/traffic_signal_controller_service" build: context: . From e2b09aee637ea6065983fe7c4543211f653bb3da Mon Sep 17 00:00:00 2001 From: Saikrishna Bairamoni <84093461+SaikrishnaBairamoni@users.noreply.github.com> Date: Wed, 19 Jul 2023 14:49:04 -0400 Subject: [PATCH 07/80] Update ci.yml --- .github/workflows/ci.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 3b71fdf10..269696dd5 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -63,7 +63,7 @@ jobs: run: | cd /home/carma-streets/ext/ apt-get install -y libperl-dev curl - curl -L -O http://sourceforge.net/projects/net-snmp/files/net-snmp/5.9.1/net-snmp-5.9.1.tar.gz + curl -k -L -O http://sourceforge.net/projects/net-snmp/files/net-snmp/5.9.1/net-snmp-5.9.1.tar.gz tar -xvzf /home/carma-streets/ext/net-snmp-5.9.1.tar.gz cd net-snmp-5.9.1/ ./configure --with-default-snmp-version="1" --with-sys-contact="@@no.where" --with-sys-location="Unknown" --with-logfile="/var/log/snmpd.log" --with-persistent-directory="/var/net-snmp" From 4c2cff5b0511dcc41cc87076f790fd4d7e7fae20 Mon Sep 17 00:00:00 2001 From: paulbourelly999 <77466294+paulbourelly999@users.noreply.github.com> Date: Wed, 16 Aug 2023 12:49:48 -0400 Subject: [PATCH 08/80] Add vehicle ped call logs (#341) * Adding devcontainer setup and logging for vehicle/ped calls * Removed dev container setup * Added Unit tests and CSV logger * Update * Updates * Fix rapidjson lib naming bug introduced by (https://github.com/Tencent/rapidjson/pull/1901 * Fix rapidjson * Updated ci to remove light clone * Update to resolve build issues * Updated Map calls from [] operator to at to make methods constant Fixed bug in streets_service_base create_daily_logger * Fixed bug * Added unit testing * Added unit tests for streets_service_base create_daily_logger * Added unit test converage for configure logger methods Update methods to be const due to sonar code smells * Updated test cases * Added javadoc comments include default values for daily_logger update unit tests * PR Updates * Added translation for enumeration * Add unit test for spat_enum --- .github/workflows/ci.yml | 4 +- docker-compose.yml | 3 +- intersection_model/Dockerfile | 1 + message_services/Dockerfile | 1 + scheduling_service/Dockerfile | 1 + signal_opt_service/Dockerfile | 1 + .../include/streets_environment_variables.h | 15 ++ .../include/streets_service.h | 18 ++ .../src/streets_service.cpp | 22 ++- .../test/test_streets_service.cpp | 43 ++++- tsc_client_service/CMakeLists.txt | 1 + tsc_client_service/Dockerfile | 1 + .../include/monitor_tsc_state.h | 52 +++++- tsc_client_service/include/ntcip_oids.h | 12 ++ .../include/spat_projection_mode.h | 26 +++ tsc_client_service/include/tsc_service.h | 18 +- tsc_client_service/manifest.json | 8 +- tsc_client_service/src/monitor_tsc_state.cpp | 131 ++++++++++++++- .../src/spat_projection_mode.cpp | 20 +++ tsc_client_service/src/tsc_service.cpp | 81 +++++---- .../test/test_monitor_state.cpp | 158 +++++++++++++++++- .../test/test_spat_projection_mode.cpp | 12 ++ tsc_client_service/test/test_tsc_service.cpp | 20 ++- 23 files changed, 591 insertions(+), 58 deletions(-) create mode 100644 streets_utils/streets_service_base/include/streets_environment_variables.h create mode 100644 tsc_client_service/include/spat_projection_mode.h create mode 100644 tsc_client_service/src/spat_projection_mode.cpp create mode 100644 tsc_client_service/test/test_spat_projection_mode.cpp diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 269696dd5..9cfab4b0a 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -53,7 +53,9 @@ jobs: - name: Install rapidjson run: | cd /home/carma-streets/ext/ - git clone --depth 1 https://github.com/Tencent/rapidjson /home/carma-streets/ext/rapidjson + git clone https://github.com/Tencent/rapidjson /home/carma-streets/ext/rapidjson + cd /home/carma-streets/ext/rapidjson + git checkout a95e013b97ca6523f32da23f5095fcc9dd6067e5 mkdir -p /home/carma-streets/ext/rapidjson/build cd /home/carma-streets/ext/rapidjson/build cmake .. diff --git a/docker-compose.yml b/docker-compose.yml index 341a5ac87..4671af0b6 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -199,9 +199,10 @@ services: WAIT_HOSTS_TIMEOUT: 300 WAIT_SLEEP_INTERVAL: 30 WAIT_HOST_CONNECT_TIMEOUT: 30 - SIMULATION_MODE: TRUE + SIMULATION_MODE: FALSE CONFIG_FILE_PATH: ../manifest.json TIME_SYNC_TOPIC: time_sync + LOGS_DIRECTORY: /home/carma-streets/tsc_client_service/logs/ volumes: - ./tsc_client_service/manifest.json:/home/carma-streets/tsc_client_service/manifest.json - ./tsc_client_service/logs/:/home/carma-streets/tsc_client_service/logs/ diff --git a/intersection_model/Dockerfile b/intersection_model/Dockerfile index 67704e9ac..02892b679 100644 --- a/intersection_model/Dockerfile +++ b/intersection_model/Dockerfile @@ -43,6 +43,7 @@ RUN echo " ------> Install rapidjson..." WORKDIR /home/carma-streets/ext RUN git clone https://github.com/Tencent/rapidjson WORKDIR /home/carma-streets/ext/rapidjson/ +RUN git checkout a95e013b97ca6523f32da23f5095fcc9dd6067e5 RUN mkdir build WORKDIR /home/carma-streets/ext/rapidjson/build RUN cmake .. && make -j diff --git a/message_services/Dockerfile b/message_services/Dockerfile index 30c271410..6a18853cb 100644 --- a/message_services/Dockerfile +++ b/message_services/Dockerfile @@ -42,6 +42,7 @@ RUN echo " ------> Install rapidjson..." WORKDIR /home/carma-streets/ext RUN git clone https://github.com/Tencent/rapidjson WORKDIR /home/carma-streets/ext/rapidjson/ +RUN git checkout a95e013b97ca6523f32da23f5095fcc9dd6067e5 RUN mkdir build WORKDIR /home/carma-streets/ext/rapidjson/build RUN cmake .. && make -j diff --git a/scheduling_service/Dockerfile b/scheduling_service/Dockerfile index 8c9e619a9..e453a843e 100644 --- a/scheduling_service/Dockerfile +++ b/scheduling_service/Dockerfile @@ -23,6 +23,7 @@ RUN echo " ------> Install rapidjson..." WORKDIR /home/carma-streets/ext RUN git clone https://github.com/Tencent/rapidjson WORKDIR /home/carma-streets/ext/rapidjson/ +RUN git checkout a95e013b97ca6523f32da23f5095fcc9dd6067e5 RUN mkdir build WORKDIR /home/carma-streets/ext/rapidjson/build RUN cmake .. && make -j diff --git a/signal_opt_service/Dockerfile b/signal_opt_service/Dockerfile index 166b4ab82..24fdb7557 100644 --- a/signal_opt_service/Dockerfile +++ b/signal_opt_service/Dockerfile @@ -12,6 +12,7 @@ RUN echo " ------> Install rapidjson..." WORKDIR /home/carma-streets/ext RUN git clone https://github.com/Tencent/rapidjson WORKDIR /home/carma-streets/ext/rapidjson/ +RUN git checkout a95e013b97ca6523f32da23f5095fcc9dd6067e5 RUN mkdir build WORKDIR /home/carma-streets/ext/rapidjson/build RUN cmake .. && make -j diff --git a/streets_utils/streets_service_base/include/streets_environment_variables.h b/streets_utils/streets_service_base/include/streets_environment_variables.h new file mode 100644 index 000000000..589f39f01 --- /dev/null +++ b/streets_utils/streets_service_base/include/streets_environment_variables.h @@ -0,0 +1,15 @@ +#pragma once + +#include + +namespace streets_service { + + static const std::string LOGS_DIRECTORY_ENV = "LOGS_DIRECTORY"; + + static const std::string SIMULATION_MODE_ENV = "SIMULATION_MODE"; + + static const std::string CONFIG_FILE_PATH_ENV = "CONFIG_FILE_PATH"; + + static const std::string TIME_SYNC_TOPIC_ENV = "TIME_SYNC_TOPIC"; + +} \ No newline at end of file diff --git a/streets_utils/streets_service_base/include/streets_service.h b/streets_utils/streets_service_base/include/streets_service.h index 9bb9a9fd4..e4837f82b 100644 --- a/streets_utils/streets_service_base/include/streets_service.h +++ b/streets_utils/streets_service_base/include/streets_service.h @@ -4,6 +4,7 @@ #include "kafka_client.h" #include "streets_clock_singleton.h" #include "time_sync_message.h" +#include "streets_environment_variables.h" #include @@ -92,6 +93,17 @@ namespace streets_service { * @return true if in simulation mode, false if in real time more */ bool is_simulation_mode() const; + /** + * @brief Method to create SPDLOG Daily rotating file logger. The logger is accessible by calling spdlog::get(name). The log + * files created by this logger will include data and time in name and will be stored in the LOGS_DIRECTORY set path. + * @param name Name of the logger + * @param extension file extension for log files created + * @param pattern pattern for output of log statments. Please review SPDLOG documentation + * (https://github.com/gabime/spdlog/wiki/3.-Custom-formatting#customizing-format-using-set_pattern). + * @param level log level to set for logger. Can be dynamically changed by getting logger and changing log level. + */ + std::shared_ptr create_daily_logger(const std::string &name, const std::string &extension = ".log", const std::string &pattern = "[%Y-%m-%d %H:%M:%S.%e] %v", + const spdlog::level::level_enum &level = spdlog::level::info ) const; private: std::string _service_name; @@ -100,11 +112,17 @@ namespace streets_service { std::shared_ptr _time_consumer; + std::string _logs_directory; + FRIEND_TEST(test_streets_service, test_consume_time_sync_message); FRIEND_TEST(test_streets_service, test_initialize_consumer); FRIEND_TEST(test_streets_service, test_initialize_producer); FRIEND_TEST(test_streets_service, test_initialize_sim); FRIEND_TEST(test_streets_service, test_get_system_config); + FRIEND_TEST(test_streets_service, test_create_daily_logger); + FRIEND_TEST(test_streets_service, test_create_daily_logger_default); + + diff --git a/streets_utils/streets_service_base/src/streets_service.cpp b/streets_utils/streets_service_base/src/streets_service.cpp index a82ce372e..0c18132bb 100644 --- a/streets_utils/streets_service_base/src/streets_service.cpp +++ b/streets_utils/streets_service_base/src/streets_service.cpp @@ -11,19 +11,20 @@ namespace streets_service { bool streets_service::initialize() { try { - std::string config_file_path = get_system_config("CONFIG_FILE_PATH"); + std::string config_file_path = get_system_config(CONFIG_FILE_PATH_ENV.c_str()); streets_configuration::create(config_file_path); - std::string sim_mode_string = get_system_config("SIMULATION_MODE"); + std::string sim_mode_string = get_system_config(SIMULATION_MODE_ENV.c_str()); _simulation_mode = sim_mode_string.compare("true") == 0 || sim_mode_string.compare("TRUE") == 0 ; streets_clock_singleton::create(_simulation_mode); _service_name = streets_configuration::get_service_name(); SPDLOG_INFO("Initializing {0} streets service in simulation mode : {1}!", _service_name, _simulation_mode); if ( _simulation_mode ) { - std::string time_sync_topic = get_system_config("TIME_SYNC_TOPIC"); + std::string time_sync_topic = get_system_config(TIME_SYNC_TOPIC_ENV.c_str()); if (!initialize_kafka_consumer(time_sync_topic, _time_consumer)){ return false; } } + _logs_directory = get_system_config(LOGS_DIRECTORY_ENV.c_str()); } catch( const streets_configuration_exception &e) { SPDLOG_ERROR("Exception occured during {0} initialization : {1}" , _service_name , e.what()); return false; @@ -36,6 +37,21 @@ namespace streets_service { } + std::shared_ptr streets_service::create_daily_logger(const std::string &name, const std::string &extension, + const std::string &pattern, const spdlog::level::level_enum &level) const + { + auto logger = spdlog::daily_logger_mt( + name, // logger name + _logs_directory +name + extension, // log file name and path + 23, // hours to rotate + 59 // minutes to rotate + ); + // Only log log statement content + logger->set_pattern(pattern); + logger->set_level(level); + logger->flush_on(level); + return logger; + } bool streets_service::initialize_kafka_producer( const std::string &producer_topic, std::shared_ptr &producer ) const { auto client = std::make_unique(); diff --git a/streets_utils/streets_service_base/test/test_streets_service.cpp b/streets_utils/streets_service_base/test/test_streets_service.cpp index 908024a1d..5b674bf09 100644 --- a/streets_utils/streets_service_base/test/test_streets_service.cpp +++ b/streets_utils/streets_service_base/test/test_streets_service.cpp @@ -2,6 +2,8 @@ #include "streets_service.h" #include "mock_kafka_consumer_worker.h" #include "mock_kafka_producer_worker.h" +#include +#include using testing::_; using testing::Return; @@ -11,9 +13,10 @@ namespace streets_service{ class test_streets_service : public testing::Test { protected: void SetUp() { - setenv("SIMULATION_MODE", "TRUE", 1); - setenv("TIME_SYNC_TOPIC", "time_sync", 1); - setenv("CONFIG_FILE_PATH", "../test/test_files/manifest.json", 1); + setenv(SIMULATION_MODE_ENV.c_str(), "TRUE", 1); + setenv(TIME_SYNC_TOPIC_ENV.c_str(), "time_sync", 1); + setenv(CONFIG_FILE_PATH_ENV.c_str(), "../test/test_files/manifest.json", 1); + setenv(LOGS_DIRECTORY_ENV.c_str(), "../logs/", 1); } public: streets_service serv; @@ -48,7 +51,41 @@ namespace streets_service{ ASSERT_EQ(1400, streets_clock_singleton::time_in_ms()); } + TEST_F(test_streets_service, test_create_daily_logger) { + serv.initialize(); + auto logger = serv.create_daily_logger("Test_log", ".test", "%v", spdlog::level::critical); + ASSERT_EQ(spdlog::level::critical, logger->level()); + ASSERT_EQ("Test_log", logger->name()); + std::fstream log_file; + std::string content; + std::time_t t = std::time(nullptr); + std::tm* now = std::localtime(&t); + char buffer[128]; + strftime(buffer, sizeof(buffer), "_%Y-%m-%d", now); + std::string file_path_string = "../logs/" + logger->name()+ buffer + ".test"; + log_file.open(file_path_string, std::ios::out); + ASSERT_TRUE(log_file.good()); + log_file.close(); + + } + TEST_F(test_streets_service, test_create_daily_logger_default) { + serv.initialize(); + auto logger = serv.create_daily_logger("default_daily"); + ASSERT_EQ(spdlog::level::info, logger->level()); + ASSERT_EQ("default_daily", logger->name()); + std::fstream log_file; + std::string content; + std::time_t t = std::time(nullptr); + std::tm* now = std::localtime(&t); + char buffer[128]; + strftime(buffer, sizeof(buffer), "_%Y-%m-%d", now); + std::string file_path_string = "../logs/" + logger->name()+ buffer + ".log"; + log_file.open(file_path_string, std::ios::out); + ASSERT_TRUE(log_file.good()); + log_file.close(); + + } TEST_F(test_streets_service, test_initialize_consumer) { serv._service_name ="TestService"; std::shared_ptr consumer; diff --git a/tsc_client_service/CMakeLists.txt b/tsc_client_service/CMakeLists.txt index 638476c49..69f13d341 100644 --- a/tsc_client_service/CMakeLists.txt +++ b/tsc_client_service/CMakeLists.txt @@ -42,6 +42,7 @@ add_library(${PROJECT_NAME}_lib src/monitor_tsc_state.cpp src/monitor_desired_phase_plan.cpp src/control_tsc_state.cpp + src/spat_projection_mode.cpp src/exceptions/control_tsc_state_exception.cpp) target_link_libraries( ${PROJECT_NAME}_lib PUBLIC diff --git a/tsc_client_service/Dockerfile b/tsc_client_service/Dockerfile index d6981a3ca..58253fad4 100644 --- a/tsc_client_service/Dockerfile +++ b/tsc_client_service/Dockerfile @@ -22,6 +22,7 @@ RUN echo " ------> Install rapidjson..." WORKDIR /home/carma-streets/ext RUN git clone https://github.com/Tencent/rapidjson WORKDIR /home/carma-streets/ext/rapidjson/ +RUN git checkout a95e013b97ca6523f32da23f5095fcc9dd6067e5 RUN mkdir build WORKDIR /home/carma-streets/ext/rapidjson/build RUN cmake .. && make -j diff --git a/tsc_client_service/include/monitor_tsc_state.h b/tsc_client_service/include/monitor_tsc_state.h index 70e5ed7b2..f6564165a 100644 --- a/tsc_client_service/include/monitor_tsc_state.h +++ b/tsc_client_service/include/monitor_tsc_state.h @@ -59,6 +59,10 @@ namespace traffic_signal_controller_service /* The sequence of vehicle phases in ring 2 of TSC*/ std::vector phase_seq_ring2_; + std::vector vehicle_calls; + + std::vector pedestrian_calls; + // Number of required following movements on receiving a spat_ptr int required_following_movements_ = 3; @@ -107,6 +111,7 @@ namespace traffic_signal_controller_service **/ std::vector> get_active_ring_sequences(int max_rings, std::unordered_map& vehicle_phase_2signalgroup_map, int sequence = 1) const; + /** * @brief Method for mapping vehicle phases and signal groups. Modifies non-const arguments by reference. * Signal group map is expected to be passed empty. @@ -119,6 +124,24 @@ namespace traffic_signal_controller_service void map_phase_and_signalgroup(const std::vector>& active_ring_sequences, std::unordered_map& vehicle_phase_2signalgroup_map, std::unordered_map& signal_group_2vehiclephase_map) const; + /** + * @brief Method to process bitwise response from NTCIP 1202 Phase Status Group Table. In this table phase information + * is returned as a single 8 bit integer and can be intepreted as follows: + * ``` + * Bit 7: Phase # = (phaseStatusGroupNumber * 8) + * Bit 6: Phase # = (phaseStatusGroupNumber * 8) - 1 + * Bit 5: Phase # = (phaseStatusGroupNumber * 8) - 2 + * Bit 4: Phase # = (phaseStatusGroupNumber * 8) - 3 + * Bit 3: Phase # = (phaseStatusGroupNumber * 8) - 4 + * Bit 2: Phase # = (phaseStatusGroupNumber * 8) - 5 + * Bit 1: Phase # = (phaseStatusGroupNumber * 8) - 6 + * Bit 0: Phase # = (phaseStatusGroupNumber * 8) - 7 + * ``` + * @param resp + * @param offset + * @return + */ + std::vector process_bitwise_response( const streets_snmp_cmd::snmp_response_obj &resp, int offset ) const; /** * @brief Method for getting maximum channels for the traffic signal controller * @return number of maximum channels in the traffic signal controller @@ -172,7 +195,7 @@ namespace traffic_signal_controller_service * @param ring_num The phase for which the concurrent phases needs to be obtained * @return a vector of phases that may be concurrent with the given phase **/ - std::vector get_concurrent_signal_groups(int phase_num); + std::vector get_concurrent_signal_groups(int phase_num) const; /** @brief Get predicted next movement event given a current event * @param current_event movement_event from the next movement needs to be predicted @@ -182,6 +205,19 @@ namespace traffic_signal_controller_service **/ signal_phase_and_timing::movement_event get_following_event(const signal_phase_and_timing::movement_event& current_event, uint64_t current_event_end_time, const signal_group_state& phase_state) const; + + /** + * @brief Helper method to convert phase numbers to signal groups ids for a vector of vehicle phases. + * @param veh_phases vector of vehicle phase numbers. + * @return vector of vehicle signal group ids. + */ + std::vector convert_veh_phases_to_signal_groups(const std::vector &veh_phases ) const; + /** + * @brief Helper method to convert phase numbers to signal group ids for a vector of pedestrian phases. + * @param ped_phases vector of pedestrian phase numbers. + * @return vector pedestrian signal group ids. + */ + std::vector convert_ped_phases_to_signal_groups(const std::vector &ped_phases ) const; //Add Friend Test to share private members FRIEND_TEST(test_monitor_state, test_get_following_movement_events); @@ -223,6 +259,16 @@ namespace traffic_signal_controller_service */ const std::unordered_map & get_signal_group_map(); + std::vector get_vehicle_calls() const; + + std::vector get_pedestrian_calls() const; + + std::string vector_to_string(const std::vector &v ) const; + /** + * @brief Poll vehicle/pedestrian calls on phases 1-16 + */ + void poll_vehicle_pedestrian_calls(); + /** * @brief Get the phase number using signal group id. * @@ -239,7 +285,9 @@ namespace traffic_signal_controller_service * @return int * @throws monitor_states_exception if phase number is less than 1. */ - int get_vehicle_signal_group_id(const int phase_number); + int get_vehicle_signal_group_id(const int phase_number) const; + + int get_pedestrian_signal_group_id(const int phase_number) const; /** * @brief Initialize tsc_state by making SNMP calls to TSC for phase sequence and timing information. diff --git a/tsc_client_service/include/ntcip_oids.h b/tsc_client_service/include/ntcip_oids.h index a5d29444f..835110cff 100644 --- a/tsc_client_service/include/ntcip_oids.h +++ b/tsc_client_service/include/ntcip_oids.h @@ -102,6 +102,17 @@ namespace ntcip_oids { * .1 is needed at the end to set the phase control. NTCIP documentation is unclear about why its required or what it represents */ static const std::string PHASE_HOLD_CONTROL = "1.3.6.1.4.1.1206.4.2.1.1.5.1.4.1"; + /** + * @brief Phase Vehicle Call Status Mask, when a bit =1, the Phase vehicle currently has a call for service. When a bit + * = 0, the Phase vehicle currently does NOT have a call for service. + */ + static const std::string PHASE_STATUS_GROUP_VEH_CALLS ="1.3.6.1.4.1.1206.4.2.1.1.4.1.8"; + /** + * @brief Phase Pedestrian Call Status Mask, when a bit= 1, the Phase pedestrian currently has a call for service. When + * a bit = 0, the Phase pedestrian currently does NOT have a call for service. + */ + static const std::string PHASE_STATUS_GROUP_PED_CALLS ="1.3.6.1.4.1.1206.4.2.1.1.4.1.9"; + /** * @brief Phase Status Group Phase Next object is used to determine whether any vehicle phases are committed by the * TSC to be served next. It returns a 8 bit value that represent a state for each of the 8 vehicle phases. When a bit = 1, @@ -136,4 +147,5 @@ namespace ntcip_oids { * When a bit = 0, the device shall not place a call for ped service on that phase. */ static const std::string PHASE_PEDESTRIAN_CALL_CONTROL = "1.3.6.1.4.1.1206.4.2.1.1.5.1.7.1"; + } \ No newline at end of file diff --git a/tsc_client_service/include/spat_projection_mode.h b/tsc_client_service/include/spat_projection_mode.h new file mode 100644 index 000000000..d581cd13e --- /dev/null +++ b/tsc_client_service/include/spat_projection_mode.h @@ -0,0 +1,26 @@ +#pragma once + +#include + +namespace traffic_signal_controller_service{ + /** + * @brief Enumeration to describe how we project future events on to the + * SPAT information received from the Traffic Signal Controller. + */ + enum class spat_projection_mode { + /** + * @brief Do not append any future information to received SPAT. + */ + no_projection = 0, + /** + * @brief Append future information reflected in received DPP + */ + dpp_projection = 1, + /** + * @brief Append future information assuming fixed time, min vehicle recall on all vehicle phases. + */ + fixed_projection =2 + }; + + spat_projection_mode spat_projection_mode_from_int( const int i ); +} \ No newline at end of file diff --git a/tsc_client_service/include/tsc_service.h b/tsc_client_service/include/tsc_service.h index 4329532eb..7b1491fee 100644 --- a/tsc_client_service/include/tsc_service.h +++ b/tsc_client_service/include/tsc_service.h @@ -22,6 +22,7 @@ #include #include "streets_service.h" #include "streets_clock_singleton.h" +#include "spat_projection_mode.h" namespace traffic_signal_controller_service { @@ -101,7 +102,7 @@ namespace traffic_signal_controller_service { int tsc_config_send_attempts = 1; // desired phase plan information consumed from desire_phase_plan Kafka topic - bool use_desired_phase_plan_update_ = false; + spat_projection_mode spat_proj_mode = spat_projection_mode::no_projection; // Queue to store snmp_cmd_structs which are objects used to run snmp HOLD and OMIT commands std::queue tsc_set_command_queue_; @@ -112,11 +113,17 @@ namespace traffic_signal_controller_service { // Configurable parameter that is used to enable logging of snmp commands to a log file if set to true. bool enable_snmp_cmd_logging_ = false; + inline static const std::string SNMP_COMMAND_LOGGER_NAME = "snmp_command"; + + inline static const std::string VEH_PED_CALL_LOGGER_NAME = "veh_ped_call"; + //Add Friend Test to share private members friend class tsc_service_test; FRIEND_TEST(tsc_service_test,test_tsc_control); FRIEND_TEST(tsc_service_test,test_produce_tsc_config_json_timeout); FRIEND_TEST(tsc_service_test,test_init_kafka_consumer_producer); + FRIEND_TEST(tsc_service_test,test_configure_snmp_cmd_logger); + FRIEND_TEST(tsc_service_test,test_configure_veh_ped_call_logger); /*** * Configuration parameter to control different interfaces to schedule the traffic signal controller @@ -244,9 +251,16 @@ namespace traffic_signal_controller_service { void set_tsc_hold_and_omit_forceoff_call(); /** - * @brief Method to configure spdlog::logger for logging snmp control commands into daily rotating csv file. + * @brief Method to configure spdlog::logger for logging snmp control commands into daily rotating log file. */ void configure_snmp_cmd_logger() const; + /** + * @brief ethod to configure spdlog::logger for logging vehicle and pedestrian calls on the traffic signal controller + * into daily rotating csvlog file. + * + */ + void configure_veh_ped_call_logger() const; + /** * @brief Method to log spat latency comparing spat time stamp to current time. Calculates and average over 20 * messages then resets count and spat_processing_time. diff --git a/tsc_client_service/manifest.json b/tsc_client_service/manifest.json index c2c2c5f17..065be9edf 100644 --- a/tsc_client_service/manifest.json +++ b/tsc_client_service/manifest.json @@ -91,10 +91,10 @@ "type": "BOOL" }, { - "name": "use_desired_phase_plan_update", - "value": true, - "description": "If true will enable monitor_desired_phase_plan to update incoming spat with calculated future movement events, using desired phase plan information from signal optimization service. If false will use TSC Configuration to predict future phases and append those to the spat based on default phase sequence.", - "type": "BOOL" + "name": "spat_projection_mode", + "value": 0, + "description": "Enumeration configuring SPat projection. 1 == DPP projection. 2 == Fixed Timing projection. Any other value will result in no projection of future", + "type": "INTEGER" }, { "name": "tsc_config_producer_topic", diff --git a/tsc_client_service/src/monitor_tsc_state.cpp b/tsc_client_service/src/monitor_tsc_state.cpp index 1fc90e14e..62cffefb7 100644 --- a/tsc_client_service/src/monitor_tsc_state.cpp +++ b/tsc_client_service/src/monitor_tsc_state.cpp @@ -149,6 +149,115 @@ namespace traffic_signal_controller_service return next_event; } + std::string tsc_state::vector_to_string(const std::vector &v) const { + std::string v_string = "["; + for (auto element: v ){ + v_string.append(std::to_string(element)); + if ( element != v.back()) { + v_string.append(", "); + } + } + v_string.append("]"); + return v_string; +} + + void tsc_state::poll_vehicle_pedestrian_calls() { + // Make SNMP requests to get vehicle and pedestrian calls + // for phases 1-16 (.1 gets 1-8, .2 gets 9-16) + auto request_type = streets_snmp_cmd::REQUEST_TYPE::GET; + std::string vehicle_call_phases_1_8 = ntcip_oids::PHASE_STATUS_GROUP_VEH_CALLS + ".1"; + std::string vehicle_call_phases_9_16 = ntcip_oids::PHASE_STATUS_GROUP_VEH_CALLS + ".2"; + std::string pedestrian_call_phases_1_8 = ntcip_oids::PHASE_STATUS_GROUP_PED_CALLS + ".1"; + std::string pedestrian_call_phases_9_16 = ntcip_oids::PHASE_STATUS_GROUP_PED_CALLS + ".2"; + + streets_snmp_cmd::snmp_response_obj veh_call_1_8; + veh_call_1_8.type = streets_snmp_cmd::RESPONSE_TYPE::INTEGER; + streets_snmp_cmd::snmp_response_obj veh_call_9_16; + veh_call_9_16.type = streets_snmp_cmd::RESPONSE_TYPE::INTEGER; + streets_snmp_cmd::snmp_response_obj ped_call_1_8; + ped_call_1_8.type = streets_snmp_cmd::RESPONSE_TYPE::INTEGER; + streets_snmp_cmd::snmp_response_obj ped_call_9_16; + ped_call_9_16.type = streets_snmp_cmd::RESPONSE_TYPE::INTEGER; + + snmp_client_worker_ ->process_snmp_request(vehicle_call_phases_1_8, request_type, veh_call_1_8); + snmp_client_worker_ ->process_snmp_request(vehicle_call_phases_9_16, request_type, veh_call_9_16); + snmp_client_worker_ ->process_snmp_request(pedestrian_call_phases_1_8, request_type, ped_call_1_8); + snmp_client_worker_ ->process_snmp_request(pedestrian_call_phases_9_16, request_type, ped_call_9_16); + + SPDLOG_INFO("Response Veh {0}, {1} Response Ped {2}, {3}", veh_call_1_8.val_int, veh_call_9_16.val_int, ped_call_1_8.val_int, ped_call_9_16.val_int); + + // Process returned int bitwise to get vehicle/pedestrian call information for 8 phases and convert to signal groups + auto veh_resp_1_8 = convert_veh_phases_to_signal_groups( process_bitwise_response(veh_call_1_8,0)); + auto veh_resp_9_16 = convert_veh_phases_to_signal_groups(process_bitwise_response(veh_call_9_16,8)); + auto ped_resp_1_8 = convert_ped_phases_to_signal_groups(process_bitwise_response(ped_call_1_8,0)); + auto ped_resp_9_16 = convert_ped_phases_to_signal_groups(process_bitwise_response(ped_call_9_16,8)); + + // Clear previously saved vehicle/pedestrian information and replace with new information + + vehicle_calls.clear(); + vehicle_calls.insert(vehicle_calls.end(), veh_resp_1_8.begin(), veh_resp_1_8.end()); + vehicle_calls.insert(vehicle_calls.end(), veh_resp_9_16.begin(), veh_resp_9_16.end()); + + pedestrian_calls.clear(); + pedestrian_calls.insert(pedestrian_calls.end(), ped_resp_1_8.begin(), ped_resp_1_8.end()); + pedestrian_calls.insert(pedestrian_calls.end(), ped_resp_9_16.begin(), ped_resp_9_16.end()); + + SPDLOG_INFO("Pedestrian calls {0}, Vehicle calls, {1}", vector_to_string(pedestrian_calls), vector_to_string(vehicle_calls)); + } + + std::vector tsc_state::process_bitwise_response( const streets_snmp_cmd::snmp_response_obj &response, int offset ) const{ + /** + * Response value is 8 bit int in which each bit is interpreted individually as 1 or 0. 1 + * indicates that the vehicle phase for that bit is committed to be next. 0 indicates this + * phase is not committed to be next. + * + * bit 0 represent vehicle phase 1 + * bit 1 represent vehicle phase 2 + * bit 2 represent vehicle phase 3 + * bit 3 represent vehicle phase 4 + * bit 4 represent vehicle phase 5 + * bit 5 represent vehicle phase 6 + * bit 6 represent vehicle phase 7 + * bit 7 represent vehicle phase 8 + * + */ + std::vector phase_numbers; + for (uint i = 0; i < 8; ++i) { + if ((response.val_int >> i) & 1) { + // Add any signal group for phase that has bit as 1 + auto phase_number = i+1+offset; + SPDLOG_INFO("Adding phase {0}", phase_number); + phase_numbers.push_back(phase_number); + } + } + return phase_numbers; + } + + std::vector tsc_state::get_pedestrian_calls() const { + return pedestrian_calls; + } + + std::vector tsc_state::get_vehicle_calls() const { + return vehicle_calls; + } + + std::vector tsc_state::convert_ped_phases_to_signal_groups( const std::vector &ped_phases ) const{ + std::vector signal_groups; + for (auto ped_phase : ped_phases) { + signal_groups.push_back(get_pedestrian_signal_group_id(ped_phase)); + } + return signal_groups; + } + + std::vector tsc_state::convert_veh_phases_to_signal_groups( const std::vector &veh_phases ) const{ + std::vector signal_groups; + for (auto veh_phase : veh_phases) { + signal_groups.push_back(get_vehicle_signal_group_id(veh_phase)); + } + return signal_groups; + } + + void tsc_state::add_future_movement_events(std::shared_ptr spat_ptr) { // Modify spat according to phase configuration @@ -417,13 +526,27 @@ namespace traffic_signal_controller_service } } - int tsc_state::get_vehicle_signal_group_id(const int phase_number ) { + int tsc_state::get_vehicle_signal_group_id(const int phase_number ) const { if (phase_number >= 1) { if ( vehicle_phase_2signalgroup_map_.find(phase_number) != vehicle_phase_2signalgroup_map_.end() ) { - return vehicle_phase_2signalgroup_map_[phase_number]; + return vehicle_phase_2signalgroup_map_.at(phase_number); + } + else { + throw monitor_states_exception("No signal group id found for vehicle phase number " + std::to_string(phase_number) + "!"); + } + } + else { + throw monitor_states_exception("Phase numbers less than 1 are invalid!"); + } + } + + int tsc_state::get_pedestrian_signal_group_id(const int phase_number) const{ + if (phase_number >= 1) { + if ( ped_phase_2signalgroup_map_.find(phase_number) != ped_phase_2signalgroup_map_.end() ) { + return ped_phase_2signalgroup_map_.at(phase_number); } else { - throw monitor_states_exception("No signal group id found for phase number " + std::to_string(phase_number) + "!"); + throw monitor_states_exception("No signal group id found for pedestrian phase number " + std::to_string(phase_number) + "!"); } } else { @@ -512,7 +635,7 @@ namespace traffic_signal_controller_service return red_duration; } - std::vector tsc_state::get_concurrent_signal_groups(int phase_num) + std::vector tsc_state::get_concurrent_signal_groups(int phase_num) const { std::vector concurrent_signal_groups; diff --git a/tsc_client_service/src/spat_projection_mode.cpp b/tsc_client_service/src/spat_projection_mode.cpp new file mode 100644 index 000000000..ea4378173 --- /dev/null +++ b/tsc_client_service/src/spat_projection_mode.cpp @@ -0,0 +1,20 @@ +#include "spat_projection_mode.h" + +namespace traffic_signal_controller_service{ + spat_projection_mode spat_projection_mode_from_int( const int i ) { + switch (i) + { + case 0: + return spat_projection_mode::no_projection; + break; + case 1: + return spat_projection_mode::dpp_projection; + break; + case 2: + return spat_projection_mode::fixed_projection; + break; + default: + return spat_projection_mode::no_projection; + } + } +} \ No newline at end of file diff --git a/tsc_client_service/src/tsc_service.cpp b/tsc_client_service/src/tsc_service.cpp index fc66624e6..9dc8238a3 100644 --- a/tsc_client_service/src/tsc_service.cpp +++ b/tsc_client_service/src/tsc_service.cpp @@ -11,12 +11,6 @@ namespace traffic_signal_controller_service { } try { - // Temporary fix for bug in CarmaClock::wait_for_initialization(). No mechanism to support notifying multiple threads - // of initialization. This fix avoids any threads waiting on initialization. Only required in SIMULATION_MODE=TRUE - // TODO: Remove initialization and fix issue in carma-time-lib (CarmaClock class) - if ( is_simulation_mode() ) { - streets_clock_singleton::update(0); - } // Intialize spat kafka producer std::string spat_topic_name = streets_configuration::get_string_config("spat_producer_topic"); @@ -63,7 +57,10 @@ namespace traffic_signal_controller_service { return false; } //Initialize TSC State - use_desired_phase_plan_update_ = streets_configuration::get_boolean_config("use_desired_phase_plan_update"); + auto spat_projection_int = streets_configuration::get_int_config("spat_projection_mode"); + + spat_proj_mode = spat_projection_mode_from_int(spat_projection_int); + if (!initialize_tsc_state(snmp_client_ptr)){ SPDLOG_ERROR("Failed to initialize tsc state"); return false; @@ -109,6 +106,7 @@ namespace traffic_signal_controller_service { if (enable_snmp_cmd_logging_) { configure_snmp_cmd_logger(); + configure_veh_ped_call_logger(); } if (is_simulation_mode()) { // Trigger spat broadcasting for EVC on startup. @@ -197,24 +195,39 @@ namespace traffic_signal_controller_service { while(spat_worker_ptr && tsc_state_ptr && spat_producer) { try { spat_worker_ptr->receive_spat(spat_ptr); - SPDLOG_DEBUG("Current SPaT : {0} ", spat_ptr->toJson()); - if(!use_desired_phase_plan_update_){ - // throws monitor_states_exception - tsc_state_ptr->add_future_movement_events(spat_ptr); - - }else{ - std::scoped_lock lck{dpp_mtx}; - // throws desired phase plan exception when no previous green information present - monitor_dpp_ptr->update_spat_future_movement_events(spat_ptr, tsc_state_ptr); - } + SPDLOG_DEBUG("Current SPaT : {0} ", spat_ptr->toJson()); + switch (spat_proj_mode) + { + case spat_projection_mode::fixed_projection: { + tsc_state_ptr->add_future_movement_events(spat_ptr); + break; + } + case spat_projection_mode::dpp_projection : { + std::scoped_lock lck{dpp_mtx}; + // throws desired phase plan exception when no previous green information present + monitor_dpp_ptr->update_spat_future_movement_events(spat_ptr, tsc_state_ptr); + break; + } + default: { + // Poll and log vehicle and pedestrian call information every 10 spat messages or at 10 Hz + if ( count%10 == 0) { + tsc_state_ptr->poll_vehicle_pedestrian_calls(); + if(auto logger = spdlog::get(VEH_PED_CALL_LOGGER_NAME); logger != nullptr ){ + logger->info("{0}, {1}, {2}", streets_clock_singleton::time_in_ms(), + tsc_state_ptr->vector_to_string(tsc_state_ptr->get_vehicle_calls()), + tsc_state_ptr->vector_to_string(tsc_state_ptr->get_pedestrian_calls()) + ); + } + } + } + } if (spat_ptr && spat_producer) { spat_producer->send(spat_ptr->toJson()); // No utility in calculating spat latency in simulation mode if ( !this->is_simulation_mode() ) { log_spat_latency(count, spat_processing_time, spat_ptr->get_intersection().get_epoch_timestamp()) ; } - } - + } } catch( const signal_phase_and_timing::signal_phase_and_timing_exception &e ) { SPDLOG_ERROR("Encountered exception, spat not published : \n {0}", e.what()); @@ -225,7 +238,7 @@ namespace traffic_signal_controller_service { catch(const traffic_signal_controller_service::monitor_states_exception &e){ SPDLOG_ERROR("Could not update movement events, spat not published. Encountered exception : \n {0}", e.what()); } - } + } SPDLOG_WARN("Stopping produce_spat_json! Are pointers null: spat_worker {0}, spat_producer {1}, tsc_state {2}", spat_worker_ptr == nullptr, spat_ptr == nullptr, tsc_state_ptr == nullptr); } @@ -387,25 +400,27 @@ namespace traffic_signal_controller_service { void tsc_service::configure_snmp_cmd_logger() const { - try{ - auto snmp_cmd_logger = spdlog::daily_logger_mt( - "snmp_cmd_logger", // logger name - streets_configuration::get_string_config("snmp_cmd_log_path")+ - streets_configuration::get_string_config("snmp_cmd_log_filename") +".log", // log file name and path - 23, // hours to rotate - 59 // minutes to rotate - ); - // Only log log statement content - snmp_cmd_logger->set_pattern("[%H:%M:%S:%e ] %v"); - snmp_cmd_logger->set_level(spdlog::level::info); - snmp_cmd_logger->flush_on(spdlog::level::info); + try { + create_daily_logger(SNMP_COMMAND_LOGGER_NAME, ".log", "[%H:%M:%S:%e ] %v", spdlog::level::info ); } catch (const spdlog::spdlog_ex& ex) { - spdlog::error( "Log initialization failed: {0}!",ex.what()); + SPDLOG_ERROR( "SNMP Command Logger initialization failed: {0}!",ex.what()); } } + void tsc_service::configure_veh_ped_call_logger() const + { + try { + auto veh_ped_call_logger = create_daily_logger(VEH_PED_CALL_LOGGER_NAME, ".cvs", "%v", spdlog::level::info ); + } + catch (const spdlog::spdlog_ex& ex) + { + SPDLOG_ERROR( "Vehicle Pedestrian Call Logger initialization failed: {0}!",ex.what()); + } + // TODO: Figure out how to termine if a new file was created or an existing file appended and only write column headers on new files + // veh_ped_call_logger->info("Timestamp (ms), Vehicle Calls (Signal Group ID), Pedestrian Calls (Signal Group ID)"); + } void tsc_service::log_spat_latency(int &count, uint64_t &spat_processing_time, uint64_t spat_time_stamp) const { // Calculate and average spat processing time over 20 messages sent if (count <= 20 ) { diff --git a/tsc_client_service/test/test_monitor_state.cpp b/tsc_client_service/test/test_monitor_state.cpp index 9f394efca..98fda40bf 100644 --- a/tsc_client_service/test/test_monitor_state.cpp +++ b/tsc_client_service/test/test_monitor_state.cpp @@ -14,6 +14,162 @@ using testing::SetArgReferee; namespace traffic_signal_controller_service { + TEST(test_monitor_state, poll_vehicle_pedestrian_calls){ + auto mock_client = std::make_shared(); + const std::string&input_oid = ""; + auto request_type= streets_snmp_cmd::REQUEST_TYPE::GET; + + // Test get max channels + streets_snmp_cmd::snmp_response_obj vehicle_calls; + vehicle_calls.val_int = 15; + vehicle_calls.type = streets_snmp_cmd::RESPONSE_TYPE::INTEGER; + + EXPECT_CALL( *mock_client, process_snmp_request(ntcip_oids::PHASE_STATUS_GROUP_VEH_CALLS + ".1", request_type , _) ).Times(1).WillRepeatedly(testing::DoAll( + SetArgReferee<2>(vehicle_calls), + Return(true))); + vehicle_calls.val_int = 0; + + EXPECT_CALL( *mock_client, process_snmp_request(ntcip_oids::PHASE_STATUS_GROUP_VEH_CALLS + ".2", request_type , _) ).Times(1).WillRepeatedly(testing::DoAll( + SetArgReferee<2>(vehicle_calls), + Return(true))); + vehicle_calls.val_int = 0; + + EXPECT_CALL( *mock_client, process_snmp_request(ntcip_oids::PHASE_STATUS_GROUP_PED_CALLS + ".1", request_type , _) ).Times(1).WillRepeatedly(testing::DoAll( + SetArgReferee<2>(vehicle_calls), + Return(true))); + vehicle_calls.val_int = 15; + + EXPECT_CALL( *mock_client, process_snmp_request(ntcip_oids::PHASE_STATUS_GROUP_PED_CALLS + ".2", request_type , _) ).Times(1).WillRepeatedly(testing::DoAll( + SetArgReferee<2>(vehicle_calls), + Return(true))); + + // Test get max channels + streets_snmp_cmd::snmp_response_obj max_channels_in_tsc; + max_channels_in_tsc.val_int = 12; + max_channels_in_tsc.type = streets_snmp_cmd::RESPONSE_TYPE::INTEGER; + + EXPECT_CALL( *mock_client, process_snmp_request(ntcip_oids::MAX_CHANNELS, request_type , _) ).Times(1).WillRepeatedly(testing::DoAll( + SetArgReferee<2>(max_channels_in_tsc), + Return(true))); + + streets_snmp_cmd::snmp_response_obj max_rings_in_tsc; + max_rings_in_tsc.val_int = 4; + max_rings_in_tsc.type = streets_snmp_cmd::RESPONSE_TYPE::INTEGER; + EXPECT_CALL( *mock_client, process_snmp_request(ntcip_oids::MAX_RINGS, request_type , _) ).Times(1).WillRepeatedly(testing::DoAll( + SetArgReferee<2>(max_rings_in_tsc), + Return(true))); + // Define Sequence Data + streets_snmp_cmd::snmp_response_obj seq_data; + seq_data.val_string = {char(1),char(2),char(5), char(6)}; + std::string seq_data_ring1_oid = ntcip_oids::SEQUENCE_DATA + "." + "1" + "." + std::to_string(1); + + EXPECT_CALL(*mock_client, process_snmp_request(seq_data_ring1_oid, request_type , _) ).Times(1).WillRepeatedly(testing::DoAll( + SetArgReferee<2>(seq_data), + Return(true))); + + seq_data.val_string = {char(3),char(4), char(7), char(8)}; + std::string seq_data_ring2_oid = ntcip_oids::SEQUENCE_DATA + "." + "1" + "." + std::to_string(2); + EXPECT_CALL(*mock_client, process_snmp_request(seq_data_ring2_oid, request_type , _) ).Times(1).WillRepeatedly(testing::DoAll( + SetArgReferee<2>(seq_data), + Return(true))); + + seq_data.val_string = {char(9),char(10)}; + std::string seq_data_ring3_oid = ntcip_oids::SEQUENCE_DATA + "." + "1" + "." + std::to_string(3); + EXPECT_CALL(*mock_client, process_snmp_request(seq_data_ring3_oid, request_type , _) ).Times(1).WillRepeatedly(testing::DoAll( + SetArgReferee<2>(seq_data), + Return(true))); + + seq_data.val_string = {char(11),char(12)}; + + std::string seq_data_ring4_oid = ntcip_oids::SEQUENCE_DATA + "." + "1" + "." + std::to_string(4); + EXPECT_CALL(*mock_client, process_snmp_request(seq_data_ring4_oid, request_type , _) ).Times(1).WillRepeatedly(testing::DoAll( + SetArgReferee<2>(seq_data), + Return(true))); + + //get_vehicle_phase channel + for(int i = 1; i <= max_channels_in_tsc.val_int; ++i){ + + // Define Control Type + streets_snmp_cmd::snmp_response_obj channel_control_resp; + if ( i > 8) + channel_control_resp.val_int = 3; + else + channel_control_resp.val_int = 2; + + std::string channel_control_oid = ntcip_oids::CHANNEL_CONTROL_TYPE_PARAMETER + "." + std::to_string(i); + EXPECT_CALL(*mock_client, process_snmp_request(channel_control_oid, request_type , _) ).Times(1).WillRepeatedly(testing::DoAll( + SetArgReferee<2>(channel_control_resp), + testing::Return(true))); + + // Define Control Source + streets_snmp_cmd::snmp_response_obj control_source_resp; + control_source_resp.val_int = i; + std::string control_source_oid = ntcip_oids::CHANNEL_CONTROL_SOURCE_PARAMETER + "." + std::to_string(i); + EXPECT_CALL(*mock_client, process_snmp_request(control_source_oid, request_type , _) ).Times(1).WillRepeatedly(testing::DoAll( + SetArgReferee<2>(control_source_resp), + Return(true))); + + + if (i > 8) { + continue; + } + // Define get min green + std::string min_green_oid = ntcip_oids::MINIMUM_GREEN + "." + std::to_string(i); + streets_snmp_cmd::snmp_response_obj min_green; + min_green.val_int = 20; + EXPECT_CALL(*mock_client, process_snmp_request(min_green_oid , request_type , _) ).Times(1).WillRepeatedly(testing::DoAll( + SetArgReferee<2>(min_green), + Return(true))); + + + // Define get max green + std::string max_green_oid = ntcip_oids::MAXIMUM_GREEN + "." + std::to_string(i); + streets_snmp_cmd::snmp_response_obj max_green; + max_green.val_int = 30; + EXPECT_CALL(*mock_client, process_snmp_request(max_green_oid , request_type , _) ).Times(1).WillRepeatedly(testing::DoAll( + SetArgReferee<2>(max_green), + Return(true))); + + + // Define get yellow Duration + std::string yellow_oid = ntcip_oids::YELLOW_CHANGE_PARAMETER + "." + std::to_string(i); + streets_snmp_cmd::snmp_response_obj yellow_duration; + yellow_duration.val_int = 40; + EXPECT_CALL(*mock_client, process_snmp_request(yellow_oid , request_type , _) ).Times(1).WillRepeatedly(testing::DoAll( + SetArgReferee<2>(yellow_duration), + Return(true))); + + + + // Define red clearance + std::string red_clearance_oid = ntcip_oids::RED_CLEAR_PARAMETER + "." + std::to_string(i); + streets_snmp_cmd::snmp_response_obj red_clearance_duration; + red_clearance_duration.val_int = 10; + EXPECT_CALL(*mock_client, process_snmp_request(red_clearance_oid , request_type , _) ).Times(1).WillRepeatedly(testing::DoAll( + SetArgReferee<2>(red_clearance_duration), + Return(true))); + + //Define get concurrent phases + std::string concurrent_phase_oid = ntcip_oids::PHASE_CONCURRENCY + "." + std::to_string(i); + streets_snmp_cmd::snmp_response_obj concurrent_phase_resp; + if ( i == 1 || i == 2) { + concurrent_phase_resp.val_string = {char(1), char(2)}; + } + else if (i == 3|| i == 4) { + concurrent_phase_resp.val_string = {char(3), char(4)}; + + } + EXPECT_CALL(*mock_client, process_snmp_request(concurrent_phase_oid , request_type , _) ).Times(1).WillRepeatedly(testing::DoAll( + SetArgReferee<2>(concurrent_phase_resp), + testing::Return(true))); + } + traffic_signal_controller_service::tsc_state worker(mock_client); + worker.initialize(); + worker.poll_vehicle_pedestrian_calls(); + + ASSERT_EQ(4, worker.get_pedestrian_calls().size()); + ASSERT_EQ(4 , worker.get_vehicle_calls().size()); + } TEST(test_monitor_state, test_get_tsc_state) { @@ -40,7 +196,7 @@ namespace traffic_signal_controller_service Return(true))); // Define Sequence Data streets_snmp_cmd::snmp_response_obj seq_data; - seq_data.val_string = {char(1),char(2)}; + seq_data.val_string = {char(1),char(2),}; std::string seq_data_ring1_oid = ntcip_oids::SEQUENCE_DATA + "." + "1" + "." + std::to_string(1); EXPECT_CALL(*mock_client, process_snmp_request(seq_data_ring1_oid, request_type , _) ).Times(1).WillRepeatedly(testing::DoAll( diff --git a/tsc_client_service/test/test_spat_projection_mode.cpp b/tsc_client_service/test/test_spat_projection_mode.cpp new file mode 100644 index 000000000..9d7c3f57f --- /dev/null +++ b/tsc_client_service/test/test_spat_projection_mode.cpp @@ -0,0 +1,12 @@ +#include +#include "spat_projection_mode.h" + +using namespace traffic_signal_controller_service; + +TEST(test_spat_projection_mode, test_from_int ) { + ASSERT_EQ( spat_projection_mode::no_projection, spat_projection_mode_from_int(0)); + ASSERT_EQ( spat_projection_mode::dpp_projection, spat_projection_mode_from_int(1)); + ASSERT_EQ( spat_projection_mode::fixed_projection, spat_projection_mode_from_int(2)); + ASSERT_EQ( spat_projection_mode::no_projection, spat_projection_mode_from_int(-1)); + ASSERT_EQ( spat_projection_mode::no_projection, spat_projection_mode_from_int(5)); +} diff --git a/tsc_client_service/test/test_tsc_service.cpp b/tsc_client_service/test/test_tsc_service.cpp index 776423f04..eaa6041e2 100644 --- a/tsc_client_service/test/test_tsc_service.cpp +++ b/tsc_client_service/test/test_tsc_service.cpp @@ -33,8 +33,11 @@ namespace traffic_signal_controller_service service.desired_phase_plan_consumer = dpp_consumer; service.phase_control_schedule_consumer = std::make_shared(); service.snmp_client_ptr = mock_snmp; - setenv("SIMULATION_MODE", "FALSE", 1); - setenv("CONFIG_FILE_PATH", "../manifest.json", 1); + service.tsc_state_ptr = std::make_shared(mock_snmp); + setenv(streets_service::SIMULATION_MODE_ENV.c_str(), "TRUE", 1); + setenv(streets_service::TIME_SYNC_TOPIC_ENV.c_str(), "time_sync", 1); + setenv(streets_service::CONFIG_FILE_PATH_ENV.c_str(), "../test/test_files/manifest.json", 1); + setenv(streets_service::LOGS_DIRECTORY_ENV.c_str(), "../logs/", 1); } @@ -383,9 +386,11 @@ namespace traffic_signal_controller_service service.initialize_spat("test_intersection",1234,std::unordered_map{ {1,8},{2,7},{3,6},{4,5},{5,4},{6,3},{7,2},{8,1}} ); ASSERT_TRUE(service.initialize_spat_worker("127.0.0.1",3456,2,false)); + service.produce_spat_json(); } + TEST_F(tsc_service_test, test_tsc_control){ // Initialize clock singleton in realtime mode @@ -476,12 +481,19 @@ namespace traffic_signal_controller_service TEST_F(tsc_service_test, test_configure_snmp_cmd_logger) { - // Initialize clock singleton in realtime mode streets_service::streets_clock_singleton::create(false); service.configure_snmp_cmd_logger(); - auto logger = spdlog::get("snmp_cmd_logger"); + auto logger = spdlog::get( service.SNMP_COMMAND_LOGGER_NAME); + ASSERT_EQ(logger->level(), spdlog::level::info); + } + + + TEST_F(tsc_service_test, test_configure_veh_ped_call_logger) { + service.configure_veh_ped_call_logger(); + auto logger = spdlog::get( service.VEH_PED_CALL_LOGGER_NAME); EXPECT_TRUE(logger != nullptr); + ASSERT_EQ(logger->level(), spdlog::level::info); } // Test tsc_service initialize without mocking snmp responses TEST_F(tsc_service_test, test_initialization_no_mock_response_from_snmp_client) { From db363fdaf9aba1577f9c4118581c15043aa53fea Mon Sep 17 00:00:00 2001 From: dev Date: Fri, 8 Sep 2023 09:52:20 -0400 Subject: [PATCH 09/80] Add default values for environment variables --- .../include/streets_environment_variables.h | 9 +++++++++ .../include/streets_service.h | 2 +- .../src/streets_service.cpp | 14 +++++++------- .../test/test_streets_service.cpp | 18 ++++++++++++------ 4 files changed, 29 insertions(+), 14 deletions(-) diff --git a/streets_utils/streets_service_base/include/streets_environment_variables.h b/streets_utils/streets_service_base/include/streets_environment_variables.h index 589f39f01..a45e8800d 100644 --- a/streets_utils/streets_service_base/include/streets_environment_variables.h +++ b/streets_utils/streets_service_base/include/streets_environment_variables.h @@ -6,10 +6,19 @@ namespace streets_service { static const std::string LOGS_DIRECTORY_ENV = "LOGS_DIRECTORY"; + static const std::string DEFAULT_LOGS_DIRECTORY = "../logs/"; + static const std::string SIMULATION_MODE_ENV = "SIMULATION_MODE"; + static const std::string DEFAULT_SIMULATION_MODE = "FALSE"; + static const std::string CONFIG_FILE_PATH_ENV = "CONFIG_FILE_PATH"; + + static const std::string DEFAULT_CONFIG_FILE_PATH = "../manifest.json"; static const std::string TIME_SYNC_TOPIC_ENV = "TIME_SYNC_TOPIC"; + + static const std::string DEFAULT_TIME_SYNC_TOPIC = "time_sync"; + } \ No newline at end of file diff --git a/streets_utils/streets_service_base/include/streets_service.h b/streets_utils/streets_service_base/include/streets_service.h index e4837f82b..ccb9fb6cd 100644 --- a/streets_utils/streets_service_base/include/streets_service.h +++ b/streets_utils/streets_service_base/include/streets_service.h @@ -75,7 +75,7 @@ namespace streets_service { * @throws runtime_error if config_name is nullptr or environment variable is not set. * @return value of environment variable. */ - std::string get_system_config(const char *config_name ) const; + std::string get_system_config(const char *config_name , const std::string &default_val) const; /** * @brief Method to consume continously consume time sync messages from Kafka topic and update * streets_clock_singleton with received data. diff --git a/streets_utils/streets_service_base/src/streets_service.cpp b/streets_utils/streets_service_base/src/streets_service.cpp index 0c18132bb..bfc422639 100644 --- a/streets_utils/streets_service_base/src/streets_service.cpp +++ b/streets_utils/streets_service_base/src/streets_service.cpp @@ -11,20 +11,20 @@ namespace streets_service { bool streets_service::initialize() { try { - std::string config_file_path = get_system_config(CONFIG_FILE_PATH_ENV.c_str()); + std::string config_file_path = get_system_config(CONFIG_FILE_PATH_ENV.c_str(), DEFAULT_CONFIG_FILE_PATH); streets_configuration::create(config_file_path); - std::string sim_mode_string = get_system_config(SIMULATION_MODE_ENV.c_str()); + std::string sim_mode_string = get_system_config(SIMULATION_MODE_ENV.c_str(),DEFAULT_SIMULATION_MODE); _simulation_mode = sim_mode_string.compare("true") == 0 || sim_mode_string.compare("TRUE") == 0 ; streets_clock_singleton::create(_simulation_mode); _service_name = streets_configuration::get_service_name(); SPDLOG_INFO("Initializing {0} streets service in simulation mode : {1}!", _service_name, _simulation_mode); if ( _simulation_mode ) { - std::string time_sync_topic = get_system_config(TIME_SYNC_TOPIC_ENV.c_str()); + std::string time_sync_topic = get_system_config(TIME_SYNC_TOPIC_ENV.c_str(), DEFAULT_TIME_SYNC_TOPIC); if (!initialize_kafka_consumer(time_sync_topic, _time_consumer)){ return false; } } - _logs_directory = get_system_config(LOGS_DIRECTORY_ENV.c_str()); + _logs_directory = get_system_config(LOGS_DIRECTORY_ENV.c_str(), DEFAULT_LOGS_DIRECTORY); } catch( const streets_configuration_exception &e) { SPDLOG_ERROR("Exception occured during {0} initialization : {1}" , _service_name , e.what()); return false; @@ -110,7 +110,7 @@ namespace streets_service { } } - std::string streets_service::get_system_config(const char *config_name) const { + std::string streets_service::get_system_config(const char *config_name, const std::string &default_val) const { if (config_name) { try { std::string config = std::getenv(config_name); @@ -118,8 +118,8 @@ namespace streets_service { return config; } catch(const std::logic_error &e) { - std::string config_name_str = config_name; - throw std::runtime_error("System config " + config_name_str + " not set!"); + SPDLOG_WARN("System config {0} was not set! Using default value {1}!" ,config_name, default_val); + return default_val; } } else { throw std::runtime_error(" Systme config param name is null pointer!"); diff --git a/streets_utils/streets_service_base/test/test_streets_service.cpp b/streets_utils/streets_service_base/test/test_streets_service.cpp index 5b674bf09..de97b411d 100644 --- a/streets_utils/streets_service_base/test/test_streets_service.cpp +++ b/streets_utils/streets_service_base/test/test_streets_service.cpp @@ -1,5 +1,6 @@ #include #include "streets_service.h" +#include "streets_environment_variables.h" #include "mock_kafka_consumer_worker.h" #include "mock_kafka_producer_worker.h" #include @@ -101,20 +102,25 @@ namespace streets_service{ }; TEST_F(test_streets_service, test_get_system_config) { - std::string simulation_mode = serv.get_system_config("SIMULATION_MODE"); + std::string simulation_mode = serv.get_system_config("SIMULATION_MODE", "DEFAULT"); ASSERT_EQ(simulation_mode, "TRUE"); - ASSERT_THROW(serv.get_system_config("NON_EXISTANT"), std::runtime_error); - ASSERT_THROW(serv.get_system_config(nullptr), std::runtime_error); + ASSERT_EQ(serv.get_system_config("NON_EXISTANT", "DEFAULT"), "DEFAULT"); + ASSERT_THROW(serv.get_system_config(nullptr, "DEFAULT"), std::runtime_error); }; TEST_F(test_streets_service, test_start) { ASSERT_TRUE(serv.initialize()); serv.start(); } - TEST_F(test_streets_service, test_initialize_exception) { - unsetenv("CONFIG_FILE_PATH"); - ASSERT_FALSE(serv.initialize()); + TEST_F(test_streets_service, test_initialize_defaults) { + unsetenv(SIMULATION_MODE_ENV.c_str()); + unsetenv(TIME_SYNC_TOPIC_ENV.c_str()); + // Default Config file path does not work here since there is not configuration + // file in the package directory. + // unsetenv(CONFIG_FILE_PATH_ENV.c_str()); + unsetenv(LOGS_DIRECTORY_ENV.c_str()); + ASSERT_TRUE(serv.initialize()); } TEST_F(test_streets_service, test_initialize_exception_config ) { From bcac231b912a4c8d461e01e4287c766e15424491 Mon Sep 17 00:00:00 2001 From: dev Date: Mon, 11 Sep 2023 08:57:41 -0400 Subject: [PATCH 10/80] Correct csv extension for file logging --- tsc_client_service/src/tsc_service.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tsc_client_service/src/tsc_service.cpp b/tsc_client_service/src/tsc_service.cpp index 9dc8238a3..be7f4dea8 100644 --- a/tsc_client_service/src/tsc_service.cpp +++ b/tsc_client_service/src/tsc_service.cpp @@ -412,7 +412,7 @@ namespace traffic_signal_controller_service { void tsc_service::configure_veh_ped_call_logger() const { try { - auto veh_ped_call_logger = create_daily_logger(VEH_PED_CALL_LOGGER_NAME, ".cvs", "%v", spdlog::level::info ); + auto veh_ped_call_logger = create_daily_logger(VEH_PED_CALL_LOGGER_NAME, ".csv", "%v", spdlog::level::info ); } catch (const spdlog::spdlog_ex& ex) { From 9c1a0f16dbe6295fa3bd672bcdce3ef0d8f2422b Mon Sep 17 00:00:00 2001 From: dev Date: Mon, 11 Sep 2023 09:06:13 -0400 Subject: [PATCH 11/80] Update Javadoc comment --- streets_utils/streets_service_base/include/streets_service.h | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/streets_utils/streets_service_base/include/streets_service.h b/streets_utils/streets_service_base/include/streets_service.h index ccb9fb6cd..c4a24584a 100644 --- a/streets_utils/streets_service_base/include/streets_service.h +++ b/streets_utils/streets_service_base/include/streets_service.h @@ -70,8 +70,9 @@ namespace streets_service { */ bool initialize_kafka_consumer( const std::string &consumer_topic, std::shared_ptr &consumer ) const; /** - * @brief Returns string value of environment variable with given name. + * @brief Returns string value of environment variable with given name. If no value is found, returns default. * @param config_name name of environment variable. + * @param default_val string default value for environment variable if no value is found. * @throws runtime_error if config_name is nullptr or environment variable is not set. * @return value of environment variable. */ From 3d91ca1b06522bcf52da32ef0ca1f0aea367bee0 Mon Sep 17 00:00:00 2001 From: dev Date: Thu, 14 Sep 2023 10:59:36 -0400 Subject: [PATCH 12/80] Address PR comments --- .../include/streets_environment_variables.h | 16 ++++----- .../src/streets_service.cpp | 27 +++++++------- .../test/test_streets_service.cpp | 36 +++++++++---------- 3 files changed, 40 insertions(+), 39 deletions(-) diff --git a/streets_utils/streets_service_base/include/streets_environment_variables.h b/streets_utils/streets_service_base/include/streets_environment_variables.h index a45e8800d..12273827e 100644 --- a/streets_utils/streets_service_base/include/streets_environment_variables.h +++ b/streets_utils/streets_service_base/include/streets_environment_variables.h @@ -4,21 +4,21 @@ namespace streets_service { - static const std::string LOGS_DIRECTORY_ENV = "LOGS_DIRECTORY"; + inline static const std::string LOGS_DIRECTORY_ENV = "LOGS_DIRECTORY"; - static const std::string DEFAULT_LOGS_DIRECTORY = "../logs/"; + inline static const std::string DEFAULT_LOGS_DIRECTORY = "../logs/"; - static const std::string SIMULATION_MODE_ENV = "SIMULATION_MODE"; + inline static const std::string SIMULATION_MODE_ENV = "SIMULATION_MODE"; - static const std::string DEFAULT_SIMULATION_MODE = "FALSE"; + inline static const std::string DEFAULT_SIMULATION_MODE = "FALSE"; - static const std::string CONFIG_FILE_PATH_ENV = "CONFIG_FILE_PATH"; + inline static const std::string CONFIG_FILE_PATH_ENV = "CONFIG_FILE_PATH"; - static const std::string DEFAULT_CONFIG_FILE_PATH = "../manifest.json"; + inline static const std::string DEFAULT_CONFIG_FILE_PATH = "../manifest.json"; - static const std::string TIME_SYNC_TOPIC_ENV = "TIME_SYNC_TOPIC"; + inline static const std::string TIME_SYNC_TOPIC_ENV = "TIME_SYNC_TOPIC"; - static const std::string DEFAULT_TIME_SYNC_TOPIC = "time_sync"; + inline static const std::string DEFAULT_TIME_SYNC_TOPIC = "time_sync"; } \ No newline at end of file diff --git a/streets_utils/streets_service_base/src/streets_service.cpp b/streets_utils/streets_service_base/src/streets_service.cpp index bfc422639..7df6033bd 100644 --- a/streets_utils/streets_service_base/src/streets_service.cpp +++ b/streets_utils/streets_service_base/src/streets_service.cpp @@ -111,20 +111,21 @@ namespace streets_service { } std::string streets_service::get_system_config(const char *config_name, const std::string &default_val) const { - if (config_name) { - try { - std::string config = std::getenv(config_name); - SPDLOG_DEBUG("Reading system config {0} as : {1}!", config_name, config); - return config; - } - catch(const std::logic_error &e) { - SPDLOG_WARN("System config {0} was not set! Using default value {1}!" ,config_name, default_val); - return default_val; - } - } else { - throw std::runtime_error(" Systme config param name is null pointer!"); + // Check for config_name nullptr and use default value + if (config_name == nullptr ) { + SPDLOG_WARN("System config_name was nullptr! Using default value {0}!" , default_val); + return default_val; + } + // If std::getenv(config_name) returns value, use this value + if (const auto config = std::getenv(config_name)) { + SPDLOG_DEBUG("Reading system config {0} as : {1}!", config_name, config); + return config; + } + // If std::getenv(config_name) returns nullptr, environment variable was not set so use default + else { + SPDLOG_WARN("System config {0} was not set! Using default value {1}!" ,config_name, default_val); + return default_val; } - return ""; } std::string streets_service::get_service_name() const { diff --git a/streets_utils/streets_service_base/test/test_streets_service.cpp b/streets_utils/streets_service_base/test/test_streets_service.cpp index de97b411d..af09c4cfd 100644 --- a/streets_utils/streets_service_base/test/test_streets_service.cpp +++ b/streets_utils/streets_service_base/test/test_streets_service.cpp @@ -24,9 +24,9 @@ namespace streets_service{ }; TEST_F(test_streets_service, test_initialize_sim) { - ASSERT_TRUE(serv.initialize()); - ASSERT_EQ( serv.get_service_name(), "test_service"); - ASSERT_TRUE(serv.is_simulation_mode()); + EXPECT_TRUE(serv.initialize()); + EXPECT_EQ( serv.get_service_name(), "test_service"); + EXPECT_TRUE(serv.is_simulation_mode()); }; TEST_F(test_streets_service, test_consume_time_sync_message) { @@ -49,14 +49,14 @@ namespace streets_service{ // consumer is_running returns false and returns control - ASSERT_EQ(1400, streets_clock_singleton::time_in_ms()); + EXPECT_EQ(1400, streets_clock_singleton::time_in_ms()); } TEST_F(test_streets_service, test_create_daily_logger) { serv.initialize(); auto logger = serv.create_daily_logger("Test_log", ".test", "%v", spdlog::level::critical); - ASSERT_EQ(spdlog::level::critical, logger->level()); - ASSERT_EQ("Test_log", logger->name()); + EXPECT_EQ(spdlog::level::critical, logger->level()); + EXPECT_EQ("Test_log", logger->name()); std::fstream log_file; std::string content; std::time_t t = std::time(nullptr); @@ -65,7 +65,7 @@ namespace streets_service{ strftime(buffer, sizeof(buffer), "_%Y-%m-%d", now); std::string file_path_string = "../logs/" + logger->name()+ buffer + ".test"; log_file.open(file_path_string, std::ios::out); - ASSERT_TRUE(log_file.good()); + EXPECT_TRUE(log_file.good()); log_file.close(); } @@ -73,8 +73,8 @@ namespace streets_service{ TEST_F(test_streets_service, test_create_daily_logger_default) { serv.initialize(); auto logger = serv.create_daily_logger("default_daily"); - ASSERT_EQ(spdlog::level::info, logger->level()); - ASSERT_EQ("default_daily", logger->name()); + EXPECT_EQ(spdlog::level::info, logger->level()); + EXPECT_EQ("default_daily", logger->name()); std::fstream log_file; std::string content; std::time_t t = std::time(nullptr); @@ -83,33 +83,33 @@ namespace streets_service{ strftime(buffer, sizeof(buffer), "_%Y-%m-%d", now); std::string file_path_string = "../logs/" + logger->name()+ buffer + ".log"; log_file.open(file_path_string, std::ios::out); - ASSERT_TRUE(log_file.good()); + EXPECT_TRUE(log_file.good()); log_file.close(); } TEST_F(test_streets_service, test_initialize_consumer) { serv._service_name ="TestService"; std::shared_ptr consumer; - ASSERT_TRUE(serv.initialize_kafka_consumer("test_topic", consumer)); + EXPECT_TRUE(serv.initialize_kafka_consumer("test_topic", consumer)); consumer->stop(); }; TEST_F(test_streets_service, test_initialize_producer) { serv._service_name ="TestService"; std::shared_ptr producer; - ASSERT_TRUE(serv.initialize_kafka_producer("test_topic", producer)); + EXPECT_TRUE(serv.initialize_kafka_producer("test_topic", producer)); producer->stop(); }; TEST_F(test_streets_service, test_get_system_config) { std::string simulation_mode = serv.get_system_config("SIMULATION_MODE", "DEFAULT"); - ASSERT_EQ(simulation_mode, "TRUE"); + EXPECT_EQ(simulation_mode, "TRUE"); - ASSERT_EQ(serv.get_system_config("NON_EXISTANT", "DEFAULT"), "DEFAULT"); - ASSERT_THROW(serv.get_system_config(nullptr, "DEFAULT"), std::runtime_error); + EXPECT_EQ(serv.get_system_config("NON_EXISTANT", "DEFAULT"), "DEFAULT"); + EXPECT_EQ(serv.get_system_config(nullptr, "DEFAULT"), "DEFAULT"); }; TEST_F(test_streets_service, test_start) { - ASSERT_TRUE(serv.initialize()); + EXPECT_TRUE(serv.initialize()); serv.start(); } @@ -120,12 +120,12 @@ namespace streets_service{ // file in the package directory. // unsetenv(CONFIG_FILE_PATH_ENV.c_str()); unsetenv(LOGS_DIRECTORY_ENV.c_str()); - ASSERT_TRUE(serv.initialize()); + EXPECT_TRUE(serv.initialize()); } TEST_F(test_streets_service, test_initialize_exception_config ) { setenv("CONFIG_FILE_PATH", "../test/test_files/invalid.json", 1); - ASSERT_FALSE(serv.initialize()); + EXPECT_FALSE(serv.initialize()); } From f6153e399073dd9c3057eb1a8cbb9a475f0a4521 Mon Sep 17 00:00:00 2001 From: dev Date: Thu, 14 Sep 2023 14:07:48 -0400 Subject: [PATCH 13/80] PR Comments --- .../streets_service_base/include/streets_service.h | 2 +- .../streets_service_base/src/streets_service.cpp | 2 +- .../streets_service_base/test/test_streets_service.cpp | 10 +++++----- 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/streets_utils/streets_service_base/include/streets_service.h b/streets_utils/streets_service_base/include/streets_service.h index c4a24584a..cfb28a260 100644 --- a/streets_utils/streets_service_base/include/streets_service.h +++ b/streets_utils/streets_service_base/include/streets_service.h @@ -76,7 +76,7 @@ namespace streets_service { * @throws runtime_error if config_name is nullptr or environment variable is not set. * @return value of environment variable. */ - std::string get_system_config(const char *config_name , const std::string &default_val) const; + std::string get_system_config(const char *config_name , const std::string &default_val) const noexcept; /** * @brief Method to consume continously consume time sync messages from Kafka topic and update * streets_clock_singleton with received data. diff --git a/streets_utils/streets_service_base/src/streets_service.cpp b/streets_utils/streets_service_base/src/streets_service.cpp index 7df6033bd..0745d2542 100644 --- a/streets_utils/streets_service_base/src/streets_service.cpp +++ b/streets_utils/streets_service_base/src/streets_service.cpp @@ -110,7 +110,7 @@ namespace streets_service { } } - std::string streets_service::get_system_config(const char *config_name, const std::string &default_val) const { + std::string streets_service::get_system_config(const char *config_name, const std::string &default_val) const noexcept{ // Check for config_name nullptr and use default value if (config_name == nullptr ) { SPDLOG_WARN("System config_name was nullptr! Using default value {0}!" , default_val); diff --git a/streets_utils/streets_service_base/test/test_streets_service.cpp b/streets_utils/streets_service_base/test/test_streets_service.cpp index af09c4cfd..b6171e128 100644 --- a/streets_utils/streets_service_base/test/test_streets_service.cpp +++ b/streets_utils/streets_service_base/test/test_streets_service.cpp @@ -1,8 +1,8 @@ #include -#include "streets_service.h" -#include "streets_environment_variables.h" -#include "mock_kafka_consumer_worker.h" -#include "mock_kafka_producer_worker.h" +#include +#include +#include +#include #include #include @@ -85,8 +85,8 @@ namespace streets_service{ log_file.open(file_path_string, std::ios::out); EXPECT_TRUE(log_file.good()); log_file.close(); - } + TEST_F(test_streets_service, test_initialize_consumer) { serv._service_name ="TestService"; std::shared_ptr consumer; From 194ddf615972ed407251a4201a6a8ec009be7d35 Mon Sep 17 00:00:00 2001 From: dev Date: Thu, 14 Sep 2023 14:21:03 -0400 Subject: [PATCH 14/80] PR update --- .../include/streets_environment_variables.h | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/streets_utils/streets_service_base/include/streets_environment_variables.h b/streets_utils/streets_service_base/include/streets_environment_variables.h index 12273827e..f798f30f4 100644 --- a/streets_utils/streets_service_base/include/streets_environment_variables.h +++ b/streets_utils/streets_service_base/include/streets_environment_variables.h @@ -4,21 +4,21 @@ namespace streets_service { - inline static const std::string LOGS_DIRECTORY_ENV = "LOGS_DIRECTORY"; + inline const std::string LOGS_DIRECTORY_ENV = "LOGS_DIRECTORY"; - inline static const std::string DEFAULT_LOGS_DIRECTORY = "../logs/"; + inline const std::string DEFAULT_LOGS_DIRECTORY = "../logs/"; - inline static const std::string SIMULATION_MODE_ENV = "SIMULATION_MODE"; + inline const std::string SIMULATION_MODE_ENV = "SIMULATION_MODE"; - inline static const std::string DEFAULT_SIMULATION_MODE = "FALSE"; + inline const std::string DEFAULT_SIMULATION_MODE = "FALSE"; - inline static const std::string CONFIG_FILE_PATH_ENV = "CONFIG_FILE_PATH"; + inline const std::string CONFIG_FILE_PATH_ENV = "CONFIG_FILE_PATH"; - inline static const std::string DEFAULT_CONFIG_FILE_PATH = "../manifest.json"; + inline const std::string DEFAULT_CONFIG_FILE_PATH = "../manifest.json"; - inline static const std::string TIME_SYNC_TOPIC_ENV = "TIME_SYNC_TOPIC"; + inline const std::string TIME_SYNC_TOPIC_ENV = "TIME_SYNC_TOPIC"; - inline static const std::string DEFAULT_TIME_SYNC_TOPIC = "time_sync"; + inline const std::string DEFAULT_TIME_SYNC_TOPIC = "time_sync"; } \ No newline at end of file From 7fab9687b95c40d55fb7dce24e2292be70b05ab5 Mon Sep 17 00:00:00 2001 From: paulbourelly999 <77466294+paulbourelly999@users.noreply.github.com> Date: Thu, 14 Sep 2023 15:59:25 -0400 Subject: [PATCH 15/80] Fixed install dependency script (#349) + Added line to get ubuntu distrib codename + stol apt debian packages push to s3 path base on codename --- build_scripts/install_dependencies.sh | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/build_scripts/install_dependencies.sh b/build_scripts/install_dependencies.sh index 05f8930d3..c8ebdd98d 100755 --- a/build_scripts/install_dependencies.sh +++ b/build_scripts/install_dependencies.sh @@ -3,8 +3,10 @@ # exit on errors set -e +# Get ubuntu distribution code name. All STOL APT debian packages are pushed to S3 bucket based on distribution codename. +. /etc/lsb-release # add the STOL APT repository -echo "deb [trusted=yes] http://s3.amazonaws.com/stol-apt-repository develop main" > /etc/apt/sources.list.d/stol-apt-repository.list +echo "deb [trusted=yes] http://s3.amazonaws.com/stol-apt-repository ${DISTRIB_CODENAME} main" > /etc/apt/sources.list.d/stol-apt-repository.list apt-get update From 05f0ea9b248e24185d765121e67172562f135c67 Mon Sep 17 00:00:00 2001 From: paulbourelly999 <77466294+paulbourelly999@users.noreply.github.com> Date: Fri, 15 Sep 2023 15:23:38 -0400 Subject: [PATCH 16/80] Clean up tsc service mrp implementation (#347) * Fix/cleanup tsc_service implementation + Add logging to streets service base clock singleton to indicate when thread is waiting on carma-clock initialization + Comment out MRP TSC Service integration which may no longer be necessary + Update SPAT_PROJECTION_ENUM to be capital + Modify unit tests to confirm carma-clock lib supports notifying multiple threads waiting on initialization + Update intall_dependecies.sh to pull carma-clock stol debian from correct repository in s3 bucket according to ubuntu distribution * Revert apt-get carma-time-lib changes, which will be in another PR * Updated unit tests and test documentation * Removed log statement --- .../src/streets_clock_singleton.cpp | 4 +- .../test/test_streets_clock_singleton.cpp | 151 +++++++++++++-- .../include/spat_projection_mode.h | 16 +- tsc_client_service/include/tsc_service.h | 3 +- .../src/spat_projection_mode.cpp | 10 +- tsc_client_service/src/tsc_service.cpp | 178 +++++++++--------- .../test/test_spat_projection_mode.cpp | 10 +- 7 files changed, 256 insertions(+), 116 deletions(-) diff --git a/streets_utils/streets_service_base/src/streets_clock_singleton.cpp b/streets_utils/streets_service_base/src/streets_clock_singleton.cpp index 2c7d68fde..e9b119cb9 100644 --- a/streets_utils/streets_service_base/src/streets_clock_singleton.cpp +++ b/streets_utils/streets_service_base/src/streets_clock_singleton.cpp @@ -10,9 +10,9 @@ namespace streets_service { uint64_t streets_clock_singleton::time_in_ms() { auto &inst = get_singleton(); - SPDLOG_TRACE("Calling thread is waiting on streets clock."); + SPDLOG_TRACE("Calling thread is waiting on streets clock initialization."); inst.wait_for_initialization(); - SPDLOG_TRACE("Thread is released after initializeation."); + SPDLOG_TRACE("Thread is released after initialization."); return inst.nowInMilliseconds(); } diff --git a/streets_utils/streets_service_base/test/test_streets_clock_singleton.cpp b/streets_utils/streets_service_base/test/test_streets_clock_singleton.cpp index fc0d903d9..335d6ba69 100644 --- a/streets_utils/streets_service_base/test/test_streets_clock_singleton.cpp +++ b/streets_utils/streets_service_base/test/test_streets_clock_singleton.cpp @@ -1,5 +1,8 @@ #include -#include "streets_clock_singleton.h" +#include +#include +#include + using namespace streets_service; using namespace std::chrono; @@ -16,32 +19,152 @@ namespace streets_service{ } }; - - TEST_F( test_streets_clock, test_simulation_mode) { - SPDLOG_INFO("Creating simulation clock"); + /** + * @brief This test case is meant to execute the streets_clock_singleton functionality in simulation mode to block a single calling thread + * requesting any time before the carma-clock object has been initialized by a time sync message from simulation and correctly unblock/wake up + * the thread once the carma clock is initialized + * + */ + TEST_F( test_streets_clock, test_simulation_mode_single_thread_initialization) { streets_clock_singleton::create(true); // Providing a seed value srand((unsigned) time(NULL)); // Get a random number int random = rand(); int old_val = random; - //Initialize time at zero to avoid wait_for_initialization hang + auto start = std::chrono::system_clock::now(); + // Set at 100 ms + auto sleep_duration = 100; + // Thread to simulate first incoming time sync message + std::thread t1([sleep_duration]() { + std::this_thread::sleep_for(std::chrono::milliseconds(sleep_duration)); + streets_clock_singleton::update(0); + }); + // Calling thread + EXPECT_EQ(streets_clock_singleton::time_in_ms(), 0); + // Measure how long calling thread was blocked + auto end = std::chrono::system_clock::now(); + auto elapsed_time = end-start; + // Assume duration is sleep duration of update thread +/- 1ms + EXPECT_NEAR( sleep_duration, std::chrono::duration_cast(elapsed_time).count(), 1 ); + t1.join(); + } + /** + * @brief This test case is meant to execute the streets_clock_singleton functionality in simulation mode to block a single calling thread + * using the sleep_until method and unblock/waking up the thread once the sleep_until value has been reached or passed. + * + */ + TEST_F( test_streets_clock, test_simulation_mode_single_thread_sleep_until) { + streets_clock_singleton::create(true); + // Initialize streets_clock_singleton to initial value streets_clock_singleton::update(0); + // Providing a seed value + srand((unsigned) time(NULL)); + // Get a random number + int random = rand(); + int old_val = random; + // Measure start of real time duration + auto start = std::chrono::system_clock::now(); + // Set at 100 ms + auto sleep_duration = 100; + // Thread to simulate incoming time sync update + std::thread t2([random, sleep_duration]() { + std::this_thread::sleep_for(std::chrono::milliseconds(sleep_duration)); + streets_clock_singleton::update(random); + }); + // Should block until streets_clock_singleton is updated to value. + streets_clock_singleton::sleep_until(random); + // Measure end of real time duration + auto end = std::chrono::system_clock::now(); + auto elapsed_time = end-start; + // Assume duration is sleep duration of update thread +/- 1ms + EXPECT_NEAR( sleep_duration, duration_cast(elapsed_time).count(), 1 ); + EXPECT_EQ(streets_clock_singleton::time_in_ms(), random); + t2.join(); - ASSERT_EQ(streets_clock_singleton::time_in_ms(), 0); + }; - streets_clock_singleton::update(random); + /** + * @brief This test case is meant to execute the streets_clock_singleton functionality in simulation mode to block multiple calling threads + * requesting any time before the carma-clock object has been initialized by a time sync message and correctly unblocking/waking up all + * waiting threads once the carma clock is initialized + * + */ + TEST_F( test_streets_clock, test_simulation_mode_multiple_thread_initialization) { + streets_clock_singleton::create(true); + // Providing a seed value + srand((unsigned) time(NULL)); + // Get a random number + int random = rand(); + int old_val = random; + auto start = std::chrono::system_clock::now(); + int t1_duration_ms, t2_duration_ms, t3_duration_ms; + // Set at 100 ms + auto sleep_duration = 100; + // Thread to simulate first incoming time sync message + std::thread t1([start, &t1_duration_ms]() { + //Blocked until streets_clock_singleton is initialized with a value + streets_clock_singleton::time_in_ms(); + auto t1_end = std::chrono::system_clock::now(); + t1_duration_ms = std::chrono::duration_cast(t1_end-start).count(); - ASSERT_EQ(streets_clock_singleton::time_in_ms(), random); - // Simulate random size timestep - random += rand(); - streets_clock_singleton::update(random); + }); + std::thread t2([start, &t2_duration_ms]() { + //Blocked until streets_clock_singleton is initialized with a value + streets_clock_singleton::time_in_ms(); + auto t2_end = std::chrono::system_clock::now(); + t2_duration_ms = std::chrono::duration_cast(t2_end-start).count(); - ASSERT_EQ(streets_clock_singleton::time_in_ms(), random); - ASSERT_NE(old_val, random); - }; + }); + std::thread t3([start, &t3_duration_ms]() { + //Blocked until streets_clock_singleton is initialized with a value + streets_clock_singleton::time_in_ms(); + auto t3_end = std::chrono::system_clock::now(); + t3_duration_ms = std::chrono::duration_cast(t3_end-start).count(); + + }); + + // Simulate time sync message update + std::this_thread::sleep_for(std::chrono::milliseconds(sleep_duration)); + streets_clock_singleton::update(0); + t1.join(); + t2.join(); + t3.join(); + // Confirm update was succesful + EXPECT_EQ(streets_clock_singleton::time_in_ms(), 0); + // // Assume duration is sleep duration of update is equal to thread duration +/- 1ms + EXPECT_NEAR( sleep_duration, t1_duration_ms, 1 ); + EXPECT_NEAR( sleep_duration, t2_duration_ms, 1 ); + EXPECT_NEAR( sleep_duration, t3_duration_ms, 1 ); + + } + /** + * @brief This test case is meant to test the functionality of the streets_clock_singleton to correctly handle multiple time updates + * in simulation mode. + */ + TEST_F(test_streets_clock, test_sim_mode_multiple_updates) { + streets_clock_singleton::create(true); + // Initialize streets_clock_singleton to initial value + streets_clock_singleton::update(0); + // Providing a seed value + srand((unsigned) time(NULL)); + // Get a random number + int random = rand(); + int old_val = random; + streets_clock_singleton::update(random); + EXPECT_EQ(streets_clock_singleton::time_in_ms(), random); + // Simulate random size timestep + random += rand(); + streets_clock_singleton::update(random); + EXPECT_EQ(streets_clock_singleton::time_in_ms(), random); + EXPECT_NE(old_val, random); + } + /** + * @brief This test case is to test the streets_clock_singleton functionality when not in simulation mode. When not in simulation mode the streets + * clock singleton should just return system time. + */ TEST_F(test_streets_clock, test_real_time) { streets_clock_singleton::create(false); EXPECT_NEAR(streets_clock_singleton::time_in_ms(), duration_cast(system_clock::now().time_since_epoch()).count(), 1); diff --git a/tsc_client_service/include/spat_projection_mode.h b/tsc_client_service/include/spat_projection_mode.h index d581cd13e..9518357d4 100644 --- a/tsc_client_service/include/spat_projection_mode.h +++ b/tsc_client_service/include/spat_projection_mode.h @@ -7,20 +7,26 @@ namespace traffic_signal_controller_service{ * @brief Enumeration to describe how we project future events on to the * SPAT information received from the Traffic Signal Controller. */ - enum class spat_projection_mode { + enum class SPAT_PROJECTION_MODE { /** * @brief Do not append any future information to received SPAT. */ - no_projection = 0, + NO_PROJECTION = 0, /** * @brief Append future information reflected in received DPP */ - dpp_projection = 1, + DPP_PROJECTION = 1, /** * @brief Append future information assuming fixed time, min vehicle recall on all vehicle phases. */ - fixed_projection =2 + FIXED_TIMING_PROJECTION = 2, + // TODO: Remove if no longer necessary for TM/TSP MMITSS Integration + // /** + // * @brief Append future information assuming reflected in received MRP (MMITSS Road Side Processor) Phase Schedule. + // */ + // MMITSS_PHASE_SCHEDULE = 3 + // ------------------------------------------------------------------------------------- }; - spat_projection_mode spat_projection_mode_from_int( const int i ); + SPAT_PROJECTION_MODE spat_projection_mode_from_int( const int i ); } \ No newline at end of file diff --git a/tsc_client_service/include/tsc_service.h b/tsc_client_service/include/tsc_service.h index 7b1491fee..ac8e6f9c5 100644 --- a/tsc_client_service/include/tsc_service.h +++ b/tsc_client_service/include/tsc_service.h @@ -102,7 +102,7 @@ namespace traffic_signal_controller_service { int tsc_config_send_attempts = 1; // desired phase plan information consumed from desire_phase_plan Kafka topic - spat_projection_mode spat_proj_mode = spat_projection_mode::no_projection; + SPAT_PROJECTION_MODE spat_proj_mode = SPAT_PROJECTION_MODE::NO_PROJECTION; // Queue to store snmp_cmd_structs which are objects used to run snmp HOLD and OMIT commands std::queue tsc_set_command_queue_; @@ -175,6 +175,7 @@ namespace traffic_signal_controller_service { * @return false if initialization is not successful. */ bool initialize_tsc_state( const std::shared_ptr _snmp_client_ptr); + /** * @brief Method to enable spat using the TSC Service SNMP Client. * diff --git a/tsc_client_service/src/spat_projection_mode.cpp b/tsc_client_service/src/spat_projection_mode.cpp index ea4378173..b3da700d4 100644 --- a/tsc_client_service/src/spat_projection_mode.cpp +++ b/tsc_client_service/src/spat_projection_mode.cpp @@ -1,20 +1,20 @@ #include "spat_projection_mode.h" namespace traffic_signal_controller_service{ - spat_projection_mode spat_projection_mode_from_int( const int i ) { + SPAT_PROJECTION_MODE spat_projection_mode_from_int( const int i ) { switch (i) { case 0: - return spat_projection_mode::no_projection; + return SPAT_PROJECTION_MODE::NO_PROJECTION; break; case 1: - return spat_projection_mode::dpp_projection; + return SPAT_PROJECTION_MODE::DPP_PROJECTION; break; case 2: - return spat_projection_mode::fixed_projection; + return SPAT_PROJECTION_MODE::FIXED_TIMING_PROJECTION; break; default: - return spat_projection_mode::no_projection; + return SPAT_PROJECTION_MODE::NO_PROJECTION; } } } \ No newline at end of file diff --git a/tsc_client_service/src/tsc_service.cpp b/tsc_client_service/src/tsc_service.cpp index be7f4dea8..5327c2117 100644 --- a/tsc_client_service/src/tsc_service.cpp +++ b/tsc_client_service/src/tsc_service.cpp @@ -11,33 +11,6 @@ namespace traffic_signal_controller_service { } try { - // Intialize spat kafka producer - std::string spat_topic_name = streets_configuration::get_string_config("spat_producer_topic"); - - std::string dpp_consumer_topic = streets_configuration::get_string_config("desired_phase_plan_consumer_topic"); - - //Phase control schedule(PCS) consumer topic - std::string pcs_consumer_topic = streets_configuration::get_string_config("phase_control_schedule_consumer_topic"); - - if (!spat_producer && !initialize_kafka_producer( spat_topic_name, spat_producer)) { - - SPDLOG_ERROR("Failed to initialize kafka spat_producer!"); - return false; - - } - - if (!desired_phase_plan_consumer && !initialize_kafka_consumer( dpp_consumer_topic, desired_phase_plan_consumer)) { - - SPDLOG_ERROR("Failed to initialize kafka desired_phase_plan_consumer!"); - return false; - - } - - if(!phase_control_schedule_consumer && !initialize_kafka_consumer(pcs_consumer_topic, phase_control_schedule_consumer)){ - SPDLOG_ERROR("Failed to initialize kafka phase_control_schedule_consumer!"); - return false; - } - // Initialize SNMP Client std::string target_ip = streets_configuration::get_string_config("target_ip"); int target_port = streets_configuration::get_int_config("target_port"); @@ -56,29 +29,73 @@ namespace traffic_signal_controller_service { SPDLOG_ERROR("Failed to initialize kafka tsc_config_producer!"); return false; } - //Initialize TSC State - auto spat_projection_int = streets_configuration::get_int_config("spat_projection_mode"); - spat_proj_mode = spat_projection_mode_from_int(spat_projection_int); - + //Initialize TSC State if (!initialize_tsc_state(snmp_client_ptr)){ SPDLOG_ERROR("Failed to initialize tsc state"); return false; } tsc_config_state_ptr = tsc_state_ptr->get_tsc_config_state(); + + // Intialize spat kafka producer + std::string spat_topic_name = streets_configuration::get_string_config("spat_producer_topic"); + if ( !spat_producer && !initialize_kafka_producer( spat_topic_name, spat_producer) ) { + + SPDLOG_ERROR("Failed to initialize kafka spat_producer!"); + return false; + + } + + std::string dpp_consumer_topic = streets_configuration::get_string_config("desired_phase_plan_consumer_topic"); + // TODO: Remove if no longer necessary for TM/TSP MMITSS Integration + // use_mmitss_mrp = streets_configuration::get_boolean_config("use_mmitss_mrp"); + // ----------------------------------------------------------------------------- + auto spat_projection_int = streets_configuration::get_int_config("spat_projection_mode"); + spat_proj_mode = spat_projection_mode_from_int(spat_projection_int); + // TODO: Remove if no longer necessary for TM/TSP MMITSS Integration + // Phase control schedule(PCS) consumer topic + // std::string pcs_consumer_topic = streets_configuration::get_string_config("phase_control_schedule_consumer_topic"); + // ----------------------------------------------------------------------------- + + // Initialize DPP consumer and monitor + if ( spat_proj_mode == SPAT_PROJECTION_MODE::DPP_PROJECTION ) { + if (!desired_phase_plan_consumer && !initialize_kafka_consumer( dpp_consumer_topic, desired_phase_plan_consumer)) { + SPDLOG_ERROR("Failed to initialize kafka desired_phase_plan_consumer!"); + return false; + } + monitor_dpp_ptr = std::make_shared( snmp_client_ptr ); + control_tsc_state_sleep_dur_ = streets_configuration::get_int_config("control_tsc_state_sleep_duration"); + // Initialize control_tsc_state ptr + control_tsc_state_ptr_ = std::make_shared(snmp_client_ptr, tsc_state_ptr); + + } + + // TODO: Remove if no longer necessary for TM/TSP MMITSS Integration + // if( SPAT_PROJECTION_MODE::MMITSS_PHASE_SCHEDULE) + // if ( !phase_control_schedule_consumer && !initialize_kafka_consumer(pcs_consumer_topic, phase_control_schedule_consumer)){ + // SPDLOG_ERROR("Failed to initialize kafka phase_control_schedule_consumer!"); + // return false; + // } + // //Initialize phase control schedule + // phase_control_schedule_ptr = std::make_shared(); + // // Initialize control_tsc_state ptr + // control_tsc_state_sleep_dur_ = streets_configuration::get_int_config("control_tsc_state_sleep_duration"); + // control_tsc_state_ptr_ = std::make_shared(snmp_client_ptr, tsc_state_ptr); + // } + // ------------------------------------------------------------------------------------------------------------------ + // Initialize spat_worker std::string socket_ip = streets_configuration::get_string_config("udp_socket_ip"); int socket_port = streets_configuration::get_int_config("udp_socket_port"); int socket_timeout = streets_configuration::get_int_config("socket_timeout"); bool use_msg_timestamp = streets_configuration::get_boolean_config("use_tsc_timestamp"); enable_snmp_cmd_logging_ = streets_configuration::get_boolean_config("enable_snmp_cmd_logging"); - use_mmitss_mrp = streets_configuration::get_boolean_config("use_mmitss_mrp"); - if (!initialize_spat_worker(socket_ip, socket_port, socket_timeout, use_msg_timestamp)) { SPDLOG_ERROR("Failed to initialize SPaT Worker"); return false; } + // Initialize intersection_client if (!initialize_intersection_client()) { SPDLOG_ERROR("Failed to initialize intersection client"); return false; @@ -92,17 +109,6 @@ namespace traffic_signal_controller_service { initialize_spat(intersection_client_ptr->get_intersection_name(), intersection_client_ptr->get_intersection_id(), all_phases); - control_tsc_state_sleep_dur_ = streets_configuration::get_int_config("control_tsc_state_sleep_duration"); - - // Initialize monitor desired phase plan - monitor_dpp_ptr = std::make_shared( snmp_client_ptr ); - - //Initialize phase control schedule - phase_control_schedule_ptr = std::make_shared(); - - // Initialize control_tsc_state ptr - control_tsc_state_ptr_ = std::make_shared(snmp_client_ptr, tsc_state_ptr); - if (enable_snmp_cmd_logging_) { configure_snmp_cmd_logger(); @@ -198,11 +204,11 @@ namespace traffic_signal_controller_service { SPDLOG_DEBUG("Current SPaT : {0} ", spat_ptr->toJson()); switch (spat_proj_mode) { - case spat_projection_mode::fixed_projection: { + case SPAT_PROJECTION_MODE::FIXED_TIMING_PROJECTION: { tsc_state_ptr->add_future_movement_events(spat_ptr); break; } - case spat_projection_mode::dpp_projection : { + case SPAT_PROJECTION_MODE::DPP_PROJECTION : { std::scoped_lock lck{dpp_mtx}; // throws desired phase plan exception when no previous green information present monitor_dpp_ptr->update_spat_future_movement_events(spat_ptr, tsc_state_ptr); @@ -284,27 +290,28 @@ namespace traffic_signal_controller_service { } } - - void tsc_service::consume_phase_control_schedule(){ - phase_control_schedule_consumer->subscribe(); - while (phase_control_schedule_consumer->is_running()) - { - const std::string payload = phase_control_schedule_consumer->consume(1000); - if (payload.length() != 0) - { - SPDLOG_DEBUG("Consumed: {0}", payload); - try { - //Update phase control schedule with the latest incoming schedule - phase_control_schedule_ptr->fromJson(payload); - //Update command queue - std::scoped_lock snmp_cmd_lck(snmp_cmd_queue_mtx); - control_tsc_state_ptr_->update_tsc_control_queue(phase_control_schedule_ptr, tsc_set_command_queue_); - } catch(streets_phase_control_schedule::streets_phase_control_schedule_exception &ex){ - SPDLOG_DEBUG("Failed to consume phase control schedule commands: {0}", ex.what()); - } - } - } - } + // TODO: Remove if no longer necessary for TM/TSP MMITSS Integration + // void tsc_service::consume_phase_control_schedule(){ + // phase_control_schedule_consumer->subscribe(); + // while (phase_control_schedule_consumer->is_running()) + // { + // const std::string payload = phase_control_schedule_consumer->consume(1000); + // if (payload.length() != 0) + // { + // SPDLOG_DEBUG("Consumed: {0}", payload); + // try { + // //Update phase control schedule with the latest incoming schedule + // phase_control_schedule_ptr->fromJson(payload); + // //Update command queue + // std::scoped_lock snmp_cmd_lck(snmp_cmd_queue_mtx); + // control_tsc_state_ptr_->update_tsc_control_queue(phase_control_schedule_ptr, tsc_set_command_queue_); + // } catch(streets_phase_control_schedule::streets_phase_control_schedule_exception &ex){ + // SPDLOG_DEBUG("Failed to consume phase control schedule commands: {0}", ex.what()); + // } + // } + // } + // } + // -------------------------------------------------------------------------------------------- void tsc_service::control_tsc_phases() { @@ -445,28 +452,31 @@ namespace traffic_signal_controller_service { std::thread spat_t(&tsc_service::produce_spat_json, this); - std::thread desired_phase_plan_t(&tsc_service::consume_desired_phase_plan, this); - - std::thread consume_phase_control_schedule_t(&tsc_service::consume_phase_control_schedule, this); - - std::thread control_phases_t(&tsc_service::control_tsc_phases, this); + if ( spat_proj_mode == SPAT_PROJECTION_MODE::DPP_PROJECTION ) { + std::thread desired_phase_plan_t(&tsc_service::consume_desired_phase_plan, this); + SPDLOG_DEBUG("Thread joined to consume desired phase plan generated by carma-streets SO service."); + desired_phase_plan_t.join(); + // Run tsc control phases + std::thread control_phases_t(&tsc_service::control_tsc_phases, this); + control_phases_t.join(); + } + // TODO: Remove if no longer necessary for TM/TSP MMITSS Integration + // An indicator to control whether launching a thread to consume carma-streets internal desired phase plan generated by singal optimization (SO) service + // or a thread to consume MMITSS external phase control schedule generated by MRP (https://github.com/mmitss/mmitss-az) + // if ( spat_proj_mode == SPAT_PROJECTION_MODE::MMITSS_PHASE_SCHEDULE ) { + // std::thread consume_phase_control_schedule_t(&tsc_service::consume_phase_control_schedule, this); + // SPDLOG_DEBUG("Thread joined to consume phase control schedule generated by MRP."); + // consume_phase_control_schedule_t.join(); + // // Run tsc control phases + // std::thread control_phases_t(&tsc_service::control_tsc_phases, this); + // control_phases_t.join(); + // } + // --------------------------------------------------------------------------------------------------------------- // Run threads as joint so that they dont overlap execution tsc_config_thread.join(); spat_t.join(); - // An indicator to control whether launching a thread to consume carma-streets internal desired phase plan generated by singal optimization (SO) service - // or a thread to consume MMITSS external phase control schedule generated by MRP (https://github.com/mmitss/mmitss-az) - if (use_mmitss_mrp) - { - SPDLOG_DEBUG("Thread joined to consume phase control schedule generated by MRP."); - consume_phase_control_schedule_t.join(); - }else{ - SPDLOG_DEBUG("Thread joined to consume desired phase plan generated by carma-streets SO service."); - desired_phase_plan_t.join(); - } - - control_phases_t.join(); } diff --git a/tsc_client_service/test/test_spat_projection_mode.cpp b/tsc_client_service/test/test_spat_projection_mode.cpp index 9d7c3f57f..967fd6708 100644 --- a/tsc_client_service/test/test_spat_projection_mode.cpp +++ b/tsc_client_service/test/test_spat_projection_mode.cpp @@ -4,9 +4,9 @@ using namespace traffic_signal_controller_service; TEST(test_spat_projection_mode, test_from_int ) { - ASSERT_EQ( spat_projection_mode::no_projection, spat_projection_mode_from_int(0)); - ASSERT_EQ( spat_projection_mode::dpp_projection, spat_projection_mode_from_int(1)); - ASSERT_EQ( spat_projection_mode::fixed_projection, spat_projection_mode_from_int(2)); - ASSERT_EQ( spat_projection_mode::no_projection, spat_projection_mode_from_int(-1)); - ASSERT_EQ( spat_projection_mode::no_projection, spat_projection_mode_from_int(5)); + ASSERT_EQ( SPAT_PROJECTION_MODE::NO_PROJECTION, spat_projection_mode_from_int(0)); + ASSERT_EQ( SPAT_PROJECTION_MODE::DPP_PROJECTION, spat_projection_mode_from_int(1)); + ASSERT_EQ( SPAT_PROJECTION_MODE::FIXED_TIMING_PROJECTION, spat_projection_mode_from_int(2)); + ASSERT_EQ( SPAT_PROJECTION_MODE::NO_PROJECTION, spat_projection_mode_from_int(-1)); + ASSERT_EQ( SPAT_PROJECTION_MODE::NO_PROJECTION, spat_projection_mode_from_int(5)); } From 09fd65a4152a5823ccbb5a40db0bdb8b48de65f4 Mon Sep 17 00:00:00 2001 From: paulbourelly999 <77466294+paulbourelly999@users.noreply.github.com> Date: Wed, 20 Sep 2023 12:41:07 -0400 Subject: [PATCH 17/80] Update third party to releases (#351) * Update third party to releases + Fix message services and intersection model build issues * Remove redudant gtest install --- intersection_model/Dockerfile | 10 ++++++---- message_services/Dockerfile | 14 ++++++++------ scheduling_service/Dockerfile | 2 +- signal_opt_service/Dockerfile | 2 +- tsc_client_service/Dockerfile | 4 ++-- 5 files changed, 18 insertions(+), 14 deletions(-) diff --git a/intersection_model/Dockerfile b/intersection_model/Dockerfile index 02892b679..493d164ea 100644 --- a/intersection_model/Dockerfile +++ b/intersection_model/Dockerfile @@ -13,16 +13,18 @@ RUN echo " ------> Install googletest..." WORKDIR /home/carma-streets/ RUN mkdir -p /home/carma-streets/ext WORKDIR /home/carma-streets/ext -RUN git clone https://github.com/google/googletest/ +RUN git clone https://github.com/google/googletest.git -b v1.13.0 WORKDIR /home/carma-streets/ext/googletest/ -RUN cmake . +RUN mkdir build +WORKDIR /home/carma-streets/ext/googletest/build +RUN cmake .. RUN make RUN make install # Install spdlog RUN echo " ------> Install spdlog... " WORKDIR /home/carma-streets/ext -RUN git clone https://github.com/gabime/spdlog.git +RUN git clone https://github.com/gabime/spdlog.git -b v1.12.0 WORKDIR /home/carma-streets/ext/spdlog/ RUN mkdir build WORKDIR /home/carma-streets/ext/spdlog/build @@ -32,7 +34,7 @@ RUN make install # Install librdkafka RUN echo " ------> Install librdkafka..." WORKDIR /home/carma-streets/ext -RUN git clone https://github.com/confluentinc/librdkafka.git +RUN git clone https://github.com/confluentinc/librdkafka.git -b v2.2.0 WORKDIR /home/carma-streets/ext/librdkafka/ RUN cmake -H. -B_cmake_build RUN cmake --build _cmake_build diff --git a/message_services/Dockerfile b/message_services/Dockerfile index 6a18853cb..49fec8aa8 100644 --- a/message_services/Dockerfile +++ b/message_services/Dockerfile @@ -1,6 +1,6 @@ FROM ubuntu:bionic-20210702 -RUN apt-get update && apt-get install -y sudo cmake gcc-7 g++-7 libboost1.65-dev libboost-thread1.65-dev libboost-regex1.65-dev libboost-log1.65-dev libboost-program-options1.65-dev libboost1.65-all-dev libxerces-c-dev libcurl4-openssl-dev autotools-dev automake sqlite3 libsqlite3-dev libpugixml-dev libmysqlclient-dev libgeographic-dev libjsoncpp-dev uuid-dev libusb-dev libusb-1.0-0-dev libftdi-dev swig liboctave-dev gpsd libgps-dev portaudio19-dev libsndfile1-dev libglib2.0-dev libglibmm-2.4-dev libpcre3-dev libsigc++-2.0-dev libxml++2.6-dev libxml2-dev liblzma-dev dpkg-dev libmysqlcppconn-dev libev-dev libuv-dev git build-essential libssl-dev qtbase5-dev qtbase5-dev-tools curl libqhttpengine-dev libgtest-dev libcpprest-dev +RUN apt-get update && apt-get install -y sudo cmake gcc-7 g++-7 libboost1.65-dev libboost-thread1.65-dev libboost-regex1.65-dev libboost-log1.65-dev libboost-program-options1.65-dev libboost1.65-all-dev libxerces-c-dev libcurl4-openssl-dev autotools-dev automake sqlite3 libsqlite3-dev libpugixml-dev libmysqlclient-dev libgeographic-dev libjsoncpp-dev uuid-dev libusb-dev libusb-1.0-0-dev libftdi-dev swig liboctave-dev gpsd libgps-dev portaudio19-dev libsndfile1-dev libglib2.0-dev libglibmm-2.4-dev libpcre3-dev libsigc++-2.0-dev libxml++2.6-dev libxml2-dev liblzma-dev dpkg-dev libmysqlcppconn-dev libev-dev libuv-dev git build-essential libssl-dev qtbase5-dev qtbase5-dev-tools curl libqhttpengine-dev libgtest-dev libcpprest-dev RUN mkdir -p /home/ @@ -10,16 +10,18 @@ RUN echo " ------> Install googletest..." WORKDIR /home/carma-streets/ RUN mkdir -p /home/carma-streets/ext WORKDIR /home/carma-streets/ext -RUN git clone https://github.com/google/googletest/ +RUN git clone https://github.com/google/googletest.git -b v1.13.0 WORKDIR /home/carma-streets/ext/googletest/ -RUN cmake . +RUN mkdir build +WORKDIR /home/carma-streets/ext/googletest/build +RUN cmake .. RUN make -RUN sudo make install +RUN make install # Install librdkafka RUN echo " ------> Install librdkafka..." WORKDIR /home/carma-streets/ext -RUN git clone https://github.com/confluentinc/librdkafka.git +RUN git clone https://github.com/confluentinc/librdkafka.git -b v2.2.0 WORKDIR /home/carma-streets/ext/librdkafka/ RUN cmake -H. -B_cmake_build RUN cmake --build _cmake_build @@ -29,7 +31,7 @@ RUN cmake --build _cmake_build --target install # Install spdlog RUN echo " ------> Install spdlog... " WORKDIR /home/carma-streets/ext -RUN git clone https://github.com/gabime/spdlog.git +RUN git clone https://github.com/gabime/spdlog.git -b v1.12.0 WORKDIR /home/carma-streets/ext/spdlog/ RUN mkdir build WORKDIR /home/carma-streets/ext/spdlog/build diff --git a/scheduling_service/Dockerfile b/scheduling_service/Dockerfile index e453a843e..58584dbc2 100644 --- a/scheduling_service/Dockerfile +++ b/scheduling_service/Dockerfile @@ -12,7 +12,7 @@ RUN mkdir -p /home/carma-streets/ # Install librdkafka RUN echo " ------> Install librdkafka..." WORKDIR /home/carma-streets/ext -RUN git clone https://github.com/confluentinc/librdkafka.git +RUN git clone https://github.com/confluentinc/librdkafka.git -b v2.2.0 WORKDIR /home/carma-streets/ext/librdkafka/ RUN cmake -H. -B_cmake_build RUN cmake --build _cmake_build diff --git a/signal_opt_service/Dockerfile b/signal_opt_service/Dockerfile index 24fdb7557..3d379fec3 100644 --- a/signal_opt_service/Dockerfile +++ b/signal_opt_service/Dockerfile @@ -21,7 +21,7 @@ RUN make install # Install librdkafka RUN echo " ------> Install librdkafka..." WORKDIR /home/carma-streets/ext -RUN git clone https://github.com/confluentinc/librdkafka.git +RUN git clone https://github.com/confluentinc/librdkafka.git -b v2.2.0 WORKDIR /home/carma-streets/ext/librdkafka/ RUN cmake -H. -B_cmake_build RUN cmake --build _cmake_build diff --git a/tsc_client_service/Dockerfile b/tsc_client_service/Dockerfile index 58253fad4..0c7a8de73 100644 --- a/tsc_client_service/Dockerfile +++ b/tsc_client_service/Dockerfile @@ -11,7 +11,7 @@ RUN mkdir -p /home/carma-streets/ # Install librdkafka RUN echo " ------> Install librdkafka..." WORKDIR /home/carma-streets/ext -RUN git clone https://github.com/confluentinc/librdkafka.git +RUN git clone https://github.com/confluentinc/librdkafka.git -b v2.2.0 WORKDIR /home/carma-streets/ext/librdkafka/ RUN cmake -H. -B_cmake_build RUN cmake --build _cmake_build @@ -20,7 +20,7 @@ RUN cmake --build _cmake_build --target install # Install rapidjson RUN echo " ------> Install rapidjson..." WORKDIR /home/carma-streets/ext -RUN git clone https://github.com/Tencent/rapidjson +RUN git clone https://github.com/Tencent/rapidjson WORKDIR /home/carma-streets/ext/rapidjson/ RUN git checkout a95e013b97ca6523f32da23f5095fcc9dd6067e5 RUN mkdir build From 18a362ac1352eebf168174110706b4c8f6fd5a52 Mon Sep 17 00:00:00 2001 From: dev Date: Fri, 29 Sep 2023 10:48:04 -0400 Subject: [PATCH 18/80] Added JSON Utility Library to carma-streets --- .sonarqube/sonar-scanner.properties | 12 +- build.sh | 1 + coverage.sh | 7 + streets_utils/streets_json/CMakeLists.txt | 76 ++++++ .../streets_json_util_libConfig.cmake.in | 7 + .../include/json_parsing_exception.hpp | 26 +++ .../streets_json/include/json_utility.hpp | 28 +++ .../streets_json/src/json_utility.cpp | 103 +++++++++ .../streets_json/test/json_utility_test.cpp | 218 ++++++++++++++++++ 9 files changed, 475 insertions(+), 3 deletions(-) create mode 100644 streets_utils/streets_json/CMakeLists.txt create mode 100644 streets_utils/streets_json/cmake/streets_json_util_libConfig.cmake.in create mode 100644 streets_utils/streets_json/include/json_parsing_exception.hpp create mode 100644 streets_utils/streets_json/include/json_utility.hpp create mode 100644 streets_utils/streets_json/src/json_utility.cpp create mode 100644 streets_utils/streets_json/test/json_utility_test.cpp diff --git a/.sonarqube/sonar-scanner.properties b/.sonarqube/sonar-scanner.properties index 992c78576..8fc1e11c6 100644 --- a/.sonarqube/sonar-scanner.properties +++ b/.sonarqube/sonar-scanner.properties @@ -36,7 +36,9 @@ sonar.coverageReportPaths= /home/carma-streets/kafka_clients/coverage/coverage.x /home/carma-streets/streets_utils/streets_timing_plan/coverage/coverage.xml, \ /home/carma-streets/streets_utils/streets_signal_optimization/coverage/coverage.xml /home/carma-streets/streets_utils/streets_signal_optimization/coverage/coverage.xml, \ -/home/carma-streets/streets_utils/streets_snmp_cmd/coverage/coverage.xml +/home/carma-streets/streets_utils/streets_snmp_cmd/coverage/coverage.xml, \ +/home/carma-streets/streets_utils/streets_json/coverage/coverage.xml + # TODO : /home/carma-streets/intersection_model/coverage/coverage.xml, \ # TODO: /home/carma-streets/message_services/coverage/coverage.xml, \ @@ -77,7 +79,8 @@ tsc_client_service, \ streets_vehicle_scheduler, \ streets_desired_phase_plan, \ streets_signal_optimization, \ -streets_snmp_cmd +streets_snmp_cmd, \ +streets_json # TODO message_services # TODO intersection_model @@ -99,6 +102,7 @@ streets_signal_optimization.sonar.projectBaseDir=/home/carma-streets/streets_uti streets_phase_control_schedule.sonar.projectBaseDir=/home/carma-streets/streets_utils/streets_phase_control_schedule streets_timing_plan.sonar.projectBaseDir=/home/carma-streets/streets_utils/streets_timing_plan streets_snmp_cmd.sonar.projectBaseDir=/home/carma-streets/streets_utils/streets_snmp_cmd +streets_json.sonar.projectBaseDir=/home/carma-streets/streets_utils/streets_json @@ -138,7 +142,8 @@ streets_timing_plan.sonar.sources =src/,include/ streets_timing_plan.sonar.exclusions =test/** streets_snmp_cmd.sonar.sources =src/,include/ streets_snmp_cmd.sonar.exclusions =test/** - +streets_json.sonar.sources =src/,include/ +streets_json.sonar.exclusions =test/** #Tests # Note: For C++ setting this field does not cause test analysis to occur. It only allows the test source code to be evaluated. @@ -159,5 +164,6 @@ streets_signal_optimization.sonar.tests=test/ streets_phase_control_schedule.sonar.tests=test/ streets_timing_plan.sonar.tests=test/ streets_snmp_cmd.sonar.tests=test/ +streets_json.sonar.tests=test/ diff --git a/build.sh b/build.sh index bde47325d..77ac31b37 100755 --- a/build.sh +++ b/build.sh @@ -35,6 +35,7 @@ MAKE_INSTALL_DIRS=( "streets_utils/streets_api/intersection_server_api" "streets_utils/streets_signal_optimization" "streets_utils/streets_snmp_cmd" + "streets_utils/streets_json" ) # only make for these subdirectories diff --git a/coverage.sh b/coverage.sh index 37937ca33..d38ab34d2 100755 --- a/coverage.sh +++ b/coverage.sh @@ -19,6 +19,13 @@ cd /home/carma-streets mkdir test_results +cd /home/carma-streets/streets_utils/streets_json/build/ +./streets_json_test --gtest_output=xml:../../../test_results/ +cd /home/carma-streets/streets_utils/streets_json +mkdir coverage +cd /home/carma-streets/ +gcovr --sonarqube streets_utils/streets_utils/coverage/coverage.xml -s -f streets_utils/streets_utils/ -r . + cd /home/carma-streets/streets_utils/streets_service_configuration/build/ ./streets_service_configuration_test --gtest_output=xml:../../../test_results/ cd /home/carma-streets/streets_utils/streets_service_configuration diff --git a/streets_utils/streets_json/CMakeLists.txt b/streets_utils/streets_json/CMakeLists.txt new file mode 100644 index 000000000..4735adec8 --- /dev/null +++ b/streets_utils/streets_json/CMakeLists.txt @@ -0,0 +1,76 @@ +cmake_minimum_required(VERSION 3.10.2) +project(streets_json_util) +# Set Flags +set(CMAKE_CXX_STANDARD 20) +# Find Packages +find_package(RapidJSON REQUIRED) +find_package(GTest REQUIRED) +# Add definition for rapidjson to include std::string +add_definitions(-DRAPIDJSON_HAS_STDSTRING=1) + +set(LIBRARY_NAME streets_json_util) +######################################################## +# Build Library +######################################################## +file(GLOB SOURCES ${CMAKE_CURRENT_SOURCE_DIR}/src/*.cpp) +add_library(${LIBRARY_NAME}_lib ${SOURCES} ) +target_include_directories(${LIBRARY_NAME}_lib + PUBLIC + $ + $ +) +target_link_libraries( ${LIBRARY_NAME}_lib + PUBLIC + rapidjson +) + +message( STATUS "Target INCLUDE DIR : " ${INCLUDE_DIRECTORIES}) + +######################################################## +# Install sensor_data_sharing_package package. +######################################################## +file(GLOB files ${CMAKE_CURRENT_SOURCE_DIR}/include/*.hpp) + +install( + TARGETS ${LIBRARY_NAME}_lib + EXPORT ${LIBRARY_NAME}_libTargets + LIBRARY DESTINATION lib + INCLUDES DESTINATION include + ARCHIVE DESTINATION lib +) +install( + EXPORT ${LIBRARY_NAME}_libTargets + FILE ${LIBRARY_NAME}_libTargets.cmake + DESTINATION lib/cmake/${LIBRARY_NAME}_lib/ + NAMESPACE ${LIBRARY_NAME}_lib:: +) +include(CMakePackageConfigHelpers) +configure_package_config_file( + cmake/${LIBRARY_NAME}_libConfig.cmake.in + ${CMAKE_CURRENT_BINARY_DIR}/${LIBRARY_NAME}_libConfig.cmake + INSTALL_DESTINATION lib/${LIBRARY_NAME}_lib/ ) +install( + FILES ${CMAKE_CURRENT_BINARY_DIR}/${LIBRARY_NAME}_libConfig.cmake + DESTINATION lib/cmake/${LIBRARY_NAME}_lib/ +) +install(FILES ${files} DESTINATION include) + +######################## +# googletest for unit testing +######################## +set(BINARY ${LIBRARY_NAME}_test) +file(GLOB_RECURSE TEST_SOURCES LIST_DIRECTORIES false test/*.hpp test/*.cpp) +set(SOURCES ${TEST_SOURCES} WORKING_DIRECTORY ${PROJECT_SOURCE_DIR}/test) +add_executable(${BINARY} ${TEST_SOURCES}) +add_test(NAME ${BINARY} COMMAND ${BINARY}) +target_include_directories(${BINARY} PUBLIC ${PROJECT_SOURCE_DIR}/include) +target_link_libraries(${BINARY} + PUBLIC + ${LIBRARY_NAME}_lib + rapidjson + ) +if(${CMAKE_VERSION} VERSION_GREATER_EQUAL "3.20") + target_link_libraries( ${BINARY} PUBLIC GTest::gtest_main ) +else() + target_link_libraries( ${BINARY} PUBLIC gtest_main ) +endif() \ No newline at end of file diff --git a/streets_utils/streets_json/cmake/streets_json_util_libConfig.cmake.in b/streets_utils/streets_json/cmake/streets_json_util_libConfig.cmake.in new file mode 100644 index 000000000..9d8592224 --- /dev/null +++ b/streets_utils/streets_json/cmake/streets_json_util_libConfig.cmake.in @@ -0,0 +1,7 @@ +@PACKAGE_INIT@ + +include(CMakeFindDependencyMacro) +find_dependency(spdlog REQUIRED) +find_dependency(RapidJSON REQUIRED) + +include("${CMAKE_CURRENT_LIST_DIR}/streets_json_util_libTargets.cmake") diff --git a/streets_utils/streets_json/include/json_parsing_exception.hpp b/streets_utils/streets_json/include/json_parsing_exception.hpp new file mode 100644 index 000000000..4e31db7f4 --- /dev/null +++ b/streets_utils/streets_json/include/json_parsing_exception.hpp @@ -0,0 +1,26 @@ +#pragma once + +#include + + + +namespace streets_utils::json_utils { + /** + * @brief Runtime error related to processing signal_phase_and_timing JSON updates. + * + * @author Paul Bourelly + */ + class json_parsing_exception : public std::runtime_error{ + public: + /** + * @brief Destructor. + */ + ~json_parsing_exception() override; + /** + * @brief Constructor. + * @param msg String exception message. + */ + explicit json_parsing_exception(const std::string &msg ) : std::runtime_error(msg){}; + + }; +} \ No newline at end of file diff --git a/streets_utils/streets_json/include/json_utility.hpp b/streets_utils/streets_json/include/json_utility.hpp new file mode 100644 index 000000000..6fc1e10ff --- /dev/null +++ b/streets_utils/streets_json/include/json_utility.hpp @@ -0,0 +1,28 @@ +#pragma once + +#include +#include +#include +#include +#include +#include "json_parsing_exception.hpp" + +namespace streets_utils::json_utils { + + rapidjson::Document validate_json( const std::string &json ); + + std::optional get_json_int_property(const std::string &prop_name, const rapidjson::Value &doc, bool required ); + + std::optional get_json_uint_property(const std::string &prop_name, const rapidjson::Value &doc, bool required ); + + std::optional get_json_bool_property(const std::string &prop_name, const rapidjson::Value &doc, bool required ); + + std::optional get_json_string_property(const std::string &prop_name, const rapidjson::Value &doc, bool required ); + + std::optional get_json_double_property(const std::string &prop_name, const rapidjson::Value &doc, bool required); + + const rapidjson::Value::ConstObject get_json_object_property(const std::string &prop_name, const rapidjson::Value &doc, bool required ); + + const rapidjson::Value::ConstArray get_json_array_property(const std::string &prop_name, const rapidjson::Value &doc, bool required); + +} \ No newline at end of file diff --git a/streets_utils/streets_json/src/json_utility.cpp b/streets_utils/streets_json/src/json_utility.cpp new file mode 100644 index 000000000..047189a51 --- /dev/null +++ b/streets_utils/streets_json/src/json_utility.cpp @@ -0,0 +1,103 @@ +#include "json_utility.hpp" + +namespace streets_utils::json_utils { + + rapidjson::Document validate_json(const std::string &json) { + rapidjson::Document doc; + doc.Parse(json); + if (doc.HasParseError()) + { + throw json_parsing_exception("Message JSON is misformatted. JSON parsing failed!"); + } + return doc; + } + std::optional get_json_int_property(const std::string &prop_name, const rapidjson::Value &doc, bool required ){ + std::optional property; + if (doc.HasMember(prop_name.c_str()) && doc.FindMember(prop_name.c_str())->value.IsInt64()) + { + property = doc[prop_name.c_str()].GetInt64(); + } + else if (required) + { + throw json_parsing_exception("Missing or incorrect type for required property " + prop_name + "!"); + } + return property; + } + + std::optional get_json_uint_property(const std::string &prop_name, const rapidjson::Value &doc, bool required ) { + std::optional property; + if (doc.HasMember(prop_name.c_str()) && doc.FindMember(prop_name.c_str())->value.IsUint64()) + { + property = doc[prop_name.c_str()].GetUint64(); + } + else if (required) + { + throw json_parsing_exception("Missing or incorrect type for required property " + prop_name + "!"); + } + return property; + }; + + std::optional get_json_bool_property(const std::string &prop_name, const rapidjson::Value &doc, bool required ) { + std::optional property; + if (doc.HasMember(prop_name.c_str()) && doc.FindMember(prop_name.c_str())->value.IsBool()) + { + property = doc[prop_name.c_str()].GetBool(); + } + else if (required) + { + throw json_parsing_exception("Missing or incorrect type for required property " + prop_name + "!"); + } + return property; + }; + + std::optional get_json_string_property(const std::string &prop_name, const rapidjson::Value &doc, bool required ){ + std::optional property; + if (doc.HasMember(prop_name.c_str()) && doc.FindMember(prop_name.c_str())->value.IsString()) + { + property = doc[prop_name.c_str()].GetString(); + } + else if (required) + { + throw json_parsing_exception("Missing or incorrect type for required property " + prop_name + "!"); + } + return property; + }; + + std::optional get_json_double_property(const std::string &prop_name, const rapidjson::Value &doc, bool required) { + std::optional property; + if (doc.HasMember(prop_name.c_str()) && doc.FindMember(prop_name.c_str())->value.IsDouble()) + { + property = doc[prop_name.c_str()].GetDouble(); + } + else if (required) + { + throw json_parsing_exception("Missing or incorrect type for required property " + prop_name + "!"); + } + return property; + }; + + const rapidjson::Value::ConstObject get_json_object_property(const std::string &prop_name, const rapidjson::Value &doc, bool required) { + if (doc.HasMember(prop_name.c_str()) && doc.FindMember(prop_name.c_str())->value.IsObject()) + { + return doc[prop_name.c_str()].GetObject(); + } + else if (required) + { + throw json_parsing_exception("Missing or incorrect type for required property " + prop_name + "!"); + } + } + + const rapidjson::Value::ConstArray get_json_array_property(const std::string &prop_name, const rapidjson::Value &doc, bool required) { + if (doc.HasMember(prop_name.c_str()) && doc.FindMember(prop_name.c_str())->value.IsArray()) + { + return doc[prop_name.c_str()].GetArray(); + } + else if (required) + { + throw json_parsing_exception("Missing or incorrect type for required property " + prop_name + "!"); + } + } + + + +} \ No newline at end of file diff --git a/streets_utils/streets_json/test/json_utility_test.cpp b/streets_utils/streets_json/test/json_utility_test.cpp new file mode 100644 index 000000000..49fd8fa59 --- /dev/null +++ b/streets_utils/streets_json/test/json_utility_test.cpp @@ -0,0 +1,218 @@ +#include +#include + +using namespace streets_utils::json_utils; + +//---------------------test validate_json--------------------- +TEST(json_utility_test, test_validate_invalid_json) { + // Empty String + std::string invalid_json = ""; + EXPECT_THROW( validate_json(invalid_json), std::runtime_error); + + // Property missing quotations + invalid_json = "{ some_propert: \"some_value\"}"; + EXPECT_THROW( validate_json(invalid_json), std::runtime_error); + +} + +TEST(json_utility_test, test_validate_valid_json) { + // Correct JSON + std::string valid_json = "{ \"some_property\": \"some_value\"}"; + auto parsed_doc = validate_json(valid_json); + EXPECT_FALSE(parsed_doc.HasParseError()); +} +//---------------------test get_json_uint_property--------------------- +TEST(json_utility_test, test_get_json_uint_required_property_present){ + // Test with required property present + std::string valid_json = "{ \"some_property\": 12345}"; + auto parsed_doc = validate_json(valid_json); + EXPECT_EQ( 12345, get_json_uint_property("some_property", parsed_doc, true)); +} + +TEST(json_utility_test, test_get_json_uint_required_property_not_present){ + // Test with required property no present + std::string valid_json = "{ \"some_property_other\": 12345}"; + auto parsed_doc = validate_json(valid_json); + EXPECT_THROW( get_json_uint_property("some_property", parsed_doc, true), std::runtime_error); +} + +TEST(json_utility_test, test_get_json_uint_required_property_wrong_type){ + // Test with required property present with wrong type + std::string valid_json = "{ \"some_property\": -12345}"; + auto parsed_doc = validate_json(valid_json); + EXPECT_THROW( get_json_uint_property("some_property", parsed_doc, true), std::runtime_error); +} +//---------------------test get_json_int_property--------------------- + +TEST(json_utility_test, test_get_json_int_required_property_present){ + // Test with required property present + std::string valid_json = "{ \"some_property\": -12345}"; + auto parsed_doc = validate_json(valid_json); + EXPECT_EQ( -12345, get_json_int_property("some_property", parsed_doc, true)); +} + +TEST(json_utility_test, test_get_json_int_required_property_not_present){ + // Test with required property no present + std::string valid_json = "{ \"some_property_other\": 12345}"; + auto parsed_doc = validate_json(valid_json); + EXPECT_THROW( get_json_int_property("some_property", parsed_doc, true), std::runtime_error); +} + +TEST(json_utility_test, test_get_json_int_required_property_wrong_type){ + // Test with required property present with wrong type + std::string valid_json = "{ \"some_property\": true}"; + auto parsed_doc = validate_json(valid_json); + EXPECT_THROW( get_json_int_property("some_property", parsed_doc, true), std::runtime_error); +} + +//---------------------test get_json_bool_property--------------------- + +TEST(json_utility_test, test_get_json_bool_required_property_present){ + // Test with required property present + std::string valid_json = "{ \"some_property\": true}"; + auto parsed_doc = validate_json(valid_json); + EXPECT_EQ( true, get_json_bool_property("some_property", parsed_doc, true)); +} + +TEST(json_utility_test, test_get_json_bool_required_property_not_present){ + // Test with required property no present + std::string valid_json = "{ \"some_property_other\": true}"; + auto parsed_doc = validate_json(valid_json); + EXPECT_THROW( get_json_bool_property("some_property", parsed_doc, true), std::runtime_error); +} + +TEST(json_utility_test, test_get_json_bool_required_property_wrong_type){ + // Test with required property present with wrong type + std::string valid_json = "{ \"some_property\": 1234}"; + auto parsed_doc = validate_json(valid_json); + EXPECT_THROW( get_json_bool_property("some_property", parsed_doc, true), std::runtime_error); +} + +//---------------------test get_json_double_property--------------------- + +TEST(json_utility_test, test_get_json_double_required_property_present){ + // Test with required property present + std::string valid_json = "{ \"some_property\": 12.3}"; + auto parsed_doc = validate_json(valid_json); + EXPECT_NEAR( 12.3, get_json_double_property("some_property", parsed_doc, true).value(), 0.01); +} + +TEST(json_utility_test, test_get_json_double_required_property_not_present){ + // Test with required property no present + std::string valid_json = "{ \"some_property_other\": 12.3}"; + auto parsed_doc = validate_json(valid_json); + EXPECT_THROW( get_json_double_property("some_property", parsed_doc, true), std::runtime_error); +} + +TEST(json_utility_test, test_get_json_double_required_property_wrong_type){ + // Test with required property present with wrong type + std::string valid_json = "{ \"some_property\": 1234}"; + auto parsed_doc = validate_json(valid_json); + EXPECT_THROW( get_json_double_property("some_property", parsed_doc, true), std::runtime_error); +} + +//---------------------test get_json_string_property--------------------- + +TEST(json_utility_test, test_get_json_string_required_property_present){ + // Test with required property present + std::string valid_json = "{ \"some_property\": \"some_property\" }"; + auto parsed_doc = validate_json(valid_json); + EXPECT_EQ("some_property", get_json_string_property("some_property", parsed_doc, true)); +} + +TEST(json_utility_test, test_get_json_string_required_property_not_present){ + // Test with required property no present + std::string valid_json = "{ \"some_property_other\": 12.3}"; + auto parsed_doc = validate_json(valid_json); + EXPECT_THROW( get_json_string_property("some_property", parsed_doc, true), std::runtime_error); +} + +TEST(json_utility_test, test_get_json_string_required_property_wrong_type){ + // Test with required property present with wrong type + std::string valid_json = "{ \"some_property\": 1234}"; + auto parsed_doc = validate_json(valid_json); + EXPECT_THROW( get_json_string_property("some_property", parsed_doc, true), std::runtime_error); +} + + +//---------------------test get_json_object_property--------------------- + +TEST(json_utility_test, test_get_json_object_required_property_present){ + // Test with required property present + std::string valid_json = "{ " + "\"some_object\": {" + "\"object_name\" : \"object\"," + "\"object_value\" : 123" + "}" + "}"; + auto parsed_doc = validate_json(valid_json); + auto object = get_json_object_property("some_object", parsed_doc, true); + EXPECT_EQ("object", get_json_string_property("object_name", object, true)); + EXPECT_EQ( 123, get_json_int_property("object_value", object, true) ); + +} + +TEST(json_utility_test, test_get_json_object_required_property_not_present){ + // Test with required property no present + std::string valid_json = "{ " + "\"some_other_object\": {" + "\"object_name\" : \"object\"," + "\"object_value\" : 123" + "}" + "}"; + auto parsed_doc = validate_json(valid_json); + EXPECT_THROW( get_json_object_property("some_object", parsed_doc, true), std::runtime_error); +} + +TEST(json_utility_test, test_get_json_object_required_property_wrong_type){ + // Test with required property present with wrong type + std::string valid_json = "{ " + "\"some_object\": [{" + "\"object_name\" : \"object\"," + "\"object_value\" : 123" + "}]" + "}"; + auto parsed_doc = validate_json(valid_json); + EXPECT_THROW( get_json_object_property("some_object", parsed_doc, true), std::runtime_error); +} + +//---------------------test get_json_array_property--------------------- + +TEST(json_utility_test, test_get_json_array_required_property_present){ + // Test with required property present + std::string valid_json = "{ " + "\"some_array\": " + "[456, 2452, -1232, 2345]" + "}"; + auto parsed_doc = validate_json(valid_json); + auto array = get_json_array_property("some_array", parsed_doc, true); + + EXPECT_EQ(4, array.Size()); + EXPECT_EQ(456, array[0].GetInt()); + EXPECT_EQ(2452, array[1].GetInt()); + EXPECT_EQ(-1232, array[2].GetInt()); + EXPECT_EQ(2345, array[3].GetInt()); +} + +TEST(json_utility_test, test_get_json_array_required_property_not_present){ + // Test with required property no present + std::string valid_json = "{ " + "\"some_other_array\": " + "[456, 2452, -1232, 2345]" + "}"; + auto parsed_doc = validate_json(valid_json); + EXPECT_THROW( get_json_array_property("some_array", parsed_doc, true), std::runtime_error); +} + +TEST(json_utility_test, test_get_json_array_required_property_wrong_type){ + // Test with required property present with wrong type + std::string valid_json = "{ " + "\"some_array\": [{" + "\"object_name\" : \"object\"," + "\"object_value\" : 123" + "}]" + "}"; + auto parsed_doc = validate_json(valid_json); + EXPECT_THROW( get_json_array_property("some_object", parsed_doc, true), std::runtime_error); +} + From 583239c447e8ec28bab3c6a1f8fbc1ea5f0dd2e3 Mon Sep 17 00:00:00 2001 From: dev Date: Fri, 29 Sep 2023 13:42:06 -0400 Subject: [PATCH 19/80] Fix json_parsing_exception destructor. --- streets_utils/streets_json/include/json_parsing_exception.hpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/streets_utils/streets_json/include/json_parsing_exception.hpp b/streets_utils/streets_json/include/json_parsing_exception.hpp index 4e31db7f4..4ab7a8262 100644 --- a/streets_utils/streets_json/include/json_parsing_exception.hpp +++ b/streets_utils/streets_json/include/json_parsing_exception.hpp @@ -15,7 +15,7 @@ namespace streets_utils::json_utils { /** * @brief Destructor. */ - ~json_parsing_exception() override; + ~json_parsing_exception() = default; /** * @brief Constructor. * @param msg String exception message. From 87fd4530ced932c927067223c79d50cd58a6ebed Mon Sep 17 00:00:00 2001 From: dev Date: Fri, 29 Sep 2023 14:02:52 -0400 Subject: [PATCH 20/80] Fix for coverage script --- coverage.sh | 2 +- streets_utils/streets_json/include/json_utility.hpp | 9 +++++++-- streets_utils/streets_json/src/json_utility.cpp | 4 ++-- 3 files changed, 10 insertions(+), 5 deletions(-) diff --git a/coverage.sh b/coverage.sh index d38ab34d2..f37af4a3e 100755 --- a/coverage.sh +++ b/coverage.sh @@ -24,7 +24,7 @@ cd /home/carma-streets/streets_utils/streets_json/build/ cd /home/carma-streets/streets_utils/streets_json mkdir coverage cd /home/carma-streets/ -gcovr --sonarqube streets_utils/streets_utils/coverage/coverage.xml -s -f streets_utils/streets_utils/ -r . +gcovr --sonarqube streets_utils/streets_utils/coverage/coverage.xml -s -f streets_utils/streets_json/ -r . cd /home/carma-streets/streets_utils/streets_service_configuration/build/ ./streets_service_configuration_test --gtest_output=xml:../../../test_results/ diff --git a/streets_utils/streets_json/include/json_utility.hpp b/streets_utils/streets_json/include/json_utility.hpp index 6fc1e10ff..02c5c9387 100644 --- a/streets_utils/streets_json/include/json_utility.hpp +++ b/streets_utils/streets_json/include/json_utility.hpp @@ -21,8 +21,13 @@ namespace streets_utils::json_utils { std::optional get_json_double_property(const std::string &prop_name, const rapidjson::Value &doc, bool required); - const rapidjson::Value::ConstObject get_json_object_property(const std::string &prop_name, const rapidjson::Value &doc, bool required ); + rapidjson::Value::ConstObject get_json_object_property(const std::string &prop_name, const rapidjson::Value &doc, bool required ); + + rapidjson::Value::ConstArray get_json_array_property(const std::string &prop_name, const rapidjson::Value &doc, bool required); + + void write_int_property(const std::string &prop_name, rapidjson::Value &doc); + + void write_uint_propert(const std::string &prop_name, rapidjson::Value &doc); - const rapidjson::Value::ConstArray get_json_array_property(const std::string &prop_name, const rapidjson::Value &doc, bool required); } \ No newline at end of file diff --git a/streets_utils/streets_json/src/json_utility.cpp b/streets_utils/streets_json/src/json_utility.cpp index 047189a51..0cae7a8cc 100644 --- a/streets_utils/streets_json/src/json_utility.cpp +++ b/streets_utils/streets_json/src/json_utility.cpp @@ -76,7 +76,7 @@ namespace streets_utils::json_utils { return property; }; - const rapidjson::Value::ConstObject get_json_object_property(const std::string &prop_name, const rapidjson::Value &doc, bool required) { + rapidjson::Value::ConstObject get_json_object_property(const std::string &prop_name, const rapidjson::Value &doc, bool required) { if (doc.HasMember(prop_name.c_str()) && doc.FindMember(prop_name.c_str())->value.IsObject()) { return doc[prop_name.c_str()].GetObject(); @@ -87,7 +87,7 @@ namespace streets_utils::json_utils { } } - const rapidjson::Value::ConstArray get_json_array_property(const std::string &prop_name, const rapidjson::Value &doc, bool required) { + rapidjson::Value::ConstArray get_json_array_property(const std::string &prop_name, const rapidjson::Value &doc, bool required) { if (doc.HasMember(prop_name.c_str()) && doc.FindMember(prop_name.c_str())->value.IsArray()) { return doc[prop_name.c_str()].GetArray(); From 408b62a71a772e2d4d84c2dee0569c1b6b026150 Mon Sep 17 00:00:00 2001 From: dev Date: Fri, 29 Sep 2023 15:15:18 -0400 Subject: [PATCH 21/80] Fix coverage script --- coverage.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/coverage.sh b/coverage.sh index f37af4a3e..a810e8abc 100755 --- a/coverage.sh +++ b/coverage.sh @@ -24,7 +24,7 @@ cd /home/carma-streets/streets_utils/streets_json/build/ cd /home/carma-streets/streets_utils/streets_json mkdir coverage cd /home/carma-streets/ -gcovr --sonarqube streets_utils/streets_utils/coverage/coverage.xml -s -f streets_utils/streets_json/ -r . +gcovr --sonarqube streets_utils/streets_json/coverage/coverage.xml -s -f streets_utils/streets_json/ -r . cd /home/carma-streets/streets_utils/streets_service_configuration/build/ ./streets_service_configuration_test --gtest_output=xml:../../../test_results/ From c267cad5e9fa9c734542d1017e7429c0dc4935a5 Mon Sep 17 00:00:00 2001 From: dev Date: Sun, 1 Oct 2023 11:27:36 -0400 Subject: [PATCH 22/80] Fix coverage script --- coverage.sh | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/coverage.sh b/coverage.sh index a810e8abc..eb3a504a0 100755 --- a/coverage.sh +++ b/coverage.sh @@ -20,8 +20,8 @@ cd /home/carma-streets mkdir test_results cd /home/carma-streets/streets_utils/streets_json/build/ -./streets_json_test --gtest_output=xml:../../../test_results/ -cd /home/carma-streets/streets_utils/streets_json +./streets_json_utils_test --gtest_output=xml:../../../test_results/ +cd /home/carma-streets/streets_util/streets_json mkdir coverage cd /home/carma-streets/ gcovr --sonarqube streets_utils/streets_json/coverage/coverage.xml -s -f streets_utils/streets_json/ -r . From 6b47073dbb333ee4f9d3f0a267489c70c54fc4ee Mon Sep 17 00:00:00 2001 From: dev Date: Sun, 1 Oct 2023 12:27:49 -0400 Subject: [PATCH 23/80] Fix sonar scanner issues --- coverage.sh | 4 +- .../streets_json/include/json_utility.hpp | 35 +++++++--- .../streets_json/src/json_utility.cpp | 16 +++-- .../streets_json/test/json_utility_test.cpp | 66 +++++++++---------- 4 files changed, 70 insertions(+), 51 deletions(-) diff --git a/coverage.sh b/coverage.sh index eb3a504a0..460c2a4e9 100755 --- a/coverage.sh +++ b/coverage.sh @@ -20,8 +20,8 @@ cd /home/carma-streets mkdir test_results cd /home/carma-streets/streets_utils/streets_json/build/ -./streets_json_utils_test --gtest_output=xml:../../../test_results/ -cd /home/carma-streets/streets_util/streets_json +./streets_json_util_test --gtest_output=xml:../../../test_results/ +cd /home/carma-streets/streets_utils/streets_json mkdir coverage cd /home/carma-streets/ gcovr --sonarqube streets_utils/streets_json/coverage/coverage.xml -s -f streets_utils/streets_json/ -r . diff --git a/streets_utils/streets_json/include/json_utility.hpp b/streets_utils/streets_json/include/json_utility.hpp index 02c5c9387..ad8a3e9a8 100644 --- a/streets_utils/streets_json/include/json_utility.hpp +++ b/streets_utils/streets_json/include/json_utility.hpp @@ -5,29 +5,46 @@ #include #include #include +#include +#include #include "json_parsing_exception.hpp" namespace streets_utils::json_utils { rapidjson::Document validate_json( const std::string &json ); - std::optional get_json_int_property(const std::string &prop_name, const rapidjson::Value &doc, bool required ); + std::optional parse_int_property(const std::string &prop_name, const rapidjson::Value &doc, bool required ); - std::optional get_json_uint_property(const std::string &prop_name, const rapidjson::Value &doc, bool required ); + std::optional parse_uint_property(const std::string &prop_name, const rapidjson::Value &doc, bool required ); - std::optional get_json_bool_property(const std::string &prop_name, const rapidjson::Value &doc, bool required ); + std::optional parse_bool_property(const std::string &prop_name, const rapidjson::Value &doc, bool required ); - std::optional get_json_string_property(const std::string &prop_name, const rapidjson::Value &doc, bool required ); + std::optional parse_string_property(const std::string &prop_name, const rapidjson::Value &doc, bool required ); - std::optional get_json_double_property(const std::string &prop_name, const rapidjson::Value &doc, bool required); + std::optional parse_double_property(const std::string &prop_name, const rapidjson::Value &doc, bool required); - rapidjson::Value::ConstObject get_json_object_property(const std::string &prop_name, const rapidjson::Value &doc, bool required ); + std::optional parse_object_property(const std::string &prop_name, const rapidjson::Value &doc, bool required ); - rapidjson::Value::ConstArray get_json_array_property(const std::string &prop_name, const rapidjson::Value &doc, bool required); + std::optional parse_array_property(const std::string &prop_name, const rapidjson::Value &doc, bool required); - void write_int_property(const std::string &prop_name, rapidjson::Value &doc); + template + inline void write_optional_property(std::string &prop_name, std::optional value, rapidjson::Value &doc, rapidjson::Document::AllocatorType &allocator) + { + if (doc.IsObject() && value->has_value()) { + // Add Member + doc.AddMember(prop_name, value, allocator); + } + }; - void write_uint_propert(const std::string &prop_name, rapidjson::Value &doc); + void write_uint_property(const std::string &prop_name, const uint val, rapidjson::Value &doc, rapidjson::Document::AllocatorType &allocator); + + void write_bool_property(const std::string &prop_name, const bool val, rapidjson::Value &doc, rapidjson::Document::AllocatorType &allocator); + + void write_string_property(const std::string &prop_name, const std::string &val, rapidjson::Value &doc, rapidjson::Document::AllocatorType &allocator); + + void write_double_property(const std::string &prop_name, const double &val, rapidjson::Value &doc, rapidjson::Document::AllocatorType &allocator); + + // void write_object_property(const std::string &prop_name, ) } \ No newline at end of file diff --git a/streets_utils/streets_json/src/json_utility.cpp b/streets_utils/streets_json/src/json_utility.cpp index 0cae7a8cc..c45ae6786 100644 --- a/streets_utils/streets_json/src/json_utility.cpp +++ b/streets_utils/streets_json/src/json_utility.cpp @@ -11,7 +11,7 @@ namespace streets_utils::json_utils { } return doc; } - std::optional get_json_int_property(const std::string &prop_name, const rapidjson::Value &doc, bool required ){ + std::optional parse_int_property(const std::string &prop_name, const rapidjson::Value &doc, bool required ){ std::optional property; if (doc.HasMember(prop_name.c_str()) && doc.FindMember(prop_name.c_str())->value.IsInt64()) { @@ -24,7 +24,7 @@ namespace streets_utils::json_utils { return property; } - std::optional get_json_uint_property(const std::string &prop_name, const rapidjson::Value &doc, bool required ) { + std::optional parse_uint_property(const std::string &prop_name, const rapidjson::Value &doc, bool required ) { std::optional property; if (doc.HasMember(prop_name.c_str()) && doc.FindMember(prop_name.c_str())->value.IsUint64()) { @@ -37,7 +37,7 @@ namespace streets_utils::json_utils { return property; }; - std::optional get_json_bool_property(const std::string &prop_name, const rapidjson::Value &doc, bool required ) { + std::optional parse_bool_property(const std::string &prop_name, const rapidjson::Value &doc, bool required ) { std::optional property; if (doc.HasMember(prop_name.c_str()) && doc.FindMember(prop_name.c_str())->value.IsBool()) { @@ -50,7 +50,7 @@ namespace streets_utils::json_utils { return property; }; - std::optional get_json_string_property(const std::string &prop_name, const rapidjson::Value &doc, bool required ){ + std::optional parse_string_property(const std::string &prop_name, const rapidjson::Value &doc, bool required ){ std::optional property; if (doc.HasMember(prop_name.c_str()) && doc.FindMember(prop_name.c_str())->value.IsString()) { @@ -63,7 +63,7 @@ namespace streets_utils::json_utils { return property; }; - std::optional get_json_double_property(const std::string &prop_name, const rapidjson::Value &doc, bool required) { + std::optional parse_double_property(const std::string &prop_name, const rapidjson::Value &doc, bool required) { std::optional property; if (doc.HasMember(prop_name.c_str()) && doc.FindMember(prop_name.c_str())->value.IsDouble()) { @@ -76,7 +76,7 @@ namespace streets_utils::json_utils { return property; }; - rapidjson::Value::ConstObject get_json_object_property(const std::string &prop_name, const rapidjson::Value &doc, bool required) { + std::optional parse_object_property(const std::string &prop_name, const rapidjson::Value &doc, bool required) { if (doc.HasMember(prop_name.c_str()) && doc.FindMember(prop_name.c_str())->value.IsObject()) { return doc[prop_name.c_str()].GetObject(); @@ -85,9 +85,10 @@ namespace streets_utils::json_utils { { throw json_parsing_exception("Missing or incorrect type for required property " + prop_name + "!"); } + return std::nullopt; } - rapidjson::Value::ConstArray get_json_array_property(const std::string &prop_name, const rapidjson::Value &doc, bool required) { + std::optional parse_array_property(const std::string &prop_name, const rapidjson::Value &doc, bool required) { if (doc.HasMember(prop_name.c_str()) && doc.FindMember(prop_name.c_str())->value.IsArray()) { return doc[prop_name.c_str()].GetArray(); @@ -96,6 +97,7 @@ namespace streets_utils::json_utils { { throw json_parsing_exception("Missing or incorrect type for required property " + prop_name + "!"); } + return std::nullopt; } diff --git a/streets_utils/streets_json/test/json_utility_test.cpp b/streets_utils/streets_json/test/json_utility_test.cpp index 49fd8fa59..15a532909 100644 --- a/streets_utils/streets_json/test/json_utility_test.cpp +++ b/streets_utils/streets_json/test/json_utility_test.cpp @@ -21,26 +21,26 @@ TEST(json_utility_test, test_validate_valid_json) { auto parsed_doc = validate_json(valid_json); EXPECT_FALSE(parsed_doc.HasParseError()); } -//---------------------test get_json_uint_property--------------------- +//---------------------test parse_uint_property--------------------- TEST(json_utility_test, test_get_json_uint_required_property_present){ // Test with required property present std::string valid_json = "{ \"some_property\": 12345}"; auto parsed_doc = validate_json(valid_json); - EXPECT_EQ( 12345, get_json_uint_property("some_property", parsed_doc, true)); + EXPECT_EQ( 12345, parse_uint_property("some_property", parsed_doc, true)); } TEST(json_utility_test, test_get_json_uint_required_property_not_present){ // Test with required property no present std::string valid_json = "{ \"some_property_other\": 12345}"; auto parsed_doc = validate_json(valid_json); - EXPECT_THROW( get_json_uint_property("some_property", parsed_doc, true), std::runtime_error); + EXPECT_THROW( parse_uint_property("some_property", parsed_doc, true), std::runtime_error); } TEST(json_utility_test, test_get_json_uint_required_property_wrong_type){ // Test with required property present with wrong type std::string valid_json = "{ \"some_property\": -12345}"; auto parsed_doc = validate_json(valid_json); - EXPECT_THROW( get_json_uint_property("some_property", parsed_doc, true), std::runtime_error); + EXPECT_THROW( parse_uint_property("some_property", parsed_doc, true), std::runtime_error); } //---------------------test get_json_int_property--------------------- @@ -48,44 +48,44 @@ TEST(json_utility_test, test_get_json_int_required_property_present){ // Test with required property present std::string valid_json = "{ \"some_property\": -12345}"; auto parsed_doc = validate_json(valid_json); - EXPECT_EQ( -12345, get_json_int_property("some_property", parsed_doc, true)); + EXPECT_EQ( -12345, parse_int_property("some_property", parsed_doc, true)); } TEST(json_utility_test, test_get_json_int_required_property_not_present){ // Test with required property no present std::string valid_json = "{ \"some_property_other\": 12345}"; auto parsed_doc = validate_json(valid_json); - EXPECT_THROW( get_json_int_property("some_property", parsed_doc, true), std::runtime_error); + EXPECT_THROW( parse_int_property("some_property", parsed_doc, true), std::runtime_error); } TEST(json_utility_test, test_get_json_int_required_property_wrong_type){ // Test with required property present with wrong type std::string valid_json = "{ \"some_property\": true}"; auto parsed_doc = validate_json(valid_json); - EXPECT_THROW( get_json_int_property("some_property", parsed_doc, true), std::runtime_error); + EXPECT_THROW( parse_int_property("some_property", parsed_doc, true), std::runtime_error); } -//---------------------test get_json_bool_property--------------------- +//---------------------test parse_bool_property--------------------- TEST(json_utility_test, test_get_json_bool_required_property_present){ // Test with required property present std::string valid_json = "{ \"some_property\": true}"; auto parsed_doc = validate_json(valid_json); - EXPECT_EQ( true, get_json_bool_property("some_property", parsed_doc, true)); + EXPECT_EQ( true, parse_bool_property("some_property", parsed_doc, true)); } TEST(json_utility_test, test_get_json_bool_required_property_not_present){ // Test with required property no present std::string valid_json = "{ \"some_property_other\": true}"; auto parsed_doc = validate_json(valid_json); - EXPECT_THROW( get_json_bool_property("some_property", parsed_doc, true), std::runtime_error); + EXPECT_THROW( parse_bool_property("some_property", parsed_doc, true), std::runtime_error); } TEST(json_utility_test, test_get_json_bool_required_property_wrong_type){ // Test with required property present with wrong type std::string valid_json = "{ \"some_property\": 1234}"; auto parsed_doc = validate_json(valid_json); - EXPECT_THROW( get_json_bool_property("some_property", parsed_doc, true), std::runtime_error); + EXPECT_THROW( parse_bool_property("some_property", parsed_doc, true), std::runtime_error); } //---------------------test get_json_double_property--------------------- @@ -94,48 +94,48 @@ TEST(json_utility_test, test_get_json_double_required_property_present){ // Test with required property present std::string valid_json = "{ \"some_property\": 12.3}"; auto parsed_doc = validate_json(valid_json); - EXPECT_NEAR( 12.3, get_json_double_property("some_property", parsed_doc, true).value(), 0.01); + EXPECT_NEAR( 12.3, parse_double_property("some_property", parsed_doc, true).value(), 0.01); } TEST(json_utility_test, test_get_json_double_required_property_not_present){ // Test with required property no present std::string valid_json = "{ \"some_property_other\": 12.3}"; auto parsed_doc = validate_json(valid_json); - EXPECT_THROW( get_json_double_property("some_property", parsed_doc, true), std::runtime_error); + EXPECT_THROW( parse_double_property("some_property", parsed_doc, true), std::runtime_error); } TEST(json_utility_test, test_get_json_double_required_property_wrong_type){ // Test with required property present with wrong type std::string valid_json = "{ \"some_property\": 1234}"; auto parsed_doc = validate_json(valid_json); - EXPECT_THROW( get_json_double_property("some_property", parsed_doc, true), std::runtime_error); + EXPECT_THROW( parse_double_property("some_property", parsed_doc, true), std::runtime_error); } -//---------------------test get_json_string_property--------------------- +//---------------------test parse_string_property--------------------- TEST(json_utility_test, test_get_json_string_required_property_present){ // Test with required property present std::string valid_json = "{ \"some_property\": \"some_property\" }"; auto parsed_doc = validate_json(valid_json); - EXPECT_EQ("some_property", get_json_string_property("some_property", parsed_doc, true)); + EXPECT_EQ("some_property", parse_string_property("some_property", parsed_doc, true)); } TEST(json_utility_test, test_get_json_string_required_property_not_present){ // Test with required property no present std::string valid_json = "{ \"some_property_other\": 12.3}"; auto parsed_doc = validate_json(valid_json); - EXPECT_THROW( get_json_string_property("some_property", parsed_doc, true), std::runtime_error); + EXPECT_THROW( parse_string_property("some_property", parsed_doc, true), std::runtime_error); } TEST(json_utility_test, test_get_json_string_required_property_wrong_type){ // Test with required property present with wrong type std::string valid_json = "{ \"some_property\": 1234}"; auto parsed_doc = validate_json(valid_json); - EXPECT_THROW( get_json_string_property("some_property", parsed_doc, true), std::runtime_error); + EXPECT_THROW( parse_string_property("some_property", parsed_doc, true), std::runtime_error); } -//---------------------test get_json_object_property--------------------- +//---------------------test parse_object_property--------------------- TEST(json_utility_test, test_get_json_object_required_property_present){ // Test with required property present @@ -146,9 +146,9 @@ TEST(json_utility_test, test_get_json_object_required_property_present){ "}" "}"; auto parsed_doc = validate_json(valid_json); - auto object = get_json_object_property("some_object", parsed_doc, true); - EXPECT_EQ("object", get_json_string_property("object_name", object, true)); - EXPECT_EQ( 123, get_json_int_property("object_value", object, true) ); + auto object = parse_object_property("some_object", parsed_doc, true); + EXPECT_EQ("object", parse_string_property("object_name", object.value(), true)); + EXPECT_EQ( 123, parse_int_property("object_value", object.value(), true) ); } @@ -161,7 +161,7 @@ TEST(json_utility_test, test_get_json_object_required_property_not_present){ "}" "}"; auto parsed_doc = validate_json(valid_json); - EXPECT_THROW( get_json_object_property("some_object", parsed_doc, true), std::runtime_error); + EXPECT_THROW( parse_object_property("some_object", parsed_doc, true), std::runtime_error); } TEST(json_utility_test, test_get_json_object_required_property_wrong_type){ @@ -173,10 +173,10 @@ TEST(json_utility_test, test_get_json_object_required_property_wrong_type){ "}]" "}"; auto parsed_doc = validate_json(valid_json); - EXPECT_THROW( get_json_object_property("some_object", parsed_doc, true), std::runtime_error); + EXPECT_THROW( parse_object_property("some_object", parsed_doc, true), std::runtime_error); } -//---------------------test get_json_array_property--------------------- +//---------------------test parse_array_property--------------------- TEST(json_utility_test, test_get_json_array_required_property_present){ // Test with required property present @@ -185,13 +185,13 @@ TEST(json_utility_test, test_get_json_array_required_property_present){ "[456, 2452, -1232, 2345]" "}"; auto parsed_doc = validate_json(valid_json); - auto array = get_json_array_property("some_array", parsed_doc, true); + auto array = parse_array_property("some_array", parsed_doc, true); - EXPECT_EQ(4, array.Size()); - EXPECT_EQ(456, array[0].GetInt()); - EXPECT_EQ(2452, array[1].GetInt()); - EXPECT_EQ(-1232, array[2].GetInt()); - EXPECT_EQ(2345, array[3].GetInt()); + EXPECT_EQ(4, array.value().Size()); + EXPECT_EQ(456, array.value()[0].GetInt()); + EXPECT_EQ(2452, array.value()[1].GetInt()); + EXPECT_EQ(-1232, array.value()[2].GetInt()); + EXPECT_EQ(2345, array.value()[3].GetInt()); } TEST(json_utility_test, test_get_json_array_required_property_not_present){ @@ -201,7 +201,7 @@ TEST(json_utility_test, test_get_json_array_required_property_not_present){ "[456, 2452, -1232, 2345]" "}"; auto parsed_doc = validate_json(valid_json); - EXPECT_THROW( get_json_array_property("some_array", parsed_doc, true), std::runtime_error); + EXPECT_THROW( parse_array_property("some_array", parsed_doc, true), std::runtime_error); } TEST(json_utility_test, test_get_json_array_required_property_wrong_type){ @@ -213,6 +213,6 @@ TEST(json_utility_test, test_get_json_array_required_property_wrong_type){ "}]" "}"; auto parsed_doc = validate_json(valid_json); - EXPECT_THROW( get_json_array_property("some_object", parsed_doc, true), std::runtime_error); + EXPECT_THROW( parse_array_property("some_object", parsed_doc, true), std::runtime_error); } From 4cc988d64ea20e18ac352efecae84d062e61ba6e Mon Sep 17 00:00:00 2001 From: dev Date: Sun, 1 Oct 2023 13:06:53 -0400 Subject: [PATCH 24/80] Get sonar scanner working --- .sonarqube/sonar-scanner.properties | 5 ++--- .../streets_json/include/json_utility.hpp | 17 ++++++----------- 2 files changed, 8 insertions(+), 14 deletions(-) diff --git a/.sonarqube/sonar-scanner.properties b/.sonarqube/sonar-scanner.properties index 8fc1e11c6..f4456c214 100644 --- a/.sonarqube/sonar-scanner.properties +++ b/.sonarqube/sonar-scanner.properties @@ -34,8 +34,7 @@ sonar.coverageReportPaths= /home/carma-streets/kafka_clients/coverage/coverage.x /home/carma-streets/streets_utils/streets_desired_phase_plan/coverage/coverage.xml, \ /home/carma-streets/streets_utils/streets_phase_control_schedule/coverage/coverage.xml, \ /home/carma-streets/streets_utils/streets_timing_plan/coverage/coverage.xml, \ -/home/carma-streets/streets_utils/streets_signal_optimization/coverage/coverage.xml -/home/carma-streets/streets_utils/streets_signal_optimization/coverage/coverage.xml, \ +/home/carma-streets/streets_utils/streets_signal_optimization/coverage/coverage.xml, \ /home/carma-streets/streets_utils/streets_snmp_cmd/coverage/coverage.xml, \ /home/carma-streets/streets_utils/streets_json/coverage/coverage.xml @@ -51,7 +50,7 @@ sonar.scm.disabled=false sonar.scm.enabled=true sonar.scm.provider=git -sonar.cpp.file.suffixes=.cpp,.h,.tpp +sonar.cpp.file.suffixes=.cpp,.h,.hpp,.tpp #sonar.objc.file.suffixes=.h,.m,.mm sonar.c.file.suffixes=- #This is the name and version displayed in the SonarCloud UI. diff --git a/streets_utils/streets_json/include/json_utility.hpp b/streets_utils/streets_json/include/json_utility.hpp index ad8a3e9a8..7bb8d91d2 100644 --- a/streets_utils/streets_json/include/json_utility.hpp +++ b/streets_utils/streets_json/include/json_utility.hpp @@ -28,23 +28,18 @@ namespace streets_utils::json_utils { std::optional parse_array_property(const std::string &prop_name, const rapidjson::Value &doc, bool required); template - inline void write_optional_property(std::string &prop_name, std::optional value, rapidjson::Value &doc, rapidjson::Document::AllocatorType &allocator) + inline void write_optional_property(const std::string &prop_name, std::optional value, rapidjson::Value &doc, rapidjson::Document::AllocatorType &allocator) { if (doc.IsObject() && value->has_value()) { // Add Member doc.AddMember(prop_name, value, allocator); } }; - - void write_uint_property(const std::string &prop_name, const uint val, rapidjson::Value &doc, rapidjson::Document::AllocatorType &allocator); - - void write_bool_property(const std::string &prop_name, const bool val, rapidjson::Value &doc, rapidjson::Document::AllocatorType &allocator); - - void write_string_property(const std::string &prop_name, const std::string &val, rapidjson::Value &doc, rapidjson::Document::AllocatorType &allocator); - - void write_double_property(const std::string &prop_name, const double &val, rapidjson::Value &doc, rapidjson::Document::AllocatorType &allocator); - - // void write_object_property(const std::string &prop_name, ) + + template + inline void write_required_property(const std::string &prop_name, T value, rapidjson::Value &doc, rapidjson::Document::AllocatorType &allocator){ + doc.AddMember(prop_name, value, allocator); + }; } \ No newline at end of file From 8ae163ef78316686634874f3dbbfa715f6de162a Mon Sep 17 00:00:00 2001 From: dev Date: Mon, 2 Oct 2023 02:07:40 -0400 Subject: [PATCH 25/80] Updates to make PR ready for review --- .sonarqube/sonar-scanner.properties | 12 +- build.sh | 2 +- coverage.sh | 8 +- .../CMakeLists.txt | 13 +- streets_utils/json_utils/README.md | 28 ++ .../cmake/json_utils_libConfig.cmake.in} | 0 .../json_utils/include/json_utils.hpp | 149 +++++++++ .../include/json_utils_exception.hpp} | 6 +- streets_utils/json_utils/src/json_utils.cpp | 105 +++++++ .../json_utils/test/json_utils_test.cpp | 289 ++++++++++++++++++ .../streets_json/include/json_utility.hpp | 45 --- .../streets_json/src/json_utility.cpp | 105 ------- .../streets_json/test/json_utility_test.cpp | 218 ------------- 13 files changed, 594 insertions(+), 386 deletions(-) rename streets_utils/{streets_json => json_utils}/CMakeLists.txt (94%) create mode 100644 streets_utils/json_utils/README.md rename streets_utils/{streets_json/cmake/streets_json_util_libConfig.cmake.in => json_utils/cmake/json_utils_libConfig.cmake.in} (100%) create mode 100644 streets_utils/json_utils/include/json_utils.hpp rename streets_utils/{streets_json/include/json_parsing_exception.hpp => json_utils/include/json_utils_exception.hpp} (67%) create mode 100644 streets_utils/json_utils/src/json_utils.cpp create mode 100644 streets_utils/json_utils/test/json_utils_test.cpp delete mode 100644 streets_utils/streets_json/include/json_utility.hpp delete mode 100644 streets_utils/streets_json/src/json_utility.cpp delete mode 100644 streets_utils/streets_json/test/json_utility_test.cpp diff --git a/.sonarqube/sonar-scanner.properties b/.sonarqube/sonar-scanner.properties index f4456c214..7ca171b75 100644 --- a/.sonarqube/sonar-scanner.properties +++ b/.sonarqube/sonar-scanner.properties @@ -36,7 +36,7 @@ sonar.coverageReportPaths= /home/carma-streets/kafka_clients/coverage/coverage.x /home/carma-streets/streets_utils/streets_timing_plan/coverage/coverage.xml, \ /home/carma-streets/streets_utils/streets_signal_optimization/coverage/coverage.xml, \ /home/carma-streets/streets_utils/streets_snmp_cmd/coverage/coverage.xml, \ -/home/carma-streets/streets_utils/streets_json/coverage/coverage.xml +/home/carma-streets/streets_utils/json_utils/coverage/coverage.xml # TODO : /home/carma-streets/intersection_model/coverage/coverage.xml, \ @@ -79,7 +79,7 @@ streets_vehicle_scheduler, \ streets_desired_phase_plan, \ streets_signal_optimization, \ streets_snmp_cmd, \ -streets_json +json_utils # TODO message_services # TODO intersection_model @@ -101,7 +101,7 @@ streets_signal_optimization.sonar.projectBaseDir=/home/carma-streets/streets_uti streets_phase_control_schedule.sonar.projectBaseDir=/home/carma-streets/streets_utils/streets_phase_control_schedule streets_timing_plan.sonar.projectBaseDir=/home/carma-streets/streets_utils/streets_timing_plan streets_snmp_cmd.sonar.projectBaseDir=/home/carma-streets/streets_utils/streets_snmp_cmd -streets_json.sonar.projectBaseDir=/home/carma-streets/streets_utils/streets_json +json_utils.sonar.projectBaseDir=/home/carma-streets/streets_utils/json_utils @@ -141,8 +141,8 @@ streets_timing_plan.sonar.sources =src/,include/ streets_timing_plan.sonar.exclusions =test/** streets_snmp_cmd.sonar.sources =src/,include/ streets_snmp_cmd.sonar.exclusions =test/** -streets_json.sonar.sources =src/,include/ -streets_json.sonar.exclusions =test/** +json_utils.sonar.sources =src/,include/ +json_utils.sonar.exclusions =test/** #Tests # Note: For C++ setting this field does not cause test analysis to occur. It only allows the test source code to be evaluated. @@ -163,6 +163,6 @@ streets_signal_optimization.sonar.tests=test/ streets_phase_control_schedule.sonar.tests=test/ streets_timing_plan.sonar.tests=test/ streets_snmp_cmd.sonar.tests=test/ -streets_json.sonar.tests=test/ +json_utils.sonar.tests=test/ diff --git a/build.sh b/build.sh index 77ac31b37..a304b381a 100755 --- a/build.sh +++ b/build.sh @@ -35,7 +35,7 @@ MAKE_INSTALL_DIRS=( "streets_utils/streets_api/intersection_server_api" "streets_utils/streets_signal_optimization" "streets_utils/streets_snmp_cmd" - "streets_utils/streets_json" + "streets_utils/json_utils" ) # only make for these subdirectories diff --git a/coverage.sh b/coverage.sh index 460c2a4e9..16ed29b07 100755 --- a/coverage.sh +++ b/coverage.sh @@ -19,12 +19,12 @@ cd /home/carma-streets mkdir test_results -cd /home/carma-streets/streets_utils/streets_json/build/ -./streets_json_util_test --gtest_output=xml:../../../test_results/ -cd /home/carma-streets/streets_utils/streets_json +cd /home/carma-streets/streets_utils/json_utils/build/ +./json_utils_test --gtest_output=xml:../../../test_results/ +cd /home/carma-streets/streets_utils/json_utils mkdir coverage cd /home/carma-streets/ -gcovr --sonarqube streets_utils/streets_json/coverage/coverage.xml -s -f streets_utils/streets_json/ -r . +gcovr --sonarqube streets_utils/json_utils/coverage/coverage.xml -s -f streets_utils/json_utils/ -r . cd /home/carma-streets/streets_utils/streets_service_configuration/build/ ./streets_service_configuration_test --gtest_output=xml:../../../test_results/ diff --git a/streets_utils/streets_json/CMakeLists.txt b/streets_utils/json_utils/CMakeLists.txt similarity index 94% rename from streets_utils/streets_json/CMakeLists.txt rename to streets_utils/json_utils/CMakeLists.txt index 4735adec8..e22f69d8e 100644 --- a/streets_utils/streets_json/CMakeLists.txt +++ b/streets_utils/json_utils/CMakeLists.txt @@ -1,14 +1,15 @@ cmake_minimum_required(VERSION 3.10.2) -project(streets_json_util) +project(streets_json_utils) # Set Flags set(CMAKE_CXX_STANDARD 20) # Find Packages find_package(RapidJSON REQUIRED) find_package(GTest REQUIRED) +find_package(spdlog REQUIRED) + # Add definition for rapidjson to include std::string add_definitions(-DRAPIDJSON_HAS_STDSTRING=1) - -set(LIBRARY_NAME streets_json_util) +set(LIBRARY_NAME json_utils) ######################################################## # Build Library ######################################################## @@ -21,7 +22,9 @@ target_include_directories(${LIBRARY_NAME}_lib ) target_link_libraries( ${LIBRARY_NAME}_lib PUBLIC - rapidjson + rapidjson + spdlog::spdlog + ) message( STATUS "Target INCLUDE DIR : " ${INCLUDE_DIRECTORIES}) @@ -68,6 +71,8 @@ target_link_libraries(${BINARY} PUBLIC ${LIBRARY_NAME}_lib rapidjson + spdlog::spdlog + ) if(${CMAKE_VERSION} VERSION_GREATER_EQUAL "3.20") target_link_libraries( ${BINARY} PUBLIC GTest::gtest_main ) diff --git a/streets_utils/json_utils/README.md b/streets_utils/json_utils/README.md new file mode 100644 index 000000000..bde987ad0 --- /dev/null +++ b/streets_utils/json_utils/README.md @@ -0,0 +1,28 @@ +# JSON Utility Library + +## Introduction + +This CARMA-Streets library contains utility functions for parsing JSON strings with [RapidJSON](https://miloyip.github.io/rapidjson/index.html). The functions exposed by this library are described below + +## Functions +`rapidjson::Document validate_json( const std::string &json )` + +Function to parse `std::string` JSON with [RapidJSON](https://miloyip.github.io/rapidjson/index.html) with [DOM parsing](https://miloyip.github.io/rapidjson/md_doc_dom.html). Throws `json_utils_exception` if string is not valid json. Otherwise returns `rapidjson::Document`` + +`std::optional parse_int_member(const std::string &member_name, const rapidjson::Value &doc, bool required )` +`std::optional parse_uint_member(const std::string &member_name, const rapidjson::Value &doc, bool required )` +`std::optional parse_bool_member(const std::string &member_name, const rapidjson::Value &doc, bool required )` +`std::optional parse_string_member(const std::string &member_name, const rapidjson::Value &doc, bool required )` +`std::optional parse_double_member(const std::string &member_name, const rapidjson::Value &doc, bool required)` +`std::optional parse_object_member(const std::string &member_name, const rapidjson::Value &doc, bool required )` +`std::optional parse_array_member(const std::string &member_name, const rapidjson::Value &doc, bool required)` + +Functions to retrieve member values from JSON object [RapidJSON](https://miloyip.github.io/rapidjson/index.html) with [DOM parsing](https://miloyip.github.io/rapidjson/md_doc_dom.html). Functions will return unintialized `std::optional` for optional members that are not found in JSON object and will throw `json_utils_exception` for required members that are not found in JSON object. + +## Include Library +To include after installing with `make install` simply add find_package call to `CMakeLists.txt` and link the library as follows. Installed CMake configuration files should handle finding and linking depedencies `RapidJSON` and `spdlog`. The library along with it's dependencies can then be included by simply using the find_package() instruction. +``` +find_package(streets_utils COMPONENT json_utils) +... +target_link_library( target PUBLIC streets_utils::json_utils) +``` \ No newline at end of file diff --git a/streets_utils/streets_json/cmake/streets_json_util_libConfig.cmake.in b/streets_utils/json_utils/cmake/json_utils_libConfig.cmake.in similarity index 100% rename from streets_utils/streets_json/cmake/streets_json_util_libConfig.cmake.in rename to streets_utils/json_utils/cmake/json_utils_libConfig.cmake.in diff --git a/streets_utils/json_utils/include/json_utils.hpp b/streets_utils/json_utils/include/json_utils.hpp new file mode 100644 index 000000000..07da2ac11 --- /dev/null +++ b/streets_utils/json_utils/include/json_utils.hpp @@ -0,0 +1,149 @@ +#pragma once +#include +#include +#include +#include +#include +#include +#include +#include +#include "json_utils_exception.hpp" + +namespace streets_utils::json_utils { + + /** + * @brief Function to parse `std::string` JSON with [RapidJSON](https://miloyip.github.io/rapidjson/index.html) with + * [DOM parsing](https://miloyip.github.io/rapidjson/md_obj_dom.html). Throws `json_utils_exception` if string is + * not valid json. Otherwise returns `rapidjson::Document` + * @param json std::string JSON to parse + * @throws json_utils_exception if passed string is not valid json. + * @return resulting `rapidjson::Document` + */ + rapidjson::Document validate_json( const std::string &json ); + /** + * @brief Functions to retrieve int member values from JSON object [RapidJSON](https://miloyip.github.io/rapidjson/index.html) + * with [DOM parsing](https://miloyip.github.io/rapidjson/md_doc_dom.html). Functions will return unintialized + * `std::optional` for optional members that are not found in JSON object and will throw `json_utils_exception`. + * for required members that are not found in JSON object. + * @param member_name string member name to search for. + * @param obj JSON object with member + * @param required bool flag to indicate whether member is required by calling code. + * @throws json_utils_exception if member is required but not found. + * @return std::optional + */ + std::optional parse_int_member(const std::string &member_name, const rapidjson::Value &obj, bool required ); + /** + * @brief Functions to retrieve uint member values from JSON object [RapidJSON](https://miloyip.github.io/rapidjson/index.html) + * with [DOM parsing](https://miloyip.github.io/rapidjson/md_doc_dom.html). Functions will return unintialized + * `std::optional` for optional members that are not found in JSON object and will throw `json_utils_exception`. + * for required members that are not found in JSON object. + * @param member_name string member name to search for. + * @param obj JSON object with member + * @param required bool flag to indicate whether member is required by calling code. + * @throws json_utils_exception if member is required but not found. + * @return std::optional + */ + std::optional parse_uint_member(const std::string &member_name, const rapidjson::Value &obj, bool required ); + /** + * @brief Functions to retrieve bool member values from JSON object [RapidJSON](https://miloyip.github.io/rapidjson/index.html) + * with [DOM parsing](https://miloyip.github.io/rapidjson/md_doc_dom.html). Functions will return unintialized + * `std::optional` for optional members that are not found in JSON object and will throw `json_utils_exception`. + * for required members that are not found in JSON object. + * @param member_name string member name to search for. + * @param obj JSON object with member + * @param required bool flag to indicate whether member is required by calling code. + * @throws json_utils_exception if member is required but not found. + * @return std::optional + */ + std::optional parse_bool_member(const std::string &member_name, const rapidjson::Value &obj, bool required ); + /** + * @brief Functions to retrieve std::string member values from JSON object [RapidJSON](https://miloyip.github.io/rapidjson/index.html) + * with [DOM parsing](https://miloyip.github.io/rapidjson/md_doc_dom.html). Functions will return unintialized + * `std::optional` for optional members that are not found in JSON object and will throw `json_utils_exception`. + * for required members that are not found in JSON object. + * @param member_name string member name to search for. + * @param obj JSON object with member + * @param required bool flag to indicate whether member is required by calling code. + * @throws json_utils_exception if member is required but not found. + * @return std::optional + */ + std::optional parse_string_member(const std::string &member_name, const rapidjson::Value &obj, bool required ); + /** + * @brief Functions to retrieve double member values from JSON object [RapidJSON](https://miloyip.github.io/rapidjson/index.html) + * with [DOM parsing](https://miloyip.github.io/rapidjson/md_doc_dom.html). Functions will return unintialized + * `std::optional` for optional members that are not found in JSON object and will throw `json_utils_exception`. + * for required members that are not found in JSON object. + * @param member_name string member name to search for. + * @param obj JSON object with member + * @param required bool flag to indicate whether member is required by calling code. + * @throws json_utils_exception if member is required but not found. + * @return std::optional + */ + std::optional parse_double_member(const std::string &member_name, const rapidjson::Value &obj, bool required); + /** + * @brief Functions to retrieve object member values from JSON object [RapidJSON](https://miloyip.github.io/rapidjson/index.html) + * with [DOM parsing](https://miloyip.github.io/rapidjson/md_doc_dom.html). Functions will return unintialized + * `std::optional` for optional members that are not found in JSON object and will throw `json_utils_exception`. + * for required members that are not found in JSON object. + * @param member_name string member name to search for. + * @param obj JSON object with member + * @param required bool flag to indicate whether member is required by calling code. + * @throws json_utils_exception if member is required but not found. + * @return std::optional + */ + std::optional parse_object_member(const std::string &member_name, const rapidjson::Value &obj, bool required ); + /** + * @brief Functions to retrieve array member values from JSON object [RapidJSON](https://miloyip.github.io/rapidjson/index.html) + * with [DOM parsing](https://miloyip.github.io/rapidjson/md_doc_dom.html). Functions will return unintialized + * `std::optional` for optional members that are not found in JSON object and will throw `json_utils_exception`. + * for required members that are not found in JSON object. + * @param member_name string member name to search for. + * @param obj JSON object with member + * @param required bool flag to indicate whether member is required by calling code. + * @throws json_utils_exception if member is required but not found. + * @return std::optional + */ + std::optional parse_array_member(const std::string &member_name, const rapidjson::Value &obj, bool required); + // TODO: Commented out because I can not get this to work for string values. Before closing PR make a decision on keeping/fixing this or removing + // it. Works for all other types other + // template + // inline void write_optional_member(const std::string &member_name, const std::optional _value, rapidjson::Value &obj, rapidjson::Document::AllocatorType &allocator) + // { + // if (obj.IsObject() && _value.has_value()) { + // // In an attempt to improve performance and since many json objects have constant + // // key names the StringRef constructor avoids copying the member name and "requires + // // that the referenced string pointers have a sufficient lifetime, which exceeds the + // // lifetime of the associated GenericValue"(https://rapidjson.org/structrapidjson_1_1_generic_string_ref.html). + + // // To explicitly force a copy we instead create a new String Value with the content of + // // the passed in parameter. Follow link to get more information from Documentation + // // (RapidJSON Documentation)[https://miloyip.github.io/rapidjson/md_obj_tutorial.html#ModifyObject] + + // // Alternatively we could pass in the string reference using StringRef(member_name). Doing so + // // we would be explicitly guaranteeing the lifetime of the passed in string reference. If we then + // // pass in a string literal from the caller of this function the AddMember method would silently fail + // // since the lifetime of the reference string is not sufficient. + // rapidjson::Value member_name_copy(member_name, allocator); + // obj.AddMember(member_name_copy, _value.value(), allocator); + // + // } + // else { + // SPDLOG_TRACE("Skipping member {0}! std::optional has_value() check failed or parameter is not writable object type value!", + // member_name); + + // } + // }; + + // template + // inline void write_required_member(const std::string &member_name, const T &value, rapidjson::Value &obj, rapidjson::Document::AllocatorType &allocator){ + // if ( obj.IsObject() && value != NULL ) { + // rapidjson::Value member_name_copy(member_name, allocator); + // obj.AddMember(member_name_copy, value, allocator); + // } + // else { + // throw json_utils_exception(""); + // } + // }; + + +} \ No newline at end of file diff --git a/streets_utils/streets_json/include/json_parsing_exception.hpp b/streets_utils/json_utils/include/json_utils_exception.hpp similarity index 67% rename from streets_utils/streets_json/include/json_parsing_exception.hpp rename to streets_utils/json_utils/include/json_utils_exception.hpp index 4ab7a8262..ec6993817 100644 --- a/streets_utils/streets_json/include/json_parsing_exception.hpp +++ b/streets_utils/json_utils/include/json_utils_exception.hpp @@ -10,17 +10,17 @@ namespace streets_utils::json_utils { * * @author Paul Bourelly */ - class json_parsing_exception : public std::runtime_error{ + class json_utils_exception : public std::runtime_error{ public: /** * @brief Destructor. */ - ~json_parsing_exception() = default; + ~json_utils_exception() = default; /** * @brief Constructor. * @param msg String exception message. */ - explicit json_parsing_exception(const std::string &msg ) : std::runtime_error(msg){}; + explicit json_utils_exception(const std::string &msg ) : std::runtime_error(msg){}; }; } \ No newline at end of file diff --git a/streets_utils/json_utils/src/json_utils.cpp b/streets_utils/json_utils/src/json_utils.cpp new file mode 100644 index 000000000..68cc79fea --- /dev/null +++ b/streets_utils/json_utils/src/json_utils.cpp @@ -0,0 +1,105 @@ +#include "json_utils.hpp" + +namespace streets_utils::json_utils { + + rapidjson::Document validate_json(const std::string &json) { + rapidjson::Document obj; + obj.Parse(json); + if (obj.HasParseError()) + { + throw json_utils_exception("Message JSON is misformatted. JSON parsing failed!"); + } + return obj; + } + std::optional parse_int_member(const std::string &member_name, const rapidjson::Value &obj, bool required ){ + std::optional member; + if (obj.HasMember(member_name.c_str()) && obj.FindMember(member_name.c_str())->value.IsInt64()) + { + member = obj[member_name.c_str()].GetInt64(); + } + else if (required) + { + throw json_utils_exception("Missing or incorrect type for required member " + member_name + "!"); + } + return member; + } + + std::optional parse_uint_member(const std::string &member_name, const rapidjson::Value &obj, bool required ) { + std::optional member; + if (obj.HasMember(member_name.c_str()) && obj.FindMember(member_name.c_str())->value.IsUint64()) + { + member = obj[member_name.c_str()].GetUint64(); + } + else if (required) + { + throw json_utils_exception("Missing or incorrect type for required member " + member_name + "!"); + } + return member; + }; + + std::optional parse_bool_member(const std::string &member_name, const rapidjson::Value &obj, bool required ) { + std::optional member; + if (obj.HasMember(member_name.c_str()) && obj.FindMember(member_name.c_str())->value.IsBool()) + { + member = obj[member_name.c_str()].GetBool(); + } + else if (required) + { + throw json_utils_exception("Missing or incorrect type for required member " + member_name + "!"); + } + return member; + }; + + std::optional parse_string_member(const std::string &member_name, const rapidjson::Value &obj, bool required ){ + std::optional member; + if (obj.HasMember(member_name.c_str()) && obj.FindMember(member_name.c_str())->value.IsString()) + { + member = obj[member_name.c_str()].GetString(); + } + else if (required) + { + throw json_utils_exception("Missing or incorrect type for required member " + member_name + "!"); + } + return member; + }; + + std::optional parse_double_member(const std::string &member_name, const rapidjson::Value &obj, bool required) { + std::optional member; + if (obj.HasMember(member_name.c_str()) && obj.FindMember(member_name.c_str())->value.IsDouble()) + { + member = obj[member_name.c_str()].GetDouble(); + } + else if (required) + { + throw json_utils_exception("Missing or incorrect type for required member " + member_name + "!"); + } + return member; + }; + + std::optional parse_object_member(const std::string &member_name, const rapidjson::Value &obj, bool required) { + if (obj.HasMember(member_name.c_str()) && obj.FindMember(member_name.c_str())->value.IsObject()) + { + return obj[member_name.c_str()].GetObject(); + } + else if (required) + { + throw json_utils_exception("Missing or incorrect type for required member " + member_name + "!"); + } + return std::nullopt; + } + + std::optional parse_array_member(const std::string &member_name, const rapidjson::Value &obj, bool required) { + if (obj.HasMember(member_name.c_str()) && obj.FindMember(member_name.c_str())->value.IsArray()) + { + return obj[member_name.c_str()].GetArray(); + } + else if (required) + { + throw json_utils_exception("Missing or incorrect type for required member " + member_name + "!"); + } + return std::nullopt; + } + + + +} \ No newline at end of file diff --git a/streets_utils/json_utils/test/json_utils_test.cpp b/streets_utils/json_utils/test/json_utils_test.cpp new file mode 100644 index 000000000..9d8d0e3d9 --- /dev/null +++ b/streets_utils/json_utils/test/json_utils_test.cpp @@ -0,0 +1,289 @@ +#include +#include +#include + +using namespace streets_utils::json_utils; + +//---------------------test validate_json--------------------- +TEST(json_utils_test, test_validate_invalid_json) { + // Empty String + std::string invalid_json = ""; + EXPECT_THROW( validate_json(invalid_json), json_utils_exception); + + // Property missing quotations + invalid_json = "{ some_propert: \"some_value\"}"; + EXPECT_THROW( validate_json(invalid_json), json_utils_exception); + +} + +TEST(json_utils_test, test_validate_valid_json) { + // Correct JSON + std::string valid_json = "{ \"some_property\": \"some_value\"}"; + auto parsed_doc = validate_json(valid_json); + EXPECT_FALSE(parsed_doc.HasParseError()); +} +//---------------------test parse_uint_member--------------------- +TEST(json_utils_test, test_get_json_uint_required_property_present){ + // Test with required property present + std::string valid_json = "{ \"some_property\": 12345}"; + auto parsed_doc = validate_json(valid_json); + EXPECT_EQ( 12345, parse_uint_member("some_property", parsed_doc, true)); +} + +TEST(json_utils_test, test_get_json_uint_required_property_not_present){ + // Test with required property no present + std::string valid_json = "{ \"some_property_other\": 12345}"; + auto parsed_doc = validate_json(valid_json); + EXPECT_THROW( parse_uint_member("some_property", parsed_doc, true), json_utils_exception); +} + +TEST(json_utils_test, test_get_json_uint_required_property_wrong_type){ + // Test with required property present with wrong type + std::string valid_json = "{ \"some_property\": -12345}"; + auto parsed_doc = validate_json(valid_json); + EXPECT_THROW( parse_uint_member("some_property", parsed_doc, true), json_utils_exception); +} + +TEST(json_utils_test, test_get_json_uint_optional_property_missing) { + // Test with required property no present + std::string valid_json = "{ \"some_property_other\": 12345}"; + auto parsed_doc = validate_json(valid_json); + auto property = parse_uint_member("some_property", parsed_doc, false); + EXPECT_FALSE( property.has_value()); +} +//---------------------test get_json_int_property--------------------- + +TEST(json_utils_test, test_get_json_int_required_property_present){ + // Test with required property present + std::string valid_json = "{ \"some_property\": -12345}"; + auto parsed_doc = validate_json(valid_json); + EXPECT_EQ( -12345, parse_int_member("some_property", parsed_doc, true)); +} + +TEST(json_utils_test, test_get_json_int_required_property_not_present){ + // Test with required property no present + std::string valid_json = "{ \"some_property_other\": 12345}"; + auto parsed_doc = validate_json(valid_json); + EXPECT_THROW( parse_int_member("some_property", parsed_doc, true), json_utils_exception); +} + +TEST(json_utils_test, test_get_json_int_required_property_wrong_type){ + // Test with required property present with wrong type + std::string valid_json = "{ \"some_property\": true}"; + auto parsed_doc = validate_json(valid_json); + EXPECT_THROW( parse_int_member("some_property", parsed_doc, true), json_utils_exception); +} + +TEST(json_utils_test, test_get_json_int_optional_property_missing) { + // Test with required property no present + std::string valid_json = "{ \"some_property_other\": 12345}"; + auto parsed_doc = validate_json(valid_json); + auto property = parse_int_member("some_property", parsed_doc, false); + EXPECT_FALSE( property.has_value()); +} +//---------------------test parse_bool_property--------------------- + +TEST(json_utils_test, test_get_json_bool_required_property_present){ + // Test with required property present + std::string valid_json = "{ \"some_property\": true}"; + auto parsed_doc = validate_json(valid_json); + EXPECT_EQ( true, parse_bool_member("some_property", parsed_doc, true)); +} + +TEST(json_utils_test, test_get_json_bool_required_property_not_present){ + // Test with required property no present + std::string valid_json = "{ \"some_property_other\": true}"; + auto parsed_doc = validate_json(valid_json); + EXPECT_THROW( parse_bool_member("some_property", parsed_doc, true), json_utils_exception); +} + +TEST(json_utils_test, test_get_json_bool_required_property_wrong_type){ + // Test with required property present with wrong type + std::string valid_json = "{ \"some_property\": 1234}"; + auto parsed_doc = validate_json(valid_json); + EXPECT_THROW( parse_bool_member("some_property", parsed_doc, true), json_utils_exception); +} + +TEST(json_utils_test, test_get_json_bool_optional_property_missing) { + // Test with required property no present + std::string valid_json = "{ \"some_property_other\": 12345}"; + auto parsed_doc = validate_json(valid_json); + auto property = parse_bool_member("some_property", parsed_doc, false); + EXPECT_FALSE( property.has_value()); +} + +//---------------------test get_json_double_property--------------------- + +TEST(json_utils_test, test_get_json_double_required_property_present){ + // Test with required property present + std::string valid_json = "{ \"some_property\": 12.3}"; + auto parsed_doc = validate_json(valid_json); + EXPECT_NEAR( 12.3, parse_double_member("some_property", parsed_doc, true).value(), 0.01); +} + +TEST(json_utils_test, test_get_json_double_required_property_not_present){ + // Test with required property no present + std::string valid_json = "{ \"some_property_other\": 12.3}"; + auto parsed_doc = validate_json(valid_json); + EXPECT_THROW( parse_double_member("some_property", parsed_doc, true), json_utils_exception); +} + +TEST(json_utils_test, test_get_json_double_required_property_wrong_type){ + // Test with required property present with wrong type + std::string valid_json = "{ \"some_property\": 1234}"; + auto parsed_doc = validate_json(valid_json); + EXPECT_THROW( parse_double_member("some_property", parsed_doc, true), json_utils_exception); +} + +TEST(json_utils_test, test_get_json_double_optional_property_missing) { + // Test with required property no present + std::string valid_json = "{ \"some_property_other\": 12345}"; + auto parsed_doc = validate_json(valid_json); + auto property = parse_double_member("some_property", parsed_doc, false); + EXPECT_FALSE( property.has_value()); +} +//---------------------test parse_string_property--------------------- + +TEST(json_utils_test, test_get_json_string_required_property_present){ + // Test with required property present + std::string valid_json = "{ \"some_property\": \"some_property\" }"; + auto parsed_doc = validate_json(valid_json); + EXPECT_EQ("some_property", parse_string_member("some_property", parsed_doc, true)); +} + +TEST(json_utils_test, test_get_json_string_required_property_not_present){ + // Test with required property no present + std::string valid_json = "{ \"some_property_other\": 12.3}"; + auto parsed_doc = validate_json(valid_json); + EXPECT_THROW( parse_string_member("some_property", parsed_doc, true), json_utils_exception); +} + +TEST(json_utils_test, test_get_json_string_required_property_wrong_type){ + // Test with required property present with wrong type + std::string valid_json = "{ \"some_property\": 1234}"; + auto parsed_doc = validate_json(valid_json); + EXPECT_THROW( parse_string_member("some_property", parsed_doc, true), json_utils_exception); +} + +TEST(json_utils_test, test_get_json_string_optional_property_missing) { + // Test with required property no present + std::string valid_json = "{ \"some_property_other\": 12345}"; + auto parsed_doc = validate_json(valid_json); + auto property = parse_string_member("some_property", parsed_doc, false); + EXPECT_FALSE( property.has_value()); +} + +//---------------------test parse_object_property--------------------- + +TEST(json_utils_test, test_get_json_object_required_property_present){ + // Test with required property present + std::string valid_json = "{ " + "\"some_object\": {" + "\"object_name\" : \"object\"," + "\"object_value\" : 123" + "}" + "}"; + auto parsed_doc = validate_json(valid_json); + auto object = parse_object_member("some_object", parsed_doc, true); + EXPECT_EQ("object", parse_string_member("object_name", object.value(), true)); + EXPECT_EQ( 123, parse_int_member("object_value", object.value(), true) ); + +} + +TEST(json_utils_test, test_get_json_object_required_property_not_present){ + // Test with required property no present + std::string valid_json = "{ " + "\"some_other_object\": {" + "\"object_name\" : \"object\"," + "\"object_value\" : 123" + "}" + "}"; + auto parsed_doc = validate_json(valid_json); + EXPECT_THROW( parse_object_member("some_object", parsed_doc, true), json_utils_exception); +} + +TEST(json_utils_test, test_get_json_object_required_property_wrong_type){ + // Test with required property present with wrong type + std::string valid_json = "{ " + "\"some_object\": [{" + "\"object_name\" : \"object\"," + "\"object_value\" : 123" + "}]" + "}"; + auto parsed_doc = validate_json(valid_json); + EXPECT_THROW( parse_object_member("some_object", parsed_doc, true), json_utils_exception); +} + +TEST(json_utils_test, test_get_json_object_optional_property_missing) { + // Test with required property no present + std::string valid_json = "{ \"some_property_other\": 12345}"; + auto parsed_doc = validate_json(valid_json); + auto property = parse_object_member("some_property", parsed_doc, false); + EXPECT_FALSE( property.has_value()); +} +//---------------------test parse_array_property--------------------- + +TEST(json_utils_test, test_get_json_array_required_property_present){ + // Test with required property present + std::string valid_json = "{ " + "\"some_array\": " + "[456, 2452, -1232, 2345]" + "}"; + auto parsed_doc = validate_json(valid_json); + auto array = parse_array_member("some_array", parsed_doc, true); + + EXPECT_EQ(4, array.value().Size()); + EXPECT_EQ(456, array.value()[0].GetInt()); + EXPECT_EQ(2452, array.value()[1].GetInt()); + EXPECT_EQ(-1232, array.value()[2].GetInt()); + EXPECT_EQ(2345, array.value()[3].GetInt()); +} + +TEST(json_utils_test, test_get_json_array_required_property_not_present){ + // Test with required property no present + std::string valid_json = "{ " + "\"some_other_array\": " + "[456, 2452, -1232, 2345]" + "}"; + auto parsed_doc = validate_json(valid_json); + EXPECT_THROW( parse_array_member("some_array", parsed_doc, true), json_utils_exception); +} + +TEST(json_utils_test, test_get_json_array_required_property_wrong_type){ + // Test with required property present with wrong type + std::string valid_json = "{ " + "\"some_array\": [{" + "\"object_name\" : \"object\"," + "\"object_value\" : 123" + "}]" + "}"; + auto parsed_doc = validate_json(valid_json); + EXPECT_THROW( parse_array_member("some_object", parsed_doc, true), json_utils_exception); +} + +TEST(json_utils_test, test_get_json_array_optional_property_missing) { + // Test with required property no present + std::string valid_json = "{ \"some_property_other\": 12345}"; + auto parsed_doc = validate_json(valid_json); + auto property = parse_array_member("some_property", parsed_doc, false); + EXPECT_FALSE( property.has_value()); +} +//---------------------test write optional property--------------------- +// TODO: Functionality not working yet, +// TEST(json_utils_test, test_write_option_property){ +// rapidjson::Value obj(rapidjson::kObjectType); +// rapidjson::Document doc; +// // Write optional present value +// std::optional double_optional; +// double_optional = 43.2; +// write_optional_member("some_double_property", double_optional, obj, doc.GetAllocator()); +// EXPECT_TRUE(obj.HasMember("some_double_property")); +// EXPECT_TRUE(obj.FindMember("some_double_property")->value.IsNumber()); +// EXPECT_NEAR(43.2, obj.FindMember("some_double_property")->value.GetDouble(), 0.01 ); + +// std::optional string_optional_not_present; +// // write_optional_member("some_string_property", string_optional_not_present, obj, doc.GetAllocator()); + +// // EXPECT_FALSE( obj.HasMember("some_string_property")); +// } + diff --git a/streets_utils/streets_json/include/json_utility.hpp b/streets_utils/streets_json/include/json_utility.hpp deleted file mode 100644 index 7bb8d91d2..000000000 --- a/streets_utils/streets_json/include/json_utility.hpp +++ /dev/null @@ -1,45 +0,0 @@ -#pragma once - -#include -#include -#include -#include -#include -#include -#include -#include "json_parsing_exception.hpp" - -namespace streets_utils::json_utils { - - rapidjson::Document validate_json( const std::string &json ); - - std::optional parse_int_property(const std::string &prop_name, const rapidjson::Value &doc, bool required ); - - std::optional parse_uint_property(const std::string &prop_name, const rapidjson::Value &doc, bool required ); - - std::optional parse_bool_property(const std::string &prop_name, const rapidjson::Value &doc, bool required ); - - std::optional parse_string_property(const std::string &prop_name, const rapidjson::Value &doc, bool required ); - - std::optional parse_double_property(const std::string &prop_name, const rapidjson::Value &doc, bool required); - - std::optional parse_object_property(const std::string &prop_name, const rapidjson::Value &doc, bool required ); - - std::optional parse_array_property(const std::string &prop_name, const rapidjson::Value &doc, bool required); - - template - inline void write_optional_property(const std::string &prop_name, std::optional value, rapidjson::Value &doc, rapidjson::Document::AllocatorType &allocator) - { - if (doc.IsObject() && value->has_value()) { - // Add Member - doc.AddMember(prop_name, value, allocator); - } - }; - - template - inline void write_required_property(const std::string &prop_name, T value, rapidjson::Value &doc, rapidjson::Document::AllocatorType &allocator){ - doc.AddMember(prop_name, value, allocator); - }; - - -} \ No newline at end of file diff --git a/streets_utils/streets_json/src/json_utility.cpp b/streets_utils/streets_json/src/json_utility.cpp deleted file mode 100644 index c45ae6786..000000000 --- a/streets_utils/streets_json/src/json_utility.cpp +++ /dev/null @@ -1,105 +0,0 @@ -#include "json_utility.hpp" - -namespace streets_utils::json_utils { - - rapidjson::Document validate_json(const std::string &json) { - rapidjson::Document doc; - doc.Parse(json); - if (doc.HasParseError()) - { - throw json_parsing_exception("Message JSON is misformatted. JSON parsing failed!"); - } - return doc; - } - std::optional parse_int_property(const std::string &prop_name, const rapidjson::Value &doc, bool required ){ - std::optional property; - if (doc.HasMember(prop_name.c_str()) && doc.FindMember(prop_name.c_str())->value.IsInt64()) - { - property = doc[prop_name.c_str()].GetInt64(); - } - else if (required) - { - throw json_parsing_exception("Missing or incorrect type for required property " + prop_name + "!"); - } - return property; - } - - std::optional parse_uint_property(const std::string &prop_name, const rapidjson::Value &doc, bool required ) { - std::optional property; - if (doc.HasMember(prop_name.c_str()) && doc.FindMember(prop_name.c_str())->value.IsUint64()) - { - property = doc[prop_name.c_str()].GetUint64(); - } - else if (required) - { - throw json_parsing_exception("Missing or incorrect type for required property " + prop_name + "!"); - } - return property; - }; - - std::optional parse_bool_property(const std::string &prop_name, const rapidjson::Value &doc, bool required ) { - std::optional property; - if (doc.HasMember(prop_name.c_str()) && doc.FindMember(prop_name.c_str())->value.IsBool()) - { - property = doc[prop_name.c_str()].GetBool(); - } - else if (required) - { - throw json_parsing_exception("Missing or incorrect type for required property " + prop_name + "!"); - } - return property; - }; - - std::optional parse_string_property(const std::string &prop_name, const rapidjson::Value &doc, bool required ){ - std::optional property; - if (doc.HasMember(prop_name.c_str()) && doc.FindMember(prop_name.c_str())->value.IsString()) - { - property = doc[prop_name.c_str()].GetString(); - } - else if (required) - { - throw json_parsing_exception("Missing or incorrect type for required property " + prop_name + "!"); - } - return property; - }; - - std::optional parse_double_property(const std::string &prop_name, const rapidjson::Value &doc, bool required) { - std::optional property; - if (doc.HasMember(prop_name.c_str()) && doc.FindMember(prop_name.c_str())->value.IsDouble()) - { - property = doc[prop_name.c_str()].GetDouble(); - } - else if (required) - { - throw json_parsing_exception("Missing or incorrect type for required property " + prop_name + "!"); - } - return property; - }; - - std::optional parse_object_property(const std::string &prop_name, const rapidjson::Value &doc, bool required) { - if (doc.HasMember(prop_name.c_str()) && doc.FindMember(prop_name.c_str())->value.IsObject()) - { - return doc[prop_name.c_str()].GetObject(); - } - else if (required) - { - throw json_parsing_exception("Missing or incorrect type for required property " + prop_name + "!"); - } - return std::nullopt; - } - - std::optional parse_array_property(const std::string &prop_name, const rapidjson::Value &doc, bool required) { - if (doc.HasMember(prop_name.c_str()) && doc.FindMember(prop_name.c_str())->value.IsArray()) - { - return doc[prop_name.c_str()].GetArray(); - } - else if (required) - { - throw json_parsing_exception("Missing or incorrect type for required property " + prop_name + "!"); - } - return std::nullopt; - } - - - -} \ No newline at end of file diff --git a/streets_utils/streets_json/test/json_utility_test.cpp b/streets_utils/streets_json/test/json_utility_test.cpp deleted file mode 100644 index 15a532909..000000000 --- a/streets_utils/streets_json/test/json_utility_test.cpp +++ /dev/null @@ -1,218 +0,0 @@ -#include -#include - -using namespace streets_utils::json_utils; - -//---------------------test validate_json--------------------- -TEST(json_utility_test, test_validate_invalid_json) { - // Empty String - std::string invalid_json = ""; - EXPECT_THROW( validate_json(invalid_json), std::runtime_error); - - // Property missing quotations - invalid_json = "{ some_propert: \"some_value\"}"; - EXPECT_THROW( validate_json(invalid_json), std::runtime_error); - -} - -TEST(json_utility_test, test_validate_valid_json) { - // Correct JSON - std::string valid_json = "{ \"some_property\": \"some_value\"}"; - auto parsed_doc = validate_json(valid_json); - EXPECT_FALSE(parsed_doc.HasParseError()); -} -//---------------------test parse_uint_property--------------------- -TEST(json_utility_test, test_get_json_uint_required_property_present){ - // Test with required property present - std::string valid_json = "{ \"some_property\": 12345}"; - auto parsed_doc = validate_json(valid_json); - EXPECT_EQ( 12345, parse_uint_property("some_property", parsed_doc, true)); -} - -TEST(json_utility_test, test_get_json_uint_required_property_not_present){ - // Test with required property no present - std::string valid_json = "{ \"some_property_other\": 12345}"; - auto parsed_doc = validate_json(valid_json); - EXPECT_THROW( parse_uint_property("some_property", parsed_doc, true), std::runtime_error); -} - -TEST(json_utility_test, test_get_json_uint_required_property_wrong_type){ - // Test with required property present with wrong type - std::string valid_json = "{ \"some_property\": -12345}"; - auto parsed_doc = validate_json(valid_json); - EXPECT_THROW( parse_uint_property("some_property", parsed_doc, true), std::runtime_error); -} -//---------------------test get_json_int_property--------------------- - -TEST(json_utility_test, test_get_json_int_required_property_present){ - // Test with required property present - std::string valid_json = "{ \"some_property\": -12345}"; - auto parsed_doc = validate_json(valid_json); - EXPECT_EQ( -12345, parse_int_property("some_property", parsed_doc, true)); -} - -TEST(json_utility_test, test_get_json_int_required_property_not_present){ - // Test with required property no present - std::string valid_json = "{ \"some_property_other\": 12345}"; - auto parsed_doc = validate_json(valid_json); - EXPECT_THROW( parse_int_property("some_property", parsed_doc, true), std::runtime_error); -} - -TEST(json_utility_test, test_get_json_int_required_property_wrong_type){ - // Test with required property present with wrong type - std::string valid_json = "{ \"some_property\": true}"; - auto parsed_doc = validate_json(valid_json); - EXPECT_THROW( parse_int_property("some_property", parsed_doc, true), std::runtime_error); -} - -//---------------------test parse_bool_property--------------------- - -TEST(json_utility_test, test_get_json_bool_required_property_present){ - // Test with required property present - std::string valid_json = "{ \"some_property\": true}"; - auto parsed_doc = validate_json(valid_json); - EXPECT_EQ( true, parse_bool_property("some_property", parsed_doc, true)); -} - -TEST(json_utility_test, test_get_json_bool_required_property_not_present){ - // Test with required property no present - std::string valid_json = "{ \"some_property_other\": true}"; - auto parsed_doc = validate_json(valid_json); - EXPECT_THROW( parse_bool_property("some_property", parsed_doc, true), std::runtime_error); -} - -TEST(json_utility_test, test_get_json_bool_required_property_wrong_type){ - // Test with required property present with wrong type - std::string valid_json = "{ \"some_property\": 1234}"; - auto parsed_doc = validate_json(valid_json); - EXPECT_THROW( parse_bool_property("some_property", parsed_doc, true), std::runtime_error); -} - -//---------------------test get_json_double_property--------------------- - -TEST(json_utility_test, test_get_json_double_required_property_present){ - // Test with required property present - std::string valid_json = "{ \"some_property\": 12.3}"; - auto parsed_doc = validate_json(valid_json); - EXPECT_NEAR( 12.3, parse_double_property("some_property", parsed_doc, true).value(), 0.01); -} - -TEST(json_utility_test, test_get_json_double_required_property_not_present){ - // Test with required property no present - std::string valid_json = "{ \"some_property_other\": 12.3}"; - auto parsed_doc = validate_json(valid_json); - EXPECT_THROW( parse_double_property("some_property", parsed_doc, true), std::runtime_error); -} - -TEST(json_utility_test, test_get_json_double_required_property_wrong_type){ - // Test with required property present with wrong type - std::string valid_json = "{ \"some_property\": 1234}"; - auto parsed_doc = validate_json(valid_json); - EXPECT_THROW( parse_double_property("some_property", parsed_doc, true), std::runtime_error); -} - -//---------------------test parse_string_property--------------------- - -TEST(json_utility_test, test_get_json_string_required_property_present){ - // Test with required property present - std::string valid_json = "{ \"some_property\": \"some_property\" }"; - auto parsed_doc = validate_json(valid_json); - EXPECT_EQ("some_property", parse_string_property("some_property", parsed_doc, true)); -} - -TEST(json_utility_test, test_get_json_string_required_property_not_present){ - // Test with required property no present - std::string valid_json = "{ \"some_property_other\": 12.3}"; - auto parsed_doc = validate_json(valid_json); - EXPECT_THROW( parse_string_property("some_property", parsed_doc, true), std::runtime_error); -} - -TEST(json_utility_test, test_get_json_string_required_property_wrong_type){ - // Test with required property present with wrong type - std::string valid_json = "{ \"some_property\": 1234}"; - auto parsed_doc = validate_json(valid_json); - EXPECT_THROW( parse_string_property("some_property", parsed_doc, true), std::runtime_error); -} - - -//---------------------test parse_object_property--------------------- - -TEST(json_utility_test, test_get_json_object_required_property_present){ - // Test with required property present - std::string valid_json = "{ " - "\"some_object\": {" - "\"object_name\" : \"object\"," - "\"object_value\" : 123" - "}" - "}"; - auto parsed_doc = validate_json(valid_json); - auto object = parse_object_property("some_object", parsed_doc, true); - EXPECT_EQ("object", parse_string_property("object_name", object.value(), true)); - EXPECT_EQ( 123, parse_int_property("object_value", object.value(), true) ); - -} - -TEST(json_utility_test, test_get_json_object_required_property_not_present){ - // Test with required property no present - std::string valid_json = "{ " - "\"some_other_object\": {" - "\"object_name\" : \"object\"," - "\"object_value\" : 123" - "}" - "}"; - auto parsed_doc = validate_json(valid_json); - EXPECT_THROW( parse_object_property("some_object", parsed_doc, true), std::runtime_error); -} - -TEST(json_utility_test, test_get_json_object_required_property_wrong_type){ - // Test with required property present with wrong type - std::string valid_json = "{ " - "\"some_object\": [{" - "\"object_name\" : \"object\"," - "\"object_value\" : 123" - "}]" - "}"; - auto parsed_doc = validate_json(valid_json); - EXPECT_THROW( parse_object_property("some_object", parsed_doc, true), std::runtime_error); -} - -//---------------------test parse_array_property--------------------- - -TEST(json_utility_test, test_get_json_array_required_property_present){ - // Test with required property present - std::string valid_json = "{ " - "\"some_array\": " - "[456, 2452, -1232, 2345]" - "}"; - auto parsed_doc = validate_json(valid_json); - auto array = parse_array_property("some_array", parsed_doc, true); - - EXPECT_EQ(4, array.value().Size()); - EXPECT_EQ(456, array.value()[0].GetInt()); - EXPECT_EQ(2452, array.value()[1].GetInt()); - EXPECT_EQ(-1232, array.value()[2].GetInt()); - EXPECT_EQ(2345, array.value()[3].GetInt()); -} - -TEST(json_utility_test, test_get_json_array_required_property_not_present){ - // Test with required property no present - std::string valid_json = "{ " - "\"some_other_array\": " - "[456, 2452, -1232, 2345]" - "}"; - auto parsed_doc = validate_json(valid_json); - EXPECT_THROW( parse_array_property("some_array", parsed_doc, true), std::runtime_error); -} - -TEST(json_utility_test, test_get_json_array_required_property_wrong_type){ - // Test with required property present with wrong type - std::string valid_json = "{ " - "\"some_array\": [{" - "\"object_name\" : \"object\"," - "\"object_value\" : 123" - "}]" - "}"; - auto parsed_doc = validate_json(valid_json); - EXPECT_THROW( parse_array_property("some_object", parsed_doc, true), std::runtime_error); -} - From e63e85497dd9e3830e65a1888acef593e00728e1 Mon Sep 17 00:00:00 2001 From: dev Date: Mon, 2 Oct 2023 02:43:44 -0400 Subject: [PATCH 26/80] Update to cmake config --- streets_utils/json_utils/cmake/json_utils_libConfig.cmake.in | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/streets_utils/json_utils/cmake/json_utils_libConfig.cmake.in b/streets_utils/json_utils/cmake/json_utils_libConfig.cmake.in index 9d8592224..6b665ab53 100644 --- a/streets_utils/json_utils/cmake/json_utils_libConfig.cmake.in +++ b/streets_utils/json_utils/cmake/json_utils_libConfig.cmake.in @@ -4,4 +4,4 @@ include(CMakeFindDependencyMacro) find_dependency(spdlog REQUIRED) find_dependency(RapidJSON REQUIRED) -include("${CMAKE_CURRENT_LIST_DIR}/streets_json_util_libTargets.cmake") +include("${CMAKE_CURRENT_LIST_DIR}/json_utils_libTargets.cmake") From 8b6e30045591108f1fec6671b1b3fd1cebfee6d3 Mon Sep 17 00:00:00 2001 From: dev Date: Mon, 2 Oct 2023 12:36:35 -0400 Subject: [PATCH 27/80] CMake namespace streets_utils --- streets_utils/json_utils/CMakeLists.txt | 25 +++++------ streets_utils/json_utils/README.md | 2 +- .../json_utils/include/json_utils.hpp | 41 +------------------ .../json_utils/test/json_utils_test.cpp | 19 +-------- 4 files changed, 14 insertions(+), 73 deletions(-) diff --git a/streets_utils/json_utils/CMakeLists.txt b/streets_utils/json_utils/CMakeLists.txt index e22f69d8e..1341d9542 100644 --- a/streets_utils/json_utils/CMakeLists.txt +++ b/streets_utils/json_utils/CMakeLists.txt @@ -2,6 +2,7 @@ cmake_minimum_required(VERSION 3.10.2) project(streets_json_utils) # Set Flags set(CMAKE_CXX_STANDARD 20) + # Find Packages find_package(RapidJSON REQUIRED) find_package(GTest REQUIRED) @@ -24,39 +25,36 @@ target_link_libraries( ${LIBRARY_NAME}_lib PUBLIC rapidjson spdlog::spdlog - ) -message( STATUS "Target INCLUDE DIR : " ${INCLUDE_DIRECTORIES}) - ######################################################## -# Install sensor_data_sharing_package package. +# Install streets_utils::json_utils_lib library. ######################################################## file(GLOB files ${CMAKE_CURRENT_SOURCE_DIR}/include/*.hpp) install( TARGETS ${LIBRARY_NAME}_lib EXPORT ${LIBRARY_NAME}_libTargets - LIBRARY DESTINATION lib - INCLUDES DESTINATION include - ARCHIVE DESTINATION lib + LIBRARY DESTINATION lib/streets_utils/ + INCLUDES DESTINATION include/streets_utils/${LIBRARY_NAME} + ARCHIVE DESTINATION lib/streets_utils/ ) install( EXPORT ${LIBRARY_NAME}_libTargets FILE ${LIBRARY_NAME}_libTargets.cmake - DESTINATION lib/cmake/${LIBRARY_NAME}_lib/ - NAMESPACE ${LIBRARY_NAME}_lib:: + DESTINATION lib/cmake/${LIBRARY_NAME}_lib + NAMESPACE streets_utils:: ) include(CMakePackageConfigHelpers) configure_package_config_file( cmake/${LIBRARY_NAME}_libConfig.cmake.in ${CMAKE_CURRENT_BINARY_DIR}/${LIBRARY_NAME}_libConfig.cmake - INSTALL_DESTINATION lib/${LIBRARY_NAME}_lib/ ) + INSTALL_DESTINATION lib/cmake/${LIBRARY_NAME}_lib) install( FILES ${CMAKE_CURRENT_BINARY_DIR}/${LIBRARY_NAME}_libConfig.cmake - DESTINATION lib/cmake/${LIBRARY_NAME}_lib/ + DESTINATION lib/cmake/${LIBRARY_NAME}_lib ) -install(FILES ${files} DESTINATION include) +install(FILES ${files} DESTINATION include/streets_utils/${LIBRARY_NAME}) ######################## # googletest for unit testing @@ -72,8 +70,7 @@ target_link_libraries(${BINARY} ${LIBRARY_NAME}_lib rapidjson spdlog::spdlog - - ) +) if(${CMAKE_VERSION} VERSION_GREATER_EQUAL "3.20") target_link_libraries( ${BINARY} PUBLIC GTest::gtest_main ) else() diff --git a/streets_utils/json_utils/README.md b/streets_utils/json_utils/README.md index bde987ad0..9892c0b3e 100644 --- a/streets_utils/json_utils/README.md +++ b/streets_utils/json_utils/README.md @@ -22,7 +22,7 @@ Functions to retrieve member values from JSON object [RapidJSON](https://miloyip ## Include Library To include after installing with `make install` simply add find_package call to `CMakeLists.txt` and link the library as follows. Installed CMake configuration files should handle finding and linking depedencies `RapidJSON` and `spdlog`. The library along with it's dependencies can then be included by simply using the find_package() instruction. ``` -find_package(streets_utils COMPONENT json_utils) +find_package(json_utils_lib) ... target_link_library( target PUBLIC streets_utils::json_utils) ``` \ No newline at end of file diff --git a/streets_utils/json_utils/include/json_utils.hpp b/streets_utils/json_utils/include/json_utils.hpp index 07da2ac11..0275251a6 100644 --- a/streets_utils/json_utils/include/json_utils.hpp +++ b/streets_utils/json_utils/include/json_utils.hpp @@ -104,46 +104,7 @@ namespace streets_utils::json_utils { * @return std::optional */ std::optional parse_array_member(const std::string &member_name, const rapidjson::Value &obj, bool required); - // TODO: Commented out because I can not get this to work for string values. Before closing PR make a decision on keeping/fixing this or removing - // it. Works for all other types other - // template - // inline void write_optional_member(const std::string &member_name, const std::optional _value, rapidjson::Value &obj, rapidjson::Document::AllocatorType &allocator) - // { - // if (obj.IsObject() && _value.has_value()) { - // // In an attempt to improve performance and since many json objects have constant - // // key names the StringRef constructor avoids copying the member name and "requires - // // that the referenced string pointers have a sufficient lifetime, which exceeds the - // // lifetime of the associated GenericValue"(https://rapidjson.org/structrapidjson_1_1_generic_string_ref.html). - - // // To explicitly force a copy we instead create a new String Value with the content of - // // the passed in parameter. Follow link to get more information from Documentation - // // (RapidJSON Documentation)[https://miloyip.github.io/rapidjson/md_obj_tutorial.html#ModifyObject] - - // // Alternatively we could pass in the string reference using StringRef(member_name). Doing so - // // we would be explicitly guaranteeing the lifetime of the passed in string reference. If we then - // // pass in a string literal from the caller of this function the AddMember method would silently fail - // // since the lifetime of the reference string is not sufficient. - // rapidjson::Value member_name_copy(member_name, allocator); - // obj.AddMember(member_name_copy, _value.value(), allocator); - // - // } - // else { - // SPDLOG_TRACE("Skipping member {0}! std::optional has_value() check failed or parameter is not writable object type value!", - // member_name); - - // } - // }; - - // template - // inline void write_required_member(const std::string &member_name, const T &value, rapidjson::Value &obj, rapidjson::Document::AllocatorType &allocator){ - // if ( obj.IsObject() && value != NULL ) { - // rapidjson::Value member_name_copy(member_name, allocator); - // obj.AddMember(member_name_copy, value, allocator); - // } - // else { - // throw json_utils_exception(""); - // } - // }; + } \ No newline at end of file diff --git a/streets_utils/json_utils/test/json_utils_test.cpp b/streets_utils/json_utils/test/json_utils_test.cpp index 9d8d0e3d9..22b99fe8b 100644 --- a/streets_utils/json_utils/test/json_utils_test.cpp +++ b/streets_utils/json_utils/test/json_utils_test.cpp @@ -268,22 +268,5 @@ TEST(json_utils_test, test_get_json_array_optional_property_missing) { auto property = parse_array_member("some_property", parsed_doc, false); EXPECT_FALSE( property.has_value()); } -//---------------------test write optional property--------------------- -// TODO: Functionality not working yet, -// TEST(json_utils_test, test_write_option_property){ -// rapidjson::Value obj(rapidjson::kObjectType); -// rapidjson::Document doc; -// // Write optional present value -// std::optional double_optional; -// double_optional = 43.2; -// write_optional_member("some_double_property", double_optional, obj, doc.GetAllocator()); -// EXPECT_TRUE(obj.HasMember("some_double_property")); -// EXPECT_TRUE(obj.FindMember("some_double_property")->value.IsNumber()); -// EXPECT_NEAR(43.2, obj.FindMember("some_double_property")->value.GetDouble(), 0.01 ); - -// std::optional string_optional_not_present; -// // write_optional_member("some_string_property", string_optional_not_present, obj, doc.GetAllocator()); - -// // EXPECT_FALSE( obj.HasMember("some_string_property")); -// } + From 04310e6534e88db639ad6e757c607e5f68e61564 Mon Sep 17 00:00:00 2001 From: dev Date: Mon, 2 Oct 2023 13:25:48 -0400 Subject: [PATCH 28/80] CMake Updates --- streets_utils/json_utils/CMakeLists.txt | 59 ++++++++++++------------- 1 file changed, 28 insertions(+), 31 deletions(-) diff --git a/streets_utils/json_utils/CMakeLists.txt b/streets_utils/json_utils/CMakeLists.txt index 1341d9542..423be1186 100644 --- a/streets_utils/json_utils/CMakeLists.txt +++ b/streets_utils/json_utils/CMakeLists.txt @@ -1,8 +1,9 @@ cmake_minimum_required(VERSION 3.10.2) -project(streets_json_utils) +project(json_utils) # Set Flags set(CMAKE_CXX_STANDARD 20) - +# GNU standard installation directories +include(GNUInstallDirs) # Find Packages find_package(RapidJSON REQUIRED) find_package(GTest REQUIRED) @@ -10,18 +11,18 @@ find_package(spdlog REQUIRED) # Add definition for rapidjson to include std::string add_definitions(-DRAPIDJSON_HAS_STDSTRING=1) -set(LIBRARY_NAME json_utils) +set(LIBRARY_NAME ${PROJECT_NAME}_lib) ######################################################## # Build Library ######################################################## file(GLOB SOURCES ${CMAKE_CURRENT_SOURCE_DIR}/src/*.cpp) -add_library(${LIBRARY_NAME}_lib ${SOURCES} ) -target_include_directories(${LIBRARY_NAME}_lib +add_library(${LIBRARY_NAME} ${SOURCES} ) +target_include_directories(${LIBRARY_NAME} PUBLIC $ $ ) -target_link_libraries( ${LIBRARY_NAME}_lib +target_link_libraries( ${LIBRARY_NAME} PUBLIC rapidjson spdlog::spdlog @@ -33,46 +34,42 @@ target_link_libraries( ${LIBRARY_NAME}_lib file(GLOB files ${CMAKE_CURRENT_SOURCE_DIR}/include/*.hpp) install( - TARGETS ${LIBRARY_NAME}_lib - EXPORT ${LIBRARY_NAME}_libTargets - LIBRARY DESTINATION lib/streets_utils/ - INCLUDES DESTINATION include/streets_utils/${LIBRARY_NAME} - ARCHIVE DESTINATION lib/streets_utils/ + TARGETS ${LIBRARY_NAME} + EXPORT ${LIBRARY_NAME}Targets + LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}/streets_utils/ + INCLUDES DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/streets_utils/${LIBRARY_NAME} + ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR}/streets_utils/ ) install( - EXPORT ${LIBRARY_NAME}_libTargets - FILE ${LIBRARY_NAME}_libTargets.cmake - DESTINATION lib/cmake/${LIBRARY_NAME}_lib + EXPORT ${LIBRARY_NAME}Targets + FILE ${LIBRARY_NAME}Targets.cmake + DESTINATION lib/cmake/${LIBRARY_NAME} NAMESPACE streets_utils:: ) include(CMakePackageConfigHelpers) configure_package_config_file( - cmake/${LIBRARY_NAME}_libConfig.cmake.in - ${CMAKE_CURRENT_BINARY_DIR}/${LIBRARY_NAME}_libConfig.cmake - INSTALL_DESTINATION lib/cmake/${LIBRARY_NAME}_lib) + cmake/${LIBRARY_NAME}Config.cmake.in + ${CMAKE_CURRENT_BINARY_DIR}/${LIBRARY_NAME}Config.cmake + INSTALL_DESTINATION lib/cmake/${LIBRARY_NAME}) install( - FILES ${CMAKE_CURRENT_BINARY_DIR}/${LIBRARY_NAME}_libConfig.cmake - DESTINATION lib/cmake/${LIBRARY_NAME}_lib + FILES ${CMAKE_CURRENT_BINARY_DIR}/${LIBRARY_NAME}Config.cmake + DESTINATION lib/cmake/${LIBRARY_NAME} ) install(FILES ${files} DESTINATION include/streets_utils/${LIBRARY_NAME}) ######################## # googletest for unit testing ######################## -set(BINARY ${LIBRARY_NAME}_test) +set(TEST_NAME ${PROJECT_NAME}_test) file(GLOB_RECURSE TEST_SOURCES LIST_DIRECTORIES false test/*.hpp test/*.cpp) -set(SOURCES ${TEST_SOURCES} WORKING_DIRECTORY ${PROJECT_SOURCE_DIR}/test) -add_executable(${BINARY} ${TEST_SOURCES}) -add_test(NAME ${BINARY} COMMAND ${BINARY}) -target_include_directories(${BINARY} PUBLIC ${PROJECT_SOURCE_DIR}/include) -target_link_libraries(${BINARY} - PUBLIC - ${LIBRARY_NAME}_lib - rapidjson - spdlog::spdlog +add_executable(${TEST_NAME} ${TEST_SOURCES}) +gtest_discover_tests(${TEST_NAME}) +target_link_libraries(${TEST_NAME} + PRIVATE + ${LIBRARY_NAME} ) if(${CMAKE_VERSION} VERSION_GREATER_EQUAL "3.20") - target_link_libraries( ${BINARY} PUBLIC GTest::gtest_main ) + target_link_libraries( ${TEST_NAME} PUBLIC GTest::gtest_main ) else() - target_link_libraries( ${BINARY} PUBLIC gtest_main ) + target_link_libraries( ${TEST_NAME} PUBLIC gtest_main ) endif() \ No newline at end of file From ddd4bb1b3548fb56c905be60eeea6f35aa36b628 Mon Sep 17 00:00:00 2001 From: dev Date: Mon, 2 Oct 2023 13:41:11 -0400 Subject: [PATCH 29/80] CMake update --- streets_utils/json_utils/CMakeLists.txt | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/streets_utils/json_utils/CMakeLists.txt b/streets_utils/json_utils/CMakeLists.txt index 423be1186..e94da4736 100644 --- a/streets_utils/json_utils/CMakeLists.txt +++ b/streets_utils/json_utils/CMakeLists.txt @@ -1,7 +1,7 @@ cmake_minimum_required(VERSION 3.10.2) project(json_utils) # Set Flags -set(CMAKE_CXX_STANDARD 20) +set(CMAKE_CXX_STANDARD 17) # GNU standard installation directories include(GNUInstallDirs) # Find Packages @@ -19,9 +19,7 @@ file(GLOB SOURCES ${CMAKE_CURRENT_SOURCE_DIR}/src/*.cpp) add_library(${LIBRARY_NAME} ${SOURCES} ) target_include_directories(${LIBRARY_NAME} PUBLIC - $ - $ -) + $) target_link_libraries( ${LIBRARY_NAME} PUBLIC rapidjson From 945938179166abecca6e0ab89e6babe2e2774d94 Mon Sep 17 00:00:00 2001 From: dev Date: Mon, 2 Oct 2023 15:23:35 -0400 Subject: [PATCH 30/80] Updated unit testing test case and suite naming --- .../json_utils/test/json_utils_test.cpp | 60 +++++++++---------- 1 file changed, 30 insertions(+), 30 deletions(-) diff --git a/streets_utils/json_utils/test/json_utils_test.cpp b/streets_utils/json_utils/test/json_utils_test.cpp index 22b99fe8b..c592c1827 100644 --- a/streets_utils/json_utils/test/json_utils_test.cpp +++ b/streets_utils/json_utils/test/json_utils_test.cpp @@ -5,7 +5,7 @@ using namespace streets_utils::json_utils; //---------------------test validate_json--------------------- -TEST(json_utils_test, test_validate_invalid_json) { +TEST(JsonUtilsTest, testValidateInvalidJson) { // Empty String std::string invalid_json = ""; EXPECT_THROW( validate_json(invalid_json), json_utils_exception); @@ -16,35 +16,35 @@ TEST(json_utils_test, test_validate_invalid_json) { } -TEST(json_utils_test, test_validate_valid_json) { +TEST(JsonUtilsTest, testValidateValidJson) { // Correct JSON std::string valid_json = "{ \"some_property\": \"some_value\"}"; auto parsed_doc = validate_json(valid_json); EXPECT_FALSE(parsed_doc.HasParseError()); } //---------------------test parse_uint_member--------------------- -TEST(json_utils_test, test_get_json_uint_required_property_present){ +TEST(JsonUtilsTest, testGetJsonUintRequiredPropertyPresent){ // Test with required property present std::string valid_json = "{ \"some_property\": 12345}"; auto parsed_doc = validate_json(valid_json); EXPECT_EQ( 12345, parse_uint_member("some_property", parsed_doc, true)); } -TEST(json_utils_test, test_get_json_uint_required_property_not_present){ +TEST(JsonUtilsTest, testGetJsonUintRequiredPropertyNotPresent){ // Test with required property no present std::string valid_json = "{ \"some_property_other\": 12345}"; auto parsed_doc = validate_json(valid_json); EXPECT_THROW( parse_uint_member("some_property", parsed_doc, true), json_utils_exception); } -TEST(json_utils_test, test_get_json_uint_required_property_wrong_type){ +TEST(JsonUtilsTest, testGetJsonUintRequiredPropertyWrongType){ // Test with required property present with wrong type std::string valid_json = "{ \"some_property\": -12345}"; auto parsed_doc = validate_json(valid_json); EXPECT_THROW( parse_uint_member("some_property", parsed_doc, true), json_utils_exception); } -TEST(json_utils_test, test_get_json_uint_optional_property_missing) { +TEST(JsonUtilsTest, testGetJsonUintOptionalPropertyMissing) { // Test with required property no present std::string valid_json = "{ \"some_property_other\": 12345}"; auto parsed_doc = validate_json(valid_json); @@ -53,28 +53,28 @@ TEST(json_utils_test, test_get_json_uint_optional_property_missing) { } //---------------------test get_json_int_property--------------------- -TEST(json_utils_test, test_get_json_int_required_property_present){ +TEST(JsonUtilsTest, testGetJsonIntRequiredPropertyPresent){ // Test with required property present std::string valid_json = "{ \"some_property\": -12345}"; auto parsed_doc = validate_json(valid_json); EXPECT_EQ( -12345, parse_int_member("some_property", parsed_doc, true)); } -TEST(json_utils_test, test_get_json_int_required_property_not_present){ +TEST(JsonUtilsTest, testGetJsonIntRequiredPropertyNotPresent){ // Test with required property no present std::string valid_json = "{ \"some_property_other\": 12345}"; auto parsed_doc = validate_json(valid_json); EXPECT_THROW( parse_int_member("some_property", parsed_doc, true), json_utils_exception); } -TEST(json_utils_test, test_get_json_int_required_property_wrong_type){ +TEST(JsonUtilsTest, testGetJsonIntRequiredPropertyWrongType){ // Test with required property present with wrong type std::string valid_json = "{ \"some_property\": true}"; auto parsed_doc = validate_json(valid_json); EXPECT_THROW( parse_int_member("some_property", parsed_doc, true), json_utils_exception); } -TEST(json_utils_test, test_get_json_int_optional_property_missing) { +TEST(JsonUtilsTest, testGetJsonIntOptionalPropertyMissing) { // Test with required property no present std::string valid_json = "{ \"some_property_other\": 12345}"; auto parsed_doc = validate_json(valid_json); @@ -83,28 +83,28 @@ TEST(json_utils_test, test_get_json_int_optional_property_missing) { } //---------------------test parse_bool_property--------------------- -TEST(json_utils_test, test_get_json_bool_required_property_present){ +TEST(JsonUtilsTest, testGetJsonBoolRequiredPropertyPresent){ // Test with required property present std::string valid_json = "{ \"some_property\": true}"; auto parsed_doc = validate_json(valid_json); EXPECT_EQ( true, parse_bool_member("some_property", parsed_doc, true)); } -TEST(json_utils_test, test_get_json_bool_required_property_not_present){ +TEST(JsonUtilsTest, testGetJsonBoolRequiredPropertyNotPresent){ // Test with required property no present std::string valid_json = "{ \"some_property_other\": true}"; auto parsed_doc = validate_json(valid_json); EXPECT_THROW( parse_bool_member("some_property", parsed_doc, true), json_utils_exception); } -TEST(json_utils_test, test_get_json_bool_required_property_wrong_type){ +TEST(JsonUtilsTest, testGetJsonBoolRequiredPropertyWrongType){ // Test with required property present with wrong type std::string valid_json = "{ \"some_property\": 1234}"; auto parsed_doc = validate_json(valid_json); EXPECT_THROW( parse_bool_member("some_property", parsed_doc, true), json_utils_exception); } -TEST(json_utils_test, test_get_json_bool_optional_property_missing) { +TEST(JsonUtilsTest, testGetJsonBoolOptionalPropertyMissing) { // Test with required property no present std::string valid_json = "{ \"some_property_other\": 12345}"; auto parsed_doc = validate_json(valid_json); @@ -114,28 +114,28 @@ TEST(json_utils_test, test_get_json_bool_optional_property_missing) { //---------------------test get_json_double_property--------------------- -TEST(json_utils_test, test_get_json_double_required_property_present){ +TEST(JsonUtilsTest, testGetJsonDoubleRequiredPropertyPresent){ // Test with required property present std::string valid_json = "{ \"some_property\": 12.3}"; auto parsed_doc = validate_json(valid_json); EXPECT_NEAR( 12.3, parse_double_member("some_property", parsed_doc, true).value(), 0.01); } -TEST(json_utils_test, test_get_json_double_required_property_not_present){ +TEST(JsonUtilsTest, testGetJsonDoubleRequiredPropertyNotPresent){ // Test with required property no present std::string valid_json = "{ \"some_property_other\": 12.3}"; auto parsed_doc = validate_json(valid_json); EXPECT_THROW( parse_double_member("some_property", parsed_doc, true), json_utils_exception); } -TEST(json_utils_test, test_get_json_double_required_property_wrong_type){ +TEST(JsonUtilsTest, testGetJsonDoubleRequiredPropertyWrongType){ // Test with required property present with wrong type std::string valid_json = "{ \"some_property\": 1234}"; auto parsed_doc = validate_json(valid_json); EXPECT_THROW( parse_double_member("some_property", parsed_doc, true), json_utils_exception); } -TEST(json_utils_test, test_get_json_double_optional_property_missing) { +TEST(JsonUtilsTest, testGetJsonDoubleOptionalPropertyMissing) { // Test with required property no present std::string valid_json = "{ \"some_property_other\": 12345}"; auto parsed_doc = validate_json(valid_json); @@ -144,28 +144,28 @@ TEST(json_utils_test, test_get_json_double_optional_property_missing) { } //---------------------test parse_string_property--------------------- -TEST(json_utils_test, test_get_json_string_required_property_present){ +TEST(JsonUtilsTest, testGetJsonStringRequiredPropertyPresent){ // Test with required property present std::string valid_json = "{ \"some_property\": \"some_property\" }"; auto parsed_doc = validate_json(valid_json); EXPECT_EQ("some_property", parse_string_member("some_property", parsed_doc, true)); } -TEST(json_utils_test, test_get_json_string_required_property_not_present){ +TEST(JsonUtilsTest, testGetJsonStringRequiredPropertyNotPresent){ // Test with required property no present std::string valid_json = "{ \"some_property_other\": 12.3}"; auto parsed_doc = validate_json(valid_json); EXPECT_THROW( parse_string_member("some_property", parsed_doc, true), json_utils_exception); } -TEST(json_utils_test, test_get_json_string_required_property_wrong_type){ +TEST(JsonUtilsTest, testGetJsonStringRequiredPropertyWrongType){ // Test with required property present with wrong type std::string valid_json = "{ \"some_property\": 1234}"; auto parsed_doc = validate_json(valid_json); EXPECT_THROW( parse_string_member("some_property", parsed_doc, true), json_utils_exception); } -TEST(json_utils_test, test_get_json_string_optional_property_missing) { +TEST(JsonUtilsTest, testGetJsonStringOptionalPropertyMissing) { // Test with required property no present std::string valid_json = "{ \"some_property_other\": 12345}"; auto parsed_doc = validate_json(valid_json); @@ -175,7 +175,7 @@ TEST(json_utils_test, test_get_json_string_optional_property_missing) { //---------------------test parse_object_property--------------------- -TEST(json_utils_test, test_get_json_object_required_property_present){ +TEST(JsonUtilsTest, testGetJsonObjectRequiredPropertyPresent){ // Test with required property present std::string valid_json = "{ " "\"some_object\": {" @@ -190,7 +190,7 @@ TEST(json_utils_test, test_get_json_object_required_property_present){ } -TEST(json_utils_test, test_get_json_object_required_property_not_present){ +TEST(JsonUtilsTest, testGetJsonObjectRequiredPropertyNotPresent){ // Test with required property no present std::string valid_json = "{ " "\"some_other_object\": {" @@ -202,7 +202,7 @@ TEST(json_utils_test, test_get_json_object_required_property_not_present){ EXPECT_THROW( parse_object_member("some_object", parsed_doc, true), json_utils_exception); } -TEST(json_utils_test, test_get_json_object_required_property_wrong_type){ +TEST(JsonUtilsTest, testGetJsonObjectRequiredPropertyWrongType){ // Test with required property present with wrong type std::string valid_json = "{ " "\"some_object\": [{" @@ -214,7 +214,7 @@ TEST(json_utils_test, test_get_json_object_required_property_wrong_type){ EXPECT_THROW( parse_object_member("some_object", parsed_doc, true), json_utils_exception); } -TEST(json_utils_test, test_get_json_object_optional_property_missing) { +TEST(JsonUtilsTest, testGetJsonObjectOptionalPropertyMissing) { // Test with required property no present std::string valid_json = "{ \"some_property_other\": 12345}"; auto parsed_doc = validate_json(valid_json); @@ -223,7 +223,7 @@ TEST(json_utils_test, test_get_json_object_optional_property_missing) { } //---------------------test parse_array_property--------------------- -TEST(json_utils_test, test_get_json_array_required_property_present){ +TEST(JsonUtilsTest, testGetJsonArrayRequiredPropertyPresent){ // Test with required property present std::string valid_json = "{ " "\"some_array\": " @@ -239,7 +239,7 @@ TEST(json_utils_test, test_get_json_array_required_property_present){ EXPECT_EQ(2345, array.value()[3].GetInt()); } -TEST(json_utils_test, test_get_json_array_required_property_not_present){ +TEST(JsonUtilsTest, testGetJsonArrayRequiredPropertyNotPresent){ // Test with required property no present std::string valid_json = "{ " "\"some_other_array\": " @@ -249,7 +249,7 @@ TEST(json_utils_test, test_get_json_array_required_property_not_present){ EXPECT_THROW( parse_array_member("some_array", parsed_doc, true), json_utils_exception); } -TEST(json_utils_test, test_get_json_array_required_property_wrong_type){ +TEST(JsonUtilsTest, testGetJsonArrayRequiredPropertyWrongType){ // Test with required property present with wrong type std::string valid_json = "{ " "\"some_array\": [{" @@ -261,7 +261,7 @@ TEST(json_utils_test, test_get_json_array_required_property_wrong_type){ EXPECT_THROW( parse_array_member("some_object", parsed_doc, true), json_utils_exception); } -TEST(json_utils_test, test_get_json_array_optional_property_missing) { +TEST(JsonUtilsTest, testGetJsonArrayOptionalPropertyMissing) { // Test with required property no present std::string valid_json = "{ \"some_property_other\": 12345}"; auto parsed_doc = validate_json(valid_json); From 2d180dac4157cbc66625d97ab5b757adcc2e27d6 Mon Sep 17 00:00:00 2001 From: dev Date: Mon, 2 Oct 2023 15:26:51 -0400 Subject: [PATCH 31/80] Update Cmake --- streets_utils/json_utils/CMakeLists.txt | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/streets_utils/json_utils/CMakeLists.txt b/streets_utils/json_utils/CMakeLists.txt index e94da4736..31bfd6025 100644 --- a/streets_utils/json_utils/CMakeLists.txt +++ b/streets_utils/json_utils/CMakeLists.txt @@ -41,17 +41,17 @@ install( install( EXPORT ${LIBRARY_NAME}Targets FILE ${LIBRARY_NAME}Targets.cmake - DESTINATION lib/cmake/${LIBRARY_NAME} + DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/cmake/${LIBRARY_NAME} NAMESPACE streets_utils:: ) include(CMakePackageConfigHelpers) configure_package_config_file( cmake/${LIBRARY_NAME}Config.cmake.in ${CMAKE_CURRENT_BINARY_DIR}/${LIBRARY_NAME}Config.cmake - INSTALL_DESTINATION lib/cmake/${LIBRARY_NAME}) + INSTALL_DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/cmake/${LIBRARY_NAME}) install( FILES ${CMAKE_CURRENT_BINARY_DIR}/${LIBRARY_NAME}Config.cmake - DESTINATION lib/cmake/${LIBRARY_NAME} + DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/cmake/${LIBRARY_NAME} ) install(FILES ${files} DESTINATION include/streets_utils/${LIBRARY_NAME}) From 242bb7016afff5a869f023eff71b1b54b987d4a0 Mon Sep 17 00:00:00 2001 From: dev Date: Mon, 2 Oct 2023 17:08:07 -0400 Subject: [PATCH 32/80] Update parse_json function --- streets_utils/json_utils/README.md | 2 +- .../json_utils/include/json_utils.hpp | 2 +- streets_utils/json_utils/src/json_utils.cpp | 2 +- .../json_utils/test/json_utils_test.cpp | 68 +++++++++---------- 4 files changed, 37 insertions(+), 37 deletions(-) diff --git a/streets_utils/json_utils/README.md b/streets_utils/json_utils/README.md index 9892c0b3e..7036d7d1a 100644 --- a/streets_utils/json_utils/README.md +++ b/streets_utils/json_utils/README.md @@ -5,7 +5,7 @@ This CARMA-Streets library contains utility functions for parsing JSON strings with [RapidJSON](https://miloyip.github.io/rapidjson/index.html). The functions exposed by this library are described below ## Functions -`rapidjson::Document validate_json( const std::string &json )` +`rapidjson::Document parse_json( const std::string &json )` Function to parse `std::string` JSON with [RapidJSON](https://miloyip.github.io/rapidjson/index.html) with [DOM parsing](https://miloyip.github.io/rapidjson/md_doc_dom.html). Throws `json_utils_exception` if string is not valid json. Otherwise returns `rapidjson::Document`` diff --git a/streets_utils/json_utils/include/json_utils.hpp b/streets_utils/json_utils/include/json_utils.hpp index 0275251a6..de2d61759 100644 --- a/streets_utils/json_utils/include/json_utils.hpp +++ b/streets_utils/json_utils/include/json_utils.hpp @@ -19,7 +19,7 @@ namespace streets_utils::json_utils { * @throws json_utils_exception if passed string is not valid json. * @return resulting `rapidjson::Document` */ - rapidjson::Document validate_json( const std::string &json ); + rapidjson::Document parse_json( const std::string &json ); /** * @brief Functions to retrieve int member values from JSON object [RapidJSON](https://miloyip.github.io/rapidjson/index.html) * with [DOM parsing](https://miloyip.github.io/rapidjson/md_doc_dom.html). Functions will return unintialized diff --git a/streets_utils/json_utils/src/json_utils.cpp b/streets_utils/json_utils/src/json_utils.cpp index 68cc79fea..99133caec 100644 --- a/streets_utils/json_utils/src/json_utils.cpp +++ b/streets_utils/json_utils/src/json_utils.cpp @@ -2,7 +2,7 @@ namespace streets_utils::json_utils { - rapidjson::Document validate_json(const std::string &json) { + rapidjson::Document parse_json(const std::string &json) { rapidjson::Document obj; obj.Parse(json); if (obj.HasParseError()) diff --git a/streets_utils/json_utils/test/json_utils_test.cpp b/streets_utils/json_utils/test/json_utils_test.cpp index c592c1827..a888c02b4 100644 --- a/streets_utils/json_utils/test/json_utils_test.cpp +++ b/streets_utils/json_utils/test/json_utils_test.cpp @@ -4,50 +4,50 @@ using namespace streets_utils::json_utils; -//---------------------test validate_json--------------------- -TEST(JsonUtilsTest, testValidateInvalidJson) { +//---------------------test parse_json--------------------- +TEST(JsonUtilsTest, testParseJsonInvalidJson) { // Empty String std::string invalid_json = ""; - EXPECT_THROW( validate_json(invalid_json), json_utils_exception); + EXPECT_THROW( parse_json(invalid_json), json_utils_exception); // Property missing quotations invalid_json = "{ some_propert: \"some_value\"}"; - EXPECT_THROW( validate_json(invalid_json), json_utils_exception); + EXPECT_THROW( parse_json(invalid_json), json_utils_exception); } -TEST(JsonUtilsTest, testValidateValidJson) { +TEST(JsonUtilsTest, testParseJsonValidJson) { // Correct JSON std::string valid_json = "{ \"some_property\": \"some_value\"}"; - auto parsed_doc = validate_json(valid_json); + auto parsed_doc = parse_json(valid_json); EXPECT_FALSE(parsed_doc.HasParseError()); } //---------------------test parse_uint_member--------------------- TEST(JsonUtilsTest, testGetJsonUintRequiredPropertyPresent){ // Test with required property present std::string valid_json = "{ \"some_property\": 12345}"; - auto parsed_doc = validate_json(valid_json); + auto parsed_doc = parse_json(valid_json); EXPECT_EQ( 12345, parse_uint_member("some_property", parsed_doc, true)); } TEST(JsonUtilsTest, testGetJsonUintRequiredPropertyNotPresent){ // Test with required property no present std::string valid_json = "{ \"some_property_other\": 12345}"; - auto parsed_doc = validate_json(valid_json); + auto parsed_doc = parse_json(valid_json); EXPECT_THROW( parse_uint_member("some_property", parsed_doc, true), json_utils_exception); } TEST(JsonUtilsTest, testGetJsonUintRequiredPropertyWrongType){ // Test with required property present with wrong type std::string valid_json = "{ \"some_property\": -12345}"; - auto parsed_doc = validate_json(valid_json); + auto parsed_doc = parse_json(valid_json); EXPECT_THROW( parse_uint_member("some_property", parsed_doc, true), json_utils_exception); } TEST(JsonUtilsTest, testGetJsonUintOptionalPropertyMissing) { // Test with required property no present std::string valid_json = "{ \"some_property_other\": 12345}"; - auto parsed_doc = validate_json(valid_json); + auto parsed_doc = parse_json(valid_json); auto property = parse_uint_member("some_property", parsed_doc, false); EXPECT_FALSE( property.has_value()); } @@ -56,28 +56,28 @@ TEST(JsonUtilsTest, testGetJsonUintOptionalPropertyMissing) { TEST(JsonUtilsTest, testGetJsonIntRequiredPropertyPresent){ // Test with required property present std::string valid_json = "{ \"some_property\": -12345}"; - auto parsed_doc = validate_json(valid_json); + auto parsed_doc = parse_json(valid_json); EXPECT_EQ( -12345, parse_int_member("some_property", parsed_doc, true)); } TEST(JsonUtilsTest, testGetJsonIntRequiredPropertyNotPresent){ // Test with required property no present std::string valid_json = "{ \"some_property_other\": 12345}"; - auto parsed_doc = validate_json(valid_json); + auto parsed_doc = parse_json(valid_json); EXPECT_THROW( parse_int_member("some_property", parsed_doc, true), json_utils_exception); } TEST(JsonUtilsTest, testGetJsonIntRequiredPropertyWrongType){ // Test with required property present with wrong type std::string valid_json = "{ \"some_property\": true}"; - auto parsed_doc = validate_json(valid_json); + auto parsed_doc = parse_json(valid_json); EXPECT_THROW( parse_int_member("some_property", parsed_doc, true), json_utils_exception); } TEST(JsonUtilsTest, testGetJsonIntOptionalPropertyMissing) { // Test with required property no present std::string valid_json = "{ \"some_property_other\": 12345}"; - auto parsed_doc = validate_json(valid_json); + auto parsed_doc = parse_json(valid_json); auto property = parse_int_member("some_property", parsed_doc, false); EXPECT_FALSE( property.has_value()); } @@ -86,28 +86,28 @@ TEST(JsonUtilsTest, testGetJsonIntOptionalPropertyMissing) { TEST(JsonUtilsTest, testGetJsonBoolRequiredPropertyPresent){ // Test with required property present std::string valid_json = "{ \"some_property\": true}"; - auto parsed_doc = validate_json(valid_json); + auto parsed_doc = parse_json(valid_json); EXPECT_EQ( true, parse_bool_member("some_property", parsed_doc, true)); } TEST(JsonUtilsTest, testGetJsonBoolRequiredPropertyNotPresent){ // Test with required property no present std::string valid_json = "{ \"some_property_other\": true}"; - auto parsed_doc = validate_json(valid_json); + auto parsed_doc = parse_json(valid_json); EXPECT_THROW( parse_bool_member("some_property", parsed_doc, true), json_utils_exception); } TEST(JsonUtilsTest, testGetJsonBoolRequiredPropertyWrongType){ // Test with required property present with wrong type std::string valid_json = "{ \"some_property\": 1234}"; - auto parsed_doc = validate_json(valid_json); + auto parsed_doc = parse_json(valid_json); EXPECT_THROW( parse_bool_member("some_property", parsed_doc, true), json_utils_exception); } TEST(JsonUtilsTest, testGetJsonBoolOptionalPropertyMissing) { // Test with required property no present std::string valid_json = "{ \"some_property_other\": 12345}"; - auto parsed_doc = validate_json(valid_json); + auto parsed_doc = parse_json(valid_json); auto property = parse_bool_member("some_property", parsed_doc, false); EXPECT_FALSE( property.has_value()); } @@ -117,28 +117,28 @@ TEST(JsonUtilsTest, testGetJsonBoolOptionalPropertyMissing) { TEST(JsonUtilsTest, testGetJsonDoubleRequiredPropertyPresent){ // Test with required property present std::string valid_json = "{ \"some_property\": 12.3}"; - auto parsed_doc = validate_json(valid_json); + auto parsed_doc = parse_json(valid_json); EXPECT_NEAR( 12.3, parse_double_member("some_property", parsed_doc, true).value(), 0.01); } TEST(JsonUtilsTest, testGetJsonDoubleRequiredPropertyNotPresent){ // Test with required property no present std::string valid_json = "{ \"some_property_other\": 12.3}"; - auto parsed_doc = validate_json(valid_json); + auto parsed_doc = parse_json(valid_json); EXPECT_THROW( parse_double_member("some_property", parsed_doc, true), json_utils_exception); } TEST(JsonUtilsTest, testGetJsonDoubleRequiredPropertyWrongType){ // Test with required property present with wrong type std::string valid_json = "{ \"some_property\": 1234}"; - auto parsed_doc = validate_json(valid_json); + auto parsed_doc = parse_json(valid_json); EXPECT_THROW( parse_double_member("some_property", parsed_doc, true), json_utils_exception); } TEST(JsonUtilsTest, testGetJsonDoubleOptionalPropertyMissing) { // Test with required property no present std::string valid_json = "{ \"some_property_other\": 12345}"; - auto parsed_doc = validate_json(valid_json); + auto parsed_doc = parse_json(valid_json); auto property = parse_double_member("some_property", parsed_doc, false); EXPECT_FALSE( property.has_value()); } @@ -147,28 +147,28 @@ TEST(JsonUtilsTest, testGetJsonDoubleOptionalPropertyMissing) { TEST(JsonUtilsTest, testGetJsonStringRequiredPropertyPresent){ // Test with required property present std::string valid_json = "{ \"some_property\": \"some_property\" }"; - auto parsed_doc = validate_json(valid_json); + auto parsed_doc = parse_json(valid_json); EXPECT_EQ("some_property", parse_string_member("some_property", parsed_doc, true)); } TEST(JsonUtilsTest, testGetJsonStringRequiredPropertyNotPresent){ // Test with required property no present std::string valid_json = "{ \"some_property_other\": 12.3}"; - auto parsed_doc = validate_json(valid_json); + auto parsed_doc = parse_json(valid_json); EXPECT_THROW( parse_string_member("some_property", parsed_doc, true), json_utils_exception); } TEST(JsonUtilsTest, testGetJsonStringRequiredPropertyWrongType){ // Test with required property present with wrong type std::string valid_json = "{ \"some_property\": 1234}"; - auto parsed_doc = validate_json(valid_json); + auto parsed_doc = parse_json(valid_json); EXPECT_THROW( parse_string_member("some_property", parsed_doc, true), json_utils_exception); } TEST(JsonUtilsTest, testGetJsonStringOptionalPropertyMissing) { // Test with required property no present std::string valid_json = "{ \"some_property_other\": 12345}"; - auto parsed_doc = validate_json(valid_json); + auto parsed_doc = parse_json(valid_json); auto property = parse_string_member("some_property", parsed_doc, false); EXPECT_FALSE( property.has_value()); } @@ -183,7 +183,7 @@ TEST(JsonUtilsTest, testGetJsonObjectRequiredPropertyPresent){ "\"object_value\" : 123" "}" "}"; - auto parsed_doc = validate_json(valid_json); + auto parsed_doc = parse_json(valid_json); auto object = parse_object_member("some_object", parsed_doc, true); EXPECT_EQ("object", parse_string_member("object_name", object.value(), true)); EXPECT_EQ( 123, parse_int_member("object_value", object.value(), true) ); @@ -198,7 +198,7 @@ TEST(JsonUtilsTest, testGetJsonObjectRequiredPropertyNotPresent){ "\"object_value\" : 123" "}" "}"; - auto parsed_doc = validate_json(valid_json); + auto parsed_doc = parse_json(valid_json); EXPECT_THROW( parse_object_member("some_object", parsed_doc, true), json_utils_exception); } @@ -210,14 +210,14 @@ TEST(JsonUtilsTest, testGetJsonObjectRequiredPropertyWrongType){ "\"object_value\" : 123" "}]" "}"; - auto parsed_doc = validate_json(valid_json); + auto parsed_doc = parse_json(valid_json); EXPECT_THROW( parse_object_member("some_object", parsed_doc, true), json_utils_exception); } TEST(JsonUtilsTest, testGetJsonObjectOptionalPropertyMissing) { // Test with required property no present std::string valid_json = "{ \"some_property_other\": 12345}"; - auto parsed_doc = validate_json(valid_json); + auto parsed_doc = parse_json(valid_json); auto property = parse_object_member("some_property", parsed_doc, false); EXPECT_FALSE( property.has_value()); } @@ -229,7 +229,7 @@ TEST(JsonUtilsTest, testGetJsonArrayRequiredPropertyPresent){ "\"some_array\": " "[456, 2452, -1232, 2345]" "}"; - auto parsed_doc = validate_json(valid_json); + auto parsed_doc = parse_json(valid_json); auto array = parse_array_member("some_array", parsed_doc, true); EXPECT_EQ(4, array.value().Size()); @@ -245,7 +245,7 @@ TEST(JsonUtilsTest, testGetJsonArrayRequiredPropertyNotPresent){ "\"some_other_array\": " "[456, 2452, -1232, 2345]" "}"; - auto parsed_doc = validate_json(valid_json); + auto parsed_doc = parse_json(valid_json); EXPECT_THROW( parse_array_member("some_array", parsed_doc, true), json_utils_exception); } @@ -257,14 +257,14 @@ TEST(JsonUtilsTest, testGetJsonArrayRequiredPropertyWrongType){ "\"object_value\" : 123" "}]" "}"; - auto parsed_doc = validate_json(valid_json); + auto parsed_doc = parse_json(valid_json); EXPECT_THROW( parse_array_member("some_object", parsed_doc, true), json_utils_exception); } TEST(JsonUtilsTest, testGetJsonArrayOptionalPropertyMissing) { // Test with required property no present std::string valid_json = "{ \"some_property_other\": 12345}"; - auto parsed_doc = validate_json(valid_json); + auto parsed_doc = parse_json(valid_json); auto property = parse_array_member("some_property", parsed_doc, false); EXPECT_FALSE( property.has_value()); } From 4a7bf4eca4c618131fec2035f8910187b8241692 Mon Sep 17 00:00:00 2001 From: dev Date: Mon, 2 Oct 2023 17:27:22 -0400 Subject: [PATCH 33/80] Update exception --- .../json_utils/include/json_utils.hpp | 32 +++++++++---------- .../include/json_utils_exception.hpp | 19 ++++------- streets_utils/json_utils/src/json_utils.cpp | 16 +++++----- .../json_utils/test/json_utils_test.cpp | 32 +++++++++---------- 4 files changed, 46 insertions(+), 53 deletions(-) diff --git a/streets_utils/json_utils/include/json_utils.hpp b/streets_utils/json_utils/include/json_utils.hpp index de2d61759..87006681f 100644 --- a/streets_utils/json_utils/include/json_utils.hpp +++ b/streets_utils/json_utils/include/json_utils.hpp @@ -13,94 +13,94 @@ namespace streets_utils::json_utils { /** * @brief Function to parse `std::string` JSON with [RapidJSON](https://miloyip.github.io/rapidjson/index.html) with - * [DOM parsing](https://miloyip.github.io/rapidjson/md_obj_dom.html). Throws `json_utils_exception` if string is + * [DOM parsing](https://miloyip.github.io/rapidjson/md_obj_dom.html). Throws `json_parse_exception` if string is * not valid json. Otherwise returns `rapidjson::Document` * @param json std::string JSON to parse - * @throws json_utils_exception if passed string is not valid json. + * @throws json_parse_exception if passed string is not valid json. * @return resulting `rapidjson::Document` */ rapidjson::Document parse_json( const std::string &json ); /** * @brief Functions to retrieve int member values from JSON object [RapidJSON](https://miloyip.github.io/rapidjson/index.html) * with [DOM parsing](https://miloyip.github.io/rapidjson/md_doc_dom.html). Functions will return unintialized - * `std::optional` for optional members that are not found in JSON object and will throw `json_utils_exception`. + * `std::optional` for optional members that are not found in JSON object and will throw `json_parse_exception`. * for required members that are not found in JSON object. * @param member_name string member name to search for. * @param obj JSON object with member * @param required bool flag to indicate whether member is required by calling code. - * @throws json_utils_exception if member is required but not found. + * @throws json_parse_exception if member is required but not found. * @return std::optional */ std::optional parse_int_member(const std::string &member_name, const rapidjson::Value &obj, bool required ); /** * @brief Functions to retrieve uint member values from JSON object [RapidJSON](https://miloyip.github.io/rapidjson/index.html) * with [DOM parsing](https://miloyip.github.io/rapidjson/md_doc_dom.html). Functions will return unintialized - * `std::optional` for optional members that are not found in JSON object and will throw `json_utils_exception`. + * `std::optional` for optional members that are not found in JSON object and will throw `json_parse_exception`. * for required members that are not found in JSON object. * @param member_name string member name to search for. * @param obj JSON object with member * @param required bool flag to indicate whether member is required by calling code. - * @throws json_utils_exception if member is required but not found. + * @throws json_parse_exception if member is required but not found. * @return std::optional */ std::optional parse_uint_member(const std::string &member_name, const rapidjson::Value &obj, bool required ); /** * @brief Functions to retrieve bool member values from JSON object [RapidJSON](https://miloyip.github.io/rapidjson/index.html) * with [DOM parsing](https://miloyip.github.io/rapidjson/md_doc_dom.html). Functions will return unintialized - * `std::optional` for optional members that are not found in JSON object and will throw `json_utils_exception`. + * `std::optional` for optional members that are not found in JSON object and will throw `json_parse_exception`. * for required members that are not found in JSON object. * @param member_name string member name to search for. * @param obj JSON object with member * @param required bool flag to indicate whether member is required by calling code. - * @throws json_utils_exception if member is required but not found. + * @throws json_parse_exception if member is required but not found. * @return std::optional */ std::optional parse_bool_member(const std::string &member_name, const rapidjson::Value &obj, bool required ); /** * @brief Functions to retrieve std::string member values from JSON object [RapidJSON](https://miloyip.github.io/rapidjson/index.html) * with [DOM parsing](https://miloyip.github.io/rapidjson/md_doc_dom.html). Functions will return unintialized - * `std::optional` for optional members that are not found in JSON object and will throw `json_utils_exception`. + * `std::optional` for optional members that are not found in JSON object and will throw `json_parse_exception`. * for required members that are not found in JSON object. * @param member_name string member name to search for. * @param obj JSON object with member * @param required bool flag to indicate whether member is required by calling code. - * @throws json_utils_exception if member is required but not found. + * @throws json_parse_exception if member is required but not found. * @return std::optional */ std::optional parse_string_member(const std::string &member_name, const rapidjson::Value &obj, bool required ); /** * @brief Functions to retrieve double member values from JSON object [RapidJSON](https://miloyip.github.io/rapidjson/index.html) * with [DOM parsing](https://miloyip.github.io/rapidjson/md_doc_dom.html). Functions will return unintialized - * `std::optional` for optional members that are not found in JSON object and will throw `json_utils_exception`. + * `std::optional` for optional members that are not found in JSON object and will throw `json_parse_exception`. * for required members that are not found in JSON object. * @param member_name string member name to search for. * @param obj JSON object with member * @param required bool flag to indicate whether member is required by calling code. - * @throws json_utils_exception if member is required but not found. + * @throws json_parse_exception if member is required but not found. * @return std::optional */ std::optional parse_double_member(const std::string &member_name, const rapidjson::Value &obj, bool required); /** * @brief Functions to retrieve object member values from JSON object [RapidJSON](https://miloyip.github.io/rapidjson/index.html) * with [DOM parsing](https://miloyip.github.io/rapidjson/md_doc_dom.html). Functions will return unintialized - * `std::optional` for optional members that are not found in JSON object and will throw `json_utils_exception`. + * `std::optional` for optional members that are not found in JSON object and will throw `json_parse_exception`. * for required members that are not found in JSON object. * @param member_name string member name to search for. * @param obj JSON object with member * @param required bool flag to indicate whether member is required by calling code. - * @throws json_utils_exception if member is required but not found. + * @throws json_parse_exception if member is required but not found. * @return std::optional */ std::optional parse_object_member(const std::string &member_name, const rapidjson::Value &obj, bool required ); /** * @brief Functions to retrieve array member values from JSON object [RapidJSON](https://miloyip.github.io/rapidjson/index.html) * with [DOM parsing](https://miloyip.github.io/rapidjson/md_doc_dom.html). Functions will return unintialized - * `std::optional` for optional members that are not found in JSON object and will throw `json_utils_exception`. + * `std::optional` for optional members that are not found in JSON object and will throw `json_parse_exception`. * for required members that are not found in JSON object. * @param member_name string member name to search for. * @param obj JSON object with member * @param required bool flag to indicate whether member is required by calling code. - * @throws json_utils_exception if member is required but not found. + * @throws json_parse_exception if member is required but not found. * @return std::optional */ std::optional parse_array_member(const std::string &member_name, const rapidjson::Value &obj, bool required); diff --git a/streets_utils/json_utils/include/json_utils_exception.hpp b/streets_utils/json_utils/include/json_utils_exception.hpp index ec6993817..b1c611d4b 100644 --- a/streets_utils/json_utils/include/json_utils_exception.hpp +++ b/streets_utils/json_utils/include/json_utils_exception.hpp @@ -6,21 +6,14 @@ namespace streets_utils::json_utils { /** - * @brief Runtime error related to processing signal_phase_and_timing JSON updates. + * @brief Runtime exception related to json_utils functions. Thrown when : + * - Passing Invalid JSON + * - Missing Required member + * - Passing rapidjson::Value that is not an object into parse functions * * @author Paul Bourelly */ - class json_utils_exception : public std::runtime_error{ - public: - /** - * @brief Destructor. - */ - ~json_utils_exception() = default; - /** - * @brief Constructor. - * @param msg String exception message. - */ - explicit json_utils_exception(const std::string &msg ) : std::runtime_error(msg){}; - + class json_parse_exception : public std::runtime_error{ + using std::runtime_error::runtime_error; }; } \ No newline at end of file diff --git a/streets_utils/json_utils/src/json_utils.cpp b/streets_utils/json_utils/src/json_utils.cpp index 99133caec..218b3c9a6 100644 --- a/streets_utils/json_utils/src/json_utils.cpp +++ b/streets_utils/json_utils/src/json_utils.cpp @@ -7,7 +7,7 @@ namespace streets_utils::json_utils { obj.Parse(json); if (obj.HasParseError()) { - throw json_utils_exception("Message JSON is misformatted. JSON parsing failed!"); + throw json_parse_exception("Message JSON is misformatted. JSON parsing failed!"); } return obj; } @@ -19,7 +19,7 @@ namespace streets_utils::json_utils { } else if (required) { - throw json_utils_exception("Missing or incorrect type for required member " + member_name + "!"); + throw json_parse_exception("Missing or incorrect type for required member " + member_name + "!"); } return member; } @@ -32,7 +32,7 @@ namespace streets_utils::json_utils { } else if (required) { - throw json_utils_exception("Missing or incorrect type for required member " + member_name + "!"); + throw json_parse_exception("Missing or incorrect type for required member " + member_name + "!"); } return member; }; @@ -45,7 +45,7 @@ namespace streets_utils::json_utils { } else if (required) { - throw json_utils_exception("Missing or incorrect type for required member " + member_name + "!"); + throw json_parse_exception("Missing or incorrect type for required member " + member_name + "!"); } return member; }; @@ -58,7 +58,7 @@ namespace streets_utils::json_utils { } else if (required) { - throw json_utils_exception("Missing or incorrect type for required member " + member_name + "!"); + throw json_parse_exception("Missing or incorrect type for required member " + member_name + "!"); } return member; }; @@ -71,7 +71,7 @@ namespace streets_utils::json_utils { } else if (required) { - throw json_utils_exception("Missing or incorrect type for required member " + member_name + "!"); + throw json_parse_exception("Missing or incorrect type for required member " + member_name + "!"); } return member; }; @@ -83,7 +83,7 @@ namespace streets_utils::json_utils { } else if (required) { - throw json_utils_exception("Missing or incorrect type for required member " + member_name + "!"); + throw json_parse_exception("Missing or incorrect type for required member " + member_name + "!"); } return std::nullopt; } @@ -95,7 +95,7 @@ namespace streets_utils::json_utils { } else if (required) { - throw json_utils_exception("Missing or incorrect type for required member " + member_name + "!"); + throw json_parse_exception("Missing or incorrect type for required member " + member_name + "!"); } return std::nullopt; } diff --git a/streets_utils/json_utils/test/json_utils_test.cpp b/streets_utils/json_utils/test/json_utils_test.cpp index a888c02b4..5de160d62 100644 --- a/streets_utils/json_utils/test/json_utils_test.cpp +++ b/streets_utils/json_utils/test/json_utils_test.cpp @@ -8,11 +8,11 @@ using namespace streets_utils::json_utils; TEST(JsonUtilsTest, testParseJsonInvalidJson) { // Empty String std::string invalid_json = ""; - EXPECT_THROW( parse_json(invalid_json), json_utils_exception); + EXPECT_THROW( parse_json(invalid_json), json_parse_exception); // Property missing quotations invalid_json = "{ some_propert: \"some_value\"}"; - EXPECT_THROW( parse_json(invalid_json), json_utils_exception); + EXPECT_THROW( parse_json(invalid_json), json_parse_exception); } @@ -34,14 +34,14 @@ TEST(JsonUtilsTest, testGetJsonUintRequiredPropertyNotPresent){ // Test with required property no present std::string valid_json = "{ \"some_property_other\": 12345}"; auto parsed_doc = parse_json(valid_json); - EXPECT_THROW( parse_uint_member("some_property", parsed_doc, true), json_utils_exception); + EXPECT_THROW( parse_uint_member("some_property", parsed_doc, true), json_parse_exception); } TEST(JsonUtilsTest, testGetJsonUintRequiredPropertyWrongType){ // Test with required property present with wrong type std::string valid_json = "{ \"some_property\": -12345}"; auto parsed_doc = parse_json(valid_json); - EXPECT_THROW( parse_uint_member("some_property", parsed_doc, true), json_utils_exception); + EXPECT_THROW( parse_uint_member("some_property", parsed_doc, true), json_parse_exception); } TEST(JsonUtilsTest, testGetJsonUintOptionalPropertyMissing) { @@ -64,14 +64,14 @@ TEST(JsonUtilsTest, testGetJsonIntRequiredPropertyNotPresent){ // Test with required property no present std::string valid_json = "{ \"some_property_other\": 12345}"; auto parsed_doc = parse_json(valid_json); - EXPECT_THROW( parse_int_member("some_property", parsed_doc, true), json_utils_exception); + EXPECT_THROW( parse_int_member("some_property", parsed_doc, true), json_parse_exception); } TEST(JsonUtilsTest, testGetJsonIntRequiredPropertyWrongType){ // Test with required property present with wrong type std::string valid_json = "{ \"some_property\": true}"; auto parsed_doc = parse_json(valid_json); - EXPECT_THROW( parse_int_member("some_property", parsed_doc, true), json_utils_exception); + EXPECT_THROW( parse_int_member("some_property", parsed_doc, true), json_parse_exception); } TEST(JsonUtilsTest, testGetJsonIntOptionalPropertyMissing) { @@ -94,14 +94,14 @@ TEST(JsonUtilsTest, testGetJsonBoolRequiredPropertyNotPresent){ // Test with required property no present std::string valid_json = "{ \"some_property_other\": true}"; auto parsed_doc = parse_json(valid_json); - EXPECT_THROW( parse_bool_member("some_property", parsed_doc, true), json_utils_exception); + EXPECT_THROW( parse_bool_member("some_property", parsed_doc, true), json_parse_exception); } TEST(JsonUtilsTest, testGetJsonBoolRequiredPropertyWrongType){ // Test with required property present with wrong type std::string valid_json = "{ \"some_property\": 1234}"; auto parsed_doc = parse_json(valid_json); - EXPECT_THROW( parse_bool_member("some_property", parsed_doc, true), json_utils_exception); + EXPECT_THROW( parse_bool_member("some_property", parsed_doc, true), json_parse_exception); } TEST(JsonUtilsTest, testGetJsonBoolOptionalPropertyMissing) { @@ -125,14 +125,14 @@ TEST(JsonUtilsTest, testGetJsonDoubleRequiredPropertyNotPresent){ // Test with required property no present std::string valid_json = "{ \"some_property_other\": 12.3}"; auto parsed_doc = parse_json(valid_json); - EXPECT_THROW( parse_double_member("some_property", parsed_doc, true), json_utils_exception); + EXPECT_THROW( parse_double_member("some_property", parsed_doc, true), json_parse_exception); } TEST(JsonUtilsTest, testGetJsonDoubleRequiredPropertyWrongType){ // Test with required property present with wrong type std::string valid_json = "{ \"some_property\": 1234}"; auto parsed_doc = parse_json(valid_json); - EXPECT_THROW( parse_double_member("some_property", parsed_doc, true), json_utils_exception); + EXPECT_THROW( parse_double_member("some_property", parsed_doc, true), json_parse_exception); } TEST(JsonUtilsTest, testGetJsonDoubleOptionalPropertyMissing) { @@ -155,14 +155,14 @@ TEST(JsonUtilsTest, testGetJsonStringRequiredPropertyNotPresent){ // Test with required property no present std::string valid_json = "{ \"some_property_other\": 12.3}"; auto parsed_doc = parse_json(valid_json); - EXPECT_THROW( parse_string_member("some_property", parsed_doc, true), json_utils_exception); + EXPECT_THROW( parse_string_member("some_property", parsed_doc, true), json_parse_exception); } TEST(JsonUtilsTest, testGetJsonStringRequiredPropertyWrongType){ // Test with required property present with wrong type std::string valid_json = "{ \"some_property\": 1234}"; auto parsed_doc = parse_json(valid_json); - EXPECT_THROW( parse_string_member("some_property", parsed_doc, true), json_utils_exception); + EXPECT_THROW( parse_string_member("some_property", parsed_doc, true), json_parse_exception); } TEST(JsonUtilsTest, testGetJsonStringOptionalPropertyMissing) { @@ -199,7 +199,7 @@ TEST(JsonUtilsTest, testGetJsonObjectRequiredPropertyNotPresent){ "}" "}"; auto parsed_doc = parse_json(valid_json); - EXPECT_THROW( parse_object_member("some_object", parsed_doc, true), json_utils_exception); + EXPECT_THROW( parse_object_member("some_object", parsed_doc, true), json_parse_exception); } TEST(JsonUtilsTest, testGetJsonObjectRequiredPropertyWrongType){ @@ -211,7 +211,7 @@ TEST(JsonUtilsTest, testGetJsonObjectRequiredPropertyWrongType){ "}]" "}"; auto parsed_doc = parse_json(valid_json); - EXPECT_THROW( parse_object_member("some_object", parsed_doc, true), json_utils_exception); + EXPECT_THROW( parse_object_member("some_object", parsed_doc, true), json_parse_exception); } TEST(JsonUtilsTest, testGetJsonObjectOptionalPropertyMissing) { @@ -246,7 +246,7 @@ TEST(JsonUtilsTest, testGetJsonArrayRequiredPropertyNotPresent){ "[456, 2452, -1232, 2345]" "}"; auto parsed_doc = parse_json(valid_json); - EXPECT_THROW( parse_array_member("some_array", parsed_doc, true), json_utils_exception); + EXPECT_THROW( parse_array_member("some_array", parsed_doc, true), json_parse_exception); } TEST(JsonUtilsTest, testGetJsonArrayRequiredPropertyWrongType){ @@ -258,7 +258,7 @@ TEST(JsonUtilsTest, testGetJsonArrayRequiredPropertyWrongType){ "}]" "}"; auto parsed_doc = parse_json(valid_json); - EXPECT_THROW( parse_array_member("some_object", parsed_doc, true), json_utils_exception); + EXPECT_THROW( parse_array_member("some_object", parsed_doc, true), json_parse_exception); } TEST(JsonUtilsTest, testGetJsonArrayOptionalPropertyMissing) { From f37d68aa9b6bfdb35d2fa03389df1190cff6d622 Mon Sep 17 00:00:00 2001 From: dev Date: Tue, 3 Oct 2023 11:00:23 -0400 Subject: [PATCH 34/80] Update CMake to rename variables to avoid name conflicts with ingesting projects --- streets_utils/json_utils/CMakeLists.txt | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/streets_utils/json_utils/CMakeLists.txt b/streets_utils/json_utils/CMakeLists.txt index 31bfd6025..373193149 100644 --- a/streets_utils/json_utils/CMakeLists.txt +++ b/streets_utils/json_utils/CMakeLists.txt @@ -15,8 +15,8 @@ set(LIBRARY_NAME ${PROJECT_NAME}_lib) ######################################################## # Build Library ######################################################## -file(GLOB SOURCES ${CMAKE_CURRENT_SOURCE_DIR}/src/*.cpp) -add_library(${LIBRARY_NAME} ${SOURCES} ) +file(GLOB JSON_UTILS_SOURCES ${CMAKE_CURRENT_SOURCE_DIR}/src/*.cpp) +add_library(${LIBRARY_NAME} ${JSON_UTILS_SOURCES} ) target_include_directories(${LIBRARY_NAME} PUBLIC $) @@ -29,7 +29,7 @@ target_link_libraries( ${LIBRARY_NAME} ######################################################## # Install streets_utils::json_utils_lib library. ######################################################## -file(GLOB files ${CMAKE_CURRENT_SOURCE_DIR}/include/*.hpp) +file(GLOB JSON_UTILS_HEADERS ${CMAKE_CURRENT_SOURCE_DIR}/include/*.hpp) install( TARGETS ${LIBRARY_NAME} @@ -53,14 +53,14 @@ install( FILES ${CMAKE_CURRENT_BINARY_DIR}/${LIBRARY_NAME}Config.cmake DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/cmake/${LIBRARY_NAME} ) -install(FILES ${files} DESTINATION include/streets_utils/${LIBRARY_NAME}) +install(FILES ${JSON_UTILS_HEADERS} DESTINATION include/streets_utils/${LIBRARY_NAME}) ######################## # googletest for unit testing ######################## set(TEST_NAME ${PROJECT_NAME}_test) -file(GLOB_RECURSE TEST_SOURCES LIST_DIRECTORIES false test/*.hpp test/*.cpp) -add_executable(${TEST_NAME} ${TEST_SOURCES}) +file(GLOB_RECURSE JSON_UTILS_TEST_SOURCES LIST_DIRECTORIES false test/*.hpp test/*.cpp) +add_executable(${TEST_NAME} ${JSON_UTILS_TEST_SOURCES}) gtest_discover_tests(${TEST_NAME}) target_link_libraries(${TEST_NAME} PRIVATE From d7ff7087b780ec3553f2b2e21ec8bf1fd421e798 Mon Sep 17 00:00:00 2001 From: dev Date: Tue, 10 Oct 2023 12:54:50 -0400 Subject: [PATCH 35/80] Update json_utils methods to use C++ primitives instead of fix width std lib --- streets_utils/json_utils/include/json_utils.hpp | 4 ++-- streets_utils/json_utils/src/json_utils.cpp | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/streets_utils/json_utils/include/json_utils.hpp b/streets_utils/json_utils/include/json_utils.hpp index 87006681f..57db4f65b 100644 --- a/streets_utils/json_utils/include/json_utils.hpp +++ b/streets_utils/json_utils/include/json_utils.hpp @@ -31,7 +31,7 @@ namespace streets_utils::json_utils { * @throws json_parse_exception if member is required but not found. * @return std::optional */ - std::optional parse_int_member(const std::string &member_name, const rapidjson::Value &obj, bool required ); + std::optional parse_int_member(const std::string &member_name, const rapidjson::Value &obj, bool required ); /** * @brief Functions to retrieve uint member values from JSON object [RapidJSON](https://miloyip.github.io/rapidjson/index.html) * with [DOM parsing](https://miloyip.github.io/rapidjson/md_doc_dom.html). Functions will return unintialized @@ -43,7 +43,7 @@ namespace streets_utils::json_utils { * @throws json_parse_exception if member is required but not found. * @return std::optional */ - std::optional parse_uint_member(const std::string &member_name, const rapidjson::Value &obj, bool required ); + std::optional parse_uint_member(const std::string &member_name, const rapidjson::Value &obj, bool required ); /** * @brief Functions to retrieve bool member values from JSON object [RapidJSON](https://miloyip.github.io/rapidjson/index.html) * with [DOM parsing](https://miloyip.github.io/rapidjson/md_doc_dom.html). Functions will return unintialized diff --git a/streets_utils/json_utils/src/json_utils.cpp b/streets_utils/json_utils/src/json_utils.cpp index 218b3c9a6..6d4550967 100644 --- a/streets_utils/json_utils/src/json_utils.cpp +++ b/streets_utils/json_utils/src/json_utils.cpp @@ -11,7 +11,7 @@ namespace streets_utils::json_utils { } return obj; } - std::optional parse_int_member(const std::string &member_name, const rapidjson::Value &obj, bool required ){ + std::optional parse_int_member(const std::string &member_name, const rapidjson::Value &obj, bool required ){ std::optional member; if (obj.HasMember(member_name.c_str()) && obj.FindMember(member_name.c_str())->value.IsInt64()) { @@ -24,7 +24,7 @@ namespace streets_utils::json_utils { return member; } - std::optional parse_uint_member(const std::string &member_name, const rapidjson::Value &obj, bool required ) { + std::optional parse_uint_member(const std::string &member_name, const rapidjson::Value &obj, bool required ) { std::optional member; if (obj.HasMember(member_name.c_str()) && obj.FindMember(member_name.c_str())->value.IsUint64()) { From 677d200668ca985bd2f359833f92b8b1a8ad0c66 Mon Sep 17 00:00:00 2001 From: dev Date: Tue, 10 Oct 2023 13:01:02 -0400 Subject: [PATCH 36/80] Update Javadoc comment --- streets_utils/json_utils/include/json_utils.hpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/streets_utils/json_utils/include/json_utils.hpp b/streets_utils/json_utils/include/json_utils.hpp index 57db4f65b..edcf70bcd 100644 --- a/streets_utils/json_utils/include/json_utils.hpp +++ b/streets_utils/json_utils/include/json_utils.hpp @@ -29,7 +29,7 @@ namespace streets_utils::json_utils { * @param obj JSON object with member * @param required bool flag to indicate whether member is required by calling code. * @throws json_parse_exception if member is required but not found. - * @return std::optional + * @return std::optional */ std::optional parse_int_member(const std::string &member_name, const rapidjson::Value &obj, bool required ); /** @@ -41,7 +41,7 @@ namespace streets_utils::json_utils { * @param obj JSON object with member * @param required bool flag to indicate whether member is required by calling code. * @throws json_parse_exception if member is required but not found. - * @return std::optional + * @return std::optional */ std::optional parse_uint_member(const std::string &member_name, const rapidjson::Value &obj, bool required ); /** From be2068ddf1fa8bc3cfda306192d5e1c8f3f74a57 Mon Sep 17 00:00:00 2001 From: dev Date: Wed, 11 Oct 2023 12:39:36 -0400 Subject: [PATCH 37/80] Update to use unsigned int instead of typdef uint --- streets_utils/json_utils/include/json_utils.hpp | 4 ++-- streets_utils/json_utils/src/json_utils.cpp | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/streets_utils/json_utils/include/json_utils.hpp b/streets_utils/json_utils/include/json_utils.hpp index edcf70bcd..f4086190c 100644 --- a/streets_utils/json_utils/include/json_utils.hpp +++ b/streets_utils/json_utils/include/json_utils.hpp @@ -41,9 +41,9 @@ namespace streets_utils::json_utils { * @param obj JSON object with member * @param required bool flag to indicate whether member is required by calling code. * @throws json_parse_exception if member is required but not found. - * @return std::optional + * @return std::optional */ - std::optional parse_uint_member(const std::string &member_name, const rapidjson::Value &obj, bool required ); + std::optional parse_uint_member(const std::string &member_name, const rapidjson::Value &obj, bool required ); /** * @brief Functions to retrieve bool member values from JSON object [RapidJSON](https://miloyip.github.io/rapidjson/index.html) * with [DOM parsing](https://miloyip.github.io/rapidjson/md_doc_dom.html). Functions will return unintialized diff --git a/streets_utils/json_utils/src/json_utils.cpp b/streets_utils/json_utils/src/json_utils.cpp index 6d4550967..56793c501 100644 --- a/streets_utils/json_utils/src/json_utils.cpp +++ b/streets_utils/json_utils/src/json_utils.cpp @@ -24,7 +24,7 @@ namespace streets_utils::json_utils { return member; } - std::optional parse_uint_member(const std::string &member_name, const rapidjson::Value &obj, bool required ) { + std::optional parse_uint_member(const std::string &member_name, const rapidjson::Value &obj, bool required ) { std::optional member; if (obj.HasMember(member_name.c_str()) && obj.FindMember(member_name.c_str())->value.IsUint64()) { From 1f5bee0a80ea15acd2f062c77b06d81d846aed9d Mon Sep 17 00:00:00 2001 From: dev Date: Tue, 17 Oct 2023 11:02:30 -0400 Subject: [PATCH 38/80] Updated workflow to avoid duplicate actions on PR --- .github/workflows/ci.yml | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 9cfab4b0a..d228a2b22 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -1,7 +1,9 @@ name: CI on: - push: pull_request: + types: [opened, synchronize, reopened] + push: + branches: [develop, master] jobs: build: defaults: From d35574c8df8b464ec61f029100a6bddbb5804164 Mon Sep 17 00:00:00 2001 From: Kyle Rush Date: Thu, 19 Oct 2023 10:29:37 -0400 Subject: [PATCH 39/80] Remove height and cost-model from RouteGraphBuilder call (#358) --- intersection_model/src/intersection_model.cpp | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) diff --git a/intersection_model/src/intersection_model.cpp b/intersection_model/src/intersection_model.cpp index 24141af2c..4e45eea0b 100755 --- a/intersection_model/src/intersection_model.cpp +++ b/intersection_model/src/intersection_model.cpp @@ -81,12 +81,9 @@ namespace intersection_model lanelet::traffic_rules::TrafficRulesFactory::create(lanelet::Locations::Germany, lanelet::Participants::Vehicle, lanelet::traffic_rules::TrafficRules::Configuration())}; - lanelet::routing::RoutingCostPtrs costPtrs{ - std::make_shared(this->laneChangeCost, this->minLaneChangeLength), - std::make_shared(this->laneChangeCost)}; - lanelet::routing::RoutingGraph::Configuration configuration; - configuration.insert(std::make_pair(lanelet::routing::RoutingGraph::ParticipantHeight, this->participantHeight)); - this->vehicleGraph_ptr = lanelet::routing::RoutingGraph::build(*this->map, *trafficRules, costPtrs, configuration); + + this->vehicleGraph_ptr = lanelet::routing::RoutingGraph::build(*this->map, *trafficRules); + if ( !this->vehicleGraph_ptr ) { return false; @@ -646,4 +643,4 @@ namespace intersection_model int_info.entering_lanelets_info.clear(); int_info.departure_lanelets_info.clear(); } -} \ No newline at end of file +} From 5ae3453a0e503fd47aa618f49344c9ae4cf960ef Mon Sep 17 00:00:00 2001 From: paulbourelly999 <77466294+paulbourelly999@users.noreply.github.com> Date: Fri, 20 Oct 2023 10:40:06 -0400 Subject: [PATCH 40/80] Adding streets_messages library which will house JSON serialization (#353) * Adding streets_messages library which will house JSON serialization/ deserialization functions and carma-streets messages. This is the new proposed package to store all carma-streets internal messages in. * Replace gnu int types with std int types * Adding package to CI process * Fix CI and cmake install * Update CMAke install * Update PR * Fix serialization * Updates to unit test and coverage script * Updated coverage script * Updated sonar project properties * Update unit tests * Update unit tests * Updated to use C++ primitives instead of std library fix with primitives * Updated unit test, Added deserialization logic * Update * Adding optional property serialization * serialization/deserialization of option fields * Update CMake * Update * New unit tests * Added license comment * Updates * Test Updates * Test updates * Unit test updates * Update to test for variant types * Update Serialization Unit testing * Update unit test for code coverage. * Update unit tests * Updates for unit test coverage * Time Confidence enumeration conversion unit testing * Added unit test for position confidence * Adding Unit tests * Update unit tests * Updated path and include calls * Added unit tests * Fix object * Update Unit tests * Update Unit tests and add license headers * Update documentation * PR Updates * Add documentation * cmake config updates * Correct CMake errors * Try throwing exception instead * Update elevation spelling * Update position_z offset to optional * Update enum conversion to throw exception for invalid values * Add license to CMake files * Added default values for required struct members to avoid gargbage initialization * Update msgcount to std::size_t --- .sonarqube/sonar-scanner.properties | 7 + build.sh | 3 +- coverage.sh | 7 + streets_utils/json_utils/CMakeLists.txt | 35 +- streets_utils/streets_messages/CMakeLists.txt | 85 ++ streets_utils/streets_messages/README.md | 44 + .../SensorDataSharingMessage.md | 93 ++ .../cmake/streets_messages_libConfig.cmake.in | 21 + ...sor_data_sharing_msg_json_deserializer.hpp | 70 ++ .../acceleration_confidence.hpp | 52 + .../acceleration_set_4_way.hpp | 36 + .../angular_velocity_confidence.hpp | 53 + .../detected_object_data.hpp | 33 + .../detected_object_data_common.hpp | 96 ++ .../equipment_type.hpp | 45 + .../heading_confidence.hpp | 53 + .../sensor_data_sharing_msg/object_type.hpp | 40 + .../obstacle/detected_obstacle_data.hpp | 29 + .../obstacle/obstacle_size.hpp | 33 + .../obstacle/obstacle_size_confidence.hpp | 32 + .../sensor_data_sharing_msg/position_3d.hpp | 38 + .../position_confidence.hpp | 78 ++ .../position_confidence_set.hpp | 24 + .../position_offset.hpp | 34 + .../positional_accuracy.hpp | 44 + .../sensor_data_sharing_msg.hpp | 49 + .../size_value_confidence.hpp | 69 ++ .../speed_confidence.hpp | 52 + .../time_confidence.hpp | 148 +++ .../sensor_data_sharing_msg/time_stamp.hpp | 47 + .../angular_velocity_confidence_set.hpp | 30 + .../vehicle/angular_velocity_set.hpp | 27 + .../vehicle/attitude.hpp | 32 + .../vehicle/attitude_confidence.hpp | 33 + .../vehicle/detected_vehicle_data.hpp | 79 ++ .../vehicle/vehicle_size.hpp | 28 + .../vehicle/vehicle_size_confidence.hpp | 32 + .../vru/animal_propelled_type.hpp | 41 + .../vru/attachment.hpp | 49 + .../vru/detected_vru_data.hpp | 44 + .../vru/human_propelled_type.hpp | 47 + .../vru/motorized_propelled_type.hpp | 46 + .../vru/personal_device_user_type.hpp | 44 + ...ensor_data_sharing_msg_json_serializer.hpp | 58 ++ ...sor_data_sharing_msg_json_deserializer.cpp | 300 ++++++ ...ensor_data_sharing_msg_json_serializer.cpp | 353 +++++++ ..._sharing_msg_json_deserialization_test.cpp | 464 +++++++++ .../acceleration_confidence_test.cpp | 31 + .../angular_velocity_confidence_test.cpp | 30 + .../animal_propelled_type_test.cpp | 28 + .../attachment_test.cpp | 29 + .../equipment_type_test.cpp | 26 + .../heading_confidence_test.cpp | 31 + .../human_propelled_type_test.cpp | 28 + .../motorized_propelled_type_test.cpp | 28 + .../object_type_test.cpp | 26 + .../personal_device_user_type.cpp | 27 + .../position_confidence_test.cpp | 38 + .../size_value_confidence_test.cpp | 36 + .../speed_confidence_test.cpp | 30 + .../time_confidence_test.cpp | 62 ++ ...ta_sharing_msg_json_serialization_test.cpp | 976 ++++++++++++++++++ 62 files changed, 4570 insertions(+), 13 deletions(-) create mode 100644 streets_utils/streets_messages/CMakeLists.txt create mode 100644 streets_utils/streets_messages/README.md create mode 100644 streets_utils/streets_messages/SensorDataSharingMessage.md create mode 100644 streets_utils/streets_messages/cmake/streets_messages_libConfig.cmake.in create mode 100644 streets_utils/streets_messages/include/deserializers/sensor_data_sharing_msg_json_deserializer.hpp create mode 100644 streets_utils/streets_messages/include/sensor_data_sharing_msg/acceleration_confidence.hpp create mode 100644 streets_utils/streets_messages/include/sensor_data_sharing_msg/acceleration_set_4_way.hpp create mode 100644 streets_utils/streets_messages/include/sensor_data_sharing_msg/angular_velocity_confidence.hpp create mode 100644 streets_utils/streets_messages/include/sensor_data_sharing_msg/detected_object_data.hpp create mode 100644 streets_utils/streets_messages/include/sensor_data_sharing_msg/detected_object_data_common.hpp create mode 100644 streets_utils/streets_messages/include/sensor_data_sharing_msg/equipment_type.hpp create mode 100644 streets_utils/streets_messages/include/sensor_data_sharing_msg/heading_confidence.hpp create mode 100644 streets_utils/streets_messages/include/sensor_data_sharing_msg/object_type.hpp create mode 100644 streets_utils/streets_messages/include/sensor_data_sharing_msg/obstacle/detected_obstacle_data.hpp create mode 100644 streets_utils/streets_messages/include/sensor_data_sharing_msg/obstacle/obstacle_size.hpp create mode 100644 streets_utils/streets_messages/include/sensor_data_sharing_msg/obstacle/obstacle_size_confidence.hpp create mode 100644 streets_utils/streets_messages/include/sensor_data_sharing_msg/position_3d.hpp create mode 100644 streets_utils/streets_messages/include/sensor_data_sharing_msg/position_confidence.hpp create mode 100644 streets_utils/streets_messages/include/sensor_data_sharing_msg/position_confidence_set.hpp create mode 100644 streets_utils/streets_messages/include/sensor_data_sharing_msg/position_offset.hpp create mode 100644 streets_utils/streets_messages/include/sensor_data_sharing_msg/positional_accuracy.hpp create mode 100644 streets_utils/streets_messages/include/sensor_data_sharing_msg/sensor_data_sharing_msg.hpp create mode 100644 streets_utils/streets_messages/include/sensor_data_sharing_msg/size_value_confidence.hpp create mode 100644 streets_utils/streets_messages/include/sensor_data_sharing_msg/speed_confidence.hpp create mode 100644 streets_utils/streets_messages/include/sensor_data_sharing_msg/time_confidence.hpp create mode 100644 streets_utils/streets_messages/include/sensor_data_sharing_msg/time_stamp.hpp create mode 100644 streets_utils/streets_messages/include/sensor_data_sharing_msg/vehicle/angular_velocity_confidence_set.hpp create mode 100644 streets_utils/streets_messages/include/sensor_data_sharing_msg/vehicle/angular_velocity_set.hpp create mode 100644 streets_utils/streets_messages/include/sensor_data_sharing_msg/vehicle/attitude.hpp create mode 100644 streets_utils/streets_messages/include/sensor_data_sharing_msg/vehicle/attitude_confidence.hpp create mode 100644 streets_utils/streets_messages/include/sensor_data_sharing_msg/vehicle/detected_vehicle_data.hpp create mode 100644 streets_utils/streets_messages/include/sensor_data_sharing_msg/vehicle/vehicle_size.hpp create mode 100644 streets_utils/streets_messages/include/sensor_data_sharing_msg/vehicle/vehicle_size_confidence.hpp create mode 100644 streets_utils/streets_messages/include/sensor_data_sharing_msg/vru/animal_propelled_type.hpp create mode 100644 streets_utils/streets_messages/include/sensor_data_sharing_msg/vru/attachment.hpp create mode 100644 streets_utils/streets_messages/include/sensor_data_sharing_msg/vru/detected_vru_data.hpp create mode 100644 streets_utils/streets_messages/include/sensor_data_sharing_msg/vru/human_propelled_type.hpp create mode 100644 streets_utils/streets_messages/include/sensor_data_sharing_msg/vru/motorized_propelled_type.hpp create mode 100644 streets_utils/streets_messages/include/sensor_data_sharing_msg/vru/personal_device_user_type.hpp create mode 100644 streets_utils/streets_messages/include/serializers/sensor_data_sharing_msg_json_serializer.hpp create mode 100644 streets_utils/streets_messages/src/deserializers/sensor_data_sharing_msg_json_deserializer.cpp create mode 100644 streets_utils/streets_messages/src/serializers/sensor_data_sharing_msg_json_serializer.cpp create mode 100644 streets_utils/streets_messages/test/deserializers/sensor_data_sharing_msg_json_deserialization_test.cpp create mode 100644 streets_utils/streets_messages/test/sensor_data_sharing_msg/acceleration_confidence_test.cpp create mode 100644 streets_utils/streets_messages/test/sensor_data_sharing_msg/angular_velocity_confidence_test.cpp create mode 100644 streets_utils/streets_messages/test/sensor_data_sharing_msg/animal_propelled_type_test.cpp create mode 100644 streets_utils/streets_messages/test/sensor_data_sharing_msg/attachment_test.cpp create mode 100644 streets_utils/streets_messages/test/sensor_data_sharing_msg/equipment_type_test.cpp create mode 100644 streets_utils/streets_messages/test/sensor_data_sharing_msg/heading_confidence_test.cpp create mode 100644 streets_utils/streets_messages/test/sensor_data_sharing_msg/human_propelled_type_test.cpp create mode 100644 streets_utils/streets_messages/test/sensor_data_sharing_msg/motorized_propelled_type_test.cpp create mode 100644 streets_utils/streets_messages/test/sensor_data_sharing_msg/object_type_test.cpp create mode 100644 streets_utils/streets_messages/test/sensor_data_sharing_msg/personal_device_user_type.cpp create mode 100644 streets_utils/streets_messages/test/sensor_data_sharing_msg/position_confidence_test.cpp create mode 100644 streets_utils/streets_messages/test/sensor_data_sharing_msg/size_value_confidence_test.cpp create mode 100644 streets_utils/streets_messages/test/sensor_data_sharing_msg/speed_confidence_test.cpp create mode 100644 streets_utils/streets_messages/test/sensor_data_sharing_msg/time_confidence_test.cpp create mode 100644 streets_utils/streets_messages/test/serializers/sensor_data_sharing_msg_json_serialization_test.cpp diff --git a/.sonarqube/sonar-scanner.properties b/.sonarqube/sonar-scanner.properties index 7ca171b75..7f09ea1da 100644 --- a/.sonarqube/sonar-scanner.properties +++ b/.sonarqube/sonar-scanner.properties @@ -36,6 +36,7 @@ sonar.coverageReportPaths= /home/carma-streets/kafka_clients/coverage/coverage.x /home/carma-streets/streets_utils/streets_timing_plan/coverage/coverage.xml, \ /home/carma-streets/streets_utils/streets_signal_optimization/coverage/coverage.xml, \ /home/carma-streets/streets_utils/streets_snmp_cmd/coverage/coverage.xml, \ +/home/carma-streets/streets_utils/streets_messages/coverage/coverage.xml, \ /home/carma-streets/streets_utils/json_utils/coverage/coverage.xml @@ -79,6 +80,7 @@ streets_vehicle_scheduler, \ streets_desired_phase_plan, \ streets_signal_optimization, \ streets_snmp_cmd, \ +streets_messages, \ json_utils # TODO message_services @@ -101,10 +103,12 @@ streets_signal_optimization.sonar.projectBaseDir=/home/carma-streets/streets_uti streets_phase_control_schedule.sonar.projectBaseDir=/home/carma-streets/streets_utils/streets_phase_control_schedule streets_timing_plan.sonar.projectBaseDir=/home/carma-streets/streets_utils/streets_timing_plan streets_snmp_cmd.sonar.projectBaseDir=/home/carma-streets/streets_utils/streets_snmp_cmd +streets_messages.sonar.projectBaseDir=/home/carma-streets/streets_utils/streets_messages json_utils.sonar.projectBaseDir=/home/carma-streets/streets_utils/json_utils + # C++ Package differences # Sources kafka_clients.sonar.sources =src/,include/ @@ -141,6 +145,8 @@ streets_timing_plan.sonar.sources =src/,include/ streets_timing_plan.sonar.exclusions =test/** streets_snmp_cmd.sonar.sources =src/,include/ streets_snmp_cmd.sonar.exclusions =test/** +streets_messages.sonar.sources =src/,include/ +streets_messages.sonar.exclusions =test/** json_utils.sonar.sources =src/,include/ json_utils.sonar.exclusions =test/** @@ -163,6 +169,7 @@ streets_signal_optimization.sonar.tests=test/ streets_phase_control_schedule.sonar.tests=test/ streets_timing_plan.sonar.tests=test/ streets_snmp_cmd.sonar.tests=test/ +streets_messages.sonar.tests=test/ json_utils.sonar.tests=test/ diff --git a/build.sh b/build.sh index a304b381a..2ed93113c 100755 --- a/build.sh +++ b/build.sh @@ -21,6 +21,8 @@ COVERAGE_FLAGS="-g --coverage -fprofile-arcs -ftest-coverage" # make install for these subdirectories MAKE_INSTALL_DIRS=( + "streets_utils/json_utils" + "streets_utils/streets_messages" "streets_utils/streets_service_configuration" "kafka_clients" "streets_utils/streets_service_base" @@ -35,7 +37,6 @@ MAKE_INSTALL_DIRS=( "streets_utils/streets_api/intersection_server_api" "streets_utils/streets_signal_optimization" "streets_utils/streets_snmp_cmd" - "streets_utils/json_utils" ) # only make for these subdirectories diff --git a/coverage.sh b/coverage.sh index 16ed29b07..f50ee43d3 100755 --- a/coverage.sh +++ b/coverage.sh @@ -26,6 +26,13 @@ mkdir coverage cd /home/carma-streets/ gcovr --sonarqube streets_utils/json_utils/coverage/coverage.xml -s -f streets_utils/json_utils/ -r . +cd /home/carma-streets/streets_utils/streets_messages/build/ +./streets_messages_lib_test --gtest_output=xml:../../../test_results/ +cd /home/carma-streets/streets_utils/streets_messages +mkdir coverage +cd /home/carma-streets/ +gcovr --sonarqube streets_utils/streets_messages/coverage/coverage.xml -s -f streets_utils/streets_messages/ + cd /home/carma-streets/streets_utils/streets_service_configuration/build/ ./streets_service_configuration_test --gtest_output=xml:../../../test_results/ cd /home/carma-streets/streets_utils/streets_service_configuration diff --git a/streets_utils/json_utils/CMakeLists.txt b/streets_utils/json_utils/CMakeLists.txt index 373193149..8db72efef 100644 --- a/streets_utils/json_utils/CMakeLists.txt +++ b/streets_utils/json_utils/CMakeLists.txt @@ -1,3 +1,16 @@ +# Copyright 2019-2023 Leidos +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http:www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. cmake_minimum_required(VERSION 3.10.2) project(json_utils) # Set Flags @@ -29,7 +42,6 @@ target_link_libraries( ${LIBRARY_NAME} ######################################################## # Install streets_utils::json_utils_lib library. ######################################################## -file(GLOB JSON_UTILS_HEADERS ${CMAKE_CURRENT_SOURCE_DIR}/include/*.hpp) install( TARGETS ${LIBRARY_NAME} @@ -41,20 +53,23 @@ install( install( EXPORT ${LIBRARY_NAME}Targets FILE ${LIBRARY_NAME}Targets.cmake - DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/cmake/${LIBRARY_NAME} + DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/${LIBRARY_NAME} NAMESPACE streets_utils:: ) include(CMakePackageConfigHelpers) configure_package_config_file( cmake/${LIBRARY_NAME}Config.cmake.in ${CMAKE_CURRENT_BINARY_DIR}/${LIBRARY_NAME}Config.cmake - INSTALL_DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/cmake/${LIBRARY_NAME}) + INSTALL_DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/${LIBRARY_NAME}) install( FILES ${CMAKE_CURRENT_BINARY_DIR}/${LIBRARY_NAME}Config.cmake - DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/cmake/${LIBRARY_NAME} + DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/${LIBRARY_NAME} ) -install(FILES ${JSON_UTILS_HEADERS} DESTINATION include/streets_utils/${LIBRARY_NAME}) +install(DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/include/ + DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/streets_utils/${LIBRARY_NAME} + FILES_MATCHING PATTERN "*.hpp" # select header files + ) ######################## # googletest for unit testing ######################## @@ -64,10 +79,6 @@ add_executable(${TEST_NAME} ${JSON_UTILS_TEST_SOURCES}) gtest_discover_tests(${TEST_NAME}) target_link_libraries(${TEST_NAME} PRIVATE - ${LIBRARY_NAME} -) -if(${CMAKE_VERSION} VERSION_GREATER_EQUAL "3.20") - target_link_libraries( ${TEST_NAME} PUBLIC GTest::gtest_main ) -else() - target_link_libraries( ${TEST_NAME} PUBLIC gtest_main ) -endif() \ No newline at end of file + ${LIBRARY_NAME} + GTest::Main + ) \ No newline at end of file diff --git a/streets_utils/streets_messages/CMakeLists.txt b/streets_utils/streets_messages/CMakeLists.txt new file mode 100644 index 000000000..f53daf8e6 --- /dev/null +++ b/streets_utils/streets_messages/CMakeLists.txt @@ -0,0 +1,85 @@ +# Copyright 2019-2023 Leidos +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http:www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +cmake_minimum_required(VERSION 3.10.2) +project(streets_messages) +# Set Flags +set(CMAKE_CXX_STANDARD 17) +# GNU standard installation directories +include(GNUInstallDirs) +# Find Packages +find_package(spdlog REQUIRED) +find_package(RapidJSON REQUIRED) +find_package(GTest REQUIRED) +find_package(json_utils_lib REQUIRED) +# Add definition for rapidjson to include std::string +add_definitions(-DRAPIDJSON_HAS_STDSTRING=1) +set(LIBRARY_NAME ${PROJECT_NAME}_lib) +######################################################## +# Build Library +######################################################## +file(GLOB STREETS_MESSAGES_SOURCES ${CMAKE_CURRENT_SOURCE_DIR}/src/**/*.cpp) +add_library(${LIBRARY_NAME} ${STREETS_MESSAGES_SOURCES} ) +target_include_directories(${LIBRARY_NAME} + PUBLIC + $ +) +target_link_libraries( ${LIBRARY_NAME} + PUBLIC + spdlog::spdlog + rapidjson + streets_utils::json_utils_lib +) +######################################################## +# Install streets_utils::streets_messages package. +######################################################## +install( + TARGETS ${LIBRARY_NAME} + EXPORT ${LIBRARY_NAME}Targets + LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}/streets_utils/ + INCLUDES DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/streets_utils/${LIBRARY_NAME} + ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR}/streets_utils/ +) +install( + EXPORT ${LIBRARY_NAME}Targets + FILE ${LIBRARY_NAME}Targets.cmake + DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/${LIBRARY_NAME} + NAMESPACE streets_utils:: +) +include(CMakePackageConfigHelpers) +configure_package_config_file( + cmake/${LIBRARY_NAME}Config.cmake.in + ${CMAKE_CURRENT_BINARY_DIR}/${LIBRARY_NAME}Config.cmake + INSTALL_DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/${LIBRARY_NAME}) +install( + FILES ${CMAKE_CURRENT_BINARY_DIR}/${LIBRARY_NAME}Config.cmake + DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/${LIBRARY_NAME} +) +install(DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/include/ + DESTINATION include/streets_utils/${LIBRARY_NAME} + FILES_MATCHING PATTERN "*.hpp" # install header files including package path + ) + +######################## +# Setup Test executable +######################## +set(TEST_NAME ${LIBRARY_NAME}_test) +file(GLOB_RECURSE TEST_SOURCES LIST_DIRECTORIES false test/**/*.hpp test/**/*.cpp) +set(SOURCES ${TEST_SOURCES} WORKING_DIRECTORY ${PROJECT_SOURCE_DIR}/test) +add_executable(${TEST_NAME} ${TEST_SOURCES}) +add_test(NAME ${TEST_NAME} COMMAND ${TEST_NAME}) +target_link_libraries(${TEST_NAME} + PRIVATE + ${LIBRARY_NAME} + GTest::Main + ) diff --git a/streets_utils/streets_messages/README.md b/streets_utils/streets_messages/README.md new file mode 100644 index 000000000..2947d1050 --- /dev/null +++ b/streets_utils/streets_messages/README.md @@ -0,0 +1,44 @@ +# Streets Messages Library + +## Introduction + +This CARMA-Streets library will be the new location for storing both CARMA Streets message data types as well as the logic to serialize and deserialize these messages. Message data types will be stored in directories with the message name as the directory name similar to the current `sensor_data_sharing_msg`. Then functions will hold the JSON serialization and deserialization logic for each message to seperate concerns betweening message serialization and message data. +> [!IMPORTANT]\ +> Currently this library only contains the sensor_data sharing message and its JSON serialization logic. Will attempt to part existing and future messages into this library. + +## Messages +**[Sensor Data Sharing Message](SensorDataSharingMessage.md)** : J3224 Message used for sharing detection level data between vehicles and infrastructure. +## Serialization and Deserialization + +CARMA Streets messages are currently all using JSON for serialization for sending via a Apache Kafka broker. To parse or write the JSON we are currently using the [rapidjson library](https://github.com/Tencent/rapidjson). In addition, we have implemented some helper functions to parse optional fields from JSON using the [std::optional](https://en.cppreference.com/w/cpp/utility/optional) class template implemented in our [json_utils library](https://github.com/usdot-fhwa-stol/carma-streets/tree/develop/streets_utils/json_utils). + +## Some future planned improvements to this library include: +- Porting existing CARMA Streets message JSON serialization/deserialization into library functions and out of message data type definitions +- introduce "compile time inheritance" for serialization and deserialization to improve ease of use (https://github.com/usdot-fhwa-stol/carma-streets/issues/356) + +> [!IMPORTANT]\ +> This is an improvement but also a deviation from how messages were previously processed in which message data types were couple to serialization. To remove this coupling new library will separate message data types from functions that serialize/deserialize them. This provides multiple benefits. By separating the logic the serialization logic from the message data types this improves maintainability by allowing us to potentially change serialization without any direct impact on message types or logic using message data. It also reduces the number of packages dependent on external libraries related to serialization like rapidjson. + +## Installation +This library includes a CMake install target that will attempt to install this library, including CMake configuration files under the following general path: + +`streets_utils\streets_messages_lib` + +Header files will be installed in the CMake default include directory + +`include\streets_utils\streets_messages_lib\` + +CMake configuration files will be installed under CMake default install directory + +`cmake\streets_messages_lib\` + +And the library binary will be installed under the CMake default Library directory + +`lib\streets_utils\` + +## Using Library + +To incorporate this library into a CARMA Streets Service simply install the library using `make install` and then use the CMake `find_package(streets_messages REQUIRED)` call to find the install package. To link this library include the streets utils namespace as follows: +``` +target_link_libraries( PUBLIC streets_utils::streets_messages) +``` \ No newline at end of file diff --git a/streets_utils/streets_messages/SensorDataSharingMessage.md b/streets_utils/streets_messages/SensorDataSharingMessage.md new file mode 100644 index 000000000..061b0e358 --- /dev/null +++ b/streets_utils/streets_messages/SensorDataSharingMessage.md @@ -0,0 +1,93 @@ +# Sensor Data Sharing Message + +## Introduction + +This CARMA-Streets library contains the Sensor Data Sharing Object as well as the logic for serailizing and deserializing this object to and from JSON. Below is an example JSON payload. This message will be produced by the **Sensor Data Sharing** CARMA Streets micro-service. The data in it will represent the incoming **Sensor Detected Object** detection. This message will be generated inline with the SAE J3224 document. +> [!IMPORTANT]\ +> The first implementation of the SDSM generator will not include functionality to filter out detections that are likely self reporting actors publishing BSMs like described in the J3224 202208 document. +## Example JSON payload +``` +{ + "msg_cnt": 1, + "source_id": "00000001", + "equipment_type": 2, + "sdsm_time_stamp": { + "second": 0, + "minute": 0, + "hour": 0, + "day": 0, + "month": 0, + "year": 0, + "offset": -32767 + }, + "ref_pos": { + "long": -1800000000, + "lat": -90000000 + }, + "ref_pos_xy_conf": { + "semi_major": 0, + "semi_minor": 0, + "orientation": 0 + }, + "objects": [ + { + "detected_object_common_data": { + "obj_type": 1, + "object_id": 0, + "obj_type_cfd": 0, + "measurement_time": -1500, + "time_confidence": 39, + "pos": { + "offset_x": -32767, + "offset_y": -32767, + "offset_z": -32767 + }, + "pos_confidence": { + "pos": 1, + "elevation": 15 + }, + "speed": 0, + "speed_confidence": 21845, + "heading": 0, + "heading_conf": 7 + }, + "detected_object_optional_data": { + "detected_vehicle_data": { + "lights": "11110000", + "veh_attitude": { + "pitch": 72000, + "roll": 14400, + "yaw": 14400 + }, + "veh_attitude_confidence": { + "pitch_confidence": 5, + "roll_confidence": 1, + "yaw_confidence": 3 + }, + "veh_ang_vel": { + "pitch_rate": 32767, + "roll_rate": 32767 + }, + "veh_ang_vel_confidence": { + "pitch_rate_confidence": 4 + }, + "size": { + "width": 4095, + "length": 4095 + }, + "vehicle_size_confidence": { + "vehicle_width_confidence": 13, + "vehicle_length_confidence": 13, + "vehicle_height_confidence": 13 + }, + "height": 127, + "vehicle_class": 23, + "class_conf": 101 + } + } + } + ] +} +``` + + diff --git a/streets_utils/streets_messages/cmake/streets_messages_libConfig.cmake.in b/streets_utils/streets_messages/cmake/streets_messages_libConfig.cmake.in new file mode 100644 index 000000000..0b441c5ee --- /dev/null +++ b/streets_utils/streets_messages/cmake/streets_messages_libConfig.cmake.in @@ -0,0 +1,21 @@ +# Copyright 2019-2023 Leidos +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http:www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +@PACKAGE_INIT@ + +include(CMakeFindDependencyMacro) +find_dependency(spdlog REQUIRED) +find_dependency(RapidJSON REQUIRED) +find_dependency(json_utils_lib REQUIRED) + +include("${CMAKE_CURRENT_LIST_DIR}/streets_messages_libTargets.cmake") diff --git a/streets_utils/streets_messages/include/deserializers/sensor_data_sharing_msg_json_deserializer.hpp b/streets_utils/streets_messages/include/deserializers/sensor_data_sharing_msg_json_deserializer.hpp new file mode 100644 index 000000000..150cadaee --- /dev/null +++ b/streets_utils/streets_messages/include/deserializers/sensor_data_sharing_msg_json_deserializer.hpp @@ -0,0 +1,70 @@ +// Copyright 2019-2023 Leidos +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +#pragma once +#include +#include +#include +#include +#include +#include +#include +#include "sensor_data_sharing_msg/sensor_data_sharing_msg.hpp" + +namespace streets_utils::messages { + sensor_data_sharing_msg from_json( const std::string &val); + + time_stamp parse_time_stamp(const rapidjson::Value &val); + + position_3d parse_position_3d(const rapidjson::Value &val); + + positional_accuracy parse_positional_accuracy(const rapidjson::Value &val); + + std::optional parse_elevation_confidence(const rapidjson::Value &val); + + std::vector parse_detected_object_list(const rapidjson::Value &val); + + detected_object_data_common parse_detected_object_data_common(const rapidjson::Value &val); + + acceleration_set_4_way parse_acceleration_4_way(const rapidjson::Value &val); + + std::optional> parse_detected_object_data_optional(const rapidjson::Value &val); + + position_offset parse_position_offset(const rapidjson::Value &val); + + position_confidence_set parse_position_confidence_set(const rapidjson::Value &val); + + detected_obstacle_data parse_detected_obstacle_data(const rapidjson::Value &val); + + detected_vehicle_data parse_detected_vehicle_data(const rapidjson::Value &val); + + detected_vru_data parse_detected_vru_data(const rapidjson::Value &val); + + obstacle_size parse_obstacle_size(const rapidjson::Value &val); + + obstacle_size_confidence parse_obstacle_size_confidence(const rapidjson::Value &val); + + std::optional> parse_propelled_information( const rapidjson::Value &val); + + attitude parse_vehicle_attitude(const rapidjson::Value &val); + + attitude_confidence parse_vehicle_attitude_confidence(const rapidjson::Value &val); + + angular_velocity_set parse_vehicle_angular_velocity_set(const rapidjson::Value &val); + + angular_velocity_confidence_set parse_vehicle_angular_velocity_confidence_set(const rapidjson::Value &val); + + vehicle_size parse_vehicle_size(const rapidjson::Value &val); + + vehicle_size_confidence parse_vehicle_size_confidence(const rapidjson::Value &val); +} \ No newline at end of file diff --git a/streets_utils/streets_messages/include/sensor_data_sharing_msg/acceleration_confidence.hpp b/streets_utils/streets_messages/include/sensor_data_sharing_msg/acceleration_confidence.hpp new file mode 100644 index 000000000..0d287b340 --- /dev/null +++ b/streets_utils/streets_messages/include/sensor_data_sharing_msg/acceleration_confidence.hpp @@ -0,0 +1,52 @@ +// Copyright 2023 Leidos +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +#pragma once + +namespace streets_utils::messages{ + enum class acceleration_confidence { + UNAVAILABLE = 0, // Not available + ACCL_100 = 1, // 100 m/s^2 + ACCL_10 = 2, // 10 m/s^2 + ACCL_5 = 3, // 5 m/s^2 + ACCL_1 = 4, // 1 m/s^2 + ACCL_0_1 = 5, // 0.1 m/s^2 + ACCL_0_05 = 6, // 0.05 m/s^2 + ACCL_0_01 = 7 // 0.01 m/s^2 + }; + + inline acceleration_confidence acceleration_confidence_from_int( const unsigned int i ) { + switch (i) + { + case 0: + return acceleration_confidence::UNAVAILABLE; + case 1: + return acceleration_confidence::ACCL_100; + case 2: + return acceleration_confidence::ACCL_10; + case 3: + return acceleration_confidence::ACCL_5; + case 4: + return acceleration_confidence::ACCL_1; + case 5: + return acceleration_confidence::ACCL_0_1; + case 6: + return acceleration_confidence::ACCL_0_05; + case 7: + return acceleration_confidence::ACCL_0_01; + default: + throw std::invalid_argument("Incompatible acceleration confidence value. Valid values : [0,7]"); + } + }; + +} \ No newline at end of file diff --git a/streets_utils/streets_messages/include/sensor_data_sharing_msg/acceleration_set_4_way.hpp b/streets_utils/streets_messages/include/sensor_data_sharing_msg/acceleration_set_4_way.hpp new file mode 100644 index 000000000..4218c94a3 --- /dev/null +++ b/streets_utils/streets_messages/include/sensor_data_sharing_msg/acceleration_set_4_way.hpp @@ -0,0 +1,36 @@ +// Copyright 2023 Leidos +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +#pragma once + + +namespace streets_utils::messages{ + struct acceleration_set_4_way{ + /** + * @brief Longitudinal acceleration in 0.01 m/s^s [-2000, 2001] + */ + int _longitudinal_accel; + /** + * @brief Lateral acceleration in 0.01 m/s^s [-2000, 2001] + */ + int _lateral_accel; + /** + * @brief Vertical acceleration in 0.02 G [-127, 127] + */ + int _vertical_accel; + /** + * @brief Angular velocity in 0.01 degrees [-32767, 32767] + */ + int _yaw_rate; + }; +} \ No newline at end of file diff --git a/streets_utils/streets_messages/include/sensor_data_sharing_msg/angular_velocity_confidence.hpp b/streets_utils/streets_messages/include/sensor_data_sharing_msg/angular_velocity_confidence.hpp new file mode 100644 index 000000000..1ffc5c39e --- /dev/null +++ b/streets_utils/streets_messages/include/sensor_data_sharing_msg/angular_velocity_confidence.hpp @@ -0,0 +1,53 @@ +// Copyright 2023 Leidos +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +#pragma once + +namespace streets_utils::messages{ + enum class angular_velocity_confidence{ + UNAVAILABLE = 0, + DEGSEC_100 = 1, // 100 degrees + DEGSEC_10 = 2, // 10 degrees + DEGSEC_05 = 3, // 5 degrees + DEGSEC_01 = 4, // 1 degrees + DEGSEC_0_1 = 5, // 0.1 degrees + DEGSEC_0_05 = 6, // 0.05 degrees + DEGSEC_0_01 = 7, // 0.01 degrees + }; + + inline angular_velocity_confidence angular_velocity_confidence_from_int( const unsigned int i ){ + switch (i) + { + case 0: + return angular_velocity_confidence::UNAVAILABLE; + case 1: + return angular_velocity_confidence::DEGSEC_100; + case 2: + return angular_velocity_confidence::DEGSEC_10; + case 3: + return angular_velocity_confidence::DEGSEC_05; + case 4: + return angular_velocity_confidence::DEGSEC_01; + case 5: + return angular_velocity_confidence::DEGSEC_0_1; + case 6: + return angular_velocity_confidence::DEGSEC_0_05; + case 7: + return angular_velocity_confidence::DEGSEC_0_01; + default: + throw std::invalid_argument("Incompatible angular velocity confidence value. Valid values : [0,7]"); + } + }; + + +} \ No newline at end of file diff --git a/streets_utils/streets_messages/include/sensor_data_sharing_msg/detected_object_data.hpp b/streets_utils/streets_messages/include/sensor_data_sharing_msg/detected_object_data.hpp new file mode 100644 index 000000000..ead61869a --- /dev/null +++ b/streets_utils/streets_messages/include/sensor_data_sharing_msg/detected_object_data.hpp @@ -0,0 +1,33 @@ +// Copyright 2023 Leidos +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +#pragma once + +#include "sensor_data_sharing_msg/detected_object_data_common.hpp" +#include "sensor_data_sharing_msg/obstacle/detected_obstacle_data.hpp" +#include "sensor_data_sharing_msg/vehicle/detected_vehicle_data.hpp" +#include "sensor_data_sharing_msg/vru/detected_vru_data.hpp" +#include + +namespace streets_utils::messages{ + struct detected_object_data { + /** + * @brief Common data for detected object. + */ + detected_object_data_common _detected_object_common_data; + /** + * @brief Detected object optional data associated with object type classification. + */ + std::optional> _detected_object_optional_data; + }; +} \ No newline at end of file diff --git a/streets_utils/streets_messages/include/sensor_data_sharing_msg/detected_object_data_common.hpp b/streets_utils/streets_messages/include/sensor_data_sharing_msg/detected_object_data_common.hpp new file mode 100644 index 000000000..26067bad1 --- /dev/null +++ b/streets_utils/streets_messages/include/sensor_data_sharing_msg/detected_object_data_common.hpp @@ -0,0 +1,96 @@ +// Copyright 2023 Leidos +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +#pragma once + +#include "sensor_data_sharing_msg/object_type.hpp" +#include "sensor_data_sharing_msg/time_confidence.hpp" +#include "sensor_data_sharing_msg/position_offset.hpp" +#include "sensor_data_sharing_msg/position_confidence_set.hpp" +#include "sensor_data_sharing_msg/speed_confidence.hpp" +#include "sensor_data_sharing_msg/heading_confidence.hpp" +#include "sensor_data_sharing_msg/acceleration_set_4_way.hpp" +#include "sensor_data_sharing_msg/acceleration_confidence.hpp" +#include "sensor_data_sharing_msg/angular_velocity_confidence.hpp" +#include +#include + +namespace streets_utils::messages { + struct detected_object_data_common{ + /** + * @brief Object type enumeration + */ + object_type _object_type = object_type::UNKNOWN; + /** + * @brief Confidence in object type classification [0,101] + */ + unsigned int _classification_confidence = 0; + /** + * @brief Object ID [0, 65535] + */ + unsigned int _object_id = 0; + /** + * @brief Time relative to SDSM timestamp assoicated with detection [-1500, 1500] + */ + int _time_measurement_offset = 0; + /** + * @brief Time Confidence enumeration for time offset. + */ + time_confidence _time_confidence = time_confidence::UNAVAILABLE; + /** + * @brief Cartesian offset from SDSM reporter reference location to represent detected object location + */ + position_offset _position_offset; + /** + * @brief Confidence in reported position + */ + position_confidence_set _pos_confidence; + /** + * @brief Object speed in unit (0.02 m/s) [0, 8191] + */ + unsigned int _speed = 0; + /** + * @brief Confidence in reported speed + */ + speed_confidence _speed_confidence = speed_confidence::UNAVAILABLE; + /** + * @brief Object speed along Z axis unit (0.02 m/s) [0, 8191] + */ + std::optional _speed_z; + /** + * @brief Confidence in reported speed z + */ + std::optional _speed_z_confidence; + /** + * @brief Heading in 0.0125 degrees [0, 28800] + */ + unsigned int _heading = 0; + /** + * @brief Confidence in reported heading + */ + heading_confidence _heading_confidence = heading_confidence::UNAVAILABLE; + /** + * @brief Acceleration in longitudinal, lateral, vertical and angular velocity. + */ + std::optional _acceleration_4_way; + + std::optional _longitudinal_acceleration_confidence; + + std::optional _lateral_acceleration_confidence; + + std::optional _vertical_accelaration_confidence; + + std::optional _yaw_rate_confidence; + + }; +} \ No newline at end of file diff --git a/streets_utils/streets_messages/include/sensor_data_sharing_msg/equipment_type.hpp b/streets_utils/streets_messages/include/sensor_data_sharing_msg/equipment_type.hpp new file mode 100644 index 000000000..d316f2210 --- /dev/null +++ b/streets_utils/streets_messages/include/sensor_data_sharing_msg/equipment_type.hpp @@ -0,0 +1,45 @@ +// Copyright 2023 Leidos +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +#pragma once + + +namespace streets_utils::messages{ + + enum class equipment_type { + UNKNOWN = 0, + RSU = 1, + OBU= 2, + VRU= 3 + }; + /** + * @brief Function to convert integers to equiment type. Default to UNKNOWN. + * @param i integer value of enum. + * @return corresponding equipement type. + */ + inline equipment_type equipment_type_from_int( const unsigned int i ) { + switch (i) + { + case 0: + return equipment_type::UNKNOWN; + case 1: + return equipment_type::RSU; + case 2: + return equipment_type::OBU; + case 3: + return equipment_type::VRU; + default: + throw std::invalid_argument("Incompatible equipment type value. Valid values : [0,3]"); + } + }; +} \ No newline at end of file diff --git a/streets_utils/streets_messages/include/sensor_data_sharing_msg/heading_confidence.hpp b/streets_utils/streets_messages/include/sensor_data_sharing_msg/heading_confidence.hpp new file mode 100644 index 000000000..1e837dea5 --- /dev/null +++ b/streets_utils/streets_messages/include/sensor_data_sharing_msg/heading_confidence.hpp @@ -0,0 +1,53 @@ +// Copyright 2023 Leidos +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +#pragma once + +namespace streets_utils::messages{ + enum class heading_confidence{ + UNAVAILABLE = 0, + PREC_10_deg = 1, // 10 degrees + PREC_05_deg = 2, // 5 degrees + PREC_01_deg = 3, // 1 degrees + PREC_0_1_deg = 4, // 0.1 degrees + PREC_0_05_deg = 5, // 0.05 degrees + PREC_0_01_deg = 6, // 0.01 degrees + PREC_0_0125_deg = 7 // 0.0125 degrees + }; + + inline heading_confidence heading_confidence_from_int( const unsigned int i ) { + switch (i) + { + case 0: + return heading_confidence::UNAVAILABLE; + case 1: + return heading_confidence::PREC_10_deg; + case 2: + return heading_confidence::PREC_05_deg; + case 3: + return heading_confidence::PREC_01_deg; + case 4: + return heading_confidence::PREC_0_1_deg; + case 5: + return heading_confidence::PREC_0_05_deg; + case 6: + return heading_confidence::PREC_0_01_deg; + case 7: + return heading_confidence::PREC_0_0125_deg; + default: + throw std::invalid_argument("Incompatible heading confidence value. Valid values : [0,7]"); + } + }; + + +} \ No newline at end of file diff --git a/streets_utils/streets_messages/include/sensor_data_sharing_msg/object_type.hpp b/streets_utils/streets_messages/include/sensor_data_sharing_msg/object_type.hpp new file mode 100644 index 000000000..34890bb4d --- /dev/null +++ b/streets_utils/streets_messages/include/sensor_data_sharing_msg/object_type.hpp @@ -0,0 +1,40 @@ +// Copyright 2023 Leidos +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +#pragma once + +namespace streets_utils::messages{ + + enum class object_type { + UNKNOWN = 0, + VEHICLE = 1, + VRU= 2, + ANIMAL= 3 + }; + + inline object_type object_type_from_int( const int i ) { + switch (i) + { + case 0: + return object_type::UNKNOWN; + case 1: + return object_type::VEHICLE; + case 2: + return object_type::VRU; + case 3: + return object_type::ANIMAL; + default: + throw std::invalid_argument("Incompatible object type value. Valid values : [0,3]"); + } + }; +} \ No newline at end of file diff --git a/streets_utils/streets_messages/include/sensor_data_sharing_msg/obstacle/detected_obstacle_data.hpp b/streets_utils/streets_messages/include/sensor_data_sharing_msg/obstacle/detected_obstacle_data.hpp new file mode 100644 index 000000000..896b2ac91 --- /dev/null +++ b/streets_utils/streets_messages/include/sensor_data_sharing_msg/obstacle/detected_obstacle_data.hpp @@ -0,0 +1,29 @@ +// Copyright 2023 Leidos +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +#pragma once + +#include "sensor_data_sharing_msg/obstacle/obstacle_size.hpp" +#include "sensor_data_sharing_msg/obstacle/obstacle_size_confidence.hpp" +namespace streets_utils::messages{ + struct detected_obstacle_data{ + /** + * @brief Size of obstacle. + */ + obstacle_size _size; + /** + * @brief Confidence of reported size. + */ + obstacle_size_confidence _size_confidence; + }; +} \ No newline at end of file diff --git a/streets_utils/streets_messages/include/sensor_data_sharing_msg/obstacle/obstacle_size.hpp b/streets_utils/streets_messages/include/sensor_data_sharing_msg/obstacle/obstacle_size.hpp new file mode 100644 index 000000000..8dd8740d1 --- /dev/null +++ b/streets_utils/streets_messages/include/sensor_data_sharing_msg/obstacle/obstacle_size.hpp @@ -0,0 +1,33 @@ +// Copyright 2023 Leidos +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +#pragma once + +#include + +namespace streets_utils::messages { + struct obstacle_size { + /** + * @brief Object width in 10 cm units [0, 1023]. + */ + unsigned int _width = 0; + /** + * @brief Object length in 10 cm units [0, 1023] + */ + unsigned int _length = 0; + /** + * @brief **Optional** Object height in 10 cm units [0, 1023] + */ + std::optional _height; + }; +} \ No newline at end of file diff --git a/streets_utils/streets_messages/include/sensor_data_sharing_msg/obstacle/obstacle_size_confidence.hpp b/streets_utils/streets_messages/include/sensor_data_sharing_msg/obstacle/obstacle_size_confidence.hpp new file mode 100644 index 000000000..97d55885b --- /dev/null +++ b/streets_utils/streets_messages/include/sensor_data_sharing_msg/obstacle/obstacle_size_confidence.hpp @@ -0,0 +1,32 @@ +// Copyright 2023 Leidos +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +#pragma once +#include "sensor_data_sharing_msg/size_value_confidence.hpp" +#include +namespace streets_utils::messages{ + struct obstacle_size_confidence{ + /** + * @brief Confidence in reported width + */ + size_value_confidence _width_confidence = size_value_confidence::UNAVAILABLE; + /** + * @brief Confidence in reported length + */ + size_value_confidence _length_confidence = size_value_confidence::UNAVAILABLE; + /** + * @brief Confidence in reported height + */ + std::optional _height_confidence; + }; +} \ No newline at end of file diff --git a/streets_utils/streets_messages/include/sensor_data_sharing_msg/position_3d.hpp b/streets_utils/streets_messages/include/sensor_data_sharing_msg/position_3d.hpp new file mode 100644 index 000000000..6ea0efa1c --- /dev/null +++ b/streets_utils/streets_messages/include/sensor_data_sharing_msg/position_3d.hpp @@ -0,0 +1,38 @@ +// Copyright 2023 Leidos +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +#pragma once + +#include +#include + +namespace streets_utils::messages{ + struct position_3d{ + /** + * @brief LSB = 1/10 micro degree Providing a range of + * plus-minus 180 degrees[-1799999999, 1800000001] + */ + int _longitude = 0; + /** + * @brief LSB = 1/10 micro degree Providing a range of + * plus-minus 90 degrees[-900000000, 900000001] + */ + int _latitude = 0; + /** + * @brief Signed units of 0.1m (10cm), in 2 octets the value + * 32767 (0x7FFF) shall indicate an invalid value [-32768,32767] + */ + std::optional _elevation; + + }; +} \ No newline at end of file diff --git a/streets_utils/streets_messages/include/sensor_data_sharing_msg/position_confidence.hpp b/streets_utils/streets_messages/include/sensor_data_sharing_msg/position_confidence.hpp new file mode 100644 index 000000000..ac06296ea --- /dev/null +++ b/streets_utils/streets_messages/include/sensor_data_sharing_msg/position_confidence.hpp @@ -0,0 +1,78 @@ +// Copyright 2023 Leidos +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +#pragma once + +namespace streets_utils::messages { + enum class position_confidence{ + UNAVAILABLE = 0, + A_500M = 1, + A_200M = 2, + A_100M = 3, + A_50M = 4, + A_20M = 5, + A_10M = 6, + A_5M = 7, + A_2M = 8, + A_1M = 9, + A_50CM = 10, + A_20CM = 11, + A_10CM = 12, + A_5CM = 13, + A_2CM = 14, + A_1CM = 15 + + }; + + inline position_confidence position_confidence_from_int( const int i ) { + switch (i) + { + case 0: + return position_confidence::UNAVAILABLE; + case 1: + return position_confidence::A_500M; + case 2: + return position_confidence::A_200M; + case 3: + return position_confidence::A_100M; + case 4: + return position_confidence::A_50M; + case 5: + return position_confidence::A_20M; + case 6: + return position_confidence::A_10M; + case 7: + return position_confidence::A_5M; + case 8: + return position_confidence::A_2M; + case 9: + return position_confidence::A_1M; + case 10: + return position_confidence::A_50CM; + case 11: + return position_confidence::A_20CM; + case 12: + return position_confidence::A_10CM; + case 13: + return position_confidence::A_5CM; + case 14: + return position_confidence::A_2CM; + case 15: + return position_confidence::A_1CM; + default: + throw std::invalid_argument("Incompatible position confidence value. Valid values : [0,15]"); + } + }; + + +} \ No newline at end of file diff --git a/streets_utils/streets_messages/include/sensor_data_sharing_msg/position_confidence_set.hpp b/streets_utils/streets_messages/include/sensor_data_sharing_msg/position_confidence_set.hpp new file mode 100644 index 000000000..edfee8315 --- /dev/null +++ b/streets_utils/streets_messages/include/sensor_data_sharing_msg/position_confidence_set.hpp @@ -0,0 +1,24 @@ +// Copyright 2023 Leidos +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +#pragma once + +#include "sensor_data_sharing_msg/position_confidence.hpp" + +namespace streets_utils::messages { + struct position_confidence_set{ + position_confidence _position_confidence; + position_confidence _elevation_confidence; + + }; +} \ No newline at end of file diff --git a/streets_utils/streets_messages/include/sensor_data_sharing_msg/position_offset.hpp b/streets_utils/streets_messages/include/sensor_data_sharing_msg/position_offset.hpp new file mode 100644 index 000000000..ffc97a5e1 --- /dev/null +++ b/streets_utils/streets_messages/include/sensor_data_sharing_msg/position_offset.hpp @@ -0,0 +1,34 @@ +// Copyright 2023 Leidos +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +#pragma once + +#include + +namespace streets_utils::messages{ + struct position_offset{ + /** + * @brief Cartesian offset in X axis from reference point in 0.1 m [-32767, 32767] + */ + int _offset_x = 0; + /** + * @brief Cartesian offset in Y axis from reference point in 0.1 m [-32767, 32767] + */ + int _offset_y = 0; + /** + * @brief Cartesian offset in Z axis from reference point in 0.1 m [-32767, 32767] + */ + std::optional _offset_z; + + }; +} \ No newline at end of file diff --git a/streets_utils/streets_messages/include/sensor_data_sharing_msg/positional_accuracy.hpp b/streets_utils/streets_messages/include/sensor_data_sharing_msg/positional_accuracy.hpp new file mode 100644 index 000000000..dcd2643b8 --- /dev/null +++ b/streets_utils/streets_messages/include/sensor_data_sharing_msg/positional_accuracy.hpp @@ -0,0 +1,44 @@ +// Copyright 2023 Leidos +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +#pragma once + + +namespace streets_utils::messages { + struct positional_accuracy{ + /** + * @brief semi-major axis accuracy at one standard dev + * range 0-12.7 meter, LSB = .05m [0, 255] + * 254 = any value equal or greater than 12.70 meter + * 255 = unavailable semi-major axis value + */ + unsigned int _semi_major_axis_accuracy = 0; + /** + * @brief semi-minor axis accuracy at one standard dev + * range 0-12.7 meter, LSB = .05m [0, 255] + * 254 = any value equal or greater than 12.70 meter + * 255 = unavailable semi-minor axis value + */ + unsigned int _semi_minor_axis_accuracy = 0; + /** + * @brief -- orientation of semi-major axis + * relative to true north (0~359.9945078786 degrees) + * LSB units of 360/65535 deg = 0.0054932479 [0,65535] + * a value of 0 shall be 0 degrees + * a value of 1 shall be 0.0054932479 degrees + * a value of 65534 shall be 359.9945078786 deg + * a value of 65535 shall be used for orientation unavailable + */ + unsigned int _semi_major_axis_orientation = 0; + }; +} \ No newline at end of file diff --git a/streets_utils/streets_messages/include/sensor_data_sharing_msg/sensor_data_sharing_msg.hpp b/streets_utils/streets_messages/include/sensor_data_sharing_msg/sensor_data_sharing_msg.hpp new file mode 100644 index 000000000..0c90d17c9 --- /dev/null +++ b/streets_utils/streets_messages/include/sensor_data_sharing_msg/sensor_data_sharing_msg.hpp @@ -0,0 +1,49 @@ +// Copyright 2023 Leidos +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +#pragma once + +#include "sensor_data_sharing_msg/equipment_type.hpp" +#include "sensor_data_sharing_msg/time_stamp.hpp" +#include "sensor_data_sharing_msg/detected_object_data.hpp" +#include "sensor_data_sharing_msg/position_3d.hpp" +#include "sensor_data_sharing_msg/positional_accuracy.hpp" + +#include +#include +#include + +namespace streets_utils::messages { + + + /** + * @brief + */ + struct sensor_data_sharing_msg { + /** + * @brief -- a count value which is incremented with each use [0,255] + * the next value after 255 shall be one value + * 0 (0x00) shall indicate that MsgCount is not available. + */ + std::size_t _msg_count = 0; + equipment_type _equipment_type = equipment_type::UNKNOWN; + position_3d _ref_positon; + std::optional _ref_position_elevation_confidence; + positional_accuracy _ref_position_confidence; + time_stamp _time_stamp; + std::string _source_id; + std::vector _objects; + + }; + +} \ No newline at end of file diff --git a/streets_utils/streets_messages/include/sensor_data_sharing_msg/size_value_confidence.hpp b/streets_utils/streets_messages/include/sensor_data_sharing_msg/size_value_confidence.hpp new file mode 100644 index 000000000..94d39afc2 --- /dev/null +++ b/streets_utils/streets_messages/include/sensor_data_sharing_msg/size_value_confidence.hpp @@ -0,0 +1,69 @@ +// Copyright 2023 Leidos +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +#pragma once + +namespace streets_utils::messages{ + enum class size_value_confidence{ + UNAVAILABLE = 0, // Not available + SIZE_100 = 1, // 100 meters + SIZE_50 = 2, // 50 meters + SIZE_20 = 3, // 20 meters + SIZE_10 = 4, // 10 meters + SIZE_5 = 5, // 5 meters + SIZE_2 = 6, // 2 meters + SIZE_1 = 7, // 1 meter + SIZE_0_5 = 8, // 50 centimeters + SIZE_0_2 = 9, // 20 centimeters + SIZE_0_1 = 10, // 10 centimeters + SIZE_0_05 = 11, // 5 centimeters + SIZE_0_02 = 12, // 2 centimeters + SIZE_0_01 = 13 // 1 centimeters + }; + + inline size_value_confidence size_value_confidence_from_int( const unsigned int i ) { + switch (i) + { + case 0: + return size_value_confidence::UNAVAILABLE; + case 1: + return size_value_confidence::SIZE_100; + case 2: + return size_value_confidence::SIZE_50; + case 3: + return size_value_confidence::SIZE_20; + case 4: + return size_value_confidence::SIZE_10; + case 5: + return size_value_confidence::SIZE_5; + case 6: + return size_value_confidence::SIZE_2; + case 7: + return size_value_confidence::SIZE_1; + case 8: + return size_value_confidence::SIZE_0_5; + case 9: + return size_value_confidence::SIZE_0_2; + case 10: + return size_value_confidence::SIZE_0_1; + case 11: + return size_value_confidence::SIZE_0_05; + case 12: + return size_value_confidence::SIZE_0_02; + case 13: + return size_value_confidence::SIZE_0_01; + default: + throw std::invalid_argument("Incompatible size confidence value. Valid values : [0,13]"); + } + } +} \ No newline at end of file diff --git a/streets_utils/streets_messages/include/sensor_data_sharing_msg/speed_confidence.hpp b/streets_utils/streets_messages/include/sensor_data_sharing_msg/speed_confidence.hpp new file mode 100644 index 000000000..7b3737902 --- /dev/null +++ b/streets_utils/streets_messages/include/sensor_data_sharing_msg/speed_confidence.hpp @@ -0,0 +1,52 @@ +// Copyright 2023 Leidos +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +#pragma once + +namespace streets_utils::messages{ + enum class speed_confidence { + UNAVAILABLE = 0, // Not available + PREC_100ms = 1, // 100 m/s + PREC_10ms = 2, // 10 m/s + PREC_5ms = 3, // 5 m/s + PREC_1ms = 4, // 1 m/s + PREC_0_1ms = 5, // 0.1 m/s + PREC_0_05ms = 6, // 0.05 m/s + PREC_0_01ms = 7 // 0.01 m/s + }; + + inline speed_confidence speed_confidence_from_int( const unsigned int i ) { + switch (i) + { + case 0: + return speed_confidence::UNAVAILABLE; + case 1: + return speed_confidence::PREC_100ms; + case 2: + return speed_confidence::PREC_10ms; + case 3: + return speed_confidence::PREC_5ms; + case 4: + return speed_confidence::PREC_1ms; + case 5: + return speed_confidence::PREC_0_1ms; + case 6: + return speed_confidence::PREC_0_05ms; + case 7: + return speed_confidence::PREC_0_01ms; + default: + throw std::invalid_argument("Incompatible speed confidence value. Valid values : [0,7]"); + } + }; + +} \ No newline at end of file diff --git a/streets_utils/streets_messages/include/sensor_data_sharing_msg/time_confidence.hpp b/streets_utils/streets_messages/include/sensor_data_sharing_msg/time_confidence.hpp new file mode 100644 index 000000000..f9b433ffc --- /dev/null +++ b/streets_utils/streets_messages/include/sensor_data_sharing_msg/time_confidence.hpp @@ -0,0 +1,148 @@ +// Copyright 2023 Leidos +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +#pragma once + +namespace streets_utils::messages{ + enum class time_confidence{ + UNAVAILABLE = 0, // unvailable + TIME_100_000 = 1, // Better than 100 seconds + TIME_050_000 = 2, // Better than 50 seconds + TIME_020_000 = 3, // Better than 20 seconds + TIME_010_000 = 4, // Better than 10 seconds + TIME_002_000 = 5, // Better than 2 seconds + TIME_001_000 = 6, // Better than 1 seconds + TIME_000_500 = 7, // Better than 0.5 seconds + TIME_000_200 = 8, // Better than 0.2 seconds + TIME_000_100 = 9, // Better than 0.1 seconds + TIME_000_050 = 10, // Better than 0.05 seconds + TIME_000_020 = 11, // Better than 0.02 seconds + TIME_000_010 = 12, // Better than 0.01 seconds + TIME_000_005 = 13, // Better than 0.005 seconds + TIME_000_002 = 14, // Better than 0.002 seconds + TIME_000_001 = 15, // Better than 0.001 seconds + TIME_000_000_5 = 16, // Better than 0.0005 seconds + TIME_000_000_2 = 17, // Better than 0.0002 seconds + TIME_000_000_1 = 18, // Better than 0.0001 seconds + TIME_000_000_05 = 19, // Better than 0.00005 seconds + TIME_000_000_02 = 20, // Better than 0.00002 seconds + TIME_000_000_01 = 21, // Better than 0.00001 seconds + TIME_000_000_005 = 22, // Better than 0.000005 seconds + TIME_000_000_002 = 23, // Better than 0.000002 seconds + TIME_000_000_001 = 24, // Better than 0.000001 seconds + TIME_000_000_000_5 = 25, // Better than 0.0000005 seconds + TIME_000_000_000_2 = 26, // Better than 0.0000002 seconds + TIME_000_000_000_1 = 27, // Better than 0.0000001 seconds + TIME_000_000_000_05 = 28, // Better than 0.00000005 seconds + TIME_000_000_000_02 = 29, // Better than 0.00000002 seconds + TIME_000_000_000_01 = 30, // Better than 0.00000001 seconds + TIME_000_000_000_005 = 31, // Better than 0.000000005 seconds + TIME_000_000_000_002 = 32, // Better than 0.000000002 seconds + TIME_000_000_000_001 = 33, // Better than 0.000000001 seconds (Better than one nano second) + TIME_000_000_000_000_5 = 34,// Better than 0.0000000005 seconds + TIME_000_000_000_000_2 = 35,// Better than 0.0000000002 seconds + TIME_000_000_000_000_1 = 36,// Better than 0.0000000001 seconds + TIME_000_000_000_000_05 = 37, // Better than 0.00000000005 seconds + TIME_000_000_000_000_02 = 38, // Better than 0.00000000002 seconds + TIME_000_000_000_000_01 = 39 // Better than 0.00000000001 Seconds + }; + + inline time_confidence time_confidence_from_int( const int i ) { + switch (i) + { + case 0: + return time_confidence::UNAVAILABLE; + case 1: + return time_confidence::TIME_100_000; + case 2: + return time_confidence::TIME_050_000 ; + case 3: + return time_confidence::TIME_020_000 ; + case 4: + return time_confidence::TIME_010_000 ; + case 5: + return time_confidence::TIME_002_000 ; + case 6: + return time_confidence::TIME_001_000 ; + case 7: + return time_confidence::TIME_000_500 ; + case 8: + return time_confidence::TIME_000_200 ; + case 9: + return time_confidence::TIME_000_100 ; + case 10: + return time_confidence::TIME_000_050 ; + case 11: + return time_confidence::TIME_000_020 ; + case 12: + return time_confidence::TIME_000_010 ; + case 13: + return time_confidence::TIME_000_005 ; + case 14: + return time_confidence::TIME_000_002 ; + case 15: + return time_confidence::TIME_000_001 ; + case 16: + return time_confidence::TIME_000_000_5; + case 17: + return time_confidence::TIME_000_000_2; + case 18: + return time_confidence::TIME_000_000_1; + case 19: + return time_confidence::TIME_000_000_05; + case 20: + return time_confidence::TIME_000_000_02; + case 21: + return time_confidence::TIME_000_000_01; + case 22: + return time_confidence::TIME_000_000_005; + case 23: + return time_confidence::TIME_000_000_002; + case 24: + return time_confidence::TIME_000_000_001; + case 25: + return time_confidence::TIME_000_000_000_5; + case 26: + return time_confidence::TIME_000_000_000_2; + case 27: + return time_confidence::TIME_000_000_000_1; + case 28: + return time_confidence::TIME_000_000_000_05; + case 29: + return time_confidence::TIME_000_000_000_02; + case 30: + return time_confidence::TIME_000_000_000_01; + case 31: + return time_confidence::TIME_000_000_000_005; + case 32: + return time_confidence::TIME_000_000_000_002; + case 33: + return time_confidence::TIME_000_000_000_001; + case 34: + return time_confidence::TIME_000_000_000_000_5; + case 35: + return time_confidence::TIME_000_000_000_000_2; + case 36: + return time_confidence::TIME_000_000_000_000_1; + case 37: + return time_confidence::TIME_000_000_000_000_05; + case 38: + return time_confidence::TIME_000_000_000_000_02; + case 39: + return time_confidence::TIME_000_000_000_000_01; + default: + throw std::invalid_argument("Incompatible time confidence value. Valid values : [0,39]"); + } + }; + +} \ No newline at end of file diff --git a/streets_utils/streets_messages/include/sensor_data_sharing_msg/time_stamp.hpp b/streets_utils/streets_messages/include/sensor_data_sharing_msg/time_stamp.hpp new file mode 100644 index 000000000..1c0f77033 --- /dev/null +++ b/streets_utils/streets_messages/include/sensor_data_sharing_msg/time_stamp.hpp @@ -0,0 +1,47 @@ +// Copyright 2023 Leidos +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +#pragma once + +namespace streets_utils::messages { + struct time_stamp { + /** + * @brief in milliseconds [0,65535]. + */ + unsigned int second = 0; + /** + * @brief in minutes [0,60]. + */ + unsigned int minute = 0; + /** + * @brief in hours [0,31]. + */ + unsigned int hour = 0; + /** + * @brief in days [0,31]. + */ + unsigned int day = 0; + /** + * @brief in months [0,12]. + */ + unsigned int month = 0; + /** + * @brief in year [0,4095] + */ + unsigned int year = 0; + /** + * @brief Minutes from UTC time (Time Zone) [-840, 840] + */ + int offset = 0; // Time zone + }; +} \ No newline at end of file diff --git a/streets_utils/streets_messages/include/sensor_data_sharing_msg/vehicle/angular_velocity_confidence_set.hpp b/streets_utils/streets_messages/include/sensor_data_sharing_msg/vehicle/angular_velocity_confidence_set.hpp new file mode 100644 index 000000000..965e381f5 --- /dev/null +++ b/streets_utils/streets_messages/include/sensor_data_sharing_msg/vehicle/angular_velocity_confidence_set.hpp @@ -0,0 +1,30 @@ +// Copyright 2023 Leidos +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +#pragma once + +#include "sensor_data_sharing_msg/angular_velocity_confidence.hpp" +#include + +namespace streets_utils::messages{ + struct angular_velocity_confidence_set{ + /** + * @brief Confidence in reported pitch rate. + */ + std::optional _pitch_rate_confidence; + /** + * @brief Confidence in reported roll rate. + */ + std::optional _roll_rate_confidence; + }; +} \ No newline at end of file diff --git a/streets_utils/streets_messages/include/sensor_data_sharing_msg/vehicle/angular_velocity_set.hpp b/streets_utils/streets_messages/include/sensor_data_sharing_msg/vehicle/angular_velocity_set.hpp new file mode 100644 index 000000000..35765571f --- /dev/null +++ b/streets_utils/streets_messages/include/sensor_data_sharing_msg/vehicle/angular_velocity_set.hpp @@ -0,0 +1,27 @@ +// Copyright 2023 Leidos +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#pragma once +namespace streets_utils::messages{ + struct angular_velocity_set{ + /** + * @brief Angular velocity for pitch axis in 0.01 degrees per second [-32767, 32767] + */ + int _pitch_rate = 0; + /** + * @brief Angular velocity for roll axis in 0.01 degrees per second [-32767, 32767] + */ + int _roll_rate = 0; + }; +} \ No newline at end of file diff --git a/streets_utils/streets_messages/include/sensor_data_sharing_msg/vehicle/attitude.hpp b/streets_utils/streets_messages/include/sensor_data_sharing_msg/vehicle/attitude.hpp new file mode 100644 index 000000000..3600cc208 --- /dev/null +++ b/streets_utils/streets_messages/include/sensor_data_sharing_msg/vehicle/attitude.hpp @@ -0,0 +1,32 @@ +// Copyright 2023 Leidos +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +#pragma once + + +namespace streets_utils::messages{ + struct attitude{ + /** + * @brief Pitch in 0.0125 degrees [-7200, 72000]. + */ + int _pitch = 0; + /** + * @brief Roll in 0.0125 degrees [-14400, 14400] + */ + int _roll = 0; + /** + * @brief Yaw in 0.0125 degrees [-14400, 14400] + */ + int _yaw = 0; + }; +} \ No newline at end of file diff --git a/streets_utils/streets_messages/include/sensor_data_sharing_msg/vehicle/attitude_confidence.hpp b/streets_utils/streets_messages/include/sensor_data_sharing_msg/vehicle/attitude_confidence.hpp new file mode 100644 index 000000000..9a84eda1d --- /dev/null +++ b/streets_utils/streets_messages/include/sensor_data_sharing_msg/vehicle/attitude_confidence.hpp @@ -0,0 +1,33 @@ +// Copyright 2023 Leidos +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +#pragma once + +#include "sensor_data_sharing_msg/heading_confidence.hpp" + +namespace streets_utils::messages{ + struct attitude_confidence { + /** + * @brief Confidence in reported pitch. + */ + heading_confidence _pitch_confidence = heading_confidence::UNAVAILABLE; + /** + * @brief Confidence in reported roll. + */ + heading_confidence _roll_confidence = heading_confidence::UNAVAILABLE; + /** + * @brief Confidence in reported yaw. + */ + heading_confidence _yaw_confidence = heading_confidence::UNAVAILABLE; + }; +} \ No newline at end of file diff --git a/streets_utils/streets_messages/include/sensor_data_sharing_msg/vehicle/detected_vehicle_data.hpp b/streets_utils/streets_messages/include/sensor_data_sharing_msg/vehicle/detected_vehicle_data.hpp new file mode 100644 index 000000000..f1ffc8f8c --- /dev/null +++ b/streets_utils/streets_messages/include/sensor_data_sharing_msg/vehicle/detected_vehicle_data.hpp @@ -0,0 +1,79 @@ +// Copyright 2023 Leidos +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +#pragma once + +#include +#include "sensor_data_sharing_msg/vehicle/attitude.hpp" +#include "sensor_data_sharing_msg/vehicle/attitude_confidence.hpp" +#include "sensor_data_sharing_msg/vehicle/angular_velocity_set.hpp" +#include "sensor_data_sharing_msg/vehicle/angular_velocity_confidence_set.hpp" +#include "sensor_data_sharing_msg/vehicle/vehicle_size.hpp" +#include "sensor_data_sharing_msg/vehicle/vehicle_size_confidence.hpp" + +namespace streets_utils::messages{ + struct detected_vehicle_data{ + /** + * @brief BIT String representing the state of each of the following vehicle + * exterior lights: + * lowBeamHeadlightsOn (0), + * highBeamHeadlightsOn (1), + * leftTurnSignalOn (2), + * rightTurnSignalOn (3), + * hazardSignalOn (4), + * automaticLightControlOn (5), + * daytimeRunningLightsOn (6), + * fogLightOn (7), + * parkingLightsOn (8) + * + */ + std::optional exterior_lights; + /** + * @brief Vehicle Attitude. + */ + std::optional _veh_attitude; + /** + * @brief Confidence in reported vehicle attitude. + */ + std::optional _attitude_confidence; + /** + * @brief Angular velocity set in pitch and roll axis. + */ + std::optional _angular_velocity; + /** + * @brief Confidence in reported angular velocity set. + */ + std::optional _angular_velocity_confidence; + /** + * @brief Vehicle two dimensional size. + */ + std::optional _size; + /** + * @brief Vehicle height in unit of 5 cm [0, 127] + */ + std::optional _vehicle_height; + /** + * @brief Confidence in reported size. + */ + std::optional _size_confidence; + /** + * @brief See BasicVehicleClass in J2735 + */ + std::optional _vehicle_class; + /** + * @brief Confidence in vehicle classification [0,101] + */ + std::optional _classification_confidence; + + }; +} \ No newline at end of file diff --git a/streets_utils/streets_messages/include/sensor_data_sharing_msg/vehicle/vehicle_size.hpp b/streets_utils/streets_messages/include/sensor_data_sharing_msg/vehicle/vehicle_size.hpp new file mode 100644 index 000000000..d838a9bb2 --- /dev/null +++ b/streets_utils/streets_messages/include/sensor_data_sharing_msg/vehicle/vehicle_size.hpp @@ -0,0 +1,28 @@ +// Copyright 2023 Leidos +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +#pragma once + +namespace streets_utils::messages{ + struct vehicle_size { + /** + * @brief Vehicle width in centimeters [0, 1023] + */ + unsigned int _width = 0; + /** + * @brief Vehicle length in centimeters [0, 4095] + */ + unsigned int _length = 0; + + }; +} \ No newline at end of file diff --git a/streets_utils/streets_messages/include/sensor_data_sharing_msg/vehicle/vehicle_size_confidence.hpp b/streets_utils/streets_messages/include/sensor_data_sharing_msg/vehicle/vehicle_size_confidence.hpp new file mode 100644 index 000000000..e1566ba34 --- /dev/null +++ b/streets_utils/streets_messages/include/sensor_data_sharing_msg/vehicle/vehicle_size_confidence.hpp @@ -0,0 +1,32 @@ +// Copyright 2023 Leidos +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +#pragma once +#include "sensor_data_sharing_msg/size_value_confidence.hpp" +#include +namespace streets_utils::messages{ + struct vehicle_size_confidence{ + /** + * @brief Confidence in reported width + */ + size_value_confidence _width_confidence = size_value_confidence::UNAVAILABLE; + /** + * @brief Confidence in reported length + */ + size_value_confidence _length_confidence = size_value_confidence::UNAVAILABLE; + /** + * @brief Confidence in reported height + */ + std::optional _height_confidence; + }; +} \ No newline at end of file diff --git a/streets_utils/streets_messages/include/sensor_data_sharing_msg/vru/animal_propelled_type.hpp b/streets_utils/streets_messages/include/sensor_data_sharing_msg/vru/animal_propelled_type.hpp new file mode 100644 index 000000000..8c097ea3b --- /dev/null +++ b/streets_utils/streets_messages/include/sensor_data_sharing_msg/vru/animal_propelled_type.hpp @@ -0,0 +1,41 @@ +// Copyright 2023 Leidos +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +#pragma once + +namespace streets_utils::messages{ + + enum class animal_propelled_type{ + UNAVAILABLE = 0, + OTHER_TYPES = 1, + ANIMAL_MOUNTED = 2, + ANIMAL_DRAWN_CARRIAGE = 3, + }; + + inline animal_propelled_type animal_propelled_type_from_int( const unsigned int i ) { + switch (i) + { + case 0: + return animal_propelled_type::UNAVAILABLE; + case 1: + return animal_propelled_type::OTHER_TYPES; + case 2: + return animal_propelled_type::ANIMAL_MOUNTED; + case 3: + return animal_propelled_type::ANIMAL_DRAWN_CARRIAGE; + default: + throw std::invalid_argument("Incompatible animal propelled type value. Valid values : [0,3]"); + } + }; + +} \ No newline at end of file diff --git a/streets_utils/streets_messages/include/sensor_data_sharing_msg/vru/attachment.hpp b/streets_utils/streets_messages/include/sensor_data_sharing_msg/vru/attachment.hpp new file mode 100644 index 000000000..22a0da64b --- /dev/null +++ b/streets_utils/streets_messages/include/sensor_data_sharing_msg/vru/attachment.hpp @@ -0,0 +1,49 @@ +// Copyright 2023 Leidos +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +#pragma once + +namespace streets_utils::messages { + enum class attachment { + UNAVAILABLE = 0 , + STROLLER = 1, + BICYLE_TRAILER = 2, + CART = 3, + WHEEL_CHAIR = 4, + OTHER_WALK_ASSIST_ATTACHMENTS = 5, + PET = 6 + }; + + inline attachment attachment_from_int( const int i ){ + switch (i) + { + case 0: + return attachment::UNAVAILABLE; + case 1: + return attachment::STROLLER; + case 2: + return attachment::BICYLE_TRAILER; + case 3: + return attachment::CART; + case 4: + return attachment::WHEEL_CHAIR; + case 5: + return attachment::OTHER_WALK_ASSIST_ATTACHMENTS; + case 6: + return attachment::PET; + default: + throw std::invalid_argument("Incompatible attachment value. Valid values : [0,6]"); + } + } + +} \ No newline at end of file diff --git a/streets_utils/streets_messages/include/sensor_data_sharing_msg/vru/detected_vru_data.hpp b/streets_utils/streets_messages/include/sensor_data_sharing_msg/vru/detected_vru_data.hpp new file mode 100644 index 000000000..9544028eb --- /dev/null +++ b/streets_utils/streets_messages/include/sensor_data_sharing_msg/vru/detected_vru_data.hpp @@ -0,0 +1,44 @@ +// Copyright 2023 Leidos +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +#pragma once + +#include "sensor_data_sharing_msg/vru/human_propelled_type.hpp" +#include "sensor_data_sharing_msg/vru/animal_propelled_type.hpp" +#include "sensor_data_sharing_msg/vru/motorized_propelled_type.hpp" +#include "sensor_data_sharing_msg/vru/attachment.hpp" +#include "sensor_data_sharing_msg/vru/personal_device_user_type.hpp" + +#include +#include + +namespace streets_utils::messages{ + struct detected_vru_data{ + /** + * @brief Propulsion type. + */ + std::optional> _propulsion; + /** + * @brief Attachment type + */ + std::optional _attachment; + /** + * @brief Attachment radius in decimeters [0,200] + */ + std::optional _attachment_radius; + /** + * @brief Personal device user type + */ + std::optional _personal_device_user_type; + }; +} \ No newline at end of file diff --git a/streets_utils/streets_messages/include/sensor_data_sharing_msg/vru/human_propelled_type.hpp b/streets_utils/streets_messages/include/sensor_data_sharing_msg/vru/human_propelled_type.hpp new file mode 100644 index 000000000..fd8e52272 --- /dev/null +++ b/streets_utils/streets_messages/include/sensor_data_sharing_msg/vru/human_propelled_type.hpp @@ -0,0 +1,47 @@ +// Copyright 2023 Leidos +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +#pragma once + +namespace streets_utils::messages { + + enum class human_propelled_type{ + UNAVAILABLE = 0, + OTHER_TYPES = 1, + ON_FOOT = 2, + SKATEBOARD = 3, + PUSH_OR_KICK_SCOOTER = 4, + WHEELCHAIR = 5 + }; + + inline human_propelled_type human_propelled_type_from_int( const int i ){ + switch (i) + { + case 0: + return human_propelled_type::UNAVAILABLE; + case 1: + return human_propelled_type::OTHER_TYPES; + case 2: + return human_propelled_type::ON_FOOT; + case 3: + return human_propelled_type::SKATEBOARD; + case 4: + return human_propelled_type::PUSH_OR_KICK_SCOOTER; + case 5: + return human_propelled_type::WHEELCHAIR; + default: + throw std::invalid_argument("Incompatible human propelled type value. Valid values : [0,5]"); + } + }; + +} \ No newline at end of file diff --git a/streets_utils/streets_messages/include/sensor_data_sharing_msg/vru/motorized_propelled_type.hpp b/streets_utils/streets_messages/include/sensor_data_sharing_msg/vru/motorized_propelled_type.hpp new file mode 100644 index 000000000..85d73f42e --- /dev/null +++ b/streets_utils/streets_messages/include/sensor_data_sharing_msg/vru/motorized_propelled_type.hpp @@ -0,0 +1,46 @@ +// Copyright 2023 Leidos +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +#pragma once + +namespace streets_utils::messages { + enum class motorized_propelled_type{ + UNAVAILABLE = 0, + OTHER_TYPES = 1, + WHEEL_CHAIR = 2, + BICYCLE = 3, + SCOOTER = 4, + SELF_BALANCING_DEVICE = 5 + }; + + inline motorized_propelled_type motorized_propelled_type_from_int( const unsigned int i ){ + switch (i) + { + case 0: + return motorized_propelled_type::UNAVAILABLE; + case 1: + return motorized_propelled_type::OTHER_TYPES; + case 2: + return motorized_propelled_type::WHEEL_CHAIR; + case 3: + return motorized_propelled_type::BICYCLE; + case 4: + return motorized_propelled_type::SCOOTER; + case 5: + return motorized_propelled_type::SELF_BALANCING_DEVICE; + default: + throw std::invalid_argument("Incompatible motorized propelled type value. Valid values : [0,5]"); + } + }; + +} \ No newline at end of file diff --git a/streets_utils/streets_messages/include/sensor_data_sharing_msg/vru/personal_device_user_type.hpp b/streets_utils/streets_messages/include/sensor_data_sharing_msg/vru/personal_device_user_type.hpp new file mode 100644 index 000000000..33fd50c43 --- /dev/null +++ b/streets_utils/streets_messages/include/sensor_data_sharing_msg/vru/personal_device_user_type.hpp @@ -0,0 +1,44 @@ +// Copyright 2023 Leidos +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +#pragma once + +namespace streets_utils::messages { + + enum class personal_device_user_type{ + UNAVAILABLE= 0, + PEDESTRIAN = 1, + PEDALCYCLIST = 2, + PUBLIC_SAFETY_WORKER = 3, + ANIMAL = 4 + }; + + inline personal_device_user_type personal_device_user_type_from_int( const unsigned int i ) { + switch (i) + { + case 0: + return personal_device_user_type::UNAVAILABLE; + case 1: + return personal_device_user_type::PEDESTRIAN; + case 2: + return personal_device_user_type::PEDALCYCLIST; + case 3: + return personal_device_user_type::PUBLIC_SAFETY_WORKER; + case 4: + return personal_device_user_type::ANIMAL; + default: + throw std::invalid_argument("Incompatible personal devvice user type value. Valid values : [0,4]"); + } + }; + +} \ No newline at end of file diff --git a/streets_utils/streets_messages/include/serializers/sensor_data_sharing_msg_json_serializer.hpp b/streets_utils/streets_messages/include/serializers/sensor_data_sharing_msg_json_serializer.hpp new file mode 100644 index 000000000..d3f4a2b27 --- /dev/null +++ b/streets_utils/streets_messages/include/serializers/sensor_data_sharing_msg_json_serializer.hpp @@ -0,0 +1,58 @@ +#pragma once + +#include +#include +#include +#include +#include +#include "sensor_data_sharing_msg/sensor_data_sharing_msg.hpp" +#include + + +namespace streets_utils::messages{ + std::string to_json(const sensor_data_sharing_msg &val); + + rapidjson::Value create_timestamp(const time_stamp &val, rapidjson::Document::AllocatorType &allocator); + + rapidjson::Value create_position_3d(const position_3d &val, rapidjson::Document::AllocatorType &allocator); + + rapidjson::Value create_positional_accuracy(const positional_accuracy &val, rapidjson::Document::AllocatorType &allocator); + + rapidjson::Value create_detected_object_list(const std::vector &val, rapidjson::Document::AllocatorType &allocator ); + + rapidjson::Value create_detected_object_data(const detected_object_data &val, rapidjson::Document::AllocatorType &allocator); + + rapidjson::Value create_detected_object_data_common(const detected_object_data_common &val, rapidjson::Document::AllocatorType &allocator); + + rapidjson::Value create_detected_object_data_optional(const std::variant &val, rapidjson::Document::AllocatorType &allocator); + + rapidjson::Value create_detected_obstacle_data(const detected_obstacle_data &val, rapidjson::Document::AllocatorType &allocator); + + rapidjson::Value create_obstacle_size(const obstacle_size &val, rapidjson::Document::AllocatorType &allocator); + + rapidjson::Value create_obstacle_size_confidence(const obstacle_size_confidence &val, rapidjson::Document::AllocatorType &allocator); + + rapidjson::Value create_detected_vru_data(const detected_vru_data &val, rapidjson::Document::AllocatorType &allocator); + + rapidjson::Value create_propelled_information(const std::variant &val, rapidjson::Document::AllocatorType &allocator); + + rapidjson::Value create_detected_vehicle_data(const detected_vehicle_data &val, rapidjson::Document::AllocatorType &allocator); + + rapidjson::Value create_vehicle_attitude(const attitude &val, rapidjson::Document::AllocatorType &allocator); + + rapidjson::Value create_vehicle_attitude_confidence(const attitude_confidence &val, rapidjson::Document::AllocatorType &allocator); + + rapidjson::Value create_angular_velocity(const angular_velocity_set &val, rapidjson::Document::AllocatorType &allocator); + + rapidjson::Value create_angular_velocity_confidence(const angular_velocity_confidence_set &val, rapidjson::Document::AllocatorType &allocator); + + rapidjson::Value create_vehicle_size(const vehicle_size &val, rapidjson::Document::AllocatorType &allocator); + + rapidjson::Value create_vehicle_size_confidence(const vehicle_size_confidence &val, rapidjson::Document::AllocatorType &allocator); + + rapidjson::Value create_accelaration_set_4_way(const acceleration_set_4_way &val, rapidjson::Document::AllocatorType &allocator); + + rapidjson::Value create_position_3d(const position_offset &val, rapidjson::Document::AllocatorType &allocator); + + rapidjson::Value create_position_confidence_set(const position_confidence_set &val, rapidjson::Document::AllocatorType &allocator); +} \ No newline at end of file diff --git a/streets_utils/streets_messages/src/deserializers/sensor_data_sharing_msg_json_deserializer.cpp b/streets_utils/streets_messages/src/deserializers/sensor_data_sharing_msg_json_deserializer.cpp new file mode 100644 index 000000000..b3b393139 --- /dev/null +++ b/streets_utils/streets_messages/src/deserializers/sensor_data_sharing_msg_json_deserializer.cpp @@ -0,0 +1,300 @@ +// Copyright 2019-2023 Leidos +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +#include "deserializers/sensor_data_sharing_msg_json_deserializer.hpp" + + +namespace streets_utils::messages { + using namespace streets_utils::json_utils; + sensor_data_sharing_msg from_json( const std::string &json ){ + rapidjson::Document document = streets_utils::json_utils::parse_json(json); + sensor_data_sharing_msg msg; + msg._msg_count = parse_uint_member("msg_cnt", document, true).value(); + msg._source_id = parse_string_member("source_id", document, true).value(); + msg._equipment_type = equipment_type_from_int(parse_uint_member("equipment_type", document, true).value()); + msg._time_stamp = parse_time_stamp(parse_object_member("sdsm_time_stamp", document, true).value()); + msg._ref_positon = parse_position_3d(parse_object_member("ref_pos", document, true).value()); + msg._ref_position_confidence = parse_positional_accuracy(parse_object_member("ref_pos_xy_conf", document, true).value()); + msg._ref_position_elevation_confidence = parse_elevation_confidence( document); + msg._objects = parse_detected_object_list(document); + return msg; + } + + time_stamp parse_time_stamp(const rapidjson::Value &val){ + time_stamp _time_stamp; + _time_stamp.offset = parse_int_member("offset", val, true).value(); + _time_stamp.second = parse_uint_member("second", val, true).value(); + _time_stamp.minute = parse_uint_member("minute", val, true).value(); + _time_stamp.hour = parse_uint_member("hour", val, true).value(); + _time_stamp.day = parse_uint_member("day", val, true).value(); + _time_stamp.month = parse_uint_member("month", val, true).value(); + _time_stamp.year = parse_uint_member("year", val, true).value(); + return _time_stamp; + + } + + position_3d parse_position_3d(const rapidjson::Value &val) { + position_3d _position_3d; + _position_3d._longitude = parse_int_member("long", val, true).value(); + _position_3d._latitude = parse_int_member("lat", val, true).value(); + // parse optional elevation + _position_3d._elevation = parse_int_member("elevation", val, false); + return _position_3d; + + } + + positional_accuracy parse_positional_accuracy(const rapidjson::Value &val){ + positional_accuracy _positional_accuracy; + _positional_accuracy._semi_major_axis_accuracy = parse_uint_member("semi_major", val, true).value(); + _positional_accuracy._semi_minor_axis_accuracy = parse_uint_member("semi_minor", val, true).value(); + _positional_accuracy._semi_major_axis_orientation = parse_uint_member("orientation",val, true).value(); + return _positional_accuracy; + + } + + std::optional parse_elevation_confidence(const rapidjson::Value &val) { + std::optional _position_confidence; + if (auto _position_confidence_value = parse_uint_member("ref_pos_el_conf", val, false); _position_confidence_value.has_value() ) { + _position_confidence = position_confidence_from_int(_position_confidence_value.value()); + } + return _position_confidence; + } + + std::vector parse_detected_object_list(const rapidjson::Value &val){ + std::vector detected_object_list; + auto json_detected_object_list = parse_array_member("objects",val, true).value(); + for (const auto &object: json_detected_object_list){ + detected_object_data data; + auto detected_object = parse_object_member("detected_object_data",object, true); + data._detected_object_common_data = parse_detected_object_data_common(parse_object_member("detected_object_common_data", detected_object.value(), true).value()); + if ( auto data_optional = parse_object_member("detected_object_optional_data", detected_object.value(), false); data_optional.has_value()) { + data._detected_object_optional_data = parse_detected_object_data_optional( data_optional.value() ); + } + detected_object_list.push_back(data); + } + return detected_object_list; + } + + detected_object_data_common parse_detected_object_data_common(const rapidjson::Value &val){ + detected_object_data_common _detected_object_common_data; + _detected_object_common_data._object_type = object_type_from_int(parse_uint_member("obj_type", val, true).value()); + _detected_object_common_data._classification_confidence = parse_uint_member("obj_type_cfd",val,true).value(); + _detected_object_common_data._object_id = parse_uint_member("object_id", val, true).value(); + _detected_object_common_data._time_measurement_offset = parse_int_member("measurement_time", val, true).value(); + _detected_object_common_data._time_confidence = time_confidence_from_int(parse_uint_member("time_confidence", val, true).value()); + _detected_object_common_data._position_offset = parse_position_offset(parse_object_member("pos" ,val, true).value()); + _detected_object_common_data._pos_confidence = parse_position_confidence_set(parse_object_member("pos_confidence", val, true).value()); + _detected_object_common_data._speed = parse_uint_member("speed", val, true).value(); + _detected_object_common_data._speed_confidence = speed_confidence_from_int(parse_uint_member("speed_confidence", val, true).value()); + // Optional + _detected_object_common_data._speed_z = parse_uint_member("speed_z", val, false); + // Optional enumeration + if ( val.HasMember("speed_z_confidence")) { + _detected_object_common_data._speed_z_confidence = speed_confidence_from_int( parse_uint_member("speed_z_confidence", val, true).value()); + } + _detected_object_common_data._heading = parse_uint_member("heading", val, true).value(); + _detected_object_common_data._heading_confidence = heading_confidence_from_int(parse_uint_member("heading_conf", val, true).value()); + + _detected_object_common_data._acceleration_4_way = parse_acceleration_4_way(parse_object_member("accel_4_way", val, true ).value()); + if ( val.HasMember("acc_cfd_x")) { + _detected_object_common_data._lateral_acceleration_confidence = acceleration_confidence_from_int(parse_uint_member("acc_cfd_x", val, true).value()); + } + if ( val.HasMember("acc_cfd_y")) { + _detected_object_common_data._longitudinal_acceleration_confidence = acceleration_confidence_from_int(parse_uint_member("acc_cfd_y", val, true).value()); + } + if ( val.HasMember("acc_cfd_z")) { + _detected_object_common_data._vertical_accelaration_confidence = acceleration_confidence_from_int(parse_uint_member("acc_cfd_z", val, true).value()); + } + if ( val.HasMember("acc_cfd_yaw")) { + _detected_object_common_data._yaw_rate_confidence = angular_velocity_confidence_from_int(parse_uint_member("acc_cfd_yaw", val, true).value()); + } + return _detected_object_common_data; + } + + std::optional> parse_detected_object_data_optional(const rapidjson::Value &val){ + std::optional> detected_optional_data; + if (val.HasMember("detected_vehicle_data")){ + detected_optional_data = parse_detected_vehicle_data(parse_object_member("detected_vehicle_data", val, true).value()); + } + else if (val.HasMember("detected_vru_data")) { + detected_optional_data = parse_detected_vru_data(parse_object_member("detected_vru_data", val, true).value()); + + } + else if (val.HasMember("detected_obstacle_data") ) { + detected_optional_data = parse_detected_obstacle_data(parse_object_member("detected_obstacle_data", val, true).value()); + } + return detected_optional_data; + } + + position_offset parse_position_offset(const rapidjson::Value &val) { + position_offset data; + data._offset_x = parse_int_member("offset_x", val, true).value(); + data._offset_y = parse_int_member("offset_y", val, true).value(); + data._offset_y = parse_int_member("offset_y", val, true).value(); + return data; + + } + + position_confidence_set parse_position_confidence_set(const rapidjson::Value &val) { + position_confidence_set data; + data._position_confidence = position_confidence_from_int( parse_uint_member("pos", val, true).value()); + data._elevation_confidence = position_confidence_from_int( parse_uint_member("elevation", val, true).value()); + return data; + } + + acceleration_set_4_way parse_acceleration_4_way(const rapidjson::Value &val) { + acceleration_set_4_way data; + data._lateral_accel = parse_int_member("lat", val, true).value(); + data._longitudinal_accel = parse_int_member("long", val, true).value(); + data._vertical_accel = parse_int_member("vert", val, true).value(); + data._yaw_rate = parse_int_member("yaw", val, true).value(); + return data; + } + + detected_obstacle_data parse_detected_obstacle_data(const rapidjson::Value &val){ + detected_obstacle_data data; + data._size = parse_obstacle_size(parse_object_member("obst_size", val, true).value()); + data._size_confidence = parse_obstacle_size_confidence(parse_object_member("obst_size_confidence", val, true).value()); + return data; + } + + detected_vehicle_data parse_detected_vehicle_data(const rapidjson::Value &val){ + detected_vehicle_data data; + data.exterior_lights = parse_string_member("lights", val, false); { + if ( val.HasMember("veh_attitude")) + data._veh_attitude = parse_vehicle_attitude(parse_object_member("veh_attitude", val, false).value()); + } + if ( val.HasMember("veh_attitude_confidence")) { + data._attitude_confidence = parse_vehicle_attitude_confidence(parse_object_member("veh_attitude_confidence", val, false).value()); + } + if ( val.HasMember("veh_ang_vel")) { + data._angular_velocity = parse_vehicle_angular_velocity_set(parse_object_member("veh_ang_vel", val, false).value()); + } + if ( val.HasMember("veh_ang_vel_confidence")) { + data._angular_velocity_confidence = parse_vehicle_angular_velocity_confidence_set(parse_object_member("veh_ang_vel_confidence", val, false).value()); + } + if ( val.HasMember("size")) { + data._size = parse_vehicle_size(parse_object_member("size", val, false).value()); + } + if ( val.HasMember("vehicle_size_confidence")) { + data._size_confidence = parse_vehicle_size_confidence(parse_object_member("vehicle_size_confidence", val, false).value()); + } + data._vehicle_height = parse_uint_member("height", val, false); + data._vehicle_class = parse_uint_member("vehicle_class", val, false); + data._classification_confidence = parse_uint_member("class_conf", val, false); + return data; + } + + + attitude_confidence parse_vehicle_attitude_confidence(const rapidjson::Value &val){ + attitude_confidence data; + data._pitch_confidence = heading_confidence_from_int(parse_uint_member("pitch_confidence", val, true).value()); + data._roll_confidence = heading_confidence_from_int(parse_uint_member("roll_confidence", val, true).value()); + data._yaw_confidence = heading_confidence_from_int(parse_uint_member("yaw_confidence", val, true).value()); + return data; + + } + + angular_velocity_set parse_vehicle_angular_velocity_set(const rapidjson::Value &val){ + angular_velocity_set data; + data._pitch_rate = parse_int_member("pitch_rate", val, true).value(); + data._roll_rate = parse_int_member("roll_rate", val, true).value(); + return data; + + } + + angular_velocity_confidence_set parse_vehicle_angular_velocity_confidence_set(const rapidjson::Value &val){ + angular_velocity_confidence_set data; + data._pitch_rate_confidence = angular_velocity_confidence_from_int( parse_uint_member("pitch_rate_confidence", val, true).value()); + data._roll_rate_confidence = angular_velocity_confidence_from_int( parse_uint_member("roll_rate_confidence", val, true).value()); + return data; + } + + vehicle_size parse_vehicle_size(const rapidjson::Value &val){ + vehicle_size data; + data._length = parse_uint_member("length", val, true).value(); + data._width = parse_uint_member("width", val, true).value(); + return data; + } + + vehicle_size_confidence parse_vehicle_size_confidence(const rapidjson::Value &val){ + vehicle_size_confidence data; + data._width_confidence = size_value_confidence_from_int(parse_uint_member("vehicle_width_confidence", val, true).value()); + data._length_confidence = size_value_confidence_from_int(parse_uint_member("vehicle_length_confidence", val, true).value()); + if (auto height_confidence_val = parse_uint_member("vehicle_height_confidence", val, false); height_confidence_val.has_value()) { + data._height_confidence = size_value_confidence(height_confidence_val.value()); + } + return data; + } + + attitude parse_vehicle_attitude(const rapidjson::Value &val) { + attitude data; + data._pitch = parse_int_member("pitch", val, true).value(); + data._roll = parse_int_member("roll", val, true).value(); + data._yaw = parse_int_member("yaw", val, true).value(); + return data; + } + + detected_vru_data parse_detected_vru_data(const rapidjson::Value &val){ + detected_vru_data data; + if ( auto basic_type_value = parse_uint_member("basic_type", val, false); basic_type_value.has_value() ) { + data._personal_device_user_type = personal_device_user_type_from_int( basic_type_value.value()); + } + if ( val.HasMember("propulsion") ) { + data._propulsion = parse_propelled_information(parse_object_member("propulsion", val, true).value()); + } + if (auto attachment_value = parse_uint_member("attachment", val, false); attachment_value.has_value() ){ + data._attachment = attachment_from_int( attachment_value.value()); + } + data._attachment_radius = parse_uint_member("radius", val, false); + return data; + } + + std::optional> parse_propelled_information( const rapidjson::Value &val){ + std::optional> data; + if (val.HasMember("human")) { + data = human_propelled_type_from_int(parse_uint_member("human", val, true).value()); + } + else if (val.HasMember("animal")) { + data = animal_propelled_type_from_int(parse_uint_member("animal", val, true).value()); + } + else if (val.HasMember("motor")) { + data = motorized_propelled_type_from_int(parse_uint_member("motor", val, true).value()); + } + else { + throw json_parse_exception("Propelled information requires one of the following to be define: human propelled type, animal propelled type, motorized propelled type."); + } + return data; + } + + + obstacle_size parse_obstacle_size(const rapidjson::Value &val) { + obstacle_size data; + data._length = parse_uint_member("length", val, true).value(); + data._width = parse_uint_member("width", val, true).value(); + // Optional height + data._height = parse_uint_member("height", val, true); + return data; + } + + obstacle_size_confidence parse_obstacle_size_confidence(const rapidjson::Value &val) { + obstacle_size_confidence data; + data._length_confidence = size_value_confidence_from_int( parse_uint_member("length_confidence", val, true).value()); + data._width_confidence = size_value_confidence_from_int( parse_uint_member("width_confidence", val, true).value()); + // Optional height value + if ( auto height_confidence_value = parse_uint_member("height_confidence", val, false); height_confidence_value.has_value() ){ + data._height_confidence = size_value_confidence_from_int( parse_uint_member("height_confidence", val, true).value()); + } + return data; + } +} \ No newline at end of file diff --git a/streets_utils/streets_messages/src/serializers/sensor_data_sharing_msg_json_serializer.cpp b/streets_utils/streets_messages/src/serializers/sensor_data_sharing_msg_json_serializer.cpp new file mode 100644 index 000000000..2e5af2033 --- /dev/null +++ b/streets_utils/streets_messages/src/serializers/sensor_data_sharing_msg_json_serializer.cpp @@ -0,0 +1,353 @@ +// Copyright 2019-2023 Leidos +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +#include "serializers/sensor_data_sharing_msg_json_serializer.hpp" + + +namespace streets_utils::messages{ + std::string to_json(const sensor_data_sharing_msg &msg) { + rapidjson::Document doc; + rapidjson::Value sdsm_json(rapidjson::kObjectType); + sdsm_json.AddMember("msg_cnt", msg._msg_count, doc.GetAllocator()); + sdsm_json.AddMember("source_id", msg._source_id, doc.GetAllocator()); + sdsm_json.AddMember("equipment_type", static_cast(msg._equipment_type), doc.GetAllocator()); + // Construct SDSM Time Stamp JSON Object + auto time_stamp_json = create_timestamp(msg._time_stamp, doc.GetAllocator()); + sdsm_json.AddMember("sdsm_time_stamp", time_stamp_json, doc.GetAllocator()); + // Construct reference position JSON Object + auto position_3d_json = create_position_3d( msg._ref_positon, doc.GetAllocator() ); + sdsm_json.AddMember("ref_pos", position_3d_json, doc.GetAllocator()); + sdsm_json.AddMember("ref_pos_xy_conf", create_positional_accuracy( msg._ref_position_confidence, doc.GetAllocator()), doc.GetAllocator()); + if ( msg._ref_position_elevation_confidence.has_value() ) + sdsm_json.AddMember("ref_pos_el_conf", static_cast(msg._ref_position_elevation_confidence.value()), doc.GetAllocator()); + // Construct object list + sdsm_json.AddMember("objects", create_detected_object_list(msg._objects, doc.GetAllocator()), doc.GetAllocator()); + + rapidjson::StringBuffer buffer; + rapidjson::Writer writer(buffer); + sdsm_json.Accept(writer); + + return buffer.GetString(); + } + + rapidjson::Value create_timestamp(const time_stamp &val, rapidjson::Document::AllocatorType &allocator){ + rapidjson::Value time_stamp_json(rapidjson::kObjectType); + time_stamp_json.AddMember("second", val.second, allocator); + time_stamp_json.AddMember("minute", val.minute, allocator); + time_stamp_json.AddMember("hour", val.hour, allocator); + time_stamp_json.AddMember("day", val.day, allocator); + time_stamp_json.AddMember("month", val.month, allocator); + time_stamp_json.AddMember("year", val.year, allocator); + time_stamp_json.AddMember("offset", val.offset, allocator); + return time_stamp_json; + } + + rapidjson::Value create_position_3d(const position_3d &val, rapidjson::Document::AllocatorType &allocator){ + rapidjson::Value position_3d_json(rapidjson::kObjectType); + position_3d_json.AddMember("long", val._longitude, allocator); + position_3d_json.AddMember("lat", val._latitude, allocator); + if ( val._elevation.has_value() ) { + position_3d_json.AddMember("elevation", val._elevation.value(), allocator); + } + return position_3d_json; + } + + rapidjson::Value create_positional_accuracy(const positional_accuracy &val, rapidjson::Document::AllocatorType &allocator) { + rapidjson::Value positional_accuracy_json(rapidjson::kObjectType); + positional_accuracy_json.AddMember("semi_major", val._semi_major_axis_accuracy, allocator); + positional_accuracy_json.AddMember("semi_minor", val._semi_minor_axis_accuracy, allocator); + positional_accuracy_json.AddMember("orientation", val._semi_major_axis_orientation, allocator); + return positional_accuracy_json; + } + rapidjson::Value create_detected_object_list(const std::vector &val, rapidjson::Document::AllocatorType &allocator ){ + rapidjson::Value detected_object_list_json(rapidjson::kArrayType); + for (const auto &detected_obect : val) { + // Create and push detected object data + detected_object_list_json.PushBack(create_detected_object_data(detected_obect, allocator), allocator); + + } + return detected_object_list_json; + } + + rapidjson::Value create_detected_object_data(const detected_object_data &val, rapidjson::Document::AllocatorType &allocator){ + rapidjson::Value detected_object_data_json(rapidjson::kObjectType); + // Create Common Data + detected_object_data_json.AddMember("detected_object_common_data",create_detected_object_data_common(val._detected_object_common_data,allocator), allocator ); + // Create Optional Data + if ( val._detected_object_optional_data.has_value() ) + detected_object_data_json.AddMember( + "detected_object_optional_data", + create_detected_object_data_optional(val._detected_object_optional_data.value(), allocator), + allocator); + return detected_object_data_json; + } + + + rapidjson::Value create_detected_object_data_common(const detected_object_data_common &val, rapidjson::Document::AllocatorType &allocator){ + rapidjson::Value detected_object_data_common_json(rapidjson::kObjectType); + detected_object_data_common_json.AddMember("obj_type", static_cast(val._object_type), allocator); + detected_object_data_common_json.AddMember("object_id", val._object_id, allocator); + detected_object_data_common_json.AddMember("obj_type_cfd", val._classification_confidence, allocator); + detected_object_data_common_json.AddMember("measurement_time", val._time_measurement_offset, allocator); + detected_object_data_common_json.AddMember("time_confidence", static_cast(val._time_confidence), allocator); + // Create Position + detected_object_data_common_json.AddMember( + "pos", + create_position_3d( val._position_offset, allocator), + allocator); + // Create Position Confidence + detected_object_data_common_json.AddMember("pos_confidence", create_position_confidence_set(val._pos_confidence, allocator), allocator ); + detected_object_data_common_json.AddMember("speed", val._speed, allocator); + detected_object_data_common_json.AddMember("speed_confidence", static_cast(val._speed_confidence), allocator); + if ( val._speed_z.has_value()) + detected_object_data_common_json.AddMember("speed_z", val._speed_z.value(), allocator); + if ( val._speed_z_confidence.has_value()) + detected_object_data_common_json.AddMember( + "speed_confidence_z", + static_cast(val._speed_z_confidence.value()), + allocator); + detected_object_data_common_json.AddMember("heading", val._heading, allocator); + detected_object_data_common_json.AddMember("heading_conf", static_cast(val._heading_confidence), allocator); + if (val._lateral_acceleration_confidence.has_value() ) { + detected_object_data_common_json.AddMember( + "acc_cfd_x", + static_cast(val._lateral_acceleration_confidence.value()), + allocator); + if (val._longitudinal_acceleration_confidence.has_value() ) + detected_object_data_common_json.AddMember( + "acc_cfd_y", + static_cast(val._longitudinal_acceleration_confidence.value()), + allocator); + if (val._vertical_accelaration_confidence.has_value()) + detected_object_data_common_json.AddMember( + "acc_cfd_z", + static_cast(val._vertical_accelaration_confidence.value()), + allocator); + if (val._yaw_rate_confidence.has_value()) + detected_object_data_common_json.AddMember( + "acc_cfd_yaw", + static_cast(val._yaw_rate_confidence.value()), + allocator); + } + if (val._acceleration_4_way.has_value()) { + // Create accel_4_way + detected_object_data_common_json.AddMember( + "accel_4_way", + create_accelaration_set_4_way(val._acceleration_4_way.value(), allocator), + allocator); + } + return detected_object_data_common_json; + } + + rapidjson::Value create_accelaration_set_4_way(const acceleration_set_4_way &val, rapidjson::Document::AllocatorType &allocator) { + rapidjson::Value accelaration_set_4_way_json(rapidjson::kObjectType); + accelaration_set_4_way_json.AddMember("lat", val._lateral_accel, allocator); + accelaration_set_4_way_json.AddMember("long", val._longitudinal_accel, allocator); + accelaration_set_4_way_json.AddMember("vert", val._vertical_accel, allocator); + accelaration_set_4_way_json.AddMember("yaw", val._yaw_rate, allocator); + return accelaration_set_4_way_json; + } + + rapidjson::Value create_position_3d(const position_offset &val, rapidjson::Document::AllocatorType &allocator){ + rapidjson::Value position_3d_json(rapidjson::kObjectType); + position_3d_json.AddMember("offset_x", val._offset_x, allocator); + position_3d_json.AddMember("offset_y", val._offset_y, allocator); + if ( val._offset_z.has_value()) { + position_3d_json.AddMember("offset_z", val._offset_z.value(), allocator); + } + return position_3d_json; + } + + rapidjson::Value create_position_confidence_set(const position_confidence_set &val, rapidjson::Document::AllocatorType &allocator) { + rapidjson::Value position_confidence_json(rapidjson::kObjectType); + position_confidence_json.AddMember("pos",static_cast(val._position_confidence), allocator); + position_confidence_json.AddMember("elevation", static_cast(val._elevation_confidence), allocator); + return position_confidence_json; + } + rapidjson::Value create_detected_object_data_optional(const std::variant &val, rapidjson::Document::AllocatorType &allocator){ + rapidjson::Value detected_object_data_optional_json(rapidjson::kObjectType); + if (std::holds_alternative(val)) { + detected_object_data_optional_json.AddMember("detected_obstacle_data",create_detected_obstacle_data(std::get(val),allocator), allocator); + return detected_object_data_optional_json; + } + else if (std::holds_alternative(val)) { + detected_object_data_optional_json.AddMember("detected_vehicle_data",create_detected_vehicle_data(std::get(val),allocator), allocator); + return detected_object_data_optional_json; + } + else if (std::holds_alternative(val)) { + detected_object_data_optional_json.AddMember("detected_vru_data",create_detected_vru_data(std::get(val),allocator), allocator); + return detected_object_data_optional_json; + } + else { + throw json_utils::json_parse_exception("If present, detected optional data must include one of the following objects : detected obstacle data, detected vehicle data, detected vru data"); + } + } + + + rapidjson::Value create_detected_obstacle_data(const detected_obstacle_data &val, rapidjson::Document::AllocatorType &allocator){ + rapidjson::Value data(rapidjson::kObjectType); + data.AddMember("obst_size", create_obstacle_size(val._size, allocator), allocator); + data.AddMember("obst_size_confidence", create_obstacle_size_confidence(val._size_confidence, allocator), allocator); + return data; + } + + rapidjson::Value create_obstacle_size(const obstacle_size &val, rapidjson::Document::AllocatorType &allocator){ + rapidjson::Value data(rapidjson::kObjectType); + data.AddMember("width", val._width, allocator); + data.AddMember("length", val._length, allocator); + // Optional Height + if (val._height.has_value() ) { + data.AddMember("height", val._height.value(), allocator); + } + + return data; + } + + rapidjson::Value create_obstacle_size_confidence(const obstacle_size_confidence &val, rapidjson::Document::AllocatorType &allocator) { + rapidjson::Value data(rapidjson::kObjectType); + data.AddMember("width_confidence", static_cast(val._width_confidence), allocator); + data.AddMember("length_confidence", static_cast(val._length_confidence), allocator); + // Optional height + if ( val._height_confidence.has_value()) { + data.AddMember("height_confidence", static_cast(val._height_confidence.value()), allocator); + } + return data; + } + + rapidjson::Value create_detected_vru_data(const detected_vru_data &val, rapidjson::Document::AllocatorType &allocator){ + rapidjson::Value data(rapidjson::kObjectType); + if (val._personal_device_user_type.has_value() ) { + data.AddMember("basic_type", static_cast(val._personal_device_user_type.value()), allocator); + } + if (val._attachment.has_value() ) { + data.AddMember("attachment", static_cast(val._attachment.value()), allocator); + } + if (val._propulsion.has_value() ) { + data.AddMember("propulsion", create_propelled_information(val._propulsion.value(), allocator), allocator); + } + if (val._attachment_radius.has_value() ) { + data.AddMember("radius", val._attachment_radius.value(), allocator); + } + return data; + } + + rapidjson::Value create_detected_vehicle_data(const detected_vehicle_data &val, rapidjson::Document::AllocatorType &allocator){ + rapidjson::Value data(rapidjson::kObjectType); + if ( val.exterior_lights.has_value() ) { + data.AddMember("lights", val.exterior_lights.value(), allocator); + } + if ( val._veh_attitude.has_value() ) { + data.AddMember("veh_attitude", create_vehicle_attitude(val._veh_attitude.value(), allocator), allocator); + } + if ( val._attitude_confidence.has_value() ) { + data.AddMember("veh_attitude_confidence", create_vehicle_attitude_confidence(val._attitude_confidence.value(), allocator), allocator); + } + if ( val._angular_velocity.has_value() ) { + data.AddMember("veh_ang_vel", create_angular_velocity(val._angular_velocity.value(), allocator), allocator); + } + if ( val._angular_velocity_confidence.has_value() ){ + data.AddMember("veh_ang_vel_confidence", create_angular_velocity_confidence(val._angular_velocity_confidence.value(),allocator), allocator); + } + if ( val._size.has_value() ) { + data.AddMember("size", create_vehicle_size(val._size.value(), allocator), allocator); + } + if ( val._size_confidence.has_value() ) { + data.AddMember("vehicle_size_confidence", create_vehicle_size_confidence(val._size_confidence.value(), allocator), allocator); + } + if (val._vehicle_height.has_value() ) { + data.AddMember("height", val._vehicle_height.value(), allocator); + } + if (val._vehicle_class.has_value() ) { + data.AddMember("vehicle_class", val._vehicle_class.value(), allocator); + } + if ( val._classification_confidence.has_value() ) { + data.AddMember("vehicle_class_conf", val._classification_confidence.value(), allocator); + } + return data; + } + + + + rapidjson::Value create_propelled_information(const std::variant &val, rapidjson::Document::AllocatorType &allocator) { + rapidjson::Value data(rapidjson::kObjectType); + if ( std::holds_alternative(val) ) { + data.AddMember("human", static_cast( std::get(val)), allocator); + return data; + } + else if ( std::holds_alternative(val) ) { + data.AddMember("animal", static_cast( std::get(val)), allocator); + return data; + } + else if ( std::holds_alternative(val) ) { + data.AddMember("motor", static_cast( std::get(val)), allocator); + return data; + } + else { + throw json_utils::json_parse_exception("If present, propelled infromation must include one of the following objects : animal propelled type, motorized propelled type, human propelled type"); + } + } + + + rapidjson::Value create_vehicle_attitude(const attitude &val, rapidjson::Document::AllocatorType &allocator){ + rapidjson::Value data(rapidjson::kObjectType); + data.AddMember("pitch", val._pitch, allocator); + data.AddMember("roll", val._roll, allocator); + data.AddMember("yaw", val._yaw, allocator); + return data; + } + + rapidjson::Value create_vehicle_attitude_confidence(const attitude_confidence &val, rapidjson::Document::AllocatorType &allocator){ + rapidjson::Value data(rapidjson::kObjectType); + data.AddMember("pitch_confidence", static_cast(val._pitch_confidence), allocator); + data.AddMember("roll_confidence", static_cast(val._roll_confidence), allocator); + data.AddMember("yaw_confidence", static_cast(val._yaw_confidence), allocator); + return data; + } + + rapidjson::Value create_angular_velocity(const angular_velocity_set &val, rapidjson::Document::AllocatorType &allocator){ + rapidjson::Value data(rapidjson::kObjectType); + data.AddMember("pitch_rate", static_cast(val._pitch_rate), allocator); + data.AddMember("roll_rate", static_cast(val._roll_rate), allocator); + return data; + } + + rapidjson::Value create_angular_velocity_confidence(const angular_velocity_confidence_set &val, rapidjson::Document::AllocatorType &allocator){ + rapidjson::Value data(rapidjson::kObjectType); + if ( val._pitch_rate_confidence.has_value() ) { + data.AddMember("pitch_rate_confidence", static_cast(val._pitch_rate_confidence.value()), allocator); + } + if ( val._roll_rate_confidence.has_value() ) { + data.AddMember("roll_rate_confidence", static_cast(val._roll_rate_confidence.value()), allocator); + } + return data; + } + + rapidjson::Value create_vehicle_size(const vehicle_size &val, rapidjson::Document::AllocatorType &allocator){ + rapidjson::Value data(rapidjson::kObjectType); + data.AddMember("width", val._width, allocator); + data.AddMember("length", val._length, allocator); + return data; + } + + rapidjson::Value create_vehicle_size_confidence(const vehicle_size_confidence &val, rapidjson::Document::AllocatorType &allocator){ + rapidjson::Value data(rapidjson::kObjectType); + data.AddMember("vehicle_width_confidence", static_cast(val._width_confidence), allocator); + data.AddMember("vehicle_length_confidence", static_cast(val._length_confidence), allocator); + if ( val._height_confidence.has_value() ) { + data.AddMember("vehicle_height_confidence", static_cast(val._height_confidence.value()), allocator); + } + return data; + } + + +} \ No newline at end of file diff --git a/streets_utils/streets_messages/test/deserializers/sensor_data_sharing_msg_json_deserialization_test.cpp b/streets_utils/streets_messages/test/deserializers/sensor_data_sharing_msg_json_deserialization_test.cpp new file mode 100644 index 000000000..3d9f49c07 --- /dev/null +++ b/streets_utils/streets_messages/test/deserializers/sensor_data_sharing_msg_json_deserialization_test.cpp @@ -0,0 +1,464 @@ +// Copyright 2019-2023 Leidos +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +#include +#include +#include +using namespace streets_utils::messages; + +TEST(sensor_dara_sharing_msg_json_deserialization_test, deserialize) { + + std::string json = "{" + "\"equipment_type\": 0," + "\"msg_cnt\": 255," + "\"ref_pos\": {" + " \"long\": 1800000001," + " \"elevation\": 30," + " \"lat\": 900000001" + "}," + "\"ref_pos_el_conf\": 10," + "\"ref_pos_xy_conf\": {" + " \"orientation\": 25000," + " \"semi_major\": 235," + " \"semi_minor\": 200" + "}," + "\"sdsm_time_stamp\": {" + " \"day\": 4," + " \"hour\": 19," + " \"minute\": 15," + " \"month\": 7," + " \"offset\": 400," + " \"second\": 5000," + " \"year\": 2007" + "}," + "\"source_id\": \"0102C304\"," + "\"objects\": [" + " {" + " \"detected_object_data\": {" + " \"detected_object_common_data\": {" + " \"acc_cfd_x\": 4," + " \"acc_cfd_y\": 5," + " \"acc_cfd_yaw\": 3," + " \"acc_cfd_z\": 6," + " \"accel_4_way\": {" + " \"lat\": -500," + " \"long\": 200," + " \"vert\": 1," + " \"yaw\": 400" + " }," + " \"heading\": 16000," + " \"heading_conf\": 4," + " \"measurement_time\": -1100," + " \"object_id\": 12200," + " \"obj_type\": 1," + " \"obj_type_cfd\": 65," + " \"pos\": {" + " \"offset_x\": 4000," + " \"offset_y\": -720," + " \"offset_z\": 20" + " }," + " \"pos_confidence\": {" + " \"elevation\": 5," + " \"pos\": 2" + " }," + " \"speed\": 2100," + " \"speed_confidence\": 3," + " \"speed_confidence_z\": 4," + " \"speed_z\": 1000," + " \"time_confidence\": 2" + " }" + " }" + " }" + " ]" + "}"; + auto msg = from_json(json ); + + EXPECT_EQ(255, msg._msg_count); + EXPECT_EQ("0102C304", msg._source_id); + EXPECT_EQ(equipment_type::UNKNOWN, msg._equipment_type); + // Confirm timestamp + EXPECT_EQ(400, msg._time_stamp.offset); + EXPECT_EQ(5000, msg._time_stamp.second); + EXPECT_EQ(15, msg._time_stamp.minute); + EXPECT_EQ(19, msg._time_stamp.hour); + EXPECT_EQ(4, msg._time_stamp.day); + EXPECT_EQ(7, msg._time_stamp.month); + EXPECT_EQ(2007, msg._time_stamp.year); + // Confirm reference position + EXPECT_EQ(1800000001,msg._ref_positon._longitude); + EXPECT_EQ(900000001, msg._ref_positon._latitude ); + // Confirm optional elevation is present + EXPECT_TRUE(msg._ref_positon._elevation.has_value()); + EXPECT_EQ(30, msg._ref_positon._elevation); + // Confirm positional accuracy + EXPECT_EQ(235, msg._ref_position_confidence._semi_major_axis_accuracy); + EXPECT_EQ(200, msg._ref_position_confidence._semi_minor_axis_accuracy); + EXPECT_EQ(25000, msg._ref_position_confidence._semi_major_axis_orientation); + // Get Detection + ASSERT_EQ(1, msg._objects.size()); + auto detection = msg._objects[0]; + // Confirm Common Data + EXPECT_EQ(12200 ,detection._detected_object_common_data._object_id); + EXPECT_EQ(object_type_from_int(1) ,detection._detected_object_common_data._object_type); + EXPECT_EQ(65,detection._detected_object_common_data._classification_confidence); + // Confirm 4 way accel + EXPECT_EQ( -500, detection._detected_object_common_data._acceleration_4_way->_lateral_accel); + EXPECT_EQ( 200, detection._detected_object_common_data._acceleration_4_way->_longitudinal_accel); + EXPECT_EQ( 1, detection._detected_object_common_data._acceleration_4_way->_vertical_accel); + EXPECT_EQ( 400, detection._detected_object_common_data._acceleration_4_way->_yaw_rate); + // Confirm acceleration confidence + EXPECT_EQ( acceleration_confidence_from_int(4), detection._detected_object_common_data._lateral_acceleration_confidence); + EXPECT_EQ( acceleration_confidence_from_int(5), detection._detected_object_common_data._longitudinal_acceleration_confidence); + EXPECT_EQ( acceleration_confidence_from_int(6), detection._detected_object_common_data._vertical_accelaration_confidence); + EXPECT_EQ( angular_velocity_confidence_from_int(3), detection._detected_object_common_data._yaw_rate_confidence); + // Confirm position + + + +} + +TEST(sensor_dara_sharing_msg_json_deserialization_test, deserialize_optional_obstacle_data) { + + std::string json = "{" + " \"equipment_type\": 1," + " \"msg_cnt\": 1," + " \"objects\": [" + " {" + " \"detected_object_data\": {" + " \"detected_object_common_data\": {" + " \"acc_cfd_x\": 4," + " \"acc_cfd_y\": 5," + " \"acc_cfd_yaw\": 3," + " \"acc_cfd_z\": 6," + " \"accel_4_way\": {" + " \"lat\": -500," + " \"long\": 200," + " \"vert\": 1," + " \"yaw\": 400" + " }," + " \"heading\": 16000," + " \"heading_conf\": 4," + " \"measurement_time\": -1100," + " \"object_id\": 12200," + " \"obj_type\": 1," + " \"obj_type_cfd\": 65," + " \"pos\": {" + " \"offset_x\": 4000," + " \"offset_y\": -720," + " \"offset_z\": 20" + " }," + " \"pos_confidence\": {" + " \"elevation\": 5," + " \"pos\": 2" + " }," + " \"speed\": 2100," + " \"speed_confidence\": 3," + " \"speed_confidence_z\": 4," + " \"speed_z\": 1000," + " \"time_confidence\": 2" + " }," + " \"detected_object_optional_data\": {" + " \"detected_obstacle_data\": {" + " \"obst_size\": {" + " \"height\": 100," + " \"length\": 300," + " \"width\": 400" + " }," + " \"obst_size_confidence\": {" + " \"height_confidence\": 8," + " \"length_confidence\": 7," + " \"width_confidence\": 6" + " }" + " }" + " }" + " }" + " }" + " ]," + " \"ref_pos\": {" + " \"long\": 600000000," + " \"elevation\": 30," + " \"lat\": 400000000" + " }," + " \"ref_pos_el_conf\": 10," + " \"ref_pos_xy_conf\": {" + " \"orientation\": 25000," + " \"semi_major\": 235," + " \"semi_minor\": 200" + " }," + " \"sdsm_time_stamp\": {" + " \"day\": 4," + " \"hour\": 19," + " \"minute\": 15," + " \"month\": 7," + " \"offset\": 400," + " \"second\": 5000," + " \"year\": 2007" + " }," + " \"source_id\": \"0102C304\"" + " }"; + auto msg = from_json(json ); + + EXPECT_EQ(1, msg._msg_count); + EXPECT_EQ("0102C304", msg._source_id); + EXPECT_EQ(equipment_type::RSU, msg._equipment_type); + // Confirm timestamp + EXPECT_EQ(400, msg._time_stamp.offset); + EXPECT_EQ(5000, msg._time_stamp.second); + EXPECT_EQ(15, msg._time_stamp.minute); + EXPECT_EQ(19, msg._time_stamp.hour); + EXPECT_EQ(4, msg._time_stamp.day); + EXPECT_EQ(7, msg._time_stamp.month); + EXPECT_EQ(2007, msg._time_stamp.year); + // Confirm reference position + EXPECT_EQ(600000000,msg._ref_positon._longitude); + EXPECT_EQ(400000000, msg._ref_positon._latitude ); + // Confirm optional elevation is present + EXPECT_TRUE(msg._ref_positon._elevation.has_value()); + EXPECT_EQ(30, msg._ref_positon._elevation); + // Confirm positional accuracy + EXPECT_EQ(235, msg._ref_position_confidence._semi_major_axis_accuracy); + EXPECT_EQ(200, msg._ref_position_confidence._semi_minor_axis_accuracy); + EXPECT_EQ(25000, msg._ref_position_confidence._semi_major_axis_orientation); + + +} + +TEST(sensor_dara_sharing_msg_json_deserialization_test, deserialize_optional_vru_data) { + + std::string json = "{" + " \"equipment_type\": 1," + " \"msg_cnt\": 1," + " \"objects\": [" + " {" + " \"detected_object_data\": {" + " \"detected_object_common_data\": {" + " \"acc_cfd_x\": 4," + " \"acc_cfd_y\": 5," + " \"acc_cfd_yaw\": 3," + " \"acc_cfd_z\": 6," + " \"accel_4_way\": {" + " \"lat\": -500," + " \"long\": 200," + " \"vert\": 1," + " \"yaw\": 400" + " }," + " \"heading\": 16000," + " \"heading_conf\": 4," + " \"measurement_time\": -1100," + " \"object_id\": 12200," + " \"obj_type\": 1," + " \"obj_type_cfd\": 65," + " \"pos\": {" + " \"offset_x\": 4000," + " \"offset_y\": -720," + " \"offset_z\": 20" + " }," + " \"pos_confidence\": {" + " \"elevation\": 5," + " \"pos\": 2" + " }," + " \"speed\": 2100," + " \"speed_confidence\": 3," + " \"speed_confidence_z\": 4," + " \"speed_z\": 1000," + " \"time_confidence\": 2" + " }," + " \"detected_object_optional_data\": {" + " \"detected_vru_data\": {" + " \"attachment\": 3," + " \"basic_type\": 1," + " \"propulsion\": {" + " \"human\": 2" + " }," + " \"radius\": 30" + " }" + " }" + " }" + " }" + " ]," + " \"ref_pos\": {" + " \"long\": 600000000," + " \"elevation\": 30," + " \"lat\": 400000000" + " }," + " \"ref_pos_el_conf\": 10," + " \"ref_pos_xy_conf\": {" + " \"orientation\": 25000," + " \"semi_major\": 235," + " \"semi_minor\": 200" + " }," + " \"sdsm_time_stamp\": {" + " \"day\": 4," + " \"hour\": 19," + " \"minute\": 15," + " \"month\": 7," + " \"offset\": 400," + " \"second\": 5000," + " \"year\": 2007" + " }," + " \"source_id\": \"0102C304\"" + " }"; + auto msg = from_json(json ); + + EXPECT_EQ(1, msg._msg_count); + EXPECT_EQ("0102C304", msg._source_id); + EXPECT_EQ(equipment_type::RSU, msg._equipment_type); + // Confirm timestamp + EXPECT_EQ(400, msg._time_stamp.offset); + EXPECT_EQ(5000, msg._time_stamp.second); + EXPECT_EQ(15, msg._time_stamp.minute); + EXPECT_EQ(19, msg._time_stamp.hour); + EXPECT_EQ(4, msg._time_stamp.day); + EXPECT_EQ(7, msg._time_stamp.month); + EXPECT_EQ(2007, msg._time_stamp.year); + // Confirm reference position + EXPECT_EQ(600000000,msg._ref_positon._longitude); + EXPECT_EQ(400000000, msg._ref_positon._latitude ); + // Confirm optional elevation is present + EXPECT_TRUE(msg._ref_positon._elevation.has_value()); + EXPECT_EQ(30, msg._ref_positon._elevation); + // Confirm positional accuracy + EXPECT_EQ(235, msg._ref_position_confidence._semi_major_axis_accuracy); + EXPECT_EQ(200, msg._ref_position_confidence._semi_minor_axis_accuracy); + EXPECT_EQ(25000, msg._ref_position_confidence._semi_major_axis_orientation); + + +} + +TEST(sensor_dara_sharing_msg_json_deserialization_test, deserialize_optional_vehicle_data) { + + std::string json = "{" + " \"equipment_type\": 1," + " \"msg_cnt\": 1," + " \"objects\": [" + " {" + " \"detected_object_data\": {" + " \"detected_object_common_data\": {" + " \"acc_cfd_x\": 4," + " \"acc_cfd_y\": 5," + " \"acc_cfd_yaw\": 3," + " \"acc_cfd_z\": 6," + " \"accel_4_way\": {" + " \"lat\": -500," + " \"long\": 200," + " \"vert\": 1," + " \"yaw\": 400" + " }," + " \"heading\": 16000," + " \"heading_conf\": 4," + " \"measurement_time\": -1100," + " \"object_id\": 12200," + " \"obj_type\": 1," + " \"obj_type_cfd\": 65," + " \"pos\": {" + " \"offset_x\": 4000," + " \"offset_y\": -720," + " \"offset_z\": 20" + " }," + " \"pos_confidence\": {" + " \"elevation\": 5," + " \"pos\": 2" + " }," + " \"speed\": 2100," + " \"speed_confidence\": 3," + " \"speed_confidence_z\": 4," + " \"speed_z\": 1000," + " \"time_confidence\": 2" + " }," + " \"detected_object_optional_data\": {" + " \"detected_vehicle_data\": {" + " \"height\": 70," + " \"lights\": 8," + " \"size\": {" + " \"length\": 700," + " \"width\": 300" + " }," + " \"veh_ang_vel\": {" + " \"pitch_rate\": 600," + " \"roll_rate\": -800" + " }," + " \"veh_ang_vel_confidence\": {" + " \"pitch_rate_confidence\": 3," + " \"roll_rate_confidence\": 4" + " }," + " \"veh_attitude\": {" + " \"pitch\": 2400," + " \"roll\": -12000," + " \"yaw\": 400" + " }," + " \"veh_attitude_confidence\": {" + " \"pitch_confidence\": 2," + " \"roll_confidence\": 3," + " \"yaw_confidence\": 4" + " }," + " \"vehicle_class\": 11," + " \"vehicle_class_conf\": 75," + " \"vehicle_size_confidence\": {" + " \"vehicle_height_confidence\": 5," + " \"vehicle_length_confidence\": 6," + " \"vehicle_width_confidence\": 7" + " }" + " }" + " }" + " }" + " }" + " ]," + " \"ref_pos\": {" + " \"long\": 600000000," + " \"elevation\": 30," + " \"lat\": 400000000" + " }," + " \"ref_pos_el_conf\": 10," + " \"ref_pos_xy_conf\": {" + " \"orientation\": 25000," + " \"semi_major\": 235," + " \"semi_minor\": 200" + " }," + " \"sdsm_time_stamp\": {" + " \"day\": 4," + " \"hour\": 19," + " \"minute\": 15," + " \"month\": 7," + " \"offset\": 400," + " \"second\": 5000," + " \"year\": 2007" + " }," + " \"source_id\": \"0102C304\"" + " }"; + auto msg = from_json(json ); + + EXPECT_EQ(1, msg._msg_count); + EXPECT_EQ("0102C304", msg._source_id); + EXPECT_EQ(equipment_type::RSU, msg._equipment_type); + // Confirm timestamp + EXPECT_EQ(400, msg._time_stamp.offset); + EXPECT_EQ(5000, msg._time_stamp.second); + EXPECT_EQ(15, msg._time_stamp.minute); + EXPECT_EQ(19, msg._time_stamp.hour); + EXPECT_EQ(4, msg._time_stamp.day); + EXPECT_EQ(7, msg._time_stamp.month); + EXPECT_EQ(2007, msg._time_stamp.year); + // Confirm reference position + EXPECT_EQ(600000000,msg._ref_positon._longitude); + EXPECT_EQ(400000000, msg._ref_positon._latitude ); + // Confirm optional elevation is present + EXPECT_TRUE(msg._ref_positon._elevation.has_value()); + EXPECT_EQ(30, msg._ref_positon._elevation); + // Confirm positional accuracy + EXPECT_EQ(235, msg._ref_position_confidence._semi_major_axis_accuracy); + EXPECT_EQ(200, msg._ref_position_confidence._semi_minor_axis_accuracy); + EXPECT_EQ(25000, msg._ref_position_confidence._semi_major_axis_orientation); + + +} + diff --git a/streets_utils/streets_messages/test/sensor_data_sharing_msg/acceleration_confidence_test.cpp b/streets_utils/streets_messages/test/sensor_data_sharing_msg/acceleration_confidence_test.cpp new file mode 100644 index 000000000..45ff78c5a --- /dev/null +++ b/streets_utils/streets_messages/test/sensor_data_sharing_msg/acceleration_confidence_test.cpp @@ -0,0 +1,31 @@ +// Copyright 2019-2023 Leidos +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +#include +#include + +using namespace streets_utils::messages; + +TEST(acceleration_confidence_test, test_from_int){ + EXPECT_EQ(acceleration_confidence::UNAVAILABLE, acceleration_confidence_from_int(0)); + EXPECT_EQ(acceleration_confidence::ACCL_100, acceleration_confidence_from_int(1)); + EXPECT_EQ(acceleration_confidence::ACCL_10, acceleration_confidence_from_int(2)); + EXPECT_EQ(acceleration_confidence::ACCL_5, acceleration_confidence_from_int(3)); + EXPECT_EQ(acceleration_confidence::ACCL_1, acceleration_confidence_from_int(4)); + EXPECT_EQ(acceleration_confidence::ACCL_0_1, acceleration_confidence_from_int(5)); + EXPECT_EQ(acceleration_confidence::ACCL_0_05, acceleration_confidence_from_int(6)); + EXPECT_EQ(acceleration_confidence::ACCL_0_01, acceleration_confidence_from_int(7)); + // Value outside of range set to unavailable + EXPECT_THROW( acceleration_confidence_from_int(8), std::invalid_argument); + +} \ No newline at end of file diff --git a/streets_utils/streets_messages/test/sensor_data_sharing_msg/angular_velocity_confidence_test.cpp b/streets_utils/streets_messages/test/sensor_data_sharing_msg/angular_velocity_confidence_test.cpp new file mode 100644 index 000000000..0eb4ddab4 --- /dev/null +++ b/streets_utils/streets_messages/test/sensor_data_sharing_msg/angular_velocity_confidence_test.cpp @@ -0,0 +1,30 @@ +// Copyright 2019-2023 Leidos +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +#include +#include + +using namespace streets_utils::messages; + +TEST(angular_velocity_confidence_test, test_from_int){ + EXPECT_EQ(angular_velocity_confidence::UNAVAILABLE, angular_velocity_confidence_from_int(0)); + EXPECT_EQ(angular_velocity_confidence::DEGSEC_100, angular_velocity_confidence_from_int(1)); + EXPECT_EQ(angular_velocity_confidence::DEGSEC_10, angular_velocity_confidence_from_int(2)); + EXPECT_EQ(angular_velocity_confidence::DEGSEC_05, angular_velocity_confidence_from_int(3)); + EXPECT_EQ(angular_velocity_confidence::DEGSEC_01, angular_velocity_confidence_from_int(4)); + EXPECT_EQ(angular_velocity_confidence::DEGSEC_0_1, angular_velocity_confidence_from_int(5)); + EXPECT_EQ(angular_velocity_confidence::DEGSEC_0_05, angular_velocity_confidence_from_int(6)); + EXPECT_EQ(angular_velocity_confidence::DEGSEC_0_01, angular_velocity_confidence_from_int(7)); + EXPECT_THROW( angular_velocity_confidence_from_int(8), std::invalid_argument); + +} \ No newline at end of file diff --git a/streets_utils/streets_messages/test/sensor_data_sharing_msg/animal_propelled_type_test.cpp b/streets_utils/streets_messages/test/sensor_data_sharing_msg/animal_propelled_type_test.cpp new file mode 100644 index 000000000..544613898 --- /dev/null +++ b/streets_utils/streets_messages/test/sensor_data_sharing_msg/animal_propelled_type_test.cpp @@ -0,0 +1,28 @@ +// Copyright 2019-2023 Leidos +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +#include +#include + +using namespace streets_utils::messages; + +TEST(animal_propelled_type_test, test_from_int){ + EXPECT_EQ(animal_propelled_type::UNAVAILABLE, animal_propelled_type_from_int(0)); + EXPECT_EQ(animal_propelled_type::OTHER_TYPES, animal_propelled_type_from_int(1)); + EXPECT_EQ(animal_propelled_type::ANIMAL_MOUNTED, animal_propelled_type_from_int(2)); + EXPECT_EQ(animal_propelled_type::ANIMAL_DRAWN_CARRIAGE, animal_propelled_type_from_int(3)); + // Value outside of range set to unavailable + EXPECT_THROW( animal_propelled_type_from_int(4), std::invalid_argument); + + +} \ No newline at end of file diff --git a/streets_utils/streets_messages/test/sensor_data_sharing_msg/attachment_test.cpp b/streets_utils/streets_messages/test/sensor_data_sharing_msg/attachment_test.cpp new file mode 100644 index 000000000..0663c4070 --- /dev/null +++ b/streets_utils/streets_messages/test/sensor_data_sharing_msg/attachment_test.cpp @@ -0,0 +1,29 @@ +// Copyright 2019-2023 Leidos +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +#include +#include + +using namespace streets_utils::messages; + +TEST(attachment_test, test_from_int){ + EXPECT_EQ(attachment::UNAVAILABLE, attachment_from_int(0)); + EXPECT_EQ(attachment::STROLLER, attachment_from_int(1)); + EXPECT_EQ(attachment::BICYLE_TRAILER, attachment_from_int(2)); + EXPECT_EQ(attachment::CART, attachment_from_int(3)); + EXPECT_EQ(attachment::WHEEL_CHAIR, attachment_from_int(4)); + EXPECT_EQ(attachment::OTHER_WALK_ASSIST_ATTACHMENTS, attachment_from_int(5)); + EXPECT_EQ(attachment::PET, attachment_from_int(6)); + EXPECT_THROW( attachment_from_int(7), std::invalid_argument); + +} \ No newline at end of file diff --git a/streets_utils/streets_messages/test/sensor_data_sharing_msg/equipment_type_test.cpp b/streets_utils/streets_messages/test/sensor_data_sharing_msg/equipment_type_test.cpp new file mode 100644 index 000000000..9c2ce567e --- /dev/null +++ b/streets_utils/streets_messages/test/sensor_data_sharing_msg/equipment_type_test.cpp @@ -0,0 +1,26 @@ +// Copyright 2019-2023 Leidos +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +#include +#include + +using namespace streets_utils::messages; + +TEST(equipment_type_test, test_from_int){ + EXPECT_EQ(equipment_type::UNKNOWN, equipment_type_from_int(0)); + EXPECT_EQ(equipment_type::RSU, equipment_type_from_int(1)); + EXPECT_EQ(equipment_type::OBU, equipment_type_from_int(2)); + EXPECT_EQ(equipment_type::VRU, equipment_type_from_int(3)); + EXPECT_THROW( equipment_type_from_int(4), std::invalid_argument); + +} \ No newline at end of file diff --git a/streets_utils/streets_messages/test/sensor_data_sharing_msg/heading_confidence_test.cpp b/streets_utils/streets_messages/test/sensor_data_sharing_msg/heading_confidence_test.cpp new file mode 100644 index 000000000..20b93b968 --- /dev/null +++ b/streets_utils/streets_messages/test/sensor_data_sharing_msg/heading_confidence_test.cpp @@ -0,0 +1,31 @@ +// Copyright 2019-2023 Leidos +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +#include +#include + +using namespace streets_utils::messages; + +TEST(heading_confidence_test, test_from_int){ + EXPECT_EQ(heading_confidence::UNAVAILABLE, heading_confidence_from_int(0)); + EXPECT_EQ(heading_confidence::PREC_10_deg, heading_confidence_from_int(1)); + EXPECT_EQ(heading_confidence::PREC_05_deg, heading_confidence_from_int(2)); + EXPECT_EQ(heading_confidence::PREC_01_deg, heading_confidence_from_int(3)); + EXPECT_EQ(heading_confidence::PREC_0_1_deg, heading_confidence_from_int(4)); + EXPECT_EQ(heading_confidence::PREC_0_05_deg, heading_confidence_from_int(5)); + EXPECT_EQ(heading_confidence::PREC_0_01_deg, heading_confidence_from_int(6)); + EXPECT_EQ(heading_confidence::PREC_0_0125_deg, heading_confidence_from_int(7)); + // Value outside of range set to unavailable + EXPECT_THROW( heading_confidence_from_int(8), std::invalid_argument); + +} \ No newline at end of file diff --git a/streets_utils/streets_messages/test/sensor_data_sharing_msg/human_propelled_type_test.cpp b/streets_utils/streets_messages/test/sensor_data_sharing_msg/human_propelled_type_test.cpp new file mode 100644 index 000000000..8484d2eba --- /dev/null +++ b/streets_utils/streets_messages/test/sensor_data_sharing_msg/human_propelled_type_test.cpp @@ -0,0 +1,28 @@ +// Copyright 2019-2023 Leidos +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +#include +#include + +using namespace streets_utils::messages; + +TEST(human_propelled_type_test, test_from_int){ + EXPECT_EQ(human_propelled_type::UNAVAILABLE, human_propelled_type_from_int(0)); + EXPECT_EQ(human_propelled_type::OTHER_TYPES, human_propelled_type_from_int(1)); + EXPECT_EQ(human_propelled_type::ON_FOOT, human_propelled_type_from_int(2)); + EXPECT_EQ(human_propelled_type::SKATEBOARD, human_propelled_type_from_int(3)); + EXPECT_EQ(human_propelled_type::PUSH_OR_KICK_SCOOTER, human_propelled_type_from_int(4)); + EXPECT_EQ(human_propelled_type::WHEELCHAIR, human_propelled_type_from_int(5)); + EXPECT_THROW( human_propelled_type_from_int(6), std::invalid_argument); + +} \ No newline at end of file diff --git a/streets_utils/streets_messages/test/sensor_data_sharing_msg/motorized_propelled_type_test.cpp b/streets_utils/streets_messages/test/sensor_data_sharing_msg/motorized_propelled_type_test.cpp new file mode 100644 index 000000000..209d64a70 --- /dev/null +++ b/streets_utils/streets_messages/test/sensor_data_sharing_msg/motorized_propelled_type_test.cpp @@ -0,0 +1,28 @@ +// Copyright 2019-2023 Leidos +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +#include +#include + +using namespace streets_utils::messages; + +TEST(motorized_propelled_type_test, test_from_int){ + EXPECT_EQ(motorized_propelled_type::UNAVAILABLE, motorized_propelled_type_from_int(0)); + EXPECT_EQ(motorized_propelled_type::OTHER_TYPES, motorized_propelled_type_from_int(1)); + EXPECT_EQ(motorized_propelled_type::WHEEL_CHAIR, motorized_propelled_type_from_int(2)); + EXPECT_EQ(motorized_propelled_type::BICYCLE, motorized_propelled_type_from_int(3)); + EXPECT_EQ(motorized_propelled_type::SCOOTER, motorized_propelled_type_from_int(4)); + EXPECT_EQ(motorized_propelled_type::SELF_BALANCING_DEVICE, motorized_propelled_type_from_int(5)); + EXPECT_THROW( motorized_propelled_type_from_int(6), std::invalid_argument); + +} \ No newline at end of file diff --git a/streets_utils/streets_messages/test/sensor_data_sharing_msg/object_type_test.cpp b/streets_utils/streets_messages/test/sensor_data_sharing_msg/object_type_test.cpp new file mode 100644 index 000000000..c39349bda --- /dev/null +++ b/streets_utils/streets_messages/test/sensor_data_sharing_msg/object_type_test.cpp @@ -0,0 +1,26 @@ +// Copyright 2019-2023 Leidos +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +#include +#include + +using namespace streets_utils::messages; + +TEST(object_type_test, test_from_int){ + EXPECT_EQ(object_type::UNKNOWN, object_type_from_int(0)); + EXPECT_EQ(object_type::VEHICLE, object_type_from_int(1)); + EXPECT_EQ(object_type::VRU, object_type_from_int(2)); + EXPECT_EQ(object_type::ANIMAL, object_type_from_int(3)); + EXPECT_THROW( object_type_from_int(4), std::invalid_argument); + +} \ No newline at end of file diff --git a/streets_utils/streets_messages/test/sensor_data_sharing_msg/personal_device_user_type.cpp b/streets_utils/streets_messages/test/sensor_data_sharing_msg/personal_device_user_type.cpp new file mode 100644 index 000000000..17c2e9598 --- /dev/null +++ b/streets_utils/streets_messages/test/sensor_data_sharing_msg/personal_device_user_type.cpp @@ -0,0 +1,27 @@ +// Copyright 2019-2023 Leidos +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +#include +#include + +using namespace streets_utils::messages; + +TEST(personal_device_user_type_test, test_from_int){ + EXPECT_EQ(personal_device_user_type::UNAVAILABLE, personal_device_user_type_from_int(0)); + EXPECT_EQ(personal_device_user_type::PEDESTRIAN, personal_device_user_type_from_int(1)); + EXPECT_EQ(personal_device_user_type::PEDALCYCLIST, personal_device_user_type_from_int(2)); + EXPECT_EQ(personal_device_user_type::PUBLIC_SAFETY_WORKER, personal_device_user_type_from_int(3)); + EXPECT_EQ(personal_device_user_type::ANIMAL, personal_device_user_type_from_int(4)); + EXPECT_THROW( personal_device_user_type_from_int(5), std::invalid_argument); + +} \ No newline at end of file diff --git a/streets_utils/streets_messages/test/sensor_data_sharing_msg/position_confidence_test.cpp b/streets_utils/streets_messages/test/sensor_data_sharing_msg/position_confidence_test.cpp new file mode 100644 index 000000000..5cdd8e79f --- /dev/null +++ b/streets_utils/streets_messages/test/sensor_data_sharing_msg/position_confidence_test.cpp @@ -0,0 +1,38 @@ +// Copyright 2019-2023 Leidos +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +#include +#include + +using namespace streets_utils::messages; + +TEST(position_confidence_test, test_from_int){ + EXPECT_EQ(position_confidence::UNAVAILABLE, position_confidence_from_int(0)); + EXPECT_EQ(position_confidence::A_500M, position_confidence_from_int(1)); + EXPECT_EQ(position_confidence::A_200M, position_confidence_from_int(2)); + EXPECT_EQ(position_confidence::A_100M, position_confidence_from_int(3)); + EXPECT_EQ(position_confidence::A_50M, position_confidence_from_int(4)); + EXPECT_EQ(position_confidence::A_20M, position_confidence_from_int(5)); + EXPECT_EQ(position_confidence::A_10M, position_confidence_from_int(6)); + EXPECT_EQ(position_confidence::A_5M, position_confidence_from_int(7)); + EXPECT_EQ(position_confidence::A_2M, position_confidence_from_int(8)); + EXPECT_EQ(position_confidence::A_1M, position_confidence_from_int(9)); + EXPECT_EQ(position_confidence::A_50CM, position_confidence_from_int(10)); + EXPECT_EQ(position_confidence::A_20CM, position_confidence_from_int(11)); + EXPECT_EQ(position_confidence::A_10CM, position_confidence_from_int(12)); + EXPECT_EQ(position_confidence::A_5CM, position_confidence_from_int(13)); + EXPECT_EQ(position_confidence::A_2CM, position_confidence_from_int(14)); + EXPECT_EQ(position_confidence::A_1CM, position_confidence_from_int(15)); + EXPECT_THROW( position_confidence_from_int(16), std::invalid_argument); + +} \ No newline at end of file diff --git a/streets_utils/streets_messages/test/sensor_data_sharing_msg/size_value_confidence_test.cpp b/streets_utils/streets_messages/test/sensor_data_sharing_msg/size_value_confidence_test.cpp new file mode 100644 index 000000000..0f7df67c5 --- /dev/null +++ b/streets_utils/streets_messages/test/sensor_data_sharing_msg/size_value_confidence_test.cpp @@ -0,0 +1,36 @@ +// Copyright 2019-2023 Leidos +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +#include +#include + +using namespace streets_utils::messages; + +TEST(size_value_confidence_test, test_from_int){ + EXPECT_EQ(size_value_confidence::UNAVAILABLE, size_value_confidence_from_int(0)); + EXPECT_EQ(size_value_confidence::SIZE_100, size_value_confidence_from_int(1)); + EXPECT_EQ(size_value_confidence::SIZE_50, size_value_confidence_from_int(2)); + EXPECT_EQ(size_value_confidence::SIZE_20, size_value_confidence_from_int(3)); + EXPECT_EQ(size_value_confidence::SIZE_10, size_value_confidence_from_int(4)); + EXPECT_EQ(size_value_confidence::SIZE_5, size_value_confidence_from_int(5)); + EXPECT_EQ(size_value_confidence::SIZE_2, size_value_confidence_from_int(6)); + EXPECT_EQ(size_value_confidence::SIZE_1, size_value_confidence_from_int(7)); + EXPECT_EQ(size_value_confidence::SIZE_0_5, size_value_confidence_from_int(8)); + EXPECT_EQ(size_value_confidence::SIZE_0_2, size_value_confidence_from_int(9)); + EXPECT_EQ(size_value_confidence::SIZE_0_1, size_value_confidence_from_int(10)); + EXPECT_EQ(size_value_confidence::SIZE_0_05, size_value_confidence_from_int(11)); + EXPECT_EQ(size_value_confidence::SIZE_0_02, size_value_confidence_from_int(12)); + EXPECT_EQ(size_value_confidence::SIZE_0_01, size_value_confidence_from_int(13)); + EXPECT_THROW( size_value_confidence_from_int(14), std::invalid_argument); + +} \ No newline at end of file diff --git a/streets_utils/streets_messages/test/sensor_data_sharing_msg/speed_confidence_test.cpp b/streets_utils/streets_messages/test/sensor_data_sharing_msg/speed_confidence_test.cpp new file mode 100644 index 000000000..19a98057a --- /dev/null +++ b/streets_utils/streets_messages/test/sensor_data_sharing_msg/speed_confidence_test.cpp @@ -0,0 +1,30 @@ +// Copyright 2019-2023 Leidos +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +#include +#include + +using namespace streets_utils::messages; + +TEST(speed_confidence, test_from_int){ + EXPECT_EQ(speed_confidence::UNAVAILABLE, speed_confidence_from_int(0)); + EXPECT_EQ(speed_confidence::PREC_100ms, speed_confidence_from_int(1)); + EXPECT_EQ(speed_confidence::PREC_10ms, speed_confidence_from_int(2)); + EXPECT_EQ(speed_confidence::PREC_5ms, speed_confidence_from_int(3)); + EXPECT_EQ(speed_confidence::PREC_1ms, speed_confidence_from_int(4)); + EXPECT_EQ(speed_confidence::PREC_0_1ms, speed_confidence_from_int(5)); + EXPECT_EQ(speed_confidence::PREC_0_05ms, speed_confidence_from_int(6)); + EXPECT_EQ(speed_confidence::PREC_0_01ms, speed_confidence_from_int(7)); + EXPECT_THROW( speed_confidence_from_int(8), std::invalid_argument); + +} \ No newline at end of file diff --git a/streets_utils/streets_messages/test/sensor_data_sharing_msg/time_confidence_test.cpp b/streets_utils/streets_messages/test/sensor_data_sharing_msg/time_confidence_test.cpp new file mode 100644 index 000000000..690bdefcb --- /dev/null +++ b/streets_utils/streets_messages/test/sensor_data_sharing_msg/time_confidence_test.cpp @@ -0,0 +1,62 @@ +// Copyright 2019-2023 Leidos +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +#include +#include + +using namespace streets_utils::messages; + +TEST(time_confidence_test, test_from_int){ + EXPECT_EQ(time_confidence::UNAVAILABLE, time_confidence_from_int(0)); + EXPECT_EQ(time_confidence::TIME_100_000, time_confidence_from_int(1)); + EXPECT_EQ(time_confidence::TIME_050_000, time_confidence_from_int(2)); + EXPECT_EQ(time_confidence::TIME_020_000, time_confidence_from_int(3)); + EXPECT_EQ(time_confidence::TIME_010_000, time_confidence_from_int(4)); + EXPECT_EQ(time_confidence::TIME_002_000, time_confidence_from_int(5)); + EXPECT_EQ(time_confidence::TIME_001_000, time_confidence_from_int(6)); + EXPECT_EQ(time_confidence::TIME_000_500, time_confidence_from_int(7)); + EXPECT_EQ(time_confidence::TIME_000_200, time_confidence_from_int(8)); + EXPECT_EQ(time_confidence::TIME_000_100, time_confidence_from_int(9)); + EXPECT_EQ(time_confidence::TIME_000_050, time_confidence_from_int(10)); + EXPECT_EQ(time_confidence::TIME_000_020, time_confidence_from_int(11)); + EXPECT_EQ(time_confidence::TIME_000_010, time_confidence_from_int(12)); + EXPECT_EQ(time_confidence::TIME_000_005, time_confidence_from_int(13)); + EXPECT_EQ(time_confidence::TIME_000_002, time_confidence_from_int(14)); + EXPECT_EQ(time_confidence::TIME_000_001, time_confidence_from_int(15)); + EXPECT_EQ(time_confidence::TIME_000_000_5, time_confidence_from_int(16)); + EXPECT_EQ(time_confidence::TIME_000_000_2, time_confidence_from_int(17)); + EXPECT_EQ(time_confidence::TIME_000_000_1, time_confidence_from_int(18)); + EXPECT_EQ(time_confidence::TIME_000_000_05, time_confidence_from_int(19)); + EXPECT_EQ(time_confidence::TIME_000_000_02, time_confidence_from_int(20)); + EXPECT_EQ(time_confidence::TIME_000_000_01, time_confidence_from_int(21)); + EXPECT_EQ(time_confidence::TIME_000_000_005, time_confidence_from_int(22)); + EXPECT_EQ(time_confidence::TIME_000_000_002, time_confidence_from_int(23)); + EXPECT_EQ(time_confidence::TIME_000_000_001, time_confidence_from_int(24)); + EXPECT_EQ(time_confidence::TIME_000_000_000_5, time_confidence_from_int(25)); + EXPECT_EQ(time_confidence::TIME_000_000_000_2, time_confidence_from_int(26)); + EXPECT_EQ(time_confidence::TIME_000_000_000_1, time_confidence_from_int(27)); + EXPECT_EQ(time_confidence::TIME_000_000_000_05, time_confidence_from_int(28)); + EXPECT_EQ(time_confidence::TIME_000_000_000_02, time_confidence_from_int(29)); + EXPECT_EQ(time_confidence::TIME_000_000_000_01, time_confidence_from_int(30)); + EXPECT_EQ(time_confidence::TIME_000_000_000_005, time_confidence_from_int(31)); + EXPECT_EQ(time_confidence::TIME_000_000_000_002, time_confidence_from_int(32)); + EXPECT_EQ(time_confidence::TIME_000_000_000_001, time_confidence_from_int(33)); + EXPECT_EQ(time_confidence::TIME_000_000_000_000_5, time_confidence_from_int(34)); + EXPECT_EQ(time_confidence::TIME_000_000_000_000_2, time_confidence_from_int(35)); + EXPECT_EQ(time_confidence::TIME_000_000_000_000_1, time_confidence_from_int(36)); + EXPECT_EQ(time_confidence::TIME_000_000_000_000_05, time_confidence_from_int(37)); + EXPECT_EQ(time_confidence::TIME_000_000_000_000_02, time_confidence_from_int(38)); + EXPECT_EQ(time_confidence::TIME_000_000_000_000_01, time_confidence_from_int(39)); + EXPECT_THROW( time_confidence_from_int(40), std::invalid_argument); + +} \ No newline at end of file diff --git a/streets_utils/streets_messages/test/serializers/sensor_data_sharing_msg_json_serialization_test.cpp b/streets_utils/streets_messages/test/serializers/sensor_data_sharing_msg_json_serialization_test.cpp new file mode 100644 index 000000000..390c201c5 --- /dev/null +++ b/streets_utils/streets_messages/test/serializers/sensor_data_sharing_msg_json_serialization_test.cpp @@ -0,0 +1,976 @@ +// Copyright 2019-2023 Leidos +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +#include +#include +#include + +using namespace streets_utils::messages; +using namespace streets_utils::json_utils; + +TEST(sensor_data_sharing_msg_json_serialization_test, confirm_required_properties_max_value) { + sensor_data_sharing_msg msg; + msg._equipment_type = equipment_type::RSU; + msg._msg_count = 255; + msg._source_id = "00000001"; + msg._ref_positon._latitude=900000001; + msg._ref_positon._longitude=1800000001; + msg._time_stamp.second = 65535; + msg._time_stamp.minute = 60; + msg._time_stamp.hour= 31; + msg._time_stamp.day = 31; + msg._time_stamp.month = 12; + msg._time_stamp.year = 4095; // Max + msg._ref_position_confidence._semi_major_axis_accuracy = 255; + msg._ref_position_confidence._semi_minor_axis_accuracy = 255; + msg._ref_position_confidence._semi_major_axis_orientation = 65535; + // Add Detected Object + detected_object_data detected_object; + detected_object._detected_object_common_data._object_id = 65525; + detected_object._detected_object_common_data._object_type = object_type::ANIMAL; + detected_object._detected_object_common_data._classification_confidence = 101; + detected_object._detected_object_common_data._heading = 28800; + detected_object._detected_object_common_data._heading_confidence = heading_confidence::PREC_01_deg; + detected_object._detected_object_common_data._position_offset._offset_x = 32767; + detected_object._detected_object_common_data._position_offset._offset_y = 32767; + detected_object._detected_object_common_data._position_offset._offset_z = 32767; + detected_object._detected_object_common_data._pos_confidence._position_confidence = position_confidence::A_10CM; + detected_object._detected_object_common_data._pos_confidence._elevation_confidence = position_confidence::A_1M; + detected_object._detected_object_common_data._speed = 8191; + detected_object._detected_object_common_data._time_measurement_offset = 1500; + detected_object._detected_object_common_data._time_confidence = time_confidence::TIME_000_000_002; + msg._objects.push_back(detected_object); + // Serialize Msg + std::string json = to_json(msg); + // String is not empty + EXPECT_FALSE(json.empty()); + // Confirm result has msg information in desired structure + auto result = parse_json(json); + + // Confirm timestamp data, Should fail at assert statements since if this require property is + // not available the other calls will fail + ASSERT_TRUE(result.HasMember("sdsm_time_stamp")); + ASSERT_TRUE(result.FindMember("sdsm_time_stamp")->value.IsObject()); + EXPECT_EQ( msg._time_stamp.second, result["sdsm_time_stamp"]["second"].GetInt()); + EXPECT_EQ( msg._time_stamp.minute, result["sdsm_time_stamp"]["minute"].GetInt()); + EXPECT_EQ( msg._time_stamp.hour, result["sdsm_time_stamp"]["hour"].GetInt()); + EXPECT_EQ( msg._time_stamp.day, result["sdsm_time_stamp"]["day"].GetInt()); + EXPECT_EQ( msg._time_stamp.month, result["sdsm_time_stamp"]["month"].GetInt()); + EXPECT_EQ( msg._time_stamp.year, result["sdsm_time_stamp"]["year"].GetInt()); + + EXPECT_EQ( msg._msg_count, result["msg_cnt"].GetUint()); + EXPECT_EQ( msg._source_id, result["source_id"].GetString()); + EXPECT_EQ( static_cast(msg._equipment_type), result["equipment_type"].GetInt()); + // Confirm ref position confidence + ASSERT_TRUE(result.HasMember("ref_pos_xy_conf")); + ASSERT_TRUE(result.FindMember("ref_pos_xy_conf")->value.IsObject()); + EXPECT_EQ(msg._ref_position_confidence._semi_major_axis_accuracy, result["ref_pos_xy_conf"]["semi_major"].GetUint()); + EXPECT_EQ(msg._ref_position_confidence._semi_minor_axis_accuracy, result["ref_pos_xy_conf"]["semi_minor"].GetUint()); + EXPECT_EQ(msg._ref_position_confidence._semi_major_axis_orientation, result["ref_pos_xy_conf"]["orientation"].GetInt()); + // Confirm optional elevation parameter is not present + EXPECT_FALSE( result.HasMember("ref_pos_el_conf")); + // Confirm ref position + ASSERT_TRUE(result.HasMember("ref_pos")); + ASSERT_TRUE(result.FindMember("ref_pos")->value.IsObject()); + EXPECT_EQ( msg._ref_positon._longitude, result["ref_pos"]["long"].GetInt64()); + EXPECT_EQ( msg._ref_positon._latitude, result["ref_pos"]["lat"].GetInt64()); + // Optional parameter is not present + EXPECT_FALSE(result["ref_pos"].HasMember("elevation")); + + // Confirm List of detected object exists + ASSERT_TRUE(result.HasMember("objects")); + ASSERT_TRUE(result.FindMember("objects")->value.IsArray()); + ASSERT_EQ(1,result.FindMember("objects")->value.GetArray().Size()); + ASSERT_TRUE(result["objects"].GetArray()[0].IsObject()); + // Confirm object properties + auto object = result["objects"].GetArray()[0].GetObject(); + // Assert Object has common data + ASSERT_TRUE(object.HasMember("detected_object_common_data")); + ASSERT_TRUE(object.FindMember("detected_object_common_data")->value.IsObject()); + ASSERT_FALSE(object.HasMember("detected_object_optional_data")); + // Retreive Object common data + auto object_common_data = object["detected_object_common_data"].GetObject(); + // Retreive Object common data from + auto msg_object_common_data = msg._objects[0]._detected_object_common_data; + // Confirm Object common data properties + EXPECT_EQ(static_cast(msg_object_common_data._object_type), object_common_data["obj_type"].GetUint()); + EXPECT_EQ(msg_object_common_data._classification_confidence, object_common_data["obj_type_cfd"].GetUint()); + EXPECT_EQ(msg_object_common_data._object_id, object_common_data["object_id"].GetUint()); + EXPECT_EQ(msg_object_common_data._time_measurement_offset, object_common_data["measurement_time"].GetInt()); + EXPECT_EQ(static_cast(msg_object_common_data._time_confidence), object_common_data["time_confidence"].GetUint()); + EXPECT_EQ(msg_object_common_data._speed, object_common_data["speed"].GetUint()); + EXPECT_EQ(msg_object_common_data._heading, object_common_data["heading"].GetUint()); + EXPECT_EQ(static_cast(msg_object_common_data._heading_confidence), object_common_data["heading_conf"].GetUint()); + // Test Optional properties not present + EXPECT_FALSE(object_common_data.HasMember("speed_z")); + EXPECT_FALSE(object_common_data.HasMember("speed_confidence_z")); + EXPECT_FALSE(object_common_data.HasMember("accel_4_way")); + EXPECT_FALSE(object_common_data.HasMember("acc_cfd_x")); + EXPECT_FALSE(object_common_data.HasMember("acc_cfd_y")); + EXPECT_FALSE(object_common_data.HasMember("acc_cfd_yaw")); + EXPECT_FALSE(object_common_data.HasMember("acc_cfd_z")); + +} + +TEST(sensor_data_sharing_msg_json_serialization_test, confirm_required_properties_min_value) { + sensor_data_sharing_msg msg; + msg._equipment_type = equipment_type::OBU; + msg._msg_count = 1; + msg._source_id = "00000001"; + msg._ref_positon._latitude=-90000000; + msg._ref_positon._longitude=-1800000000; + msg._time_stamp.second = 0; + msg._time_stamp.minute = 0; + msg._time_stamp.hour= 0; + msg._time_stamp.day = 0; + msg._time_stamp.month = 0; + msg._time_stamp.year = 0; + msg._ref_position_confidence._semi_major_axis_accuracy = 0; + msg._ref_position_confidence._semi_minor_axis_accuracy = 0; + msg._ref_position_confidence._semi_major_axis_orientation = 0; + // Add Detected Object + detected_object_data detected_object; + detected_object._detected_object_common_data._object_id = 0; + detected_object._detected_object_common_data._object_type = object_type::VEHICLE; + detected_object._detected_object_common_data._classification_confidence = 0; + detected_object._detected_object_common_data._heading = 0; + detected_object._detected_object_common_data._heading_confidence = heading_confidence::PREC_0_0125_deg; + detected_object._detected_object_common_data._position_offset._offset_x = -32767; + detected_object._detected_object_common_data._position_offset._offset_y = -32767; + detected_object._detected_object_common_data._position_offset._offset_z = -32767; + detected_object._detected_object_common_data._pos_confidence._position_confidence = position_confidence::A_500M; + detected_object._detected_object_common_data._pos_confidence._elevation_confidence = position_confidence::A_1CM; + detected_object._detected_object_common_data._speed = 0; + detected_object._detected_object_common_data._time_measurement_offset = -1500; + detected_object._detected_object_common_data._time_confidence = time_confidence::TIME_000_000_000_000_01; + msg._objects.push_back(detected_object); + // Serialize Msg + std::string json = to_json(msg); + // String is not empty + EXPECT_FALSE(json.empty()); + // Confirm result has msg information in desired structure + auto result = parse_json(json); + + // Confirm timestamp data, Should fail at assert statements since if this require property is + // not available the other calls will fail + ASSERT_TRUE(result.HasMember("sdsm_time_stamp")); + ASSERT_TRUE(result.FindMember("sdsm_time_stamp")->value.IsObject()); + EXPECT_EQ( msg._time_stamp.second, result["sdsm_time_stamp"]["second"].GetInt()); + EXPECT_EQ( msg._time_stamp.minute, result["sdsm_time_stamp"]["minute"].GetInt()); + EXPECT_EQ( msg._time_stamp.hour, result["sdsm_time_stamp"]["hour"].GetInt()); + EXPECT_EQ( msg._time_stamp.day, result["sdsm_time_stamp"]["day"].GetInt()); + EXPECT_EQ( msg._time_stamp.month, result["sdsm_time_stamp"]["month"].GetInt()); + EXPECT_EQ( msg._time_stamp.year, result["sdsm_time_stamp"]["year"].GetInt()); + + EXPECT_EQ( msg._msg_count, result["msg_cnt"].GetUint()); + EXPECT_EQ( msg._source_id, result["source_id"].GetString()); + EXPECT_EQ( static_cast(msg._equipment_type), result["equipment_type"].GetInt()); + // Confirm ref position confidence + ASSERT_TRUE(result.HasMember("ref_pos_xy_conf")); + ASSERT_TRUE(result.FindMember("ref_pos_xy_conf")->value.IsObject()); + EXPECT_EQ(msg._ref_position_confidence._semi_major_axis_accuracy, result["ref_pos_xy_conf"]["semi_major"].GetUint()); + EXPECT_EQ(msg._ref_position_confidence._semi_minor_axis_accuracy, result["ref_pos_xy_conf"]["semi_minor"].GetUint()); + EXPECT_EQ(msg._ref_position_confidence._semi_major_axis_orientation, result["ref_pos_xy_conf"]["orientation"].GetInt()); + // Confirm optional elevation parameter is not present + EXPECT_FALSE( result.HasMember("ref_pos_el_conf")); + // Confirm ref position + ASSERT_TRUE(result.HasMember("ref_pos")); + ASSERT_TRUE(result.FindMember("ref_pos")->value.IsObject()); + EXPECT_EQ( msg._ref_positon._longitude, result["ref_pos"]["long"].GetInt64()); + EXPECT_EQ( msg._ref_positon._latitude, result["ref_pos"]["lat"].GetInt64()); + // Optional parameter is not present + EXPECT_FALSE(result["ref_pos"].HasMember("elevation")); + + // Confirm List of detected object exists + ASSERT_TRUE(result.HasMember("objects")); + ASSERT_TRUE(result.FindMember("objects")->value.IsArray()); + ASSERT_EQ(1,result.FindMember("objects")->value.GetArray().Size()); + ASSERT_TRUE(result["objects"].GetArray()[0].IsObject()); + // Confirm object properties + auto object = result["objects"].GetArray()[0].GetObject(); + // Assert Object has common data + ASSERT_TRUE(object.HasMember("detected_object_common_data")); + ASSERT_TRUE(object.FindMember("detected_object_common_data")->value.IsObject()); + ASSERT_FALSE(object.HasMember("detected_object_optional_data")); + // Retreive Object common data + auto object_common_data = object["detected_object_common_data"].GetObject(); + // Retreive Object common data from + auto msg_object_common_data = msg._objects[0]._detected_object_common_data; + // Confirm Object common data properties + EXPECT_EQ(static_cast(msg_object_common_data._object_type), object_common_data["obj_type"].GetUint()); + EXPECT_EQ(msg_object_common_data._classification_confidence, object_common_data["obj_type_cfd"].GetUint()); + EXPECT_EQ(msg_object_common_data._object_id, object_common_data["object_id"].GetUint()); + EXPECT_EQ(msg_object_common_data._time_measurement_offset, object_common_data["measurement_time"].GetInt()); + EXPECT_EQ(static_cast(msg_object_common_data._time_confidence), object_common_data["time_confidence"].GetUint()); + EXPECT_EQ(msg_object_common_data._speed, object_common_data["speed"].GetUint()); + EXPECT_EQ(msg_object_common_data._heading, object_common_data["heading"].GetUint()); + EXPECT_EQ(static_cast(msg_object_common_data._heading_confidence), object_common_data["heading_conf"].GetUint()); + // Test Optional properties not present + EXPECT_FALSE(object_common_data.HasMember("speed_z")); + EXPECT_FALSE(object_common_data.HasMember("speed_confidence_z")); + EXPECT_FALSE(object_common_data.HasMember("accel_4_way")); + EXPECT_FALSE(object_common_data.HasMember("acc_cfd_x")); + EXPECT_FALSE(object_common_data.HasMember("acc_cfd_y")); + EXPECT_FALSE(object_common_data.HasMember("acc_cfd_yaw")); + EXPECT_FALSE(object_common_data.HasMember("acc_cfd_z")); + +} + +TEST(sensor_data_sharing_msg_json_serialization_test, confirm_optional_vru_human_properties) { + sensor_data_sharing_msg msg; + msg._equipment_type = equipment_type::OBU; + msg._msg_count = 1; + msg._source_id = "00000001"; + msg._ref_positon._latitude=-90000000; + msg._ref_positon._longitude=-1800000000; + msg._ref_positon._elevation = -32768; + msg._time_stamp.second = 0; + msg._time_stamp.minute = 0; + msg._time_stamp.hour= 0; + msg._time_stamp.day = 0; + msg._time_stamp.month = 0; + msg._time_stamp.year = 0; + msg._ref_position_confidence._semi_major_axis_accuracy = 0; + msg._ref_position_confidence._semi_minor_axis_accuracy = 0; + msg._ref_position_confidence._semi_major_axis_orientation = 0; + msg._ref_position_elevation_confidence = position_confidence::A_10M; + // Add Detected Object + detected_object_data detected_object; + detected_object._detected_object_common_data._object_id = 0; + detected_object._detected_object_common_data._object_type = object_type::VEHICLE; + detected_object._detected_object_common_data._classification_confidence = 0; + detected_object._detected_object_common_data._heading = 0; + detected_object._detected_object_common_data._heading_confidence = heading_confidence::PREC_0_0125_deg; + detected_object._detected_object_common_data._position_offset._offset_x = -32767; + detected_object._detected_object_common_data._position_offset._offset_y = -32767; + detected_object._detected_object_common_data._position_offset._offset_z = -32767; + detected_object._detected_object_common_data._pos_confidence._position_confidence = position_confidence::A_500M; + detected_object._detected_object_common_data._pos_confidence._elevation_confidence = position_confidence::A_1CM; + detected_object._detected_object_common_data._speed = 0; + detected_object._detected_object_common_data._speed_z = 8191; + detected_object._detected_object_common_data._speed_confidence = speed_confidence::UNAVAILABLE; + detected_object._detected_object_common_data._speed_z_confidence = speed_confidence::PREC_100ms; + // Create acceleration 4 way + acceleration_set_4_way accel_set; + accel_set._lateral_accel = 2001; + accel_set._longitudinal_accel =2001; + accel_set._vertical_accel = 127; + accel_set._yaw_rate = 32767; + detected_object._detected_object_common_data._acceleration_4_way= accel_set; + // Create acceleration confidence set + detected_object._detected_object_common_data._lateral_acceleration_confidence = acceleration_confidence::ACCL_0_1; + detected_object._detected_object_common_data._longitudinal_acceleration_confidence = acceleration_confidence::ACCL_100; + detected_object._detected_object_common_data._vertical_accelaration_confidence = acceleration_confidence::ACCL_0_01; + detected_object._detected_object_common_data._yaw_rate_confidence = angular_velocity_confidence::UNAVAILABLE; + + detected_object._detected_object_common_data._time_measurement_offset = -1500; + detected_object._detected_object_common_data._time_confidence = time_confidence::TIME_000_000_000_000_01; + // Add Detected VRU Optional data + detected_vru_data detected_vru; + detected_vru._attachment = attachment::WHEEL_CHAIR; + detected_vru._personal_device_user_type = personal_device_user_type::PEDESTRIAN; + detected_vru._attachment_radius = 200; + detected_vru._propulsion = human_propelled_type::WHEELCHAIR; + ASSERT_TRUE( detected_vru._propulsion.has_value()); + detected_object._detected_object_optional_data = detected_vru; + msg._objects.push_back(detected_object); + // Serialize Msg + std::string json = to_json(msg); + // String is not empty + EXPECT_FALSE(json.empty()); + // Confirm result has msg information in desired structure + auto result = parse_json(json); + + // Confirm timestamp data, Should fail at assert statements since if this require property is + // not available the other calls will fail + ASSERT_TRUE(result.HasMember("sdsm_time_stamp")); + ASSERT_TRUE(result.FindMember("sdsm_time_stamp")->value.IsObject()); + EXPECT_EQ( msg._time_stamp.second, result["sdsm_time_stamp"]["second"].GetInt()); + EXPECT_EQ( msg._time_stamp.minute, result["sdsm_time_stamp"]["minute"].GetInt()); + EXPECT_EQ( msg._time_stamp.hour, result["sdsm_time_stamp"]["hour"].GetInt()); + EXPECT_EQ( msg._time_stamp.day, result["sdsm_time_stamp"]["day"].GetInt()); + EXPECT_EQ( msg._time_stamp.month, result["sdsm_time_stamp"]["month"].GetInt()); + EXPECT_EQ( msg._time_stamp.year, result["sdsm_time_stamp"]["year"].GetInt()); + + EXPECT_EQ( msg._msg_count, result["msg_cnt"].GetUint()); + EXPECT_EQ( msg._source_id, result["source_id"].GetString()); + EXPECT_EQ( static_cast(msg._equipment_type), result["equipment_type"].GetInt()); + // Confirm ref position confidence + ASSERT_TRUE(result.HasMember("ref_pos_xy_conf")); + ASSERT_TRUE(result.FindMember("ref_pos_xy_conf")->value.IsObject()); + EXPECT_EQ(msg._ref_position_confidence._semi_major_axis_accuracy, result["ref_pos_xy_conf"]["semi_major"].GetUint()); + EXPECT_EQ(msg._ref_position_confidence._semi_minor_axis_accuracy, result["ref_pos_xy_conf"]["semi_minor"].GetUint()); + EXPECT_EQ(msg._ref_position_confidence._semi_major_axis_orientation, result["ref_pos_xy_conf"]["orientation"].GetInt()); + // Confirm optional elevation parameter is present + EXPECT_TRUE( result.HasMember("ref_pos_el_conf")); + // Confirm ref position + ASSERT_TRUE(result.HasMember("ref_pos")); + ASSERT_TRUE(result.FindMember("ref_pos")->value.IsObject()); + EXPECT_EQ( msg._ref_positon._longitude, result["ref_pos"]["long"].GetInt64()); + EXPECT_EQ( msg._ref_positon._latitude, result["ref_pos"]["lat"].GetInt64()); + // Confirm optional elevation parameter is present + EXPECT_EQ( msg._ref_positon._elevation, result["ref_pos"]["elevation"].GetInt()); + EXPECT_EQ( msg._ref_position_elevation_confidence, position_confidence_from_int( result["ref_pos_el_conf"].GetUint())); + + // Confirm List of detected object exists + ASSERT_TRUE(result.HasMember("objects")); + ASSERT_TRUE(result.FindMember("objects")->value.IsArray()); + ASSERT_EQ(1,result.FindMember("objects")->value.GetArray().Size()); + ASSERT_TRUE(result["objects"].GetArray()[0].IsObject()); + // Confirm object properties + auto object = result["objects"].GetArray()[0].GetObject(); + // Assert Object has common data + ASSERT_TRUE(object.HasMember("detected_object_common_data")); + ASSERT_TRUE(object.FindMember("detected_object_common_data")->value.IsObject()); + // Retreive Object common data + auto object_common_data = object["detected_object_common_data"].GetObject(); + // Retreive Object common data from + auto msg_object_common_data = msg._objects[0]._detected_object_common_data; + // Confirm Object common data properties + EXPECT_EQ(static_cast(msg_object_common_data._object_type), object_common_data["obj_type"].GetUint()); + EXPECT_EQ(msg_object_common_data._classification_confidence, object_common_data["obj_type_cfd"].GetUint()); + EXPECT_EQ(msg_object_common_data._object_id, object_common_data["object_id"].GetUint()); + EXPECT_EQ(msg_object_common_data._time_measurement_offset, object_common_data["measurement_time"].GetInt()); + EXPECT_EQ(static_cast(msg_object_common_data._time_confidence), object_common_data["time_confidence"].GetUint()); + EXPECT_EQ(msg_object_common_data._speed, object_common_data["speed"].GetUint()); + EXPECT_EQ(msg_object_common_data._speed_confidence, speed_confidence_from_int (object_common_data["speed_confidence"].GetUint())); + EXPECT_EQ(msg_object_common_data._heading, object_common_data["heading"].GetUint()); + EXPECT_EQ(static_cast(msg_object_common_data._heading_confidence), object_common_data["heading_conf"].GetUint()); + // Test Optional properties present + EXPECT_EQ(msg_object_common_data._speed_z, object_common_data["speed_z"].GetUint()); + EXPECT_EQ(msg_object_common_data._speed_z_confidence, speed_confidence_from_int(object_common_data["speed_confidence_z"].GetUint()) ); + // Confirm accel 4 way values + EXPECT_TRUE(object_common_data.HasMember("accel_4_way")); + EXPECT_EQ( msg_object_common_data._acceleration_4_way->_lateral_accel, object_common_data["accel_4_way"]["lat"].GetInt()); + EXPECT_EQ( msg_object_common_data._acceleration_4_way->_longitudinal_accel, object_common_data["accel_4_way"]["long"].GetInt()); + EXPECT_EQ( msg_object_common_data._acceleration_4_way->_vertical_accel, object_common_data["accel_4_way"]["vert"].GetInt()); + EXPECT_EQ( msg_object_common_data._acceleration_4_way->_yaw_rate, object_common_data["accel_4_way"]["yaw"].GetInt()); + // Confirm acceleration confidence + EXPECT_EQ(msg_object_common_data._lateral_acceleration_confidence,acceleration_confidence_from_int( object_common_data["acc_cfd_x"].GetUint())); + EXPECT_EQ(msg_object_common_data._longitudinal_acceleration_confidence,acceleration_confidence_from_int( object_common_data["acc_cfd_y"].GetUint())); + EXPECT_EQ(msg_object_common_data._vertical_accelaration_confidence,acceleration_confidence_from_int( object_common_data["acc_cfd_z"].GetUint())); + EXPECT_EQ(msg_object_common_data._yaw_rate_confidence,angular_velocity_confidence_from_int( object_common_data["acc_cfd_yaw"].GetUint())); + // Expect vru optional fields information + ASSERT_TRUE(object.HasMember("detected_object_optional_data")); + ASSERT_TRUE(object["detected_object_optional_data"].HasMember("detected_vru_data") ); + auto msg_object_optional_data = std::get(msg._objects[0]._detected_object_optional_data.value()); + auto option_vru_json_data = object["detected_object_optional_data"]["detected_vru_data"].GetObject(); + EXPECT_EQ( msg_object_optional_data._attachment.value(), attachment_from_int( option_vru_json_data["attachment"].GetUint())); + EXPECT_EQ( msg_object_optional_data._attachment_radius.value(), option_vru_json_data["radius"].GetUint()); + EXPECT_EQ( msg_object_optional_data._personal_device_user_type.value(), personal_device_user_type_from_int(option_vru_json_data["basic_type"].GetUint())); + EXPECT_EQ( std::get(msg_object_optional_data._propulsion.value()), human_propelled_type_from_int(option_vru_json_data["propulsion"]["human"].GetUint())); +} +TEST(sensor_data_sharing_msg_json_serialization_test, confirm_optional_vru_animal_properties) { + sensor_data_sharing_msg msg; + msg._equipment_type = equipment_type::OBU; + msg._msg_count = 1; + msg._source_id = "00000001"; + msg._ref_positon._latitude=-90000000; + msg._ref_positon._longitude=-1800000000; + msg._ref_positon._elevation = -32768; + msg._time_stamp.second = 0; + msg._time_stamp.minute = 0; + msg._time_stamp.hour= 0; + msg._time_stamp.day = 0; + msg._time_stamp.month = 0; + msg._time_stamp.year = 0; + msg._ref_position_confidence._semi_major_axis_accuracy = 0; + msg._ref_position_confidence._semi_minor_axis_accuracy = 0; + msg._ref_position_confidence._semi_major_axis_orientation = 0; + msg._ref_position_elevation_confidence = position_confidence::A_10M; + // Add Detected Object + detected_object_data detected_object; + detected_object._detected_object_common_data._object_id = 0; + detected_object._detected_object_common_data._object_type = object_type::VEHICLE; + detected_object._detected_object_common_data._classification_confidence = 0; + detected_object._detected_object_common_data._heading = 0; + detected_object._detected_object_common_data._heading_confidence = heading_confidence::PREC_0_0125_deg; + detected_object._detected_object_common_data._position_offset._offset_x = -32767; + detected_object._detected_object_common_data._position_offset._offset_y = -32767; + detected_object._detected_object_common_data._position_offset._offset_z = -32767; + detected_object._detected_object_common_data._pos_confidence._position_confidence = position_confidence::A_500M; + detected_object._detected_object_common_data._pos_confidence._elevation_confidence = position_confidence::A_1CM; + detected_object._detected_object_common_data._speed = 0; + detected_object._detected_object_common_data._speed_z = 8191; + detected_object._detected_object_common_data._speed_confidence = speed_confidence::UNAVAILABLE; + detected_object._detected_object_common_data._speed_z_confidence = speed_confidence::PREC_100ms; + // Create acceleration 4 way + acceleration_set_4_way accel_set; + accel_set._lateral_accel = 2001; + accel_set._longitudinal_accel =2001; + accel_set._vertical_accel = 127; + accel_set._yaw_rate = 32767; + detected_object._detected_object_common_data._acceleration_4_way= accel_set; + // Create acceleration confidence set + detected_object._detected_object_common_data._lateral_acceleration_confidence = acceleration_confidence::ACCL_0_1; + detected_object._detected_object_common_data._longitudinal_acceleration_confidence = acceleration_confidence::ACCL_100; + detected_object._detected_object_common_data._vertical_accelaration_confidence = acceleration_confidence::ACCL_0_01; + detected_object._detected_object_common_data._yaw_rate_confidence = angular_velocity_confidence::UNAVAILABLE; + + + detected_object._detected_object_common_data._time_measurement_offset = -1500; + detected_object._detected_object_common_data._time_confidence = time_confidence::TIME_000_000_000_000_01; + // Add Detected VRU Optional data + detected_vru_data detected_vru; + detected_vru._attachment = attachment::WHEEL_CHAIR; + detected_vru._personal_device_user_type = personal_device_user_type::PEDESTRIAN; + detected_vru._attachment_radius = 200; + detected_vru._propulsion = animal_propelled_type::ANIMAL_DRAWN_CARRIAGE; + ASSERT_TRUE( detected_vru._propulsion.has_value()); + detected_object._detected_object_optional_data = detected_vru; + msg._objects.push_back(detected_object); + // Serialize Msg + std::string json = to_json(msg); + // String is not empty + EXPECT_FALSE(json.empty()); + // Confirm result has msg information in desired structure + auto result = parse_json(json); + + // Confirm timestamp data, Should fail at assert statements since if this require property is + // not available the other calls will fail + ASSERT_TRUE(result.HasMember("sdsm_time_stamp")); + ASSERT_TRUE(result.FindMember("sdsm_time_stamp")->value.IsObject()); + EXPECT_EQ( msg._time_stamp.second, result["sdsm_time_stamp"]["second"].GetInt()); + EXPECT_EQ( msg._time_stamp.minute, result["sdsm_time_stamp"]["minute"].GetInt()); + EXPECT_EQ( msg._time_stamp.hour, result["sdsm_time_stamp"]["hour"].GetInt()); + EXPECT_EQ( msg._time_stamp.day, result["sdsm_time_stamp"]["day"].GetInt()); + EXPECT_EQ( msg._time_stamp.month, result["sdsm_time_stamp"]["month"].GetInt()); + EXPECT_EQ( msg._time_stamp.year, result["sdsm_time_stamp"]["year"].GetInt()); + + EXPECT_EQ( msg._msg_count, result["msg_cnt"].GetUint()); + EXPECT_EQ( msg._source_id, result["source_id"].GetString()); + EXPECT_EQ( static_cast(msg._equipment_type), result["equipment_type"].GetInt()); + // Confirm ref position confidence + ASSERT_TRUE(result.HasMember("ref_pos_xy_conf")); + ASSERT_TRUE(result.FindMember("ref_pos_xy_conf")->value.IsObject()); + EXPECT_EQ(msg._ref_position_confidence._semi_major_axis_accuracy, result["ref_pos_xy_conf"]["semi_major"].GetUint()); + EXPECT_EQ(msg._ref_position_confidence._semi_minor_axis_accuracy, result["ref_pos_xy_conf"]["semi_minor"].GetUint()); + EXPECT_EQ(msg._ref_position_confidence._semi_major_axis_orientation, result["ref_pos_xy_conf"]["orientation"].GetInt()); + // Confirm optional elevation parameter is present + EXPECT_TRUE( result.HasMember("ref_pos_el_conf")); + // Confirm ref position + ASSERT_TRUE(result.HasMember("ref_pos")); + ASSERT_TRUE(result.FindMember("ref_pos")->value.IsObject()); + EXPECT_EQ( msg._ref_positon._longitude, result["ref_pos"]["long"].GetInt64()); + EXPECT_EQ( msg._ref_positon._latitude, result["ref_pos"]["lat"].GetInt64()); + // Confirm optional elevation parameter is present + EXPECT_EQ( msg._ref_positon._elevation, result["ref_pos"]["elevation"].GetInt()); + EXPECT_EQ( msg._ref_position_elevation_confidence, position_confidence_from_int( result["ref_pos_el_conf"].GetUint())); + + // Confirm List of detected object exists + ASSERT_TRUE(result.HasMember("objects")); + ASSERT_TRUE(result.FindMember("objects")->value.IsArray()); + ASSERT_EQ(1,result.FindMember("objects")->value.GetArray().Size()); + ASSERT_TRUE(result["objects"].GetArray()[0].IsObject()); + // Confirm object properties + auto object = result["objects"].GetArray()[0].GetObject(); + // Assert Object has common data + ASSERT_TRUE(object.HasMember("detected_object_common_data")); + ASSERT_TRUE(object.FindMember("detected_object_common_data")->value.IsObject()); + // Retreive Object common data + auto object_common_data = object["detected_object_common_data"].GetObject(); + // Retreive Object common data from + auto msg_object_common_data = msg._objects[0]._detected_object_common_data; + // Confirm Object common data properties + EXPECT_EQ(static_cast(msg_object_common_data._object_type), object_common_data["obj_type"].GetUint()); + EXPECT_EQ(msg_object_common_data._classification_confidence, object_common_data["obj_type_cfd"].GetUint()); + EXPECT_EQ(msg_object_common_data._object_id, object_common_data["object_id"].GetUint()); + EXPECT_EQ(msg_object_common_data._time_measurement_offset, object_common_data["measurement_time"].GetInt()); + EXPECT_EQ(static_cast(msg_object_common_data._time_confidence), object_common_data["time_confidence"].GetUint()); + EXPECT_EQ(msg_object_common_data._speed, object_common_data["speed"].GetUint()); + EXPECT_EQ(msg_object_common_data._speed_confidence, speed_confidence_from_int (object_common_data["speed_confidence"].GetUint())); + EXPECT_EQ(msg_object_common_data._heading, object_common_data["heading"].GetUint()); + EXPECT_EQ(static_cast(msg_object_common_data._heading_confidence), object_common_data["heading_conf"].GetUint()); + // Test Optional properties present + EXPECT_EQ(msg_object_common_data._speed_z, object_common_data["speed_z"].GetUint()); + EXPECT_EQ(msg_object_common_data._speed_z_confidence, speed_confidence_from_int(object_common_data["speed_confidence_z"].GetUint()) ); + // Confirm accel 4 way values + EXPECT_TRUE(object_common_data.HasMember("accel_4_way")); + EXPECT_EQ( msg_object_common_data._acceleration_4_way->_lateral_accel, object_common_data["accel_4_way"]["lat"].GetInt()); + EXPECT_EQ( msg_object_common_data._acceleration_4_way->_longitudinal_accel, object_common_data["accel_4_way"]["long"].GetInt()); + EXPECT_EQ( msg_object_common_data._acceleration_4_way->_vertical_accel, object_common_data["accel_4_way"]["vert"].GetInt()); + EXPECT_EQ( msg_object_common_data._acceleration_4_way->_yaw_rate, object_common_data["accel_4_way"]["yaw"].GetInt()); + // Confirm acceleration confidence + EXPECT_EQ(msg_object_common_data._lateral_acceleration_confidence,acceleration_confidence_from_int( object_common_data["acc_cfd_x"].GetUint())); + EXPECT_EQ(msg_object_common_data._longitudinal_acceleration_confidence,acceleration_confidence_from_int( object_common_data["acc_cfd_y"].GetUint())); + EXPECT_EQ(msg_object_common_data._vertical_accelaration_confidence,acceleration_confidence_from_int( object_common_data["acc_cfd_z"].GetUint())); + EXPECT_EQ(msg_object_common_data._yaw_rate_confidence,angular_velocity_confidence_from_int( object_common_data["acc_cfd_yaw"].GetUint())); + // Expect vru optional fields information + ASSERT_TRUE(object.HasMember("detected_object_optional_data")); + ASSERT_TRUE(object["detected_object_optional_data"].HasMember("detected_vru_data") ); + auto msg_object_optional_data = std::get(msg._objects[0]._detected_object_optional_data.value()); + auto option_vru_json_data = object["detected_object_optional_data"]["detected_vru_data"].GetObject(); + EXPECT_EQ( msg_object_optional_data._attachment.value(), attachment_from_int( option_vru_json_data["attachment"].GetUint())); + EXPECT_EQ( msg_object_optional_data._attachment_radius.value(), option_vru_json_data["radius"].GetUint()); + EXPECT_EQ( msg_object_optional_data._personal_device_user_type.value(), personal_device_user_type_from_int(option_vru_json_data["basic_type"].GetUint())); + EXPECT_EQ( std::get(msg_object_optional_data._propulsion.value()), animal_propelled_type_from_int(option_vru_json_data["propulsion"]["animal"].GetUint())); +} + + +TEST(sensor_data_sharing_msg_json_serialization_test, confirm_optional_vru_motor_properties) { + sensor_data_sharing_msg msg; + msg._equipment_type = equipment_type::OBU; + msg._msg_count = 1; + msg._source_id = "00000001"; + msg._ref_positon._latitude=-90000000; + msg._ref_positon._longitude=-1800000000; + msg._ref_positon._elevation = -32768; + msg._time_stamp.second = 0; + msg._time_stamp.minute = 0; + msg._time_stamp.hour= 0; + msg._time_stamp.day = 0; + msg._time_stamp.month = 0; + msg._time_stamp.year = 0; + msg._ref_position_confidence._semi_major_axis_accuracy = 0; + msg._ref_position_confidence._semi_minor_axis_accuracy = 0; + msg._ref_position_confidence._semi_major_axis_orientation = 0; + msg._ref_position_elevation_confidence = position_confidence::A_10M; + // Add Detected Object + detected_object_data detected_object; + detected_object._detected_object_common_data._object_id = 0; + detected_object._detected_object_common_data._object_type = object_type::VEHICLE; + detected_object._detected_object_common_data._classification_confidence = 0; + detected_object._detected_object_common_data._heading = 0; + detected_object._detected_object_common_data._heading_confidence = heading_confidence::PREC_0_0125_deg; + detected_object._detected_object_common_data._position_offset._offset_x = -32767; + detected_object._detected_object_common_data._position_offset._offset_y = -32767; + detected_object._detected_object_common_data._position_offset._offset_z = -32767; + detected_object._detected_object_common_data._pos_confidence._position_confidence = position_confidence::A_500M; + detected_object._detected_object_common_data._pos_confidence._elevation_confidence = position_confidence::A_1CM; + detected_object._detected_object_common_data._speed = 0; + detected_object._detected_object_common_data._speed_z = 8191; + detected_object._detected_object_common_data._speed_confidence = speed_confidence::UNAVAILABLE; + detected_object._detected_object_common_data._speed_z_confidence = speed_confidence::PREC_100ms; + // Create acceleration 4 way + acceleration_set_4_way accel_set; + accel_set._lateral_accel = 2001; + accel_set._longitudinal_accel =2001; + accel_set._vertical_accel = 127; + accel_set._yaw_rate = 32767; + detected_object._detected_object_common_data._acceleration_4_way= accel_set; + // Create acceleration confidence set + detected_object._detected_object_common_data._lateral_acceleration_confidence = acceleration_confidence::ACCL_0_1; + detected_object._detected_object_common_data._longitudinal_acceleration_confidence = acceleration_confidence::ACCL_100; + detected_object._detected_object_common_data._vertical_accelaration_confidence = acceleration_confidence::ACCL_0_01; + detected_object._detected_object_common_data._yaw_rate_confidence = angular_velocity_confidence::UNAVAILABLE; + + detected_object._detected_object_common_data._time_measurement_offset = -1500; + detected_object._detected_object_common_data._time_confidence = time_confidence::TIME_000_000_000_000_01; + // Add Detected VRU Optional data + detected_vru_data detected_vru; + detected_vru._attachment = attachment::WHEEL_CHAIR; + detected_vru._personal_device_user_type = personal_device_user_type::PEDESTRIAN; + detected_vru._attachment_radius = 200; + detected_vru._propulsion = motorized_propelled_type::SCOOTER; + ASSERT_TRUE( detected_vru._propulsion.has_value()); + detected_object._detected_object_optional_data = detected_vru; + msg._objects.push_back(detected_object); + // Serialize Msg + std::string json = to_json(msg); + // String is not empty + EXPECT_FALSE(json.empty()); + // Confirm result has msg information in desired structure + auto result = parse_json(json); + + // Confirm timestamp data, Should fail at assert statements since if this require property is + // not available the other calls will fail + ASSERT_TRUE(result.HasMember("sdsm_time_stamp")); + ASSERT_TRUE(result.FindMember("sdsm_time_stamp")->value.IsObject()); + EXPECT_EQ( msg._time_stamp.second, result["sdsm_time_stamp"]["second"].GetInt()); + EXPECT_EQ( msg._time_stamp.minute, result["sdsm_time_stamp"]["minute"].GetInt()); + EXPECT_EQ( msg._time_stamp.hour, result["sdsm_time_stamp"]["hour"].GetInt()); + EXPECT_EQ( msg._time_stamp.day, result["sdsm_time_stamp"]["day"].GetInt()); + EXPECT_EQ( msg._time_stamp.month, result["sdsm_time_stamp"]["month"].GetInt()); + EXPECT_EQ( msg._time_stamp.year, result["sdsm_time_stamp"]["year"].GetInt()); + + EXPECT_EQ( msg._msg_count, result["msg_cnt"].GetUint()); + EXPECT_EQ( msg._source_id, result["source_id"].GetString()); + EXPECT_EQ( static_cast(msg._equipment_type), result["equipment_type"].GetInt()); + // Confirm ref position confidence + ASSERT_TRUE(result.HasMember("ref_pos_xy_conf")); + ASSERT_TRUE(result.FindMember("ref_pos_xy_conf")->value.IsObject()); + EXPECT_EQ(msg._ref_position_confidence._semi_major_axis_accuracy, result["ref_pos_xy_conf"]["semi_major"].GetUint()); + EXPECT_EQ(msg._ref_position_confidence._semi_minor_axis_accuracy, result["ref_pos_xy_conf"]["semi_minor"].GetUint()); + EXPECT_EQ(msg._ref_position_confidence._semi_major_axis_orientation, result["ref_pos_xy_conf"]["orientation"].GetInt()); + // Confirm optional elevation parameter is present + EXPECT_TRUE( result.HasMember("ref_pos_el_conf")); + // Confirm ref position + ASSERT_TRUE(result.HasMember("ref_pos")); + ASSERT_TRUE(result.FindMember("ref_pos")->value.IsObject()); + EXPECT_EQ( msg._ref_positon._longitude, result["ref_pos"]["long"].GetInt64()); + EXPECT_EQ( msg._ref_positon._latitude, result["ref_pos"]["lat"].GetInt64()); + // Confirm optional elevation parameter is present + EXPECT_EQ( msg._ref_positon._elevation, result["ref_pos"]["elevation"].GetInt()); + EXPECT_EQ( msg._ref_position_elevation_confidence, position_confidence_from_int( result["ref_pos_el_conf"].GetUint())); + + // Confirm List of detected object exists + ASSERT_TRUE(result.HasMember("objects")); + ASSERT_TRUE(result.FindMember("objects")->value.IsArray()); + ASSERT_EQ(1,result.FindMember("objects")->value.GetArray().Size()); + ASSERT_TRUE(result["objects"].GetArray()[0].IsObject()); + // Confirm object properties + auto object = result["objects"].GetArray()[0].GetObject(); + // Assert Object has common data + ASSERT_TRUE(object.HasMember("detected_object_common_data")); + ASSERT_TRUE(object.FindMember("detected_object_common_data")->value.IsObject()); + // Retreive Object common data + auto object_common_data = object["detected_object_common_data"].GetObject(); + // Retreive Object common data from + auto msg_object_common_data = msg._objects[0]._detected_object_common_data; + // Confirm Object common data properties + EXPECT_EQ(static_cast(msg_object_common_data._object_type), object_common_data["obj_type"].GetUint()); + EXPECT_EQ(msg_object_common_data._classification_confidence, object_common_data["obj_type_cfd"].GetUint()); + EXPECT_EQ(msg_object_common_data._object_id, object_common_data["object_id"].GetUint()); + EXPECT_EQ(msg_object_common_data._time_measurement_offset, object_common_data["measurement_time"].GetInt()); + EXPECT_EQ(static_cast(msg_object_common_data._time_confidence), object_common_data["time_confidence"].GetUint()); + EXPECT_EQ(msg_object_common_data._speed, object_common_data["speed"].GetUint()); + EXPECT_EQ(msg_object_common_data._speed_confidence, speed_confidence_from_int (object_common_data["speed_confidence"].GetUint())); + EXPECT_EQ(msg_object_common_data._heading, object_common_data["heading"].GetUint()); + EXPECT_EQ(static_cast(msg_object_common_data._heading_confidence), object_common_data["heading_conf"].GetUint()); + // Test Optional properties present + EXPECT_EQ(msg_object_common_data._speed_z, object_common_data["speed_z"].GetUint()); + EXPECT_EQ(msg_object_common_data._speed_z_confidence, speed_confidence_from_int(object_common_data["speed_confidence_z"].GetUint()) ); + // Confirm accel 4 way values + EXPECT_TRUE(object_common_data.HasMember("accel_4_way")); + EXPECT_EQ( msg_object_common_data._acceleration_4_way->_lateral_accel, object_common_data["accel_4_way"]["lat"].GetInt()); + EXPECT_EQ( msg_object_common_data._acceleration_4_way->_longitudinal_accel, object_common_data["accel_4_way"]["long"].GetInt()); + EXPECT_EQ( msg_object_common_data._acceleration_4_way->_vertical_accel, object_common_data["accel_4_way"]["vert"].GetInt()); + EXPECT_EQ( msg_object_common_data._acceleration_4_way->_yaw_rate, object_common_data["accel_4_way"]["yaw"].GetInt()); + // Confirm acceleration confidence + EXPECT_EQ(msg_object_common_data._lateral_acceleration_confidence,acceleration_confidence_from_int( object_common_data["acc_cfd_x"].GetUint())); + EXPECT_EQ(msg_object_common_data._longitudinal_acceleration_confidence,acceleration_confidence_from_int( object_common_data["acc_cfd_y"].GetUint())); + EXPECT_EQ(msg_object_common_data._vertical_accelaration_confidence,acceleration_confidence_from_int( object_common_data["acc_cfd_z"].GetUint())); + EXPECT_EQ(msg_object_common_data._yaw_rate_confidence,angular_velocity_confidence_from_int( object_common_data["acc_cfd_yaw"].GetUint())); + // Expect vru optional fields information + ASSERT_TRUE(object.HasMember("detected_object_optional_data")); + ASSERT_TRUE(object["detected_object_optional_data"].HasMember("detected_vru_data") ); + auto msg_object_optional_data = std::get(msg._objects[0]._detected_object_optional_data.value()); + auto option_vru_json_data = object["detected_object_optional_data"]["detected_vru_data"].GetObject(); + EXPECT_EQ( msg_object_optional_data._attachment.value(), attachment_from_int( option_vru_json_data["attachment"].GetUint())); + EXPECT_EQ( msg_object_optional_data._attachment_radius.value(), option_vru_json_data["radius"].GetUint()); + EXPECT_EQ( msg_object_optional_data._personal_device_user_type.value(), personal_device_user_type_from_int(option_vru_json_data["basic_type"].GetUint())); + EXPECT_EQ( std::get(msg_object_optional_data._propulsion.value()), motorized_propelled_type_from_int(option_vru_json_data["propulsion"]["motor"].GetUint())); +} + + +TEST(sensor_data_sharing_msg_json_serialization_test, confirm_optional_obstacle_properties) { + sensor_data_sharing_msg msg; + msg._equipment_type = equipment_type::OBU; + msg._msg_count = 1; + msg._source_id = "00000001"; + msg._ref_positon._latitude=-90000000; + msg._ref_positon._longitude=-1800000000; + msg._time_stamp.second = 0; + msg._time_stamp.minute = 0; + msg._time_stamp.hour= 0; + msg._time_stamp.day = 0; + msg._time_stamp.month = 0; + msg._time_stamp.year = 0; + msg._ref_position_confidence._semi_major_axis_accuracy = 0; + msg._ref_position_confidence._semi_minor_axis_accuracy = 0; + msg._ref_position_confidence._semi_major_axis_orientation = 0; + // Add Detected Object + detected_object_data detected_object; + detected_object._detected_object_common_data._object_id = 0; + detected_object._detected_object_common_data._object_type = object_type::VEHICLE; + detected_object._detected_object_common_data._classification_confidence = 0; + detected_object._detected_object_common_data._heading = 0; + detected_object._detected_object_common_data._heading_confidence = heading_confidence::PREC_0_0125_deg; + detected_object._detected_object_common_data._position_offset._offset_x = -32767; + detected_object._detected_object_common_data._position_offset._offset_y = -32767; + detected_object._detected_object_common_data._position_offset._offset_z = -32767; + detected_object._detected_object_common_data._pos_confidence._position_confidence = position_confidence::A_500M; + detected_object._detected_object_common_data._pos_confidence._elevation_confidence = position_confidence::A_1CM; + detected_object._detected_object_common_data._speed = 0; + detected_object._detected_object_common_data._time_measurement_offset = -1500; + detected_object._detected_object_common_data._time_confidence = time_confidence::TIME_000_000_000_000_01; + // Add Detected VRU Optional data + detected_obstacle_data detected_obstacle; + detected_obstacle._size._height = 1023; + detected_obstacle._size._width = 1023; + detected_obstacle._size._length = 1023; + + obstacle_size_confidence _size_confidence; + detected_obstacle._size_confidence._height_confidence = size_value_confidence::SIZE_0_01; + detected_obstacle._size_confidence._width_confidence = size_value_confidence::SIZE_0_01; + detected_obstacle._size_confidence._length_confidence = size_value_confidence::SIZE_0_01; + + + detected_object._detected_object_optional_data = detected_obstacle; + msg._objects.push_back(detected_object); + // Serialize Msg + std::string json = to_json(msg); + // String is not empty + EXPECT_FALSE(json.empty()); + // Confirm result has msg information in desired structure + auto result = parse_json(json); + + // Confirm timestamp data, Should fail at assert statements since if this require property is + // not available the other calls will fail + ASSERT_TRUE(result.HasMember("sdsm_time_stamp")); + ASSERT_TRUE(result.FindMember("sdsm_time_stamp")->value.IsObject()); + EXPECT_EQ( msg._time_stamp.second, result["sdsm_time_stamp"]["second"].GetInt()); + EXPECT_EQ( msg._time_stamp.minute, result["sdsm_time_stamp"]["minute"].GetInt()); + EXPECT_EQ( msg._time_stamp.hour, result["sdsm_time_stamp"]["hour"].GetInt()); + EXPECT_EQ( msg._time_stamp.day, result["sdsm_time_stamp"]["day"].GetInt()); + EXPECT_EQ( msg._time_stamp.month, result["sdsm_time_stamp"]["month"].GetInt()); + EXPECT_EQ( msg._time_stamp.year, result["sdsm_time_stamp"]["year"].GetInt()); + + EXPECT_EQ( msg._msg_count, result["msg_cnt"].GetUint()); + EXPECT_EQ( msg._source_id, result["source_id"].GetString()); + EXPECT_EQ( static_cast(msg._equipment_type), result["equipment_type"].GetInt()); + // Confirm ref position confidence + ASSERT_TRUE(result.HasMember("ref_pos_xy_conf")); + ASSERT_TRUE(result.FindMember("ref_pos_xy_conf")->value.IsObject()); + EXPECT_EQ(msg._ref_position_confidence._semi_major_axis_accuracy, result["ref_pos_xy_conf"]["semi_major"].GetUint()); + EXPECT_EQ(msg._ref_position_confidence._semi_minor_axis_accuracy, result["ref_pos_xy_conf"]["semi_minor"].GetUint()); + EXPECT_EQ(msg._ref_position_confidence._semi_major_axis_orientation, result["ref_pos_xy_conf"]["orientation"].GetInt()); + // Confirm optional elevation parameter is not present + EXPECT_FALSE( result.HasMember("ref_pos_el_conf")); + // Confirm ref position + ASSERT_TRUE(result.HasMember("ref_pos")); + ASSERT_TRUE(result.FindMember("ref_pos")->value.IsObject()); + EXPECT_EQ( msg._ref_positon._longitude, result["ref_pos"]["long"].GetInt64()); + EXPECT_EQ( msg._ref_positon._latitude, result["ref_pos"]["lat"].GetInt64()); + // Optional parameter is not present + EXPECT_FALSE(result["ref_pos"].HasMember("elevation")); + + // Confirm List of detected object exists + ASSERT_TRUE(result.HasMember("objects")); + ASSERT_TRUE(result.FindMember("objects")->value.IsArray()); + ASSERT_EQ(1,result.FindMember("objects")->value.GetArray().Size()); + ASSERT_TRUE(result["objects"].GetArray()[0].IsObject()); + // Confirm object properties + auto object = result["objects"].GetArray()[0].GetObject(); + // Assert Object has common data + ASSERT_TRUE(object.HasMember("detected_object_common_data")); + ASSERT_TRUE(object.FindMember("detected_object_common_data")->value.IsObject()); + // Retreive Object common data + auto object_common_data = object["detected_object_common_data"].GetObject(); + // Retreive Object common data from + auto msg_object_common_data = msg._objects[0]._detected_object_common_data; + // Confirm Object common data properties + EXPECT_EQ(static_cast(msg_object_common_data._object_type), object_common_data["obj_type"].GetUint()); + EXPECT_EQ(msg_object_common_data._classification_confidence, object_common_data["obj_type_cfd"].GetUint()); + EXPECT_EQ(msg_object_common_data._object_id, object_common_data["object_id"].GetUint()); + EXPECT_EQ(msg_object_common_data._time_measurement_offset, object_common_data["measurement_time"].GetInt()); + EXPECT_EQ(static_cast(msg_object_common_data._time_confidence), object_common_data["time_confidence"].GetUint()); + EXPECT_EQ(msg_object_common_data._speed, object_common_data["speed"].GetUint()); + EXPECT_EQ(msg_object_common_data._heading, object_common_data["heading"].GetUint()); + EXPECT_EQ(static_cast(msg_object_common_data._heading_confidence), object_common_data["heading_conf"].GetUint()); + // Test Optional properties not present + EXPECT_FALSE(object_common_data.HasMember("speed_z")); + EXPECT_FALSE(object_common_data.HasMember("speed_confidence_z")); + EXPECT_FALSE(object_common_data.HasMember("accel_4_way")); + EXPECT_FALSE(object_common_data.HasMember("acc_cfd_x")); + EXPECT_FALSE(object_common_data.HasMember("acc_cfd_y")); + EXPECT_FALSE(object_common_data.HasMember("acc_cfd_yaw")); + EXPECT_FALSE(object_common_data.HasMember("acc_cfd_z")); + // Expect obstacle optional fields information + ASSERT_TRUE(object.HasMember("detected_object_optional_data")); + ASSERT_TRUE(object["detected_object_optional_data"].HasMember("detected_obstacle_data") ); + auto msg_object_optional_data = std::get(msg._objects[0]._detected_object_optional_data.value()); + auto option_obstacle_json_data = object["detected_object_optional_data"]["detected_obstacle_data"].GetObject(); + // Confirm Size + EXPECT_EQ( msg_object_optional_data._size._length, option_obstacle_json_data["obst_size"]["length"].GetUint()); + EXPECT_EQ( msg_object_optional_data._size._width, option_obstacle_json_data["obst_size"]["width"].GetUint()); + EXPECT_EQ( msg_object_optional_data._size._height, option_obstacle_json_data["obst_size"]["height"].GetUint()); + // Confirm Size confidence + EXPECT_EQ( msg_object_optional_data._size_confidence._length_confidence, size_value_confidence_from_int(option_obstacle_json_data["obst_size_confidence"]["length_confidence"].GetUint())); + EXPECT_EQ( msg_object_optional_data._size_confidence._width_confidence, size_value_confidence_from_int(option_obstacle_json_data["obst_size_confidence"]["width_confidence"].GetUint())); + EXPECT_EQ( msg_object_optional_data._size_confidence._height_confidence.value(), size_value_confidence_from_int(option_obstacle_json_data["obst_size_confidence"]["height_confidence"].GetUint())); + + + +} + +TEST(sensor_data_sharing_msg_json_serialization_test, confirm_optional_vehicle_properties) { + sensor_data_sharing_msg msg; + msg._equipment_type = equipment_type::OBU; + msg._msg_count = 1; + msg._source_id = "00000001"; + msg._ref_positon._latitude=-90000000; + msg._ref_positon._longitude=-1800000000; + msg._time_stamp.second = 0; + msg._time_stamp.minute = 0; + msg._time_stamp.hour= 0; + msg._time_stamp.day = 0; + msg._time_stamp.month = 0; + msg._time_stamp.year = 0; + msg._ref_position_confidence._semi_major_axis_accuracy = 0; + msg._ref_position_confidence._semi_minor_axis_accuracy = 0; + msg._ref_position_confidence._semi_major_axis_orientation = 0; + // Add Detected Object + detected_object_data detected_object; + detected_object._detected_object_common_data._object_id = 0; + detected_object._detected_object_common_data._object_type = object_type::VEHICLE; + detected_object._detected_object_common_data._classification_confidence = 0; + detected_object._detected_object_common_data._heading = 0; + detected_object._detected_object_common_data._heading_confidence = heading_confidence::PREC_0_0125_deg; + detected_object._detected_object_common_data._position_offset._offset_x = -32767; + detected_object._detected_object_common_data._position_offset._offset_y = -32767; + detected_object._detected_object_common_data._position_offset._offset_z = -32767; + detected_object._detected_object_common_data._pos_confidence._position_confidence = position_confidence::A_500M; + detected_object._detected_object_common_data._pos_confidence._elevation_confidence = position_confidence::A_1CM; + detected_object._detected_object_common_data._speed = 0; + detected_object._detected_object_common_data._time_measurement_offset = -1500; + detected_object._detected_object_common_data._time_confidence = time_confidence::TIME_000_000_000_000_01; + // Add detected vehicle optional data + detected_vehicle_data detected_vehicle; + vehicle_size _vehicle_size; + _vehicle_size._width = 4095; + _vehicle_size._length = 4095; + detected_vehicle._size = _vehicle_size; + detected_vehicle._vehicle_height = 127; + + vehicle_size_confidence _vehicle_size_confidence; + + _vehicle_size_confidence._height_confidence = size_value_confidence::SIZE_0_01; + _vehicle_size_confidence._width_confidence = size_value_confidence::SIZE_0_01; + _vehicle_size_confidence._length_confidence = size_value_confidence::SIZE_0_01; + detected_vehicle._size_confidence = _vehicle_size_confidence; + + detected_vehicle.exterior_lights = "11110000"; + + angular_velocity_set _angular_velocity_set; + _angular_velocity_set._pitch_rate = 32767; + _angular_velocity_set._roll_rate = 32767; + detected_vehicle._angular_velocity = _angular_velocity_set; + + angular_velocity_confidence_set _angular_velocity_confidence_set; + + _angular_velocity_confidence_set._pitch_rate_confidence = angular_velocity_confidence::DEGSEC_01; + _angular_velocity_confidence_set._roll_rate_confidence = angular_velocity_confidence::DEGSEC_01; + detected_vehicle._angular_velocity_confidence = _angular_velocity_confidence_set; + + attitude veh_attitude; + veh_attitude._pitch = 72000; + veh_attitude._roll = 14400; + veh_attitude._yaw = 14400; + detected_vehicle._veh_attitude = veh_attitude; + + attitude_confidence veh_attitude_confidence; + veh_attitude_confidence._pitch_confidence = heading_confidence::PREC_0_05_deg; + veh_attitude_confidence._roll_confidence = heading_confidence::PREC_10_deg; + veh_attitude_confidence._yaw_confidence = heading_confidence::PREC_01_deg; + detected_vehicle._attitude_confidence = veh_attitude_confidence; + + detected_vehicle._vehicle_class = 23; + detected_vehicle._classification_confidence = 101; + + detected_object._detected_object_optional_data = detected_vehicle; + msg._objects.push_back(detected_object); + // Serialize Msg + std::string json = to_json(msg); + // String is not empty + EXPECT_FALSE(json.empty()); + // Confirm result has msg information in desired structure + auto result = parse_json(json); + + // Confirm timestamp data, Should fail at assert statements since if this require property is + // not available the other calls will fail + ASSERT_TRUE(result.HasMember("sdsm_time_stamp")); + ASSERT_TRUE(result.FindMember("sdsm_time_stamp")->value.IsObject()); + EXPECT_EQ( msg._time_stamp.second, result["sdsm_time_stamp"]["second"].GetInt()); + EXPECT_EQ( msg._time_stamp.minute, result["sdsm_time_stamp"]["minute"].GetInt()); + EXPECT_EQ( msg._time_stamp.hour, result["sdsm_time_stamp"]["hour"].GetInt()); + EXPECT_EQ( msg._time_stamp.day, result["sdsm_time_stamp"]["day"].GetInt()); + EXPECT_EQ( msg._time_stamp.month, result["sdsm_time_stamp"]["month"].GetInt()); + EXPECT_EQ( msg._time_stamp.year, result["sdsm_time_stamp"]["year"].GetInt()); + + EXPECT_EQ( msg._msg_count, result["msg_cnt"].GetUint()); + EXPECT_EQ( msg._source_id, result["source_id"].GetString()); + EXPECT_EQ( static_cast(msg._equipment_type), result["equipment_type"].GetInt()); + // Confirm ref position confidence + ASSERT_TRUE(result.HasMember("ref_pos_xy_conf")); + ASSERT_TRUE(result.FindMember("ref_pos_xy_conf")->value.IsObject()); + EXPECT_EQ(msg._ref_position_confidence._semi_major_axis_accuracy, result["ref_pos_xy_conf"]["semi_major"].GetUint()); + EXPECT_EQ(msg._ref_position_confidence._semi_minor_axis_accuracy, result["ref_pos_xy_conf"]["semi_minor"].GetUint()); + EXPECT_EQ(msg._ref_position_confidence._semi_major_axis_orientation, result["ref_pos_xy_conf"]["orientation"].GetInt()); + // Confirm optional elevation parameter is not present + EXPECT_FALSE( result.HasMember("ref_pos_el_conf")); + // Confirm ref position + ASSERT_TRUE(result.HasMember("ref_pos")); + ASSERT_TRUE(result.FindMember("ref_pos")->value.IsObject()); + EXPECT_EQ( msg._ref_positon._longitude, result["ref_pos"]["long"].GetInt64()); + EXPECT_EQ( msg._ref_positon._latitude, result["ref_pos"]["lat"].GetInt64()); + // Optional parameter is not present + EXPECT_FALSE(result["ref_pos"].HasMember("elevation")); + + // Confirm List of detected object exists + ASSERT_TRUE(result.HasMember("objects")); + ASSERT_TRUE(result.FindMember("objects")->value.IsArray()); + ASSERT_EQ(1,result.FindMember("objects")->value.GetArray().Size()); + ASSERT_TRUE(result["objects"].GetArray()[0].IsObject()); + // Confirm object properties + auto object = result["objects"].GetArray()[0].GetObject(); + // Assert Object has common data + ASSERT_TRUE(object.HasMember("detected_object_common_data")); + ASSERT_TRUE(object.FindMember("detected_object_common_data")->value.IsObject()); + // Retreive Object common data + auto object_common_data = object["detected_object_common_data"].GetObject(); + // Retreive Object common data from + auto msg_object_common_data = msg._objects[0]._detected_object_common_data; + // Confirm Object common data properties + EXPECT_EQ(static_cast(msg_object_common_data._object_type), object_common_data["obj_type"].GetUint()); + EXPECT_EQ(msg_object_common_data._classification_confidence, object_common_data["obj_type_cfd"].GetUint()); + EXPECT_EQ(msg_object_common_data._object_id, object_common_data["object_id"].GetUint()); + EXPECT_EQ(msg_object_common_data._time_measurement_offset, object_common_data["measurement_time"].GetInt()); + EXPECT_EQ(static_cast(msg_object_common_data._time_confidence), object_common_data["time_confidence"].GetUint()); + EXPECT_EQ(msg_object_common_data._speed, object_common_data["speed"].GetUint()); + EXPECT_EQ(msg_object_common_data._heading, object_common_data["heading"].GetUint()); + EXPECT_EQ(static_cast(msg_object_common_data._heading_confidence), object_common_data["heading_conf"].GetUint()); + // Test Optional properties not present + EXPECT_FALSE(object_common_data.HasMember("speed_z")); + EXPECT_FALSE(object_common_data.HasMember("speed_confidence_z")); + EXPECT_FALSE(object_common_data.HasMember("accel_4_way")); + EXPECT_FALSE(object_common_data.HasMember("acc_cfd_x")); + EXPECT_FALSE(object_common_data.HasMember("acc_cfd_y")); + EXPECT_FALSE(object_common_data.HasMember("acc_cfd_yaw")); + EXPECT_FALSE(object_common_data.HasMember("acc_cfd_z")); + // Expect vehicle optional fields information + ASSERT_TRUE(object.HasMember("detected_object_optional_data")); + ASSERT_TRUE(object["detected_object_optional_data"].HasMember("detected_vehicle_data") ); + auto msg_object_optional_data = std::get(msg._objects[0]._detected_object_optional_data.value()); + auto option_vehicle_json_data = object["detected_object_optional_data"]["detected_vehicle_data"].GetObject(); + + EXPECT_EQ( msg_object_optional_data.exterior_lights, option_vehicle_json_data["lights"].GetString()); + // Confirm angular velocity + EXPECT_EQ( msg_object_optional_data._angular_velocity.value()._pitch_rate, option_vehicle_json_data["veh_ang_vel"]["pitch_rate"].GetInt()); + EXPECT_EQ( msg_object_optional_data._angular_velocity.value()._roll_rate, option_vehicle_json_data["veh_ang_vel"]["roll_rate"].GetInt()); + // Confirm attitude + EXPECT_EQ( msg_object_optional_data._veh_attitude.value()._pitch, option_vehicle_json_data["veh_attitude"]["pitch"].GetInt()); + EXPECT_EQ( msg_object_optional_data._veh_attitude.value()._roll, option_vehicle_json_data["veh_attitude"]["roll"].GetInt()); + EXPECT_EQ( msg_object_optional_data._veh_attitude.value()._yaw, option_vehicle_json_data["veh_attitude"]["yaw"].GetInt()); + // Confirm size + EXPECT_EQ( msg_object_optional_data._size.value()._length, option_vehicle_json_data["size"]["length"].GetUint()); + EXPECT_EQ( msg_object_optional_data._size.value()._width, option_vehicle_json_data["size"]["width"].GetUint()); + EXPECT_EQ( msg_object_optional_data._vehicle_height, option_vehicle_json_data["height"].GetUint()); + // Confirm angular velocity confidence + EXPECT_EQ( msg_object_optional_data._angular_velocity_confidence.value()._pitch_rate_confidence, angular_velocity_confidence_from_int(option_vehicle_json_data["veh_ang_vel_confidence"]["pitch_rate_confidence"].GetUint())); + EXPECT_EQ( msg_object_optional_data._angular_velocity_confidence.value()._roll_rate_confidence, angular_velocity_confidence_from_int(option_vehicle_json_data["veh_ang_vel_confidence"]["roll_rate_confidence"].GetUint())); + // Confirm attitude confidence + EXPECT_EQ( msg_object_optional_data._attitude_confidence.value()._pitch_confidence, heading_confidence_from_int(option_vehicle_json_data["veh_attitude_confidence"]["pitch_confidence"].GetUint())); + EXPECT_EQ( msg_object_optional_data._attitude_confidence.value()._roll_confidence, heading_confidence_from_int(option_vehicle_json_data["veh_attitude_confidence"]["roll_confidence"].GetUint())); + EXPECT_EQ( msg_object_optional_data._attitude_confidence.value()._yaw_confidence, heading_confidence_from_int(option_vehicle_json_data["veh_attitude_confidence"]["yaw_confidence"].GetUint())); + // Confirm size confidence + EXPECT_EQ( msg_object_optional_data._size_confidence.value()._length_confidence, size_value_confidence_from_int(option_vehicle_json_data["vehicle_size_confidence"]["vehicle_length_confidence"].GetUint())); + EXPECT_EQ( msg_object_optional_data._size_confidence.value()._width_confidence, size_value_confidence_from_int(option_vehicle_json_data["vehicle_size_confidence"]["vehicle_width_confidence"].GetUint())); + EXPECT_EQ( msg_object_optional_data._size_confidence.value()._height_confidence, size_value_confidence_from_int(option_vehicle_json_data["vehicle_size_confidence"]["vehicle_height_confidence"].GetUint())); + // Confirm vehicle class + EXPECT_EQ( msg_object_optional_data._vehicle_class, option_vehicle_json_data["vehicle_class"].GetUint()); + // Confirm classification confidence + EXPECT_EQ( msg_object_optional_data._classification_confidence, option_vehicle_json_data["vehicle_class_conf"].GetUint()); + +} + From f3956e85198315f22b034315489990660d17eef1 Mon Sep 17 00:00:00 2001 From: paulbourelly999 <77466294+paulbourelly999@users.noreply.github.com> Date: Tue, 14 Nov 2023 11:03:41 -0500 Subject: [PATCH 41/80] Streets Service Base Lanelet Aware Docker Image (#361) * Initial working comment * Update CI * Get CI working * update privileges * change script shebang * Updates * Updates * Undo CI changes * Added streets_service_base image * PR Updates * PR Updates * Adding github actions to build streets service base * Update github action * Update build_streets_base_images.yml * Update workflow * Update github actions * Update repo README with status badge and documenation * Update documentation * Comments to github actions --- .../workflows/build_streets_base_images.yml | 42 +++++++++++ README.md | 5 ++ build_scripts/README.md | 7 ++ .../install_lanelet2_dependencies.sh | 70 +++++++++++++++++++ .../install_rest_server_dependencies.sh | 10 +-- ...stall_streets_service_base_dependencies.sh | 49 +++++++++++++ streets_service_base/Dockerfile | 4 ++ streets_service_base/README.md | 16 +++++ streets_service_base_lanelet_aware/Dockerfile | 3 + streets_service_base_lanelet_aware/README.md | 18 +++++ .../streets_service_base/CMakeLists.txt | 4 +- 11 files changed, 222 insertions(+), 6 deletions(-) create mode 100644 .github/workflows/build_streets_base_images.yml create mode 100644 build_scripts/README.md create mode 100755 build_scripts/install_lanelet2_dependencies.sh create mode 100755 build_scripts/install_streets_service_base_dependencies.sh create mode 100644 streets_service_base/Dockerfile create mode 100644 streets_service_base/README.md create mode 100644 streets_service_base_lanelet_aware/Dockerfile create mode 100644 streets_service_base_lanelet_aware/README.md diff --git a/.github/workflows/build_streets_base_images.yml b/.github/workflows/build_streets_base_images.yml new file mode 100644 index 000000000..9b37bc698 --- /dev/null +++ b/.github/workflows/build_streets_base_images.yml @@ -0,0 +1,42 @@ +name: Build and Push Base Images +on: + pull_request: + types: [opened, synchronize, reopened] + push: + branches: [develop, master] +jobs: + docker-build: + strategy: + matrix: + include: + - ubuntu-codename: bionic + build_lanelet_aware: true + # Currently install_lanelet2_dependencies.sh script only works for bionic. Disabled lanelet_aware build on newer distributions pending script updates. + - ubuntu-codename: focal + build_lanelet_aware: false + # Currently install_lanelet2_dependencies.sh script only works for bionic. Disabled lanelet_aware build on newer distributions pending script updates. + - ubuntu-codename: jammy + build_lanelet_aware: false + runs-on: ubuntu-latest + steps: + - name: Log in to the Container registry + uses: docker/login-action@v2 + with: + username: ${{ secrets.DOCKERHUB_USERNAME }} + password: ${{ secrets.DOCKERHUB_TOKEN }} + - name: Docker Build Streets Service Base + uses: docker/build-push-action@v3 + with: + push: true + build-args: | + UBUNTU_CODENAME=${{ matrix.ubuntu-codename }} + tags: usdotfhwastoldev/streets_service_base:${{ matrix.ubuntu-codename }} + file: ./streets_service_base/Dockerfile + - name: Docker Build Streets Service Base Lanelet Aware + if: ${{ matrix.build_lanelet_aware }} + uses: docker/build-push-action@v3 + with: + push: true + tags: usdotfhwastoldev/streets_service_base_lanelet_aware:bionic + file: ./streets_service_base_lanelet_aware/Dockerfile + \ No newline at end of file diff --git a/README.md b/README.md index 6e3fa2dc9..4c9653804 100644 --- a/README.md +++ b/README.md @@ -13,6 +13,8 @@ | Scheduling Service | Message Services | Intersection Model | Signal Opt Service | Tsc Service | |-----|-----|-----|-----|-----| [![Docker Cloud Build Status](https://img.shields.io/docker/cloud/build/usdotfhwastoldev/scheduling_service?label=scheduling%20service)](https://hub.docker.com/repository/docker/usdotfhwastoldev/scheduling_service) | [![Docker Cloud Build Status](https://img.shields.io/docker/cloud/build/usdotfhwastoldev/message_services?label=message%20services)](https://hub.docker.com/repository/docker/usdotfhwastoldev/message_services) | [ ![Docker Cloud Build Status](https://img.shields.io/docker/cloud/build/usdotfhwastoldev/intersection_model?label=intersection%20model)](https://hub.docker.com/repository/docker/usdotfhwastoldev/intersection_model) | [![Docker Cloud Build Status](https://img.shields.io/docker/cloud/build/usdotfhwastoldev/signal_opt_service?label=signal%20opt%20service)](https://hub.docker.com/repository/docker/usdotfhwastoldev/signal_opt_service) | [![Docker Cloud Build Status](https://img.shields.io/docker/cloud/build/usdotfhwastoldev/tsc_service?label=tsc%20service&logoColor=%232496ED)](https://hub.docker.com/repository/docker/usdotfhwastoldev/tsc_service) | +# CARMA Streets Base Image Builds +[![Build and Push Base Images](https://github.com/usdot-fhwa-stol/carma-streets/actions/workflows/build_streets_base_images.yml/badge.svg)](https://github.com/usdot-fhwa-stol/carma-streets/actions/workflows/build_streets_base_images.yml) CARMA Streets is a component of CARMA ecosystem, which enables such a coordination among different transportation users. This component provides an interface for CDA participants to interact with the road infrastructure. CARMA Streets is also an edge-computing unit that improves the efficiency and performance of the Transportation Systems Management and Operations (TSMO) strategies. @@ -22,6 +24,9 @@ CARMA Streets architecture is based on a scalable services and layered architect ## Deployment Docker is the primary deployment mechanism to containerize one or more services. The CARMA Streets application and other major frameworks such as Kafka will run in their own separate containers. This document will be updated with a detailed Docker deployment strategy during later design phases. +## Base Images +To make creating new CARMA Streets services easier and to make our CI/CD more efficient, we have introduced new CARMA Streets base images. These images can be used as a starting point and common build/runtime environment for CARMA Streets services. There are currently two CARMA Streets base images , for which documentation and Dockerfiles can be found [Streets Service Base](streets_service_base/README.md) and [Streets Service Base Lanelet Aware](streets_service_base_lanelet_aware/README.md). + # CARMAStreets The primary carma-streets repository can be found [here](https://github.com/usdot-fhwa-stol/carma-streets) and is part of the [USDOT FHWA STOL](https://github.com/usdot-fhwa-stol/) github organization. Documentation on how the carma-streets functions, how it will evolve over time, and how you can contribute can be found at the above links, as well as on the [Doxygen Source Code Documentation](https://usdot-fhwa-stol.github.io/documentation/carma-streets/). diff --git a/build_scripts/README.md b/build_scripts/README.md new file mode 100644 index 000000000..76c6af0ec --- /dev/null +++ b/build_scripts/README.md @@ -0,0 +1,7 @@ +# Build Scripts +## Introduction +The directory contains different scripts used for the build process of **CARMA Streets** service images and base images. +## Install Streets Service Base Dependencies +This script is used to install some common dependencies of **CARMA Streets** services. It assumes that build tools like a compiler, cmake, and other basic C++ build and test dependencies are already installed. Currently we use this script to install **CARMA Streets** service dependencies on top of `carma-builds-64x` (https://github.com/usdot-fhwa-stol/carma-builds) images to create base images for **CARMA Streets** services. +## Install Lanelet2 dependencies +This script installs [lanelet2](https://github.com/fzi-forschungszentrum-informatik/Lanelet2) along with its depedencies. **CARMA Streets** services that require spatial understanding of the surrounding roadway use [lanelet2](https://github.com/fzi-forschungszentrum-informatik/Lanelet2) maps and libraries. This script is used in conjuction with the [streets_service_base_lanelet_aware](../streets_service_base_lanelet_aware/README.md) Dockerfile to create a base image which includes these dependencies. \ No newline at end of file diff --git a/build_scripts/install_lanelet2_dependencies.sh b/build_scripts/install_lanelet2_dependencies.sh new file mode 100755 index 000000000..a0d02f0e0 --- /dev/null +++ b/build_scripts/install_lanelet2_dependencies.sh @@ -0,0 +1,70 @@ +#!/bin/bash +# Script assumes base image of carma-builds-64x +set -e +# Get ubuntu distribution code name. All STOL APT debian packages are pushed to S3 bucket based on distribution codename. +# shellcheck source=/dev/null +source /etc/lsb-release +# add the STOL APT repository +echo "deb [trusted=yes] http://s3.amazonaws.com/stol-apt-repository ${DISTRIB_CODENAME} main" > /etc/apt/sources.list.d/stol-apt-repository.list +echo "deb http://packages.ros.org/ros/ubuntu ${DISTRIB_CODENAME} main" > /etc/apt/sources.list.d/ros-latest.list +DEBIAN_FRONTEND=noninteractive apt install --no-install-recommends --yes --quiet gnupg +wget -qO- https://raw.githubusercontent.com/ros/rosdistro/master/ros.asc | apt-key add - +apt-get update +DEPENDENCIES=( + autotools-dev + automake + sqlite3 + libsqlite3-dev + libpugixml-dev + libgeographic-dev + ros-melodic-catkin + python-rospkg + libeigen3-dev + libtool + libboost-all-dev +) +DEBIAN_FRONTEND=noninteractive apt install --no-install-recommends --yes --quiet "${DEPENDENCIES[@]}" +# Install PROJ, a package for coordinate transformations +git clone https://github.com/OSGeo/PROJ.git /tmp/PROJ --branch 6.2.1 +cd /tmp/PROJ +./autogen.sh +./configure +make +make install +cd .. +rm -r PROJ + +# Download a cmake module for PROJ +wget -P /usr/local/share/cmake-3.27/Modules https://raw.githubusercontent.com/mloskot/cmake-modules/master/modules/FindPROJ4.cmake + + +# Once catkin is installed only the required lanelet2 packages will be pulled in from carma +# NOTE: The lanelet2_python package requires additional dependencies that have not yet been installed so it is removed for now +mkdir carma_lanelet2 +cd carma_lanelet2 +mkdir src +cd src +git init +echo "temp" +git remote add origin -f https://github.com/usdot-fhwa-stol/autoware.ai.git +git config core.sparsecheckout true +# See https://www.shellcheck.net/wiki/SC2129 for reference on syntax +{ +echo "common/hardcoded_params/*" +echo "common/lanelet2_extension/*" +echo "lanelet2/*" +echo "mrt_cmake_modules/*" + } >> .git/info/sparse-checkout +git pull --depth 1 origin refactor_lanelet2_extension +git checkout refactor_lanelet2_extension +rm -r lanelet2/lanelet2_python +rm -r lanelet2/lanelet2_examples +cd .. + +# In order to trick lanelet2 into building the ROS_VERSION environment variable must be set +# In order to fully decouple lanelet2_extension from ros the LANELET2_EXTENSION_LOGGER_TYPE environment variable must be set +# shellcheck source=/dev/null +source /opt/ros/melodic/setup.bash +ROS_VERSION=1 LANELET2_EXTENSION_LOGGER_TYPE=1 catkin_make install +cd .. +rm -r carma_lanelet2 \ No newline at end of file diff --git a/build_scripts/install_rest_server_dependencies.sh b/build_scripts/install_rest_server_dependencies.sh index 1d59e710f..5d909f629 100755 --- a/build_scripts/install_rest_server_dependencies.sh +++ b/build_scripts/install_rest_server_dependencies.sh @@ -1,4 +1,4 @@ -#!/bin/sh +#!/bin/bash # exit on errors set -e @@ -7,9 +7,11 @@ set -e apt-get update # NOTE: libwebsockets-dev from Ubuntu 20 on is sufficient -DEPENDENCIES="libssl-dev \ - qtbase5-dev \ - qtbase5-dev-tools" +DEPENDENCIES=( + libssl-dev + qtbase5-dev + qtbase5-dev-tools +) # install all things needed for deployment, always done apt-get install -y $DEPENDENCIES diff --git a/build_scripts/install_streets_service_base_dependencies.sh b/build_scripts/install_streets_service_base_dependencies.sh new file mode 100755 index 000000000..7a29a57ab --- /dev/null +++ b/build_scripts/install_streets_service_base_dependencies.sh @@ -0,0 +1,49 @@ +#!/bin/bash +set -e +# Get ubuntu distribution code name. All STOL APT debian packages are pushed to S3 bucket based on distribution codename. +# shellcheck source=/dev/null +source /etc/lsb-release +# add the STOL APT repository +echo "deb [trusted=yes] http://s3.amazonaws.com/stol-apt-repository ${DISTRIB_CODENAME} main" > /etc/apt/sources.list.d/stol-apt-repository.list +apt update +DEPENDENCIES=( + libboost-all-dev + carma-clock-1 +) + +DEBIAN_FRONTEND=noninteractive apt install --no-install-recommends --yes --quiet "${DEPENDENCIES[@]}" + +# Install librdkafka from instructions provided here https://github.com/confluentinc/librdkafka/tree/master/packaging/cmake +echo " ------> Install librdkafka..." +cd /tmp +git clone https://github.com/confluentinc/librdkafka.git -b v2.2.0 +cd librdkafka/ +cmake -H. -B_cmake_build +cmake --build _cmake_build +cmake --build _cmake_build --target install +cd ../ +rm -r librdkafka + +# Install rapidjson commit instead of release since there has been no new release since August 2016 +# but repo is still under active development +echo " ------> Install rapidjson..." +cd /tmp +git clone https://github.com/Tencent/rapidjson +cd rapidjson/ +git checkout a95e013b97ca6523f32da23f5095fcc9dd6067e5 +cmake -Bbuild +cmake --build build +cmake --install build +cd .. +rm -r rapidjson + +# Install spdlog +echo " ------> Install spdlog... " +cd /tmp +git clone https://github.com/gabime/spdlog.git -b v1.12.0 +cd spdlog +cmake -Bbuild +cmake --build build +cmake --install build +cd .. +rm -r spdlog diff --git a/streets_service_base/Dockerfile b/streets_service_base/Dockerfile new file mode 100644 index 000000000..a2a4a5dc9 --- /dev/null +++ b/streets_service_base/Dockerfile @@ -0,0 +1,4 @@ +ARG UBUNTU_CODENAME +FROM ghcr.io/usdot-fhwa-stol/carma-builds-x64:${UBUNTU_CODENAME} +COPY ./build_scripts /opt/carma-streets/build_scripts +RUN /opt/carma-streets/build_scripts/install_streets_service_base_dependencies.sh diff --git a/streets_service_base/README.md b/streets_service_base/README.md new file mode 100644 index 000000000..5659906da --- /dev/null +++ b/streets_service_base/README.md @@ -0,0 +1,16 @@ +# Streets Service Base +## Introduction +This base image is intended for any basic **CARMA Streets** service. The **streets service base** image installs our logging library [spdlog](https://github.com/gabime/spdlog), kafka client library [librdkafka](https://github.com/confluentinc/librdkafka), and our JSON parsing/serializing library (rapidjson)[https://miloyip.github.io/rapidjson/] on top of the `carma-builds-x64` image to provide common CARMA Streets dependencies.The `carma-builds-x64` image is built from the (carma-builds)[https://github.com/usdot-fhwa-stol/carma-builds] github repository. This repository creates images in different ubuntu distributions and CPU architectures that function as build environments for C++ applications. Additionally, the **streets serivce base** image adds our debian package repository `http://s3.amazonaws.com/stol-apt-repository` to apt and installs (carma-time-lib)[https://github.com/usdot-fhwa-stol/carma-time-lib], the library we use to allow our CARMA Streets services to function both in simulation and real-world. This base image should be used for any CARMA Streets services. + +> [!IMPORTANT]\ +> Currently this base image is only being used for the **Sensor Data Sharing Service** but the **Signal Optimization Service**, **Scheduling Service** and the **TSC Service** should all be updated to build off this base image. + +## Usage +Simply use the following to extend this image +``` +FROM usdotfhwastoldev/streets_service_base: +``` +To build this image, use a docker build command similar to the one below +``` +docker build -t usdotfhwastoldev/streets_service_base: -f streets_service_base/Dockerfile --build-arg=UBUNTU_CODENAME= . +``` \ No newline at end of file diff --git a/streets_service_base_lanelet_aware/Dockerfile b/streets_service_base_lanelet_aware/Dockerfile new file mode 100644 index 000000000..1eba3fb0d --- /dev/null +++ b/streets_service_base_lanelet_aware/Dockerfile @@ -0,0 +1,3 @@ +FROM usdotfhwastoldev/streets_service_base:bionic +ENV LANELET2_MAP="/home/carma-streets/MAP/Intersection.osm" +RUN /opt/carma-streets/build_scripts/install_lanelet2_dependencies.sh diff --git a/streets_service_base_lanelet_aware/README.md b/streets_service_base_lanelet_aware/README.md new file mode 100644 index 000000000..2ef7cdd51 --- /dev/null +++ b/streets_service_base_lanelet_aware/README.md @@ -0,0 +1,18 @@ +# Streets Service Base Lanelet Aware +## Introduction +This base image is intented for **CARMA Streets** services that require [lanelet2](https://github.com/fzi-forschungszentrum-informatik/Lanelet2) for spatial understanding of the surrouding area. Currently both **Message Service** and **Intersection Model** both depend on [lanelet2](https://github.com/fzi-forschungszentrum-informatik/Lanelet2) for this purpose. The new propose **Sensor Data Sharing Service** will also need lanelet2 for map transforms and for eventual perception. To allow all three services and any future services easy access to this dependency, this new **Streets Service Base Lanelet Aware** image exist. +> [!IMPORTANT]\ +> Currently this base image is only being used for the **Sensor Data Sharing Service** but both the **Intersection_Model** and **Message Service** should be ported to this base image as well. + +## Usage +Simply use the following to extend this image +``` +FROM usdotfhwastoldev/streets_service_base_lanelet_aware: +``` +The image also contains the environment variable **LANELET2_MAP**, which is assigned a default value of `/home/carma-streets/MAP/Intersection.osm`, but can be overwritten using the docker-compose environment variable section of the service description. An example is shown belo +``` + lanlet_aware_service: + image: usdotfhwastoldev/lanlet_aware_service:develop + environment: + LANELET2_MAP: /new/path/to/lanelet2/map.osm +``` \ No newline at end of file diff --git a/streets_utils/streets_service_base/CMakeLists.txt b/streets_utils/streets_service_base/CMakeLists.txt index e4afbee72..04469805e 100644 --- a/streets_utils/streets_service_base/CMakeLists.txt +++ b/streets_utils/streets_service_base/CMakeLists.txt @@ -78,9 +78,9 @@ set(SOURCES ${TEST_SOURCES} WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/test) add_executable(${BINARY} ${TEST_SOURCES} ) add_test(NAME ${BINARY} COMMAND ${BINARY}) target_link_libraries(${BINARY} - PUBLIC + PRIVATE ${PROJECT_NAME}_lib - GTest::gtest + GTest::GTest ) target_include_directories(${BINARY} PUBLIC From 1cc90c439cc9f5feae9e066a2d605068c23b2a08 Mon Sep 17 00:00:00 2001 From: Saikrishna Bairamoni <84093461+SaikrishnaBairamoni@users.noreply.github.com> Date: Tue, 14 Nov 2023 11:04:44 -0500 Subject: [PATCH 42/80] Update sonar-scanner.properties (#359) * Update sonar-scanner.properties * update * Add Java action * update sonar scanner version 5 * Remove unwanted sonar properties of sonar source and target --- .github/workflows/ci.yml | 22 ++++++++++++++++++---- 1 file changed, 18 insertions(+), 4 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index d228a2b22..f5c4dab1e 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -15,7 +15,7 @@ jobs: env: DEBIAN_FRONTEND: noninteractive INIT_ENV: "/home/carma-streets/.base-image/init-env.sh" - SONAR_SCANNER_VERSION: "4.6.2.2472" + SONAR_SCANNER_VERSION: "5.0.1.3006" TERM: xterm options: "--user root" steps: @@ -119,23 +119,37 @@ jobs: # cd /home/carma-streets/carma_lanelet2/install/ # ls -a # echo "source /home/carma-streets/carma_lanelet2/install/setup.bash" >> "$INIT_ENV" + - name: Set up JDK 17 + uses: actions/setup-java@v3 # The setup-java action provides the functionality for GitHub Actions runners for Downloading and setting up a requested version of Java + with: + java-version: 17 + distribution: "temurin" - name: Install Sonar run: | SONAR_DIR=/opt/sonarqube mkdir $SONAR_DIR curl -o $SONAR_DIR/sonar-scanner.zip https://binaries.sonarsource.com/Distribution/sonar-scanner-cli/sonar-scanner-cli-${SONAR_SCANNER_VERSION}-linux.zip curl -o $SONAR_DIR/build-wrapper.zip https://sonarcloud.io/static/cpp/build-wrapper-linux-x86.zip - curl -sL https://deb.nodesource.com/setup_16.x |bash - + curl -sL https://deb.nodesource.com/setup_16.x | bash - apt-get install -y nodejs unzip + # Set the JAVA_HOME to a compatible version of Java, e.g., Java 17 + export JAVA_HOME=$GITHUB_WORKSPACE/java-17 cd $SONAR_DIR for ZIP in *.zip; do unzip "$ZIP" -d . rm "$ZIP" done - mv $(ls $SONAR_DIR |grep "sonar-scanner-") $SONAR_DIR/sonar-scanner/ - mv $(ls $SONAR_DIR |grep "build-wrapper-") $SONAR_DIR/build-wrapper/ + mv $(ls $SONAR_DIR | grep "sonar-scanner-") $SONAR_DIR/sonar-scanner/ + mv $(ls $SONAR_DIR | grep "build-wrapper-") $SONAR_DIR/build-wrapper/ echo $SONAR_DIR/sonar-scanner/bin >> $GITHUB_PATH echo $SONAR_DIR/build-wrapper >> $GITHUB_PATH + env: + JAVA_HOME: $GITHUB_WORKSPACE/java-17 + + - name: Check Java Version + run: | + java -version + echo $JAVA_HOME - name: Build run: | source ${INIT_ENV} From 1d0cf7ee8eeb70f41c9924aa1ce64dbe550db526 Mon Sep 17 00:00:00 2001 From: paulbourelly999 <77466294+paulbourelly999@users.noreply.github.com> Date: Tue, 14 Nov 2023 16:07:32 -0500 Subject: [PATCH 43/80] Update unit tests to use time_threshold for time comparisons (#362) Update kafka client to be member of streets service --- .../include/streets_service.h | 6 ++++-- .../src/streets_service.cpp | 16 ++++++++++------ .../test/test_streets_clock_singleton.cpp | 19 ++++++++++--------- 3 files changed, 24 insertions(+), 17 deletions(-) diff --git a/streets_utils/streets_service_base/include/streets_service.h b/streets_utils/streets_service_base/include/streets_service.h index cfb28a260..0db3b183e 100644 --- a/streets_utils/streets_service_base/include/streets_service.h +++ b/streets_utils/streets_service_base/include/streets_service.h @@ -60,7 +60,7 @@ namespace streets_service { * @param producer shared_ptr to kafka producer. * @return true if initialization is successful and false if initialization fails. */ - bool initialize_kafka_producer( const std::string &producer_topic, std::shared_ptr &producer ) const; + bool initialize_kafka_producer( const std::string &producer_topic, std::shared_ptr &producer ); /** * @brief Helper method to initialise Kafka consumer. NOTE: Will assign consumer group id as service_name. * @@ -68,7 +68,7 @@ namespace streets_service { * @param consumer shared_ptr to kafka consumer. * @return true if initialization is successful and false if initialization fails. */ - bool initialize_kafka_consumer( const std::string &consumer_topic, std::shared_ptr &consumer ) const; + bool initialize_kafka_consumer( const std::string &consumer_topic, std::shared_ptr &consumer ); /** * @brief Returns string value of environment variable with given name. If no value is found, returns default. * @param config_name name of environment variable. @@ -111,6 +111,8 @@ namespace streets_service { bool _simulation_mode; + std::unique_ptr _kafka_client; + std::shared_ptr _time_consumer; std::string _logs_directory; diff --git a/streets_utils/streets_service_base/src/streets_service.cpp b/streets_utils/streets_service_base/src/streets_service.cpp index 0745d2542..76d0723cb 100644 --- a/streets_utils/streets_service_base/src/streets_service.cpp +++ b/streets_utils/streets_service_base/src/streets_service.cpp @@ -52,12 +52,14 @@ namespace streets_service { logger->flush_on(level); return logger; } - bool streets_service::initialize_kafka_producer( const std::string &producer_topic, std::shared_ptr &producer ) const { + bool streets_service::initialize_kafka_producer( const std::string &producer_topic, std::shared_ptr &producer ) { - auto client = std::make_unique(); + if ( !_kafka_client ) { + _kafka_client = std::make_unique(); + } std::string bootstrap_server = streets_configuration::get_string_config("bootstrap_server"); - producer = client->create_producer(bootstrap_server, producer_topic); + producer = _kafka_client->create_producer(bootstrap_server, producer_topic); if (!producer->init()) { SPDLOG_CRITICAL("Kafka producer initialize error on topic {0}", producer_topic); @@ -67,10 +69,12 @@ namespace streets_service { return true; } - bool streets_service::initialize_kafka_consumer(const std::string &consumer_topic, std::shared_ptr &kafka_consumer ) const{ - auto client = std::make_unique(); + bool streets_service::initialize_kafka_consumer(const std::string &consumer_topic, std::shared_ptr &kafka_consumer ){ + if ( !_kafka_client ) { + _kafka_client = std::make_unique(); + } std::string bootstrap_server = streets_configuration::get_string_config("bootstrap_server"); - kafka_consumer = client->create_consumer(bootstrap_server, consumer_topic, _service_name); + kafka_consumer = _kafka_client->create_consumer(bootstrap_server, consumer_topic, _service_name); if (!kafka_consumer->init()) { SPDLOG_CRITICAL("Kafka initialize error"); diff --git a/streets_utils/streets_service_base/test/test_streets_clock_singleton.cpp b/streets_utils/streets_service_base/test/test_streets_clock_singleton.cpp index 335d6ba69..79278d5f3 100644 --- a/streets_utils/streets_service_base/test/test_streets_clock_singleton.cpp +++ b/streets_utils/streets_service_base/test/test_streets_clock_singleton.cpp @@ -17,6 +17,7 @@ namespace streets_service{ void TearDown() { SPDLOG_INFO("Tear Down"); } + int time_comparison_threshold_ms = 20; }; /** @@ -46,7 +47,7 @@ namespace streets_service{ auto end = std::chrono::system_clock::now(); auto elapsed_time = end-start; // Assume duration is sleep duration of update thread +/- 1ms - EXPECT_NEAR( sleep_duration, std::chrono::duration_cast(elapsed_time).count(), 1 ); + EXPECT_NEAR( sleep_duration, std::chrono::duration_cast(elapsed_time).count(), time_comparison_threshold_ms ); t1.join(); } /** @@ -78,7 +79,7 @@ namespace streets_service{ auto end = std::chrono::system_clock::now(); auto elapsed_time = end-start; // Assume duration is sleep duration of update thread +/- 1ms - EXPECT_NEAR( sleep_duration, duration_cast(elapsed_time).count(), 1 ); + EXPECT_NEAR( sleep_duration, duration_cast(elapsed_time).count(), time_comparison_threshold_ms ); EXPECT_EQ(streets_clock_singleton::time_in_ms(), random); t2.join(); @@ -135,9 +136,9 @@ namespace streets_service{ EXPECT_EQ(streets_clock_singleton::time_in_ms(), 0); // // Assume duration is sleep duration of update is equal to thread duration +/- 1ms - EXPECT_NEAR( sleep_duration, t1_duration_ms, 1 ); - EXPECT_NEAR( sleep_duration, t2_duration_ms, 1 ); - EXPECT_NEAR( sleep_duration, t3_duration_ms, 1 ); + EXPECT_NEAR( sleep_duration, t1_duration_ms, time_comparison_threshold_ms ); + EXPECT_NEAR( sleep_duration, t2_duration_ms, time_comparison_threshold_ms ); + EXPECT_NEAR( sleep_duration, t3_duration_ms, time_comparison_threshold_ms ); } /** @@ -167,18 +168,18 @@ namespace streets_service{ */ TEST_F(test_streets_clock, test_real_time) { streets_clock_singleton::create(false); - EXPECT_NEAR(streets_clock_singleton::time_in_ms(), duration_cast(system_clock::now().time_since_epoch()).count(), 1); + EXPECT_NEAR(streets_clock_singleton::time_in_ms(), duration_cast(system_clock::now().time_since_epoch()).count(), time_comparison_threshold_ms); // allow time to change sleep(1); - EXPECT_NEAR(streets_clock_singleton::time_in_ms(), duration_cast(system_clock::now().time_since_epoch()).count(), 1); + EXPECT_NEAR(streets_clock_singleton::time_in_ms(), duration_cast(system_clock::now().time_since_epoch()).count(), time_comparison_threshold_ms); auto cur_time = streets_clock_singleton::time_in_ms(); streets_clock_singleton::sleep_for(2000); - EXPECT_NEAR(cur_time+2000, duration_cast(system_clock::now().time_since_epoch()).count(), 1); + EXPECT_NEAR(cur_time+2000, duration_cast(system_clock::now().time_since_epoch()).count(), time_comparison_threshold_ms); cur_time = streets_clock_singleton::time_in_ms(); auto sleep_until_time = cur_time + 3000; streets_clock_singleton::sleep_until(sleep_until_time); - EXPECT_NEAR(sleep_until_time, duration_cast(system_clock::now().time_since_epoch()).count(), 1); + EXPECT_NEAR(sleep_until_time, duration_cast(system_clock::now().time_since_epoch()).count(), time_comparison_threshold_ms); } } \ No newline at end of file From 90abff42eea5a4d97ccad63ad539a704b958b785 Mon Sep 17 00:00:00 2001 From: paulbourelly999 <77466294+paulbourelly999@users.noreply.github.com> Date: Fri, 17 Nov 2023 14:57:42 -0500 Subject: [PATCH 44/80] Allow for mocking of kafka consumers and producers initialized by streets_service_base (#364) * updates * Update Unit tests * Updates to unit tests * Updates * PR * PR Updates * Update README * CMake Update * Updates * Update README * Update type * Updates * PR Updates --- kafka_clients/README.md | 17 ++++ kafka_clients/include/kafka_client.h | 7 +- kafka_clients/include/mock_kafka_client.h | 19 ++++ kafka_clients/readme.md | 29 ------ .../streets_service_base/CMakeLists.txt | 21 +++-- .../include/streets_service.h | 5 ++ .../src/streets_service.cpp | 1 + .../streets_service_base/test/test_main.cpp | 6 -- .../test/test_streets_service.cpp | 88 +++++++++++++++++-- 9 files changed, 136 insertions(+), 57 deletions(-) create mode 100644 kafka_clients/README.md create mode 100644 kafka_clients/include/mock_kafka_client.h delete mode 100644 kafka_clients/readme.md delete mode 100644 streets_utils/streets_service_base/test/test_main.cpp diff --git a/kafka_clients/README.md b/kafka_clients/README.md new file mode 100644 index 000000000..557314f45 --- /dev/null +++ b/kafka_clients/README.md @@ -0,0 +1,17 @@ +# Kafka Clients +## Introduction +[librdkafka_github]: https://github.com/confluentinc/librdkafka +This is a library that wraps [librdkafka][librdkafka_github] C++ kafka client library. It consists of three main classes: +`kafka_client`: Class used to setup Kafka connection and create producers and consumers. +`kafka_producer_worker`: Kafka producer. Constructor takes in kafka broker, topic to produce to, and an optional parameter for partition. +`kafka_consumer_worker`: Kafka consumer. Constructor takes in kafka broker, topic to subscribe to, and consumer group to join. +More [Kafka Documentation](https://kafka.apache.org/documentation/) is avaiable online. +## Usage +To include in you CMake project simply find the `kafka_clients_lib` package and link it to your target. Note: this package requires that [librdkafka][librdkafka_github] is installed. It also requires that [spdlog](https://github.com/gabime/spdlog) is installed. +```cmake +find_package(kafka_clients_lib REQUIRED) +target_link_libraries( + PUBLIC + kafka_clients_lib::kafka_clients_lib +) +``` \ No newline at end of file diff --git a/kafka_clients/include/kafka_client.h b/kafka_clients/include/kafka_client.h index 344284f3c..4e333df62 100644 --- a/kafka_clients/include/kafka_client.h +++ b/kafka_clients/include/kafka_client.h @@ -15,11 +15,10 @@ namespace kafka_clients class kafka_client { public: - std::shared_ptr create_consumer(const std::string &broker_str, const std::string &topic_str, + virtual ~kafka_client()=default; + virtual std::shared_ptr create_consumer(const std::string &broker_str, const std::string &topic_str, const std::string &group_id_str) const; - std::shared_ptr create_producer(const std::string &broker_str, const std::string &topic_str) const; - rapidjson::Document read_json_file(const std::string &json_file) const; - std::string get_value_by_doc(rapidjson::Document &doc, const char *key) const; + virtual std::shared_ptr create_producer(const std::string &broker_str, const std::string &topic_str) const; }; } diff --git a/kafka_clients/include/mock_kafka_client.h b/kafka_clients/include/mock_kafka_client.h new file mode 100644 index 000000000..9f93815a9 --- /dev/null +++ b/kafka_clients/include/mock_kafka_client.h @@ -0,0 +1,19 @@ +#pragma once +#include "kafka_client.h" +#include +namespace kafka_clients { + /** + * @brief Mock kafka client used for unit testing using gmock. For documentation using gmock mocks + * (https://google.github.io/googletest/gmock_for_dummies.html). + * + * @author + */ + class mock_kafka_client : public kafka_client { + public: + mock_kafka_client() {}; + ~mock_kafka_client() = default; + MOCK_METHOD(std::shared_ptr, create_consumer,(const std::string &broker_str, const std::string &topic_str, const std::string &group_id_str),(const, override)); + MOCK_METHOD(std::shared_ptr, create_producer,(const std::string &broker_str, const std::string &topic_str),(const, override)); + + }; +} \ No newline at end of file diff --git a/kafka_clients/readme.md b/kafka_clients/readme.md deleted file mode 100644 index a3f544651..000000000 --- a/kafka_clients/readme.md +++ /dev/null @@ -1,29 +0,0 @@ - -``` -git clone https://github.com/google/googletest/ -cmake . -make -sudo make install - -refer to https://github.com/edenhill/librdkafka -./configure - make - sudo make install - -git clone https://github.com/gabime/spdlog.git -cd spdlog && mkdir build && cd build -cmake .. && make -j -sudo make install - -https://github.com/Tencent/rapidjson -git clone https://github.com/Tencent/rapidjson -cd rapidjson && mkdir build && cd build -cmake .. -make -sudo make install - -git clone https://github.com/usdot-fhwa-stol/carma-streets -cd kafka_clients && mkdir build && cd build -cmake .. && make -j -sudo make install -``` \ No newline at end of file diff --git a/streets_utils/streets_service_base/CMakeLists.txt b/streets_utils/streets_service_base/CMakeLists.txt index 04469805e..a5d9757a8 100644 --- a/streets_utils/streets_service_base/CMakeLists.txt +++ b/streets_utils/streets_service_base/CMakeLists.txt @@ -72,20 +72,19 @@ install(FILES ${templates} DESTINATION include/internal) ######################## # googletest for unit testing ######################## -set(BINARY ${PROJECT_NAME}_test) +enable_testing() +set(TEST_EXECUTABLE ${PROJECT_NAME}_test) file(GLOB_RECURSE TEST_SOURCES LIST_DIRECTORIES false test/*.h test/*.cpp) set(SOURCES ${TEST_SOURCES} WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/test) -add_executable(${BINARY} ${TEST_SOURCES} ) -add_test(NAME ${BINARY} COMMAND ${BINARY}) -target_link_libraries(${BINARY} + +add_executable(${TEST_EXECUTABLE} ${TEST_SOURCES} ) +target_link_libraries(${TEST_EXECUTABLE} PRIVATE ${PROJECT_NAME}_lib - GTest::GTest + GTest::Main ) -target_include_directories(${BINARY} - PUBLIC - $ - $ - PRIVATE - ${CMAKE_CURRENT_SOURCE_DIR}/src +include(GoogleTest) +gtest_discover_tests(${TEST_EXECUTABLE} + DISCOVERY_MODE PRE_TEST + WORKING_DIRECTORY ${PROJECT_SOURCE_DIR}/test ) \ No newline at end of file diff --git a/streets_utils/streets_service_base/include/streets_service.h b/streets_utils/streets_service_base/include/streets_service.h index 0db3b183e..6cf058208 100644 --- a/streets_utils/streets_service_base/include/streets_service.h +++ b/streets_utils/streets_service_base/include/streets_service.h @@ -105,6 +105,7 @@ namespace streets_service { */ std::shared_ptr create_daily_logger(const std::string &name, const std::string &extension = ".log", const std::string &pattern = "[%Y-%m-%d %H:%M:%S.%e] %v", const spdlog::level::level_enum &level = spdlog::level::info ) const; + private: std::string _service_name; @@ -120,10 +121,14 @@ namespace streets_service { FRIEND_TEST(test_streets_service, test_consume_time_sync_message); FRIEND_TEST(test_streets_service, test_initialize_consumer); FRIEND_TEST(test_streets_service, test_initialize_producer); + FRIEND_TEST(test_streets_service, test_initialize_consumer_fail); + FRIEND_TEST(test_streets_service, test_initialize_producer_fail); FRIEND_TEST(test_streets_service, test_initialize_sim); + FRIEND_TEST(test_streets_service, test_initialize_sim_fail); FRIEND_TEST(test_streets_service, test_get_system_config); FRIEND_TEST(test_streets_service, test_create_daily_logger); FRIEND_TEST(test_streets_service, test_create_daily_logger_default); + FRIEND_TEST(test_streets_service, test_start); diff --git a/streets_utils/streets_service_base/src/streets_service.cpp b/streets_utils/streets_service_base/src/streets_service.cpp index 76d0723cb..f63f8deb1 100644 --- a/streets_utils/streets_service_base/src/streets_service.cpp +++ b/streets_utils/streets_service_base/src/streets_service.cpp @@ -139,4 +139,5 @@ namespace streets_service { bool streets_service::is_simulation_mode() const { return _simulation_mode; } + } \ No newline at end of file diff --git a/streets_utils/streets_service_base/test/test_main.cpp b/streets_utils/streets_service_base/test/test_main.cpp deleted file mode 100644 index 1e925a90b..000000000 --- a/streets_utils/streets_service_base/test/test_main.cpp +++ /dev/null @@ -1,6 +0,0 @@ -#include "gtest/gtest.h" -int main(int argc, char **argv) -{ - ::testing::InitGoogleTest(&argc, argv); - return RUN_ALL_TESTS(); -} \ No newline at end of file diff --git a/streets_utils/streets_service_base/test/test_streets_service.cpp b/streets_utils/streets_service_base/test/test_streets_service.cpp index b6171e128..25c929649 100644 --- a/streets_utils/streets_service_base/test/test_streets_service.cpp +++ b/streets_utils/streets_service_base/test/test_streets_service.cpp @@ -1,33 +1,58 @@ #include +#include #include #include #include #include +#include #include #include using testing::_; using testing::Return; +using testing::AnyNumber; namespace streets_service{ class test_streets_service : public testing::Test { - protected: - void SetUp() { + public: + std::shared_ptr mock_consumer; + std::shared_ptr mock_producer; + + void SetUp() override { + + setenv(SIMULATION_MODE_ENV.c_str(), "TRUE", 1); setenv(TIME_SYNC_TOPIC_ENV.c_str(), "time_sync", 1); setenv(CONFIG_FILE_PATH_ENV.c_str(), "../test/test_files/manifest.json", 1); setenv(LOGS_DIRECTORY_ENV.c_str(), "../logs/", 1); } - public: streets_service serv; }; TEST_F(test_streets_service, test_initialize_sim) { + serv._kafka_client = std::make_unique(); + mock_consumer = std::make_shared(); + + // Set mock client to return mock producer and consumer respectively on calls to create_producer and create_consumer + EXPECT_CALL(dynamic_cast(*serv._kafka_client), create_consumer("127.0.0.1:9092", "time_sync" , "test_service") ).Times(1).WillRepeatedly(Return(mock_consumer)); + EXPECT_CALL(*mock_consumer, init() ).Times(1).WillRepeatedly(Return(true)); EXPECT_TRUE(serv.initialize()); EXPECT_EQ( serv.get_service_name(), "test_service"); EXPECT_TRUE(serv.is_simulation_mode()); }; + + TEST_F(test_streets_service, test_initialize_sim_fail) { + serv._kafka_client = std::make_unique(); + mock_consumer = std::make_shared(); + + // Set mock client to return mock producer and consumer respectively on calls to create_producer and create_consumer + EXPECT_CALL(dynamic_cast(*serv._kafka_client), create_consumer("127.0.0.1:9092", "time_sync" , "test_service") ).Times(1).WillRepeatedly(Return(mock_consumer)); + EXPECT_CALL(*mock_consumer, init() ).Times(1).WillRepeatedly(Return(false)); + EXPECT_FALSE(serv.initialize()); + EXPECT_EQ( serv.get_service_name(), "test_service"); + EXPECT_TRUE(serv.is_simulation_mode()); + }; TEST_F(test_streets_service, test_consume_time_sync_message) { auto mock_time_consumer = std::make_shared(); @@ -44,6 +69,8 @@ namespace streets_service{ "\"seq\":123" "}" )); + // Create Carma Clock Singleton + streets_clock_singleton::create(true); serv.consume_time_sync_message(); // Skip empty message and skip incorrect message and consume real message then // consumer is_running returns false and returns control @@ -88,17 +115,63 @@ namespace streets_service{ } TEST_F(test_streets_service, test_initialize_consumer) { - serv._service_name ="TestService"; + serv._service_name ="test_service"; std::shared_ptr consumer; + mock_consumer = std::make_shared(); + serv._kafka_client = std::make_unique(); + + // Set mock client to return mock producer and consumer respectively on calls to create_producer and create_consumer + EXPECT_CALL(dynamic_cast(*serv._kafka_client), create_consumer("127.0.0.1:9092", "test_topic" , "test_service") ).Times(1).WillRepeatedly(Return(mock_consumer)); + // Set mock client to return mock producer and consumer respectively on calls to create_producer and create_consumer + EXPECT_CALL(*mock_consumer, init() ).Times(1).WillRepeatedly(Return(true)); + // Create streets configuration singleton + streets_configuration::create("../test/test_files/manifest.json"); EXPECT_TRUE(serv.initialize_kafka_consumer("test_topic", consumer)); - consumer->stop(); + }; + +TEST_F(test_streets_service, test_initialize_consumer_fail) { + serv._service_name ="test_service"; + std::shared_ptr consumer; + mock_consumer = std::make_shared(); + serv._kafka_client = std::make_unique(); + + // Set mock client to return mock producer and consumer respectively on calls to create_producer and create_consumer + EXPECT_CALL(dynamic_cast(*serv._kafka_client), create_consumer("127.0.0.1:9092", "test_topic" , "test_service") ).Times(1).WillRepeatedly(Return(mock_consumer)); + // Set mock client to return mock producer and consumer respectively on calls to create_producer and create_consumer + EXPECT_CALL(*mock_consumer, init() ).Times(1).WillRepeatedly(Return(false)); + // Create streets configuration singleton + streets_configuration::create("../test/test_files/manifest.json"); + EXPECT_FALSE(serv.initialize_kafka_consumer("test_topic", consumer)); }; TEST_F(test_streets_service, test_initialize_producer) { - serv._service_name ="TestService"; + serv._service_name ="test_service"; std::shared_ptr producer; + mock_producer = std::make_shared(); + serv._kafka_client = std::make_unique(); + + // Set mock client to return mock producer and producer respectively on calls to create_producer and create_producer + EXPECT_CALL(dynamic_cast(*serv._kafka_client), create_producer("127.0.0.1:9092", "test_topic") ).Times(1).WillRepeatedly(Return(mock_producer)); + // Set mock client to return mock producer and producer respectively on calls to create_producer and create_producer + EXPECT_CALL(*mock_producer, init() ).Times(1).WillRepeatedly(Return(true)); + // Create streets configuration singleton + streets_configuration::create("../test/test_files/manifest.json"); EXPECT_TRUE(serv.initialize_kafka_producer("test_topic", producer)); - producer->stop(); + }; + + TEST_F(test_streets_service, test_initialize_producer_fail) { + serv._service_name ="test_service"; + std::shared_ptr producer; + mock_producer = std::make_shared(); + serv._kafka_client = std::make_unique(); + + // Set mock client to return mock producer and producer respectively on calls to create_producer and create_producer + EXPECT_CALL(dynamic_cast(*serv._kafka_client), create_producer("127.0.0.1:9092", "test_topic" ) ).Times(1).WillRepeatedly(Return(mock_producer)); + // Set mock client to return mock producer and producer respectively on calls to create_producer and create_producer + EXPECT_CALL(*mock_producer, init() ).Times(1).WillRepeatedly(Return(false)); + // Create streets configuration singleton + streets_configuration::create("../test/test_files/manifest.json"); + EXPECT_FALSE(serv.initialize_kafka_producer("test_topic", producer)); }; TEST_F(test_streets_service, test_get_system_config) { @@ -110,6 +183,7 @@ namespace streets_service{ }; TEST_F(test_streets_service, test_start) { EXPECT_TRUE(serv.initialize()); + serv.start(); } From 9e37b6a160adee9333d64976e49e3f273dc8e646 Mon Sep 17 00:00:00 2001 From: Anish_deva <51463994+adev4a@users.noreply.github.com> Date: Fri, 17 Nov 2023 16:40:43 -0500 Subject: [PATCH 45/80] add detected objects message definition (#365) * add detected objects message definition * update variable names * update namespace * fix namespace * update array var to vector and add missing parameter * update readme for Detected Objects * fix comment --- .../DetectedObjectsMessage.md | 87 ++++++++++++++++++ streets_utils/streets_messages/README.md | 7 +- ...sor_data_sharing_msg_json_deserializer.hpp | 6 +- .../detected_object_msg/cartesian_point.hpp | 35 +++++++ .../detected_object_msg.hpp | 47 ++++++++++ .../include/detected_object_msg/size.hpp | 33 +++++++ .../include/detected_object_msg/vector_3d.hpp | 32 +++++++ .../acceleration_confidence.hpp | 2 +- .../acceleration_set_4_way.hpp | 4 +- .../angular_velocity_confidence.hpp | 2 +- .../detected_object_data.hpp | 2 +- .../detected_object_data_common.hpp | 12 +-- .../equipment_type.hpp | 6 +- .../heading_confidence.hpp | 2 +- .../sensor_data_sharing_msg/object_type.hpp | 6 +- .../obstacle/detected_obstacle_data.hpp | 2 +- .../obstacle/obstacle_size.hpp | 2 +- .../obstacle/obstacle_size_confidence.hpp | 2 +- .../sensor_data_sharing_msg/position_3d.hpp | 8 +- .../position_confidence.hpp | 6 +- .../position_confidence_set.hpp | 2 +- .../position_offset.hpp | 2 +- .../positional_accuracy.hpp | 10 +- .../sensor_data_sharing_msg.hpp | 8 +- .../size_value_confidence.hpp | 12 +-- .../speed_confidence.hpp | 2 +- .../time_confidence.hpp | 10 +- .../sensor_data_sharing_msg/time_stamp.hpp | 2 +- .../angular_velocity_confidence_set.hpp | 2 +- .../vehicle/angular_velocity_set.hpp | 2 +- .../vehicle/attitude.hpp | 6 +- .../vehicle/attitude_confidence.hpp | 2 +- .../vehicle/detected_vehicle_data.hpp | 24 ++--- .../vehicle/vehicle_size.hpp | 6 +- .../vehicle/vehicle_size_confidence.hpp | 2 +- .../vru/animal_propelled_type.hpp | 4 +- .../vru/attachment.hpp | 4 +- .../vru/detected_vru_data.hpp | 2 +- .../vru/human_propelled_type.hpp | 4 +- .../vru/motorized_propelled_type.hpp | 6 +- .../vru/personal_device_user_type.hpp | 4 +- ...ensor_data_sharing_msg_json_serializer.hpp | 8 +- ...sor_data_sharing_msg_json_deserializer.cpp | 28 +++--- ...ensor_data_sharing_msg_json_serializer.cpp | 34 +++---- ..._sharing_msg_json_deserialization_test.cpp | 11 +-- .../acceleration_confidence_test.cpp | 2 +- .../angular_velocity_confidence_test.cpp | 2 +- .../animal_propelled_type_test.cpp | 2 +- .../attachment_test.cpp | 2 +- .../equipment_type_test.cpp | 2 +- .../heading_confidence_test.cpp | 2 +- .../human_propelled_type_test.cpp | 2 +- .../motorized_propelled_type_test.cpp | 2 +- .../object_type_test.cpp | 2 +- .../personal_device_user_type.cpp | 2 +- .../position_confidence_test.cpp | 2 +- .../size_value_confidence_test.cpp | 2 +- .../speed_confidence_test.cpp | 2 +- .../time_confidence_test.cpp | 4 +- ...ta_sharing_msg_json_serialization_test.cpp | 91 +++++++++---------- 60 files changed, 427 insertions(+), 194 deletions(-) create mode 100644 streets_utils/streets_messages/DetectedObjectsMessage.md create mode 100644 streets_utils/streets_messages/include/detected_object_msg/cartesian_point.hpp create mode 100644 streets_utils/streets_messages/include/detected_object_msg/detected_object_msg.hpp create mode 100644 streets_utils/streets_messages/include/detected_object_msg/size.hpp create mode 100644 streets_utils/streets_messages/include/detected_object_msg/vector_3d.hpp diff --git a/streets_utils/streets_messages/DetectedObjectsMessage.md b/streets_utils/streets_messages/DetectedObjectsMessage.md new file mode 100644 index 000000000..fe0ae50ad --- /dev/null +++ b/streets_utils/streets_messages/DetectedObjectsMessage.md @@ -0,0 +1,87 @@ +# Detected Objects Message + +## Introduction + +This CARMA-Streets library contains the Detected Objects Message as well as the logic for deserializing this object from JSON. Below is an example JSON payload. This message will be produced by an external Sensor capable of detecting objects. The data in it will represent the incoming **Sensor Detected Object** detection. This is a custom message definition for the detected objects. + +## Example JSON payload +``` +{ + "type": "CAR", + "confidence": 0.7, + "sensorId": "sensor1", + "projString": "projection String2", + "objectId": "Object7", + "position": { + "x": -1.1, + "y": -2, + "z": -3.2 + }, + "positionCovariance": [ + [ + 1, + 0, + 0 + ], + [ + 1, + 0, + 0 + ], + [ + 1, + 0, + 0 + ] + ], + "velocity": { + "x": 1, + "y": 1, + "z": 1 + }, + "velocityCovariance": [ + [ + 1, + 0, + 0 + ], + [ + 1, + 0, + 0 + ], + [ + 1, + 0, + 0 + ] + ], + "angularVelocity": { + "x": 0.1, + "y": 0.2, + "z": 0.3 + }, + "angularVelocityCovariance": [ + [ + 1, + 0, + 0 + ], + [ + 1, + 0, + 0 + ], + [ + 1, + 0, + 0 + ] + ], + "size": { + "length": 2, + "height": 1, + "width": 0.5 + } +} +``` diff --git a/streets_utils/streets_messages/README.md b/streets_utils/streets_messages/README.md index 2947d1050..c15ea8af0 100644 --- a/streets_utils/streets_messages/README.md +++ b/streets_utils/streets_messages/README.md @@ -2,12 +2,13 @@ ## Introduction -This CARMA-Streets library will be the new location for storing both CARMA Streets message data types as well as the logic to serialize and deserialize these messages. Message data types will be stored in directories with the message name as the directory name similar to the current `sensor_data_sharing_msg`. Then functions will hold the JSON serialization and deserialization logic for each message to seperate concerns betweening message serialization and message data. +This CARMA-Streets library will be the new location for storing both CARMA Streets message data types as well as the logic to serialize and deserialize these messages. Message data types will be stored in directories with the message name as the directory name similar to the current `sensor_data_sharing_msg`. Then functions will hold the JSON serialization and deserialization logic for each message to seperate concerns between message serialization and message data. > [!IMPORTANT]\ -> Currently this library only contains the sensor_data sharing message and its JSON serialization logic. Will attempt to part existing and future messages into this library. +> Currently this library only contains the sensor_data sharing message and the detected objects message, with their JSON serialization logic. Will attempt to part existing and future messages into this library. ## Messages **[Sensor Data Sharing Message](SensorDataSharingMessage.md)** : J3224 Message used for sharing detection level data between vehicles and infrastructure. +**[Detected Objects Message](DetectedObjectsMessage.md)** : Custom Message used for sharing object detection information between sensors and ITS actors ## Serialization and Deserialization CARMA Streets messages are currently all using JSON for serialization for sending via a Apache Kafka broker. To parse or write the JSON we are currently using the [rapidjson library](https://github.com/Tencent/rapidjson). In addition, we have implemented some helper functions to parse optional fields from JSON using the [std::optional](https://en.cppreference.com/w/cpp/utility/optional) class template implemented in our [json_utils library](https://github.com/usdot-fhwa-stol/carma-streets/tree/develop/streets_utils/json_utils). @@ -24,7 +25,7 @@ This library includes a CMake install target that will attempt to install this l `streets_utils\streets_messages_lib` -Header files will be installed in the CMake default include directory +Header files will be installed in the CMake default include directory `include\streets_utils\streets_messages_lib\` diff --git a/streets_utils/streets_messages/include/deserializers/sensor_data_sharing_msg_json_deserializer.hpp b/streets_utils/streets_messages/include/deserializers/sensor_data_sharing_msg_json_deserializer.hpp index 150cadaee..dd661ac83 100644 --- a/streets_utils/streets_messages/include/deserializers/sensor_data_sharing_msg_json_deserializer.hpp +++ b/streets_utils/streets_messages/include/deserializers/sensor_data_sharing_msg_json_deserializer.hpp @@ -11,7 +11,7 @@ // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. -#pragma once +#pragma once #include #include #include @@ -21,7 +21,7 @@ #include #include "sensor_data_sharing_msg/sensor_data_sharing_msg.hpp" -namespace streets_utils::messages { +namespace streets_utils::messages::sdsm { sensor_data_sharing_msg from_json( const std::string &val); time_stamp parse_time_stamp(const rapidjson::Value &val); @@ -54,7 +54,7 @@ namespace streets_utils::messages { obstacle_size_confidence parse_obstacle_size_confidence(const rapidjson::Value &val); - std::optional> parse_propelled_information( const rapidjson::Value &val); + std::optional> parse_propelled_information( const rapidjson::Value &val); attitude parse_vehicle_attitude(const rapidjson::Value &val); diff --git a/streets_utils/streets_messages/include/detected_object_msg/cartesian_point.hpp b/streets_utils/streets_messages/include/detected_object_msg/cartesian_point.hpp new file mode 100644 index 000000000..5b7b188fd --- /dev/null +++ b/streets_utils/streets_messages/include/detected_object_msg/cartesian_point.hpp @@ -0,0 +1,35 @@ +// Copyright 2023 Leidos +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +#pragma once + +#include +#include +#include + + +namespace streets_utils::messages::detected_objects_msg { + + + /** + * @brief Struct for world frame coordinates. + */ + struct cartesian_point{ + double _x; + double _y; + double _z; + }; + + + +} \ No newline at end of file diff --git a/streets_utils/streets_messages/include/detected_object_msg/detected_object_msg.hpp b/streets_utils/streets_messages/include/detected_object_msg/detected_object_msg.hpp new file mode 100644 index 000000000..15d7c5683 --- /dev/null +++ b/streets_utils/streets_messages/include/detected_object_msg/detected_object_msg.hpp @@ -0,0 +1,47 @@ +// Copyright 2023 Leidos +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +#pragma once + +#include +#include +#include + +#include +#include +#include + +namespace streets_utils::messages::detected_objects_msg { + + /** + * @brief Sensor Detected Object information + */ + struct detected_objects_msg { + + std::string _type; + double _confidence; + std::string _sensor_id; + std::string _proj_string; + std::string _object_id; + cartesian_point _position; + std::vector> _position_covariance; + vector_3d _velocity; + std::vector> _velocity_covariance; + vector_3d _angular_velocity; + std::vector> _angular_velocity_covariance; + size _size; + + }; + + +} \ No newline at end of file diff --git a/streets_utils/streets_messages/include/detected_object_msg/size.hpp b/streets_utils/streets_messages/include/detected_object_msg/size.hpp new file mode 100644 index 000000000..b86b9dd46 --- /dev/null +++ b/streets_utils/streets_messages/include/detected_object_msg/size.hpp @@ -0,0 +1,33 @@ +// Copyright 2023 Leidos +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +#pragma once + +#include +#include +#include + + +namespace streets_utils::messages::detected_objects_msg { + + /** + * @brief Struct for size dimensions. + */ + struct size{ + + double _length; /**length in meters */ + double _height; /**height in meters */ + double _width; /**width in meters */ + }; + +} \ No newline at end of file diff --git a/streets_utils/streets_messages/include/detected_object_msg/vector_3d.hpp b/streets_utils/streets_messages/include/detected_object_msg/vector_3d.hpp new file mode 100644 index 000000000..40f062359 --- /dev/null +++ b/streets_utils/streets_messages/include/detected_object_msg/vector_3d.hpp @@ -0,0 +1,32 @@ +// Copyright 2023 Leidos +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +#pragma once + +#include +#include +#include + + +namespace streets_utils::messages::detected_objects_msg { + + /** + * @brief 3 dimensional vector struct. + */ + struct vector_3d{ + double _x; + double _y; + double _z; + }; + +} \ No newline at end of file diff --git a/streets_utils/streets_messages/include/sensor_data_sharing_msg/acceleration_confidence.hpp b/streets_utils/streets_messages/include/sensor_data_sharing_msg/acceleration_confidence.hpp index 0d287b340..d9ba549ad 100644 --- a/streets_utils/streets_messages/include/sensor_data_sharing_msg/acceleration_confidence.hpp +++ b/streets_utils/streets_messages/include/sensor_data_sharing_msg/acceleration_confidence.hpp @@ -13,7 +13,7 @@ // limitations under the License. #pragma once -namespace streets_utils::messages{ +namespace streets_utils::messages::sdsm{ enum class acceleration_confidence { UNAVAILABLE = 0, // Not available ACCL_100 = 1, // 100 m/s^2 diff --git a/streets_utils/streets_messages/include/sensor_data_sharing_msg/acceleration_set_4_way.hpp b/streets_utils/streets_messages/include/sensor_data_sharing_msg/acceleration_set_4_way.hpp index 4218c94a3..cb4e599e6 100644 --- a/streets_utils/streets_messages/include/sensor_data_sharing_msg/acceleration_set_4_way.hpp +++ b/streets_utils/streets_messages/include/sensor_data_sharing_msg/acceleration_set_4_way.hpp @@ -14,7 +14,7 @@ #pragma once -namespace streets_utils::messages{ +namespace streets_utils::messages::sdsm{ struct acceleration_set_4_way{ /** * @brief Longitudinal acceleration in 0.01 m/s^s [-2000, 2001] @@ -27,7 +27,7 @@ namespace streets_utils::messages{ /** * @brief Vertical acceleration in 0.02 G [-127, 127] */ - int _vertical_accel; + int _vertical_accel; /** * @brief Angular velocity in 0.01 degrees [-32767, 32767] */ diff --git a/streets_utils/streets_messages/include/sensor_data_sharing_msg/angular_velocity_confidence.hpp b/streets_utils/streets_messages/include/sensor_data_sharing_msg/angular_velocity_confidence.hpp index 1ffc5c39e..9916f5165 100644 --- a/streets_utils/streets_messages/include/sensor_data_sharing_msg/angular_velocity_confidence.hpp +++ b/streets_utils/streets_messages/include/sensor_data_sharing_msg/angular_velocity_confidence.hpp @@ -13,7 +13,7 @@ // limitations under the License. #pragma once -namespace streets_utils::messages{ +namespace streets_utils::messages::sdsm{ enum class angular_velocity_confidence{ UNAVAILABLE = 0, DEGSEC_100 = 1, // 100 degrees diff --git a/streets_utils/streets_messages/include/sensor_data_sharing_msg/detected_object_data.hpp b/streets_utils/streets_messages/include/sensor_data_sharing_msg/detected_object_data.hpp index ead61869a..87f4b552e 100644 --- a/streets_utils/streets_messages/include/sensor_data_sharing_msg/detected_object_data.hpp +++ b/streets_utils/streets_messages/include/sensor_data_sharing_msg/detected_object_data.hpp @@ -19,7 +19,7 @@ #include "sensor_data_sharing_msg/vru/detected_vru_data.hpp" #include -namespace streets_utils::messages{ +namespace streets_utils::messages::sdsm{ struct detected_object_data { /** * @brief Common data for detected object. diff --git a/streets_utils/streets_messages/include/sensor_data_sharing_msg/detected_object_data_common.hpp b/streets_utils/streets_messages/include/sensor_data_sharing_msg/detected_object_data_common.hpp index 26067bad1..a4f70e021 100644 --- a/streets_utils/streets_messages/include/sensor_data_sharing_msg/detected_object_data_common.hpp +++ b/streets_utils/streets_messages/include/sensor_data_sharing_msg/detected_object_data_common.hpp @@ -25,10 +25,10 @@ #include #include -namespace streets_utils::messages { +namespace streets_utils::messages::sdsm { struct detected_object_data_common{ /** - * @brief Object type enumeration + * @brief Object type enumeration */ object_type _object_type = object_type::UNKNOWN; /** @@ -36,7 +36,7 @@ namespace streets_utils::messages { */ unsigned int _classification_confidence = 0; /** - * @brief Object ID [0, 65535] + * @brief Object ID [0, 65535] */ unsigned int _object_id = 0; /** @@ -60,7 +60,7 @@ namespace streets_utils::messages { */ unsigned int _speed = 0; /** - * @brief Confidence in reported speed + * @brief Confidence in reported speed */ speed_confidence _speed_confidence = speed_confidence::UNAVAILABLE; /** @@ -68,7 +68,7 @@ namespace streets_utils::messages { */ std::optional _speed_z; /** - * @brief Confidence in reported speed z + * @brief Confidence in reported speed z */ std::optional _speed_z_confidence; /** @@ -83,7 +83,7 @@ namespace streets_utils::messages { * @brief Acceleration in longitudinal, lateral, vertical and angular velocity. */ std::optional _acceleration_4_way; - + std::optional _longitudinal_acceleration_confidence; std::optional _lateral_acceleration_confidence; diff --git a/streets_utils/streets_messages/include/sensor_data_sharing_msg/equipment_type.hpp b/streets_utils/streets_messages/include/sensor_data_sharing_msg/equipment_type.hpp index d316f2210..a82911346 100644 --- a/streets_utils/streets_messages/include/sensor_data_sharing_msg/equipment_type.hpp +++ b/streets_utils/streets_messages/include/sensor_data_sharing_msg/equipment_type.hpp @@ -14,13 +14,13 @@ #pragma once -namespace streets_utils::messages{ - +namespace streets_utils::messages::sdsm{ + enum class equipment_type { UNKNOWN = 0, RSU = 1, OBU= 2, - VRU= 3 + VRU= 3 }; /** * @brief Function to convert integers to equiment type. Default to UNKNOWN. diff --git a/streets_utils/streets_messages/include/sensor_data_sharing_msg/heading_confidence.hpp b/streets_utils/streets_messages/include/sensor_data_sharing_msg/heading_confidence.hpp index 1e837dea5..b83e5fbf8 100644 --- a/streets_utils/streets_messages/include/sensor_data_sharing_msg/heading_confidence.hpp +++ b/streets_utils/streets_messages/include/sensor_data_sharing_msg/heading_confidence.hpp @@ -13,7 +13,7 @@ // limitations under the License. #pragma once -namespace streets_utils::messages{ +namespace streets_utils::messages::sdsm{ enum class heading_confidence{ UNAVAILABLE = 0, PREC_10_deg = 1, // 10 degrees diff --git a/streets_utils/streets_messages/include/sensor_data_sharing_msg/object_type.hpp b/streets_utils/streets_messages/include/sensor_data_sharing_msg/object_type.hpp index 34890bb4d..cf7da54d1 100644 --- a/streets_utils/streets_messages/include/sensor_data_sharing_msg/object_type.hpp +++ b/streets_utils/streets_messages/include/sensor_data_sharing_msg/object_type.hpp @@ -13,13 +13,13 @@ // limitations under the License. #pragma once -namespace streets_utils::messages{ - +namespace streets_utils::messages::sdsm{ + enum class object_type { UNKNOWN = 0, VEHICLE = 1, VRU= 2, - ANIMAL= 3 + ANIMAL= 3 }; inline object_type object_type_from_int( const int i ) { diff --git a/streets_utils/streets_messages/include/sensor_data_sharing_msg/obstacle/detected_obstacle_data.hpp b/streets_utils/streets_messages/include/sensor_data_sharing_msg/obstacle/detected_obstacle_data.hpp index 896b2ac91..1c4fda26e 100644 --- a/streets_utils/streets_messages/include/sensor_data_sharing_msg/obstacle/detected_obstacle_data.hpp +++ b/streets_utils/streets_messages/include/sensor_data_sharing_msg/obstacle/detected_obstacle_data.hpp @@ -15,7 +15,7 @@ #include "sensor_data_sharing_msg/obstacle/obstacle_size.hpp" #include "sensor_data_sharing_msg/obstacle/obstacle_size_confidence.hpp" -namespace streets_utils::messages{ +namespace streets_utils::messages::sdsm{ struct detected_obstacle_data{ /** * @brief Size of obstacle. diff --git a/streets_utils/streets_messages/include/sensor_data_sharing_msg/obstacle/obstacle_size.hpp b/streets_utils/streets_messages/include/sensor_data_sharing_msg/obstacle/obstacle_size.hpp index 8dd8740d1..bd2bd5911 100644 --- a/streets_utils/streets_messages/include/sensor_data_sharing_msg/obstacle/obstacle_size.hpp +++ b/streets_utils/streets_messages/include/sensor_data_sharing_msg/obstacle/obstacle_size.hpp @@ -15,7 +15,7 @@ #include -namespace streets_utils::messages { +namespace streets_utils::messages::sdsm { struct obstacle_size { /** * @brief Object width in 10 cm units [0, 1023]. diff --git a/streets_utils/streets_messages/include/sensor_data_sharing_msg/obstacle/obstacle_size_confidence.hpp b/streets_utils/streets_messages/include/sensor_data_sharing_msg/obstacle/obstacle_size_confidence.hpp index 97d55885b..bd8e60a9a 100644 --- a/streets_utils/streets_messages/include/sensor_data_sharing_msg/obstacle/obstacle_size_confidence.hpp +++ b/streets_utils/streets_messages/include/sensor_data_sharing_msg/obstacle/obstacle_size_confidence.hpp @@ -14,7 +14,7 @@ #pragma once #include "sensor_data_sharing_msg/size_value_confidence.hpp" #include -namespace streets_utils::messages{ +namespace streets_utils::messages::sdsm{ struct obstacle_size_confidence{ /** * @brief Confidence in reported width diff --git a/streets_utils/streets_messages/include/sensor_data_sharing_msg/position_3d.hpp b/streets_utils/streets_messages/include/sensor_data_sharing_msg/position_3d.hpp index 6ea0efa1c..34785aee1 100644 --- a/streets_utils/streets_messages/include/sensor_data_sharing_msg/position_3d.hpp +++ b/streets_utils/streets_messages/include/sensor_data_sharing_msg/position_3d.hpp @@ -16,20 +16,20 @@ #include #include -namespace streets_utils::messages{ +namespace streets_utils::messages::sdsm{ struct position_3d{ /** - * @brief LSB = 1/10 micro degree Providing a range of + * @brief LSB = 1/10 micro degree Providing a range of * plus-minus 180 degrees[-1799999999, 1800000001] */ int _longitude = 0; /** - * @brief LSB = 1/10 micro degree Providing a range of + * @brief LSB = 1/10 micro degree Providing a range of * plus-minus 90 degrees[-900000000, 900000001] */ int _latitude = 0; /** - * @brief Signed units of 0.1m (10cm), in 2 octets the value + * @brief Signed units of 0.1m (10cm), in 2 octets the value * 32767 (0x7FFF) shall indicate an invalid value [-32768,32767] */ std::optional _elevation; diff --git a/streets_utils/streets_messages/include/sensor_data_sharing_msg/position_confidence.hpp b/streets_utils/streets_messages/include/sensor_data_sharing_msg/position_confidence.hpp index ac06296ea..62f3dccd5 100644 --- a/streets_utils/streets_messages/include/sensor_data_sharing_msg/position_confidence.hpp +++ b/streets_utils/streets_messages/include/sensor_data_sharing_msg/position_confidence.hpp @@ -13,10 +13,10 @@ // limitations under the License. #pragma once -namespace streets_utils::messages { +namespace streets_utils::messages::sdsm { enum class position_confidence{ UNAVAILABLE = 0, - A_500M = 1, + A_500M = 1, A_200M = 2, A_100M = 3, A_50M = 4, @@ -31,7 +31,7 @@ namespace streets_utils::messages { A_5CM = 13, A_2CM = 14, A_1CM = 15 - + }; inline position_confidence position_confidence_from_int( const int i ) { diff --git a/streets_utils/streets_messages/include/sensor_data_sharing_msg/position_confidence_set.hpp b/streets_utils/streets_messages/include/sensor_data_sharing_msg/position_confidence_set.hpp index edfee8315..47a77892b 100644 --- a/streets_utils/streets_messages/include/sensor_data_sharing_msg/position_confidence_set.hpp +++ b/streets_utils/streets_messages/include/sensor_data_sharing_msg/position_confidence_set.hpp @@ -15,7 +15,7 @@ #include "sensor_data_sharing_msg/position_confidence.hpp" -namespace streets_utils::messages { +namespace streets_utils::messages::sdsm { struct position_confidence_set{ position_confidence _position_confidence; position_confidence _elevation_confidence; diff --git a/streets_utils/streets_messages/include/sensor_data_sharing_msg/position_offset.hpp b/streets_utils/streets_messages/include/sensor_data_sharing_msg/position_offset.hpp index ffc97a5e1..4bbd0ecb3 100644 --- a/streets_utils/streets_messages/include/sensor_data_sharing_msg/position_offset.hpp +++ b/streets_utils/streets_messages/include/sensor_data_sharing_msg/position_offset.hpp @@ -15,7 +15,7 @@ #include -namespace streets_utils::messages{ +namespace streets_utils::messages::sdsm{ struct position_offset{ /** * @brief Cartesian offset in X axis from reference point in 0.1 m [-32767, 32767] diff --git a/streets_utils/streets_messages/include/sensor_data_sharing_msg/positional_accuracy.hpp b/streets_utils/streets_messages/include/sensor_data_sharing_msg/positional_accuracy.hpp index dcd2643b8..de0018085 100644 --- a/streets_utils/streets_messages/include/sensor_data_sharing_msg/positional_accuracy.hpp +++ b/streets_utils/streets_messages/include/sensor_data_sharing_msg/positional_accuracy.hpp @@ -14,28 +14,28 @@ #pragma once -namespace streets_utils::messages { +namespace streets_utils::messages::sdsm { struct positional_accuracy{ /** - * @brief semi-major axis accuracy at one standard dev + * @brief semi-major axis accuracy at one standard dev * range 0-12.7 meter, LSB = .05m [0, 255] * 254 = any value equal or greater than 12.70 meter * 255 = unavailable semi-major axis value */ unsigned int _semi_major_axis_accuracy = 0; /** - * @brief semi-minor axis accuracy at one standard dev + * @brief semi-minor axis accuracy at one standard dev * range 0-12.7 meter, LSB = .05m [0, 255] * 254 = any value equal or greater than 12.70 meter * 255 = unavailable semi-minor axis value */ unsigned int _semi_minor_axis_accuracy = 0; /** - * @brief -- orientation of semi-major axis + * @brief -- orientation of semi-major axis * relative to true north (0~359.9945078786 degrees) * LSB units of 360/65535 deg = 0.0054932479 [0,65535] * a value of 0 shall be 0 degrees - * a value of 1 shall be 0.0054932479 degrees + * a value of 1 shall be 0.0054932479 degrees * a value of 65534 shall be 359.9945078786 deg * a value of 65535 shall be used for orientation unavailable */ diff --git a/streets_utils/streets_messages/include/sensor_data_sharing_msg/sensor_data_sharing_msg.hpp b/streets_utils/streets_messages/include/sensor_data_sharing_msg/sensor_data_sharing_msg.hpp index 0c90d17c9..c76baa709 100644 --- a/streets_utils/streets_messages/include/sensor_data_sharing_msg/sensor_data_sharing_msg.hpp +++ b/streets_utils/streets_messages/include/sensor_data_sharing_msg/sensor_data_sharing_msg.hpp @@ -23,16 +23,16 @@ #include #include -namespace streets_utils::messages { +namespace streets_utils::messages::sdsm { /** - * @brief + * @brief */ struct sensor_data_sharing_msg { /** * @brief -- a count value which is incremented with each use [0,255] - * the next value after 255 shall be one value + * the next value after 255 shall be one value * 0 (0x00) shall indicate that MsgCount is not available. */ std::size_t _msg_count = 0; @@ -43,7 +43,7 @@ namespace streets_utils::messages { time_stamp _time_stamp; std::string _source_id; std::vector _objects; - + }; } \ No newline at end of file diff --git a/streets_utils/streets_messages/include/sensor_data_sharing_msg/size_value_confidence.hpp b/streets_utils/streets_messages/include/sensor_data_sharing_msg/size_value_confidence.hpp index 94d39afc2..33e98e4f2 100644 --- a/streets_utils/streets_messages/include/sensor_data_sharing_msg/size_value_confidence.hpp +++ b/streets_utils/streets_messages/include/sensor_data_sharing_msg/size_value_confidence.hpp @@ -13,7 +13,7 @@ // limitations under the License. #pragma once -namespace streets_utils::messages{ +namespace streets_utils::messages::sdsm{ enum class size_value_confidence{ UNAVAILABLE = 0, // Not available SIZE_100 = 1, // 100 meters @@ -28,7 +28,7 @@ namespace streets_utils::messages{ SIZE_0_1 = 10, // 10 centimeters SIZE_0_05 = 11, // 5 centimeters SIZE_0_02 = 12, // 2 centimeters - SIZE_0_01 = 13 // 1 centimeters + SIZE_0_01 = 13 // 1 centimeters }; inline size_value_confidence size_value_confidence_from_int( const unsigned int i ) { @@ -38,7 +38,7 @@ namespace streets_utils::messages{ return size_value_confidence::UNAVAILABLE; case 1: return size_value_confidence::SIZE_100; - case 2: + case 2: return size_value_confidence::SIZE_50; case 3: return size_value_confidence::SIZE_20; @@ -48,7 +48,7 @@ namespace streets_utils::messages{ return size_value_confidence::SIZE_5; case 6: return size_value_confidence::SIZE_2; - case 7: + case 7: return size_value_confidence::SIZE_1; case 8: return size_value_confidence::SIZE_0_5; @@ -58,12 +58,12 @@ namespace streets_utils::messages{ return size_value_confidence::SIZE_0_1; case 11: return size_value_confidence::SIZE_0_05; - case 12: + case 12: return size_value_confidence::SIZE_0_02; case 13: return size_value_confidence::SIZE_0_01; default: throw std::invalid_argument("Incompatible size confidence value. Valid values : [0,13]"); } - } + } } \ No newline at end of file diff --git a/streets_utils/streets_messages/include/sensor_data_sharing_msg/speed_confidence.hpp b/streets_utils/streets_messages/include/sensor_data_sharing_msg/speed_confidence.hpp index 7b3737902..76c238691 100644 --- a/streets_utils/streets_messages/include/sensor_data_sharing_msg/speed_confidence.hpp +++ b/streets_utils/streets_messages/include/sensor_data_sharing_msg/speed_confidence.hpp @@ -13,7 +13,7 @@ // limitations under the License. #pragma once -namespace streets_utils::messages{ +namespace streets_utils::messages::sdsm{ enum class speed_confidence { UNAVAILABLE = 0, // Not available PREC_100ms = 1, // 100 m/s diff --git a/streets_utils/streets_messages/include/sensor_data_sharing_msg/time_confidence.hpp b/streets_utils/streets_messages/include/sensor_data_sharing_msg/time_confidence.hpp index f9b433ffc..8e30f030c 100644 --- a/streets_utils/streets_messages/include/sensor_data_sharing_msg/time_confidence.hpp +++ b/streets_utils/streets_messages/include/sensor_data_sharing_msg/time_confidence.hpp @@ -13,7 +13,7 @@ // limitations under the License. #pragma once -namespace streets_utils::messages{ +namespace streets_utils::messages::sdsm{ enum class time_confidence{ UNAVAILABLE = 0, // unvailable TIME_100_000 = 1, // Better than 100 seconds @@ -48,13 +48,13 @@ namespace streets_utils::messages{ TIME_000_000_000_01 = 30, // Better than 0.00000001 seconds TIME_000_000_000_005 = 31, // Better than 0.000000005 seconds TIME_000_000_000_002 = 32, // Better than 0.000000002 seconds - TIME_000_000_000_001 = 33, // Better than 0.000000001 seconds (Better than one nano second) + TIME_000_000_000_001 = 33, // Better than 0.000000001 seconds (Better than one nano second) TIME_000_000_000_000_5 = 34,// Better than 0.0000000005 seconds TIME_000_000_000_000_2 = 35,// Better than 0.0000000002 seconds TIME_000_000_000_000_1 = 36,// Better than 0.0000000001 seconds TIME_000_000_000_000_05 = 37, // Better than 0.00000000005 seconds TIME_000_000_000_000_02 = 38, // Better than 0.00000000002 seconds - TIME_000_000_000_000_01 = 39 // Better than 0.00000000001 Seconds + TIME_000_000_000_000_01 = 39 // Better than 0.00000000001 Seconds }; inline time_confidence time_confidence_from_int( const int i ) { @@ -64,7 +64,7 @@ namespace streets_utils::messages{ return time_confidence::UNAVAILABLE; case 1: return time_confidence::TIME_100_000; - case 2: + case 2: return time_confidence::TIME_050_000 ; case 3: return time_confidence::TIME_020_000 ; @@ -127,7 +127,7 @@ namespace streets_utils::messages{ case 32: return time_confidence::TIME_000_000_000_002; case 33: - return time_confidence::TIME_000_000_000_001; + return time_confidence::TIME_000_000_000_001; case 34: return time_confidence::TIME_000_000_000_000_5; case 35: diff --git a/streets_utils/streets_messages/include/sensor_data_sharing_msg/time_stamp.hpp b/streets_utils/streets_messages/include/sensor_data_sharing_msg/time_stamp.hpp index 1c0f77033..72f4c992f 100644 --- a/streets_utils/streets_messages/include/sensor_data_sharing_msg/time_stamp.hpp +++ b/streets_utils/streets_messages/include/sensor_data_sharing_msg/time_stamp.hpp @@ -13,7 +13,7 @@ // limitations under the License. #pragma once -namespace streets_utils::messages { +namespace streets_utils::messages::sdsm { struct time_stamp { /** * @brief in milliseconds [0,65535]. diff --git a/streets_utils/streets_messages/include/sensor_data_sharing_msg/vehicle/angular_velocity_confidence_set.hpp b/streets_utils/streets_messages/include/sensor_data_sharing_msg/vehicle/angular_velocity_confidence_set.hpp index 965e381f5..69a97e343 100644 --- a/streets_utils/streets_messages/include/sensor_data_sharing_msg/vehicle/angular_velocity_confidence_set.hpp +++ b/streets_utils/streets_messages/include/sensor_data_sharing_msg/vehicle/angular_velocity_confidence_set.hpp @@ -16,7 +16,7 @@ #include "sensor_data_sharing_msg/angular_velocity_confidence.hpp" #include -namespace streets_utils::messages{ +namespace streets_utils::messages::sdsm{ struct angular_velocity_confidence_set{ /** * @brief Confidence in reported pitch rate. diff --git a/streets_utils/streets_messages/include/sensor_data_sharing_msg/vehicle/angular_velocity_set.hpp b/streets_utils/streets_messages/include/sensor_data_sharing_msg/vehicle/angular_velocity_set.hpp index 35765571f..467b6c118 100644 --- a/streets_utils/streets_messages/include/sensor_data_sharing_msg/vehicle/angular_velocity_set.hpp +++ b/streets_utils/streets_messages/include/sensor_data_sharing_msg/vehicle/angular_velocity_set.hpp @@ -13,7 +13,7 @@ // limitations under the License. #pragma once -namespace streets_utils::messages{ +namespace streets_utils::messages::sdsm{ struct angular_velocity_set{ /** * @brief Angular velocity for pitch axis in 0.01 degrees per second [-32767, 32767] diff --git a/streets_utils/streets_messages/include/sensor_data_sharing_msg/vehicle/attitude.hpp b/streets_utils/streets_messages/include/sensor_data_sharing_msg/vehicle/attitude.hpp index 3600cc208..3b90ec685 100644 --- a/streets_utils/streets_messages/include/sensor_data_sharing_msg/vehicle/attitude.hpp +++ b/streets_utils/streets_messages/include/sensor_data_sharing_msg/vehicle/attitude.hpp @@ -14,18 +14,18 @@ #pragma once -namespace streets_utils::messages{ +namespace streets_utils::messages::sdsm{ struct attitude{ /** * @brief Pitch in 0.0125 degrees [-7200, 72000]. */ int _pitch = 0; /** - * @brief Roll in 0.0125 degrees [-14400, 14400] + * @brief Roll in 0.0125 degrees [-14400, 14400] */ int _roll = 0; /** - * @brief Yaw in 0.0125 degrees [-14400, 14400] + * @brief Yaw in 0.0125 degrees [-14400, 14400] */ int _yaw = 0; }; diff --git a/streets_utils/streets_messages/include/sensor_data_sharing_msg/vehicle/attitude_confidence.hpp b/streets_utils/streets_messages/include/sensor_data_sharing_msg/vehicle/attitude_confidence.hpp index 9a84eda1d..7280b85d8 100644 --- a/streets_utils/streets_messages/include/sensor_data_sharing_msg/vehicle/attitude_confidence.hpp +++ b/streets_utils/streets_messages/include/sensor_data_sharing_msg/vehicle/attitude_confidence.hpp @@ -15,7 +15,7 @@ #include "sensor_data_sharing_msg/heading_confidence.hpp" -namespace streets_utils::messages{ +namespace streets_utils::messages::sdsm{ struct attitude_confidence { /** * @brief Confidence in reported pitch. diff --git a/streets_utils/streets_messages/include/sensor_data_sharing_msg/vehicle/detected_vehicle_data.hpp b/streets_utils/streets_messages/include/sensor_data_sharing_msg/vehicle/detected_vehicle_data.hpp index f1ffc8f8c..428fb769d 100644 --- a/streets_utils/streets_messages/include/sensor_data_sharing_msg/vehicle/detected_vehicle_data.hpp +++ b/streets_utils/streets_messages/include/sensor_data_sharing_msg/vehicle/detected_vehicle_data.hpp @@ -21,21 +21,21 @@ #include "sensor_data_sharing_msg/vehicle/vehicle_size.hpp" #include "sensor_data_sharing_msg/vehicle/vehicle_size_confidence.hpp" -namespace streets_utils::messages{ +namespace streets_utils::messages::sdsm{ struct detected_vehicle_data{ /** - * @brief BIT String representing the state of each of the following vehicle + * @brief BIT String representing the state of each of the following vehicle * exterior lights: - * lowBeamHeadlightsOn (0), - * highBeamHeadlightsOn (1), - * leftTurnSignalOn (2), - * rightTurnSignalOn (3), - * hazardSignalOn (4), + * lowBeamHeadlightsOn (0), + * highBeamHeadlightsOn (1), + * leftTurnSignalOn (2), + * rightTurnSignalOn (3), + * hazardSignalOn (4), * automaticLightControlOn (5), - * daytimeRunningLightsOn (6), - * fogLightOn (7), - * parkingLightsOn (8) - * + * daytimeRunningLightsOn (6), + * fogLightOn (7), + * parkingLightsOn (8) + * */ std::optional exterior_lights; /** @@ -65,7 +65,7 @@ namespace streets_utils::messages{ /** * @brief Confidence in reported size. */ - std::optional _size_confidence; + std::optional _size_confidence; /** * @brief See BasicVehicleClass in J2735 */ diff --git a/streets_utils/streets_messages/include/sensor_data_sharing_msg/vehicle/vehicle_size.hpp b/streets_utils/streets_messages/include/sensor_data_sharing_msg/vehicle/vehicle_size.hpp index d838a9bb2..285756122 100644 --- a/streets_utils/streets_messages/include/sensor_data_sharing_msg/vehicle/vehicle_size.hpp +++ b/streets_utils/streets_messages/include/sensor_data_sharing_msg/vehicle/vehicle_size.hpp @@ -13,7 +13,7 @@ // limitations under the License. #pragma once -namespace streets_utils::messages{ +namespace streets_utils::messages::sdsm{ struct vehicle_size { /** * @brief Vehicle width in centimeters [0, 1023] @@ -22,7 +22,7 @@ namespace streets_utils::messages{ /** * @brief Vehicle length in centimeters [0, 4095] */ - unsigned int _length = 0; - + unsigned int _length = 0; + }; } \ No newline at end of file diff --git a/streets_utils/streets_messages/include/sensor_data_sharing_msg/vehicle/vehicle_size_confidence.hpp b/streets_utils/streets_messages/include/sensor_data_sharing_msg/vehicle/vehicle_size_confidence.hpp index e1566ba34..1ae7b1b63 100644 --- a/streets_utils/streets_messages/include/sensor_data_sharing_msg/vehicle/vehicle_size_confidence.hpp +++ b/streets_utils/streets_messages/include/sensor_data_sharing_msg/vehicle/vehicle_size_confidence.hpp @@ -14,7 +14,7 @@ #pragma once #include "sensor_data_sharing_msg/size_value_confidence.hpp" #include -namespace streets_utils::messages{ +namespace streets_utils::messages::sdsm{ struct vehicle_size_confidence{ /** * @brief Confidence in reported width diff --git a/streets_utils/streets_messages/include/sensor_data_sharing_msg/vru/animal_propelled_type.hpp b/streets_utils/streets_messages/include/sensor_data_sharing_msg/vru/animal_propelled_type.hpp index 8c097ea3b..2aca3151d 100644 --- a/streets_utils/streets_messages/include/sensor_data_sharing_msg/vru/animal_propelled_type.hpp +++ b/streets_utils/streets_messages/include/sensor_data_sharing_msg/vru/animal_propelled_type.hpp @@ -13,7 +13,7 @@ // limitations under the License. #pragma once -namespace streets_utils::messages{ +namespace streets_utils::messages::sdsm{ enum class animal_propelled_type{ UNAVAILABLE = 0, @@ -29,7 +29,7 @@ namespace streets_utils::messages{ return animal_propelled_type::UNAVAILABLE; case 1: return animal_propelled_type::OTHER_TYPES; - case 2: + case 2: return animal_propelled_type::ANIMAL_MOUNTED; case 3: return animal_propelled_type::ANIMAL_DRAWN_CARRIAGE; diff --git a/streets_utils/streets_messages/include/sensor_data_sharing_msg/vru/attachment.hpp b/streets_utils/streets_messages/include/sensor_data_sharing_msg/vru/attachment.hpp index 22a0da64b..eb99127ac 100644 --- a/streets_utils/streets_messages/include/sensor_data_sharing_msg/vru/attachment.hpp +++ b/streets_utils/streets_messages/include/sensor_data_sharing_msg/vru/attachment.hpp @@ -13,7 +13,7 @@ // limitations under the License. #pragma once -namespace streets_utils::messages { +namespace streets_utils::messages::sdsm { enum class attachment { UNAVAILABLE = 0 , STROLLER = 1, @@ -31,7 +31,7 @@ namespace streets_utils::messages { return attachment::UNAVAILABLE; case 1: return attachment::STROLLER; - case 2: + case 2: return attachment::BICYLE_TRAILER; case 3: return attachment::CART; diff --git a/streets_utils/streets_messages/include/sensor_data_sharing_msg/vru/detected_vru_data.hpp b/streets_utils/streets_messages/include/sensor_data_sharing_msg/vru/detected_vru_data.hpp index 9544028eb..85b492a91 100644 --- a/streets_utils/streets_messages/include/sensor_data_sharing_msg/vru/detected_vru_data.hpp +++ b/streets_utils/streets_messages/include/sensor_data_sharing_msg/vru/detected_vru_data.hpp @@ -22,7 +22,7 @@ #include #include -namespace streets_utils::messages{ +namespace streets_utils::messages::sdsm{ struct detected_vru_data{ /** * @brief Propulsion type. diff --git a/streets_utils/streets_messages/include/sensor_data_sharing_msg/vru/human_propelled_type.hpp b/streets_utils/streets_messages/include/sensor_data_sharing_msg/vru/human_propelled_type.hpp index fd8e52272..1cd86e342 100644 --- a/streets_utils/streets_messages/include/sensor_data_sharing_msg/vru/human_propelled_type.hpp +++ b/streets_utils/streets_messages/include/sensor_data_sharing_msg/vru/human_propelled_type.hpp @@ -13,7 +13,7 @@ // limitations under the License. #pragma once -namespace streets_utils::messages { +namespace streets_utils::messages::sdsm { enum class human_propelled_type{ UNAVAILABLE = 0, @@ -31,7 +31,7 @@ namespace streets_utils::messages { return human_propelled_type::UNAVAILABLE; case 1: return human_propelled_type::OTHER_TYPES; - case 2: + case 2: return human_propelled_type::ON_FOOT; case 3: return human_propelled_type::SKATEBOARD; diff --git a/streets_utils/streets_messages/include/sensor_data_sharing_msg/vru/motorized_propelled_type.hpp b/streets_utils/streets_messages/include/sensor_data_sharing_msg/vru/motorized_propelled_type.hpp index 85d73f42e..a4c854760 100644 --- a/streets_utils/streets_messages/include/sensor_data_sharing_msg/vru/motorized_propelled_type.hpp +++ b/streets_utils/streets_messages/include/sensor_data_sharing_msg/vru/motorized_propelled_type.hpp @@ -13,7 +13,7 @@ // limitations under the License. #pragma once -namespace streets_utils::messages { +namespace streets_utils::messages::sdsm { enum class motorized_propelled_type{ UNAVAILABLE = 0, OTHER_TYPES = 1, @@ -22,7 +22,7 @@ namespace streets_utils::messages { SCOOTER = 4, SELF_BALANCING_DEVICE = 5 }; - + inline motorized_propelled_type motorized_propelled_type_from_int( const unsigned int i ){ switch (i) { @@ -30,7 +30,7 @@ namespace streets_utils::messages { return motorized_propelled_type::UNAVAILABLE; case 1: return motorized_propelled_type::OTHER_TYPES; - case 2: + case 2: return motorized_propelled_type::WHEEL_CHAIR; case 3: return motorized_propelled_type::BICYCLE; diff --git a/streets_utils/streets_messages/include/sensor_data_sharing_msg/vru/personal_device_user_type.hpp b/streets_utils/streets_messages/include/sensor_data_sharing_msg/vru/personal_device_user_type.hpp index 33fd50c43..c2ef34579 100644 --- a/streets_utils/streets_messages/include/sensor_data_sharing_msg/vru/personal_device_user_type.hpp +++ b/streets_utils/streets_messages/include/sensor_data_sharing_msg/vru/personal_device_user_type.hpp @@ -13,7 +13,7 @@ // limitations under the License. #pragma once -namespace streets_utils::messages { +namespace streets_utils::messages::sdsm { enum class personal_device_user_type{ UNAVAILABLE= 0, @@ -30,7 +30,7 @@ namespace streets_utils::messages { return personal_device_user_type::UNAVAILABLE; case 1: return personal_device_user_type::PEDESTRIAN; - case 2: + case 2: return personal_device_user_type::PEDALCYCLIST; case 3: return personal_device_user_type::PUBLIC_SAFETY_WORKER; diff --git a/streets_utils/streets_messages/include/serializers/sensor_data_sharing_msg_json_serializer.hpp b/streets_utils/streets_messages/include/serializers/sensor_data_sharing_msg_json_serializer.hpp index d3f4a2b27..830e34683 100644 --- a/streets_utils/streets_messages/include/serializers/sensor_data_sharing_msg_json_serializer.hpp +++ b/streets_utils/streets_messages/include/serializers/sensor_data_sharing_msg_json_serializer.hpp @@ -9,7 +9,7 @@ #include -namespace streets_utils::messages{ +namespace streets_utils::messages::sdsm{ std::string to_json(const sensor_data_sharing_msg &val); rapidjson::Value create_timestamp(const time_stamp &val, rapidjson::Document::AllocatorType &allocator); @@ -17,7 +17,7 @@ namespace streets_utils::messages{ rapidjson::Value create_position_3d(const position_3d &val, rapidjson::Document::AllocatorType &allocator); rapidjson::Value create_positional_accuracy(const positional_accuracy &val, rapidjson::Document::AllocatorType &allocator); - + rapidjson::Value create_detected_object_list(const std::vector &val, rapidjson::Document::AllocatorType &allocator ); rapidjson::Value create_detected_object_data(const detected_object_data &val, rapidjson::Document::AllocatorType &allocator); @@ -35,7 +35,7 @@ namespace streets_utils::messages{ rapidjson::Value create_detected_vru_data(const detected_vru_data &val, rapidjson::Document::AllocatorType &allocator); rapidjson::Value create_propelled_information(const std::variant &val, rapidjson::Document::AllocatorType &allocator); - + rapidjson::Value create_detected_vehicle_data(const detected_vehicle_data &val, rapidjson::Document::AllocatorType &allocator); rapidjson::Value create_vehicle_attitude(const attitude &val, rapidjson::Document::AllocatorType &allocator); @@ -53,6 +53,6 @@ namespace streets_utils::messages{ rapidjson::Value create_accelaration_set_4_way(const acceleration_set_4_way &val, rapidjson::Document::AllocatorType &allocator); rapidjson::Value create_position_3d(const position_offset &val, rapidjson::Document::AllocatorType &allocator); - + rapidjson::Value create_position_confidence_set(const position_confidence_set &val, rapidjson::Document::AllocatorType &allocator); } \ No newline at end of file diff --git a/streets_utils/streets_messages/src/deserializers/sensor_data_sharing_msg_json_deserializer.cpp b/streets_utils/streets_messages/src/deserializers/sensor_data_sharing_msg_json_deserializer.cpp index b3b393139..291db160a 100644 --- a/streets_utils/streets_messages/src/deserializers/sensor_data_sharing_msg_json_deserializer.cpp +++ b/streets_utils/streets_messages/src/deserializers/sensor_data_sharing_msg_json_deserializer.cpp @@ -14,7 +14,7 @@ #include "deserializers/sensor_data_sharing_msg_json_deserializer.hpp" -namespace streets_utils::messages { +namespace streets_utils::messages::sdsm { using namespace streets_utils::json_utils; sensor_data_sharing_msg from_json( const std::string &json ){ rapidjson::Document document = streets_utils::json_utils::parse_json(json); @@ -28,7 +28,7 @@ namespace streets_utils::messages { msg._ref_position_elevation_confidence = parse_elevation_confidence( document); msg._objects = parse_detected_object_list(document); return msg; - } + } time_stamp parse_time_stamp(const rapidjson::Value &val){ time_stamp _time_stamp; @@ -40,14 +40,14 @@ namespace streets_utils::messages { _time_stamp.month = parse_uint_member("month", val, true).value(); _time_stamp.year = parse_uint_member("year", val, true).value(); return _time_stamp; - + } position_3d parse_position_3d(const rapidjson::Value &val) { position_3d _position_3d; _position_3d._longitude = parse_int_member("long", val, true).value(); _position_3d._latitude = parse_int_member("lat", val, true).value(); - // parse optional elevation + // parse optional elevation _position_3d._elevation = parse_int_member("elevation", val, false); return _position_3d; @@ -59,7 +59,7 @@ namespace streets_utils::messages { _positional_accuracy._semi_minor_axis_accuracy = parse_uint_member("semi_minor", val, true).value(); _positional_accuracy._semi_major_axis_orientation = parse_uint_member("orientation",val, true).value(); return _positional_accuracy; - + } std::optional parse_elevation_confidence(const rapidjson::Value &val) { @@ -67,7 +67,7 @@ namespace streets_utils::messages { if (auto _position_confidence_value = parse_uint_member("ref_pos_el_conf", val, false); _position_confidence_value.has_value() ) { _position_confidence = position_confidence_from_int(_position_confidence_value.value()); } - return _position_confidence; + return _position_confidence; } std::vector parse_detected_object_list(const rapidjson::Value &val){ @@ -104,19 +104,19 @@ namespace streets_utils::messages { } _detected_object_common_data._heading = parse_uint_member("heading", val, true).value(); _detected_object_common_data._heading_confidence = heading_confidence_from_int(parse_uint_member("heading_conf", val, true).value()); - + _detected_object_common_data._acceleration_4_way = parse_acceleration_4_way(parse_object_member("accel_4_way", val, true ).value()); if ( val.HasMember("acc_cfd_x")) { - _detected_object_common_data._lateral_acceleration_confidence = acceleration_confidence_from_int(parse_uint_member("acc_cfd_x", val, true).value()); + _detected_object_common_data._lateral_acceleration_confidence = acceleration_confidence_from_int(parse_uint_member("acc_cfd_x", val, true).value()); } if ( val.HasMember("acc_cfd_y")) { - _detected_object_common_data._longitudinal_acceleration_confidence = acceleration_confidence_from_int(parse_uint_member("acc_cfd_y", val, true).value()); + _detected_object_common_data._longitudinal_acceleration_confidence = acceleration_confidence_from_int(parse_uint_member("acc_cfd_y", val, true).value()); } if ( val.HasMember("acc_cfd_z")) { - _detected_object_common_data._vertical_accelaration_confidence = acceleration_confidence_from_int(parse_uint_member("acc_cfd_z", val, true).value()); + _detected_object_common_data._vertical_accelaration_confidence = acceleration_confidence_from_int(parse_uint_member("acc_cfd_z", val, true).value()); } if ( val.HasMember("acc_cfd_yaw")) { - _detected_object_common_data._yaw_rate_confidence = angular_velocity_confidence_from_int(parse_uint_member("acc_cfd_yaw", val, true).value()); + _detected_object_common_data._yaw_rate_confidence = angular_velocity_confidence_from_int(parse_uint_member("acc_cfd_yaw", val, true).value()); } return _detected_object_common_data; } @@ -142,7 +142,7 @@ namespace streets_utils::messages { data._offset_y = parse_int_member("offset_y", val, true).value(); data._offset_y = parse_int_member("offset_y", val, true).value(); return data; - + } position_confidence_set parse_position_confidence_set(const rapidjson::Value &val) { @@ -266,7 +266,7 @@ namespace streets_utils::messages { data = human_propelled_type_from_int(parse_uint_member("human", val, true).value()); } else if (val.HasMember("animal")) { - data = animal_propelled_type_from_int(parse_uint_member("animal", val, true).value()); + data = animal_propelled_type_from_int(parse_uint_member("animal", val, true).value()); } else if (val.HasMember("motor")) { data = motorized_propelled_type_from_int(parse_uint_member("motor", val, true).value()); @@ -275,7 +275,7 @@ namespace streets_utils::messages { throw json_parse_exception("Propelled information requires one of the following to be define: human propelled type, animal propelled type, motorized propelled type."); } return data; - } + } obstacle_size parse_obstacle_size(const rapidjson::Value &val) { diff --git a/streets_utils/streets_messages/src/serializers/sensor_data_sharing_msg_json_serializer.cpp b/streets_utils/streets_messages/src/serializers/sensor_data_sharing_msg_json_serializer.cpp index 2e5af2033..7341ba711 100644 --- a/streets_utils/streets_messages/src/serializers/sensor_data_sharing_msg_json_serializer.cpp +++ b/streets_utils/streets_messages/src/serializers/sensor_data_sharing_msg_json_serializer.cpp @@ -14,7 +14,7 @@ #include "serializers/sensor_data_sharing_msg_json_serializer.hpp" -namespace streets_utils::messages{ +namespace streets_utils::messages::sdsm{ std::string to_json(const sensor_data_sharing_msg &msg) { rapidjson::Document doc; rapidjson::Value sdsm_json(rapidjson::kObjectType); @@ -36,7 +36,7 @@ namespace streets_utils::messages{ rapidjson::StringBuffer buffer; rapidjson::Writer writer(buffer); sdsm_json.Accept(writer); - + return buffer.GetString(); } @@ -74,7 +74,7 @@ namespace streets_utils::messages{ for (const auto &detected_obect : val) { // Create and push detected object data detected_object_list_json.PushBack(create_detected_object_data(detected_obect, allocator), allocator); - + } return detected_object_list_json; } @@ -86,8 +86,8 @@ namespace streets_utils::messages{ // Create Optional Data if ( val._detected_object_optional_data.has_value() ) detected_object_data_json.AddMember( - "detected_object_optional_data", - create_detected_object_data_optional(val._detected_object_optional_data.value(), allocator), + "detected_object_optional_data", + create_detected_object_data_optional(val._detected_object_optional_data.value(), allocator), allocator); return detected_object_data_json; } @@ -100,10 +100,10 @@ namespace streets_utils::messages{ detected_object_data_common_json.AddMember("obj_type_cfd", val._classification_confidence, allocator); detected_object_data_common_json.AddMember("measurement_time", val._time_measurement_offset, allocator); detected_object_data_common_json.AddMember("time_confidence", static_cast(val._time_confidence), allocator); - // Create Position + // Create Position detected_object_data_common_json.AddMember( - "pos", - create_position_3d( val._position_offset, allocator), + "pos", + create_position_3d( val._position_offset, allocator), allocator); // Create Position Confidence detected_object_data_common_json.AddMember("pos_confidence", create_position_confidence_set(val._pos_confidence, allocator), allocator ); @@ -113,37 +113,37 @@ namespace streets_utils::messages{ detected_object_data_common_json.AddMember("speed_z", val._speed_z.value(), allocator); if ( val._speed_z_confidence.has_value()) detected_object_data_common_json.AddMember( - "speed_confidence_z", + "speed_confidence_z", static_cast(val._speed_z_confidence.value()), allocator); detected_object_data_common_json.AddMember("heading", val._heading, allocator); detected_object_data_common_json.AddMember("heading_conf", static_cast(val._heading_confidence), allocator); if (val._lateral_acceleration_confidence.has_value() ) { detected_object_data_common_json.AddMember( - "acc_cfd_x", + "acc_cfd_x", static_cast(val._lateral_acceleration_confidence.value()), allocator); if (val._longitudinal_acceleration_confidence.has_value() ) detected_object_data_common_json.AddMember( - "acc_cfd_y", + "acc_cfd_y", static_cast(val._longitudinal_acceleration_confidence.value()), allocator); if (val._vertical_accelaration_confidence.has_value()) detected_object_data_common_json.AddMember( - "acc_cfd_z", + "acc_cfd_z", static_cast(val._vertical_accelaration_confidence.value()), allocator); if (val._yaw_rate_confidence.has_value()) detected_object_data_common_json.AddMember( - "acc_cfd_yaw", + "acc_cfd_yaw", static_cast(val._yaw_rate_confidence.value()), allocator); } if (val._acceleration_4_way.has_value()) { // Create accel_4_way detected_object_data_common_json.AddMember( - "accel_4_way", - create_accelaration_set_4_way(val._acceleration_4_way.value(), allocator), + "accel_4_way", + create_accelaration_set_4_way(val._acceleration_4_way.value(), allocator), allocator); } return detected_object_data_common_json; @@ -189,7 +189,7 @@ namespace streets_utils::messages{ return detected_object_data_optional_json; } else { - throw json_utils::json_parse_exception("If present, detected optional data must include one of the following objects : detected obstacle data, detected vehicle data, detected vru data"); + throw json_utils::json_parse_exception("If present, detected optional data must include one of the following objects : detected obstacle data, detected vehicle data, detected vru data"); } } @@ -293,7 +293,7 @@ namespace streets_utils::messages{ return data; } else { - throw json_utils::json_parse_exception("If present, propelled infromation must include one of the following objects : animal propelled type, motorized propelled type, human propelled type"); + throw json_utils::json_parse_exception("If present, propelled infromation must include one of the following objects : animal propelled type, motorized propelled type, human propelled type"); } } diff --git a/streets_utils/streets_messages/test/deserializers/sensor_data_sharing_msg_json_deserialization_test.cpp b/streets_utils/streets_messages/test/deserializers/sensor_data_sharing_msg_json_deserialization_test.cpp index 3d9f49c07..b3ac04608 100644 --- a/streets_utils/streets_messages/test/deserializers/sensor_data_sharing_msg_json_deserialization_test.cpp +++ b/streets_utils/streets_messages/test/deserializers/sensor_data_sharing_msg_json_deserialization_test.cpp @@ -14,10 +14,10 @@ #include #include #include -using namespace streets_utils::messages; +using namespace streets_utils::messages::sdsm; TEST(sensor_dara_sharing_msg_json_deserialization_test, deserialize) { - + std::string json = "{" "\"equipment_type\": 0," "\"msg_cnt\": 255," @@ -128,7 +128,7 @@ TEST(sensor_dara_sharing_msg_json_deserialization_test, deserialize) { } TEST(sensor_dara_sharing_msg_json_deserialization_test, deserialize_optional_obstacle_data) { - + std::string json = "{" " \"equipment_type\": 1," " \"msg_cnt\": 1," @@ -234,7 +234,7 @@ TEST(sensor_dara_sharing_msg_json_deserialization_test, deserialize_optional_obs } TEST(sensor_dara_sharing_msg_json_deserialization_test, deserialize_optional_vru_data) { - + std::string json = "{" " \"equipment_type\": 1," " \"msg_cnt\": 1," @@ -336,7 +336,7 @@ TEST(sensor_dara_sharing_msg_json_deserialization_test, deserialize_optional_vru } TEST(sensor_dara_sharing_msg_json_deserialization_test, deserialize_optional_vehicle_data) { - + std::string json = "{" " \"equipment_type\": 1," " \"msg_cnt\": 1," @@ -461,4 +461,3 @@ TEST(sensor_dara_sharing_msg_json_deserialization_test, deserialize_optional_veh } - diff --git a/streets_utils/streets_messages/test/sensor_data_sharing_msg/acceleration_confidence_test.cpp b/streets_utils/streets_messages/test/sensor_data_sharing_msg/acceleration_confidence_test.cpp index 45ff78c5a..2549b95ab 100644 --- a/streets_utils/streets_messages/test/sensor_data_sharing_msg/acceleration_confidence_test.cpp +++ b/streets_utils/streets_messages/test/sensor_data_sharing_msg/acceleration_confidence_test.cpp @@ -14,7 +14,7 @@ #include #include -using namespace streets_utils::messages; +using namespace streets_utils::messages::sdsm; TEST(acceleration_confidence_test, test_from_int){ EXPECT_EQ(acceleration_confidence::UNAVAILABLE, acceleration_confidence_from_int(0)); diff --git a/streets_utils/streets_messages/test/sensor_data_sharing_msg/angular_velocity_confidence_test.cpp b/streets_utils/streets_messages/test/sensor_data_sharing_msg/angular_velocity_confidence_test.cpp index 0eb4ddab4..3ff05a9d9 100644 --- a/streets_utils/streets_messages/test/sensor_data_sharing_msg/angular_velocity_confidence_test.cpp +++ b/streets_utils/streets_messages/test/sensor_data_sharing_msg/angular_velocity_confidence_test.cpp @@ -14,7 +14,7 @@ #include #include -using namespace streets_utils::messages; +using namespace streets_utils::messages::sdsm; TEST(angular_velocity_confidence_test, test_from_int){ EXPECT_EQ(angular_velocity_confidence::UNAVAILABLE, angular_velocity_confidence_from_int(0)); diff --git a/streets_utils/streets_messages/test/sensor_data_sharing_msg/animal_propelled_type_test.cpp b/streets_utils/streets_messages/test/sensor_data_sharing_msg/animal_propelled_type_test.cpp index 544613898..ae4101c3a 100644 --- a/streets_utils/streets_messages/test/sensor_data_sharing_msg/animal_propelled_type_test.cpp +++ b/streets_utils/streets_messages/test/sensor_data_sharing_msg/animal_propelled_type_test.cpp @@ -14,7 +14,7 @@ #include #include -using namespace streets_utils::messages; +using namespace streets_utils::messages::sdsm; TEST(animal_propelled_type_test, test_from_int){ EXPECT_EQ(animal_propelled_type::UNAVAILABLE, animal_propelled_type_from_int(0)); diff --git a/streets_utils/streets_messages/test/sensor_data_sharing_msg/attachment_test.cpp b/streets_utils/streets_messages/test/sensor_data_sharing_msg/attachment_test.cpp index 0663c4070..eaaf6cd81 100644 --- a/streets_utils/streets_messages/test/sensor_data_sharing_msg/attachment_test.cpp +++ b/streets_utils/streets_messages/test/sensor_data_sharing_msg/attachment_test.cpp @@ -14,7 +14,7 @@ #include #include -using namespace streets_utils::messages; +using namespace streets_utils::messages::sdsm; TEST(attachment_test, test_from_int){ EXPECT_EQ(attachment::UNAVAILABLE, attachment_from_int(0)); diff --git a/streets_utils/streets_messages/test/sensor_data_sharing_msg/equipment_type_test.cpp b/streets_utils/streets_messages/test/sensor_data_sharing_msg/equipment_type_test.cpp index 9c2ce567e..963459aca 100644 --- a/streets_utils/streets_messages/test/sensor_data_sharing_msg/equipment_type_test.cpp +++ b/streets_utils/streets_messages/test/sensor_data_sharing_msg/equipment_type_test.cpp @@ -14,7 +14,7 @@ #include #include -using namespace streets_utils::messages; +using namespace streets_utils::messages::sdsm; TEST(equipment_type_test, test_from_int){ EXPECT_EQ(equipment_type::UNKNOWN, equipment_type_from_int(0)); diff --git a/streets_utils/streets_messages/test/sensor_data_sharing_msg/heading_confidence_test.cpp b/streets_utils/streets_messages/test/sensor_data_sharing_msg/heading_confidence_test.cpp index 20b93b968..82254e1f1 100644 --- a/streets_utils/streets_messages/test/sensor_data_sharing_msg/heading_confidence_test.cpp +++ b/streets_utils/streets_messages/test/sensor_data_sharing_msg/heading_confidence_test.cpp @@ -14,7 +14,7 @@ #include #include -using namespace streets_utils::messages; +using namespace streets_utils::messages::sdsm; TEST(heading_confidence_test, test_from_int){ EXPECT_EQ(heading_confidence::UNAVAILABLE, heading_confidence_from_int(0)); diff --git a/streets_utils/streets_messages/test/sensor_data_sharing_msg/human_propelled_type_test.cpp b/streets_utils/streets_messages/test/sensor_data_sharing_msg/human_propelled_type_test.cpp index 8484d2eba..f90ac943a 100644 --- a/streets_utils/streets_messages/test/sensor_data_sharing_msg/human_propelled_type_test.cpp +++ b/streets_utils/streets_messages/test/sensor_data_sharing_msg/human_propelled_type_test.cpp @@ -14,7 +14,7 @@ #include #include -using namespace streets_utils::messages; +using namespace streets_utils::messages::sdsm; TEST(human_propelled_type_test, test_from_int){ EXPECT_EQ(human_propelled_type::UNAVAILABLE, human_propelled_type_from_int(0)); diff --git a/streets_utils/streets_messages/test/sensor_data_sharing_msg/motorized_propelled_type_test.cpp b/streets_utils/streets_messages/test/sensor_data_sharing_msg/motorized_propelled_type_test.cpp index 209d64a70..de899dadc 100644 --- a/streets_utils/streets_messages/test/sensor_data_sharing_msg/motorized_propelled_type_test.cpp +++ b/streets_utils/streets_messages/test/sensor_data_sharing_msg/motorized_propelled_type_test.cpp @@ -14,7 +14,7 @@ #include #include -using namespace streets_utils::messages; +using namespace streets_utils::messages::sdsm; TEST(motorized_propelled_type_test, test_from_int){ EXPECT_EQ(motorized_propelled_type::UNAVAILABLE, motorized_propelled_type_from_int(0)); diff --git a/streets_utils/streets_messages/test/sensor_data_sharing_msg/object_type_test.cpp b/streets_utils/streets_messages/test/sensor_data_sharing_msg/object_type_test.cpp index c39349bda..82aa28884 100644 --- a/streets_utils/streets_messages/test/sensor_data_sharing_msg/object_type_test.cpp +++ b/streets_utils/streets_messages/test/sensor_data_sharing_msg/object_type_test.cpp @@ -14,7 +14,7 @@ #include #include -using namespace streets_utils::messages; +using namespace streets_utils::messages::sdsm; TEST(object_type_test, test_from_int){ EXPECT_EQ(object_type::UNKNOWN, object_type_from_int(0)); diff --git a/streets_utils/streets_messages/test/sensor_data_sharing_msg/personal_device_user_type.cpp b/streets_utils/streets_messages/test/sensor_data_sharing_msg/personal_device_user_type.cpp index 17c2e9598..fcdadc3d4 100644 --- a/streets_utils/streets_messages/test/sensor_data_sharing_msg/personal_device_user_type.cpp +++ b/streets_utils/streets_messages/test/sensor_data_sharing_msg/personal_device_user_type.cpp @@ -14,7 +14,7 @@ #include #include -using namespace streets_utils::messages; +using namespace streets_utils::messages::sdsm; TEST(personal_device_user_type_test, test_from_int){ EXPECT_EQ(personal_device_user_type::UNAVAILABLE, personal_device_user_type_from_int(0)); diff --git a/streets_utils/streets_messages/test/sensor_data_sharing_msg/position_confidence_test.cpp b/streets_utils/streets_messages/test/sensor_data_sharing_msg/position_confidence_test.cpp index 5cdd8e79f..e76f6c5ec 100644 --- a/streets_utils/streets_messages/test/sensor_data_sharing_msg/position_confidence_test.cpp +++ b/streets_utils/streets_messages/test/sensor_data_sharing_msg/position_confidence_test.cpp @@ -14,7 +14,7 @@ #include #include -using namespace streets_utils::messages; +using namespace streets_utils::messages::sdsm; TEST(position_confidence_test, test_from_int){ EXPECT_EQ(position_confidence::UNAVAILABLE, position_confidence_from_int(0)); diff --git a/streets_utils/streets_messages/test/sensor_data_sharing_msg/size_value_confidence_test.cpp b/streets_utils/streets_messages/test/sensor_data_sharing_msg/size_value_confidence_test.cpp index 0f7df67c5..d10b8eae5 100644 --- a/streets_utils/streets_messages/test/sensor_data_sharing_msg/size_value_confidence_test.cpp +++ b/streets_utils/streets_messages/test/sensor_data_sharing_msg/size_value_confidence_test.cpp @@ -14,7 +14,7 @@ #include #include -using namespace streets_utils::messages; +using namespace streets_utils::messages::sdsm; TEST(size_value_confidence_test, test_from_int){ EXPECT_EQ(size_value_confidence::UNAVAILABLE, size_value_confidence_from_int(0)); diff --git a/streets_utils/streets_messages/test/sensor_data_sharing_msg/speed_confidence_test.cpp b/streets_utils/streets_messages/test/sensor_data_sharing_msg/speed_confidence_test.cpp index 19a98057a..113f80246 100644 --- a/streets_utils/streets_messages/test/sensor_data_sharing_msg/speed_confidence_test.cpp +++ b/streets_utils/streets_messages/test/sensor_data_sharing_msg/speed_confidence_test.cpp @@ -14,7 +14,7 @@ #include #include -using namespace streets_utils::messages; +using namespace streets_utils::messages::sdsm; TEST(speed_confidence, test_from_int){ EXPECT_EQ(speed_confidence::UNAVAILABLE, speed_confidence_from_int(0)); diff --git a/streets_utils/streets_messages/test/sensor_data_sharing_msg/time_confidence_test.cpp b/streets_utils/streets_messages/test/sensor_data_sharing_msg/time_confidence_test.cpp index 690bdefcb..c6bec601d 100644 --- a/streets_utils/streets_messages/test/sensor_data_sharing_msg/time_confidence_test.cpp +++ b/streets_utils/streets_messages/test/sensor_data_sharing_msg/time_confidence_test.cpp @@ -14,7 +14,7 @@ #include #include -using namespace streets_utils::messages; +using namespace streets_utils::messages::sdsm; TEST(time_confidence_test, test_from_int){ EXPECT_EQ(time_confidence::UNAVAILABLE, time_confidence_from_int(0)); @@ -55,7 +55,7 @@ TEST(time_confidence_test, test_from_int){ EXPECT_EQ(time_confidence::TIME_000_000_000_000_2, time_confidence_from_int(35)); EXPECT_EQ(time_confidence::TIME_000_000_000_000_1, time_confidence_from_int(36)); EXPECT_EQ(time_confidence::TIME_000_000_000_000_05, time_confidence_from_int(37)); - EXPECT_EQ(time_confidence::TIME_000_000_000_000_02, time_confidence_from_int(38)); + EXPECT_EQ(time_confidence::TIME_000_000_000_000_02, time_confidence_from_int(38)); EXPECT_EQ(time_confidence::TIME_000_000_000_000_01, time_confidence_from_int(39)); EXPECT_THROW( time_confidence_from_int(40), std::invalid_argument); diff --git a/streets_utils/streets_messages/test/serializers/sensor_data_sharing_msg_json_serialization_test.cpp b/streets_utils/streets_messages/test/serializers/sensor_data_sharing_msg_json_serialization_test.cpp index 390c201c5..332427dbd 100644 --- a/streets_utils/streets_messages/test/serializers/sensor_data_sharing_msg_json_serialization_test.cpp +++ b/streets_utils/streets_messages/test/serializers/sensor_data_sharing_msg_json_serialization_test.cpp @@ -15,7 +15,7 @@ #include #include -using namespace streets_utils::messages; +using namespace streets_utils::messages::sdsm; using namespace streets_utils::json_utils; TEST(sensor_data_sharing_msg_json_serialization_test, confirm_required_properties_max_value) { @@ -57,7 +57,7 @@ TEST(sensor_data_sharing_msg_json_serialization_test, confirm_required_propertie // Confirm result has msg information in desired structure auto result = parse_json(json); - // Confirm timestamp data, Should fail at assert statements since if this require property is + // Confirm timestamp data, Should fail at assert statements since if this require property is // not available the other calls will fail ASSERT_TRUE(result.HasMember("sdsm_time_stamp")); ASSERT_TRUE(result.FindMember("sdsm_time_stamp")->value.IsObject()); @@ -81,7 +81,7 @@ TEST(sensor_data_sharing_msg_json_serialization_test, confirm_required_propertie EXPECT_FALSE( result.HasMember("ref_pos_el_conf")); // Confirm ref position ASSERT_TRUE(result.HasMember("ref_pos")); - ASSERT_TRUE(result.FindMember("ref_pos")->value.IsObject()); + ASSERT_TRUE(result.FindMember("ref_pos")->value.IsObject()); EXPECT_EQ( msg._ref_positon._longitude, result["ref_pos"]["long"].GetInt64()); EXPECT_EQ( msg._ref_positon._latitude, result["ref_pos"]["lat"].GetInt64()); // Optional parameter is not present @@ -100,9 +100,9 @@ TEST(sensor_data_sharing_msg_json_serialization_test, confirm_required_propertie ASSERT_FALSE(object.HasMember("detected_object_optional_data")); // Retreive Object common data auto object_common_data = object["detected_object_common_data"].GetObject(); - // Retreive Object common data from + // Retreive Object common data from auto msg_object_common_data = msg._objects[0]._detected_object_common_data; - // Confirm Object common data properties + // Confirm Object common data properties EXPECT_EQ(static_cast(msg_object_common_data._object_type), object_common_data["obj_type"].GetUint()); EXPECT_EQ(msg_object_common_data._classification_confidence, object_common_data["obj_type_cfd"].GetUint()); EXPECT_EQ(msg_object_common_data._object_id, object_common_data["object_id"].GetUint()); @@ -134,7 +134,7 @@ TEST(sensor_data_sharing_msg_json_serialization_test, confirm_required_propertie msg._time_stamp.hour= 0; msg._time_stamp.day = 0; msg._time_stamp.month = 0; - msg._time_stamp.year = 0; + msg._time_stamp.year = 0; msg._ref_position_confidence._semi_major_axis_accuracy = 0; msg._ref_position_confidence._semi_minor_axis_accuracy = 0; msg._ref_position_confidence._semi_major_axis_orientation = 0; @@ -161,7 +161,7 @@ TEST(sensor_data_sharing_msg_json_serialization_test, confirm_required_propertie // Confirm result has msg information in desired structure auto result = parse_json(json); - // Confirm timestamp data, Should fail at assert statements since if this require property is + // Confirm timestamp data, Should fail at assert statements since if this require property is // not available the other calls will fail ASSERT_TRUE(result.HasMember("sdsm_time_stamp")); ASSERT_TRUE(result.FindMember("sdsm_time_stamp")->value.IsObject()); @@ -185,7 +185,7 @@ TEST(sensor_data_sharing_msg_json_serialization_test, confirm_required_propertie EXPECT_FALSE( result.HasMember("ref_pos_el_conf")); // Confirm ref position ASSERT_TRUE(result.HasMember("ref_pos")); - ASSERT_TRUE(result.FindMember("ref_pos")->value.IsObject()); + ASSERT_TRUE(result.FindMember("ref_pos")->value.IsObject()); EXPECT_EQ( msg._ref_positon._longitude, result["ref_pos"]["long"].GetInt64()); EXPECT_EQ( msg._ref_positon._latitude, result["ref_pos"]["lat"].GetInt64()); // Optional parameter is not present @@ -204,9 +204,9 @@ TEST(sensor_data_sharing_msg_json_serialization_test, confirm_required_propertie ASSERT_FALSE(object.HasMember("detected_object_optional_data")); // Retreive Object common data auto object_common_data = object["detected_object_common_data"].GetObject(); - // Retreive Object common data from + // Retreive Object common data from auto msg_object_common_data = msg._objects[0]._detected_object_common_data; - // Confirm Object common data properties + // Confirm Object common data properties EXPECT_EQ(static_cast(msg_object_common_data._object_type), object_common_data["obj_type"].GetUint()); EXPECT_EQ(msg_object_common_data._classification_confidence, object_common_data["obj_type_cfd"].GetUint()); EXPECT_EQ(msg_object_common_data._object_id, object_common_data["object_id"].GetUint()); @@ -239,7 +239,7 @@ TEST(sensor_data_sharing_msg_json_serialization_test, confirm_optional_vru_human msg._time_stamp.hour= 0; msg._time_stamp.day = 0; msg._time_stamp.month = 0; - msg._time_stamp.year = 0; + msg._time_stamp.year = 0; msg._ref_position_confidence._semi_major_axis_accuracy = 0; msg._ref_position_confidence._semi_minor_axis_accuracy = 0; msg._ref_position_confidence._semi_major_axis_orientation = 0; @@ -291,7 +291,7 @@ TEST(sensor_data_sharing_msg_json_serialization_test, confirm_optional_vru_human // Confirm result has msg information in desired structure auto result = parse_json(json); - // Confirm timestamp data, Should fail at assert statements since if this require property is + // Confirm timestamp data, Should fail at assert statements since if this require property is // not available the other calls will fail ASSERT_TRUE(result.HasMember("sdsm_time_stamp")); ASSERT_TRUE(result.FindMember("sdsm_time_stamp")->value.IsObject()); @@ -315,7 +315,7 @@ TEST(sensor_data_sharing_msg_json_serialization_test, confirm_optional_vru_human EXPECT_TRUE( result.HasMember("ref_pos_el_conf")); // Confirm ref position ASSERT_TRUE(result.HasMember("ref_pos")); - ASSERT_TRUE(result.FindMember("ref_pos")->value.IsObject()); + ASSERT_TRUE(result.FindMember("ref_pos")->value.IsObject()); EXPECT_EQ( msg._ref_positon._longitude, result["ref_pos"]["long"].GetInt64()); EXPECT_EQ( msg._ref_positon._latitude, result["ref_pos"]["lat"].GetInt64()); // Confirm optional elevation parameter is present @@ -334,9 +334,9 @@ TEST(sensor_data_sharing_msg_json_serialization_test, confirm_optional_vru_human ASSERT_TRUE(object.FindMember("detected_object_common_data")->value.IsObject()); // Retreive Object common data auto object_common_data = object["detected_object_common_data"].GetObject(); - // Retreive Object common data from + // Retreive Object common data from auto msg_object_common_data = msg._objects[0]._detected_object_common_data; - // Confirm Object common data properties + // Confirm Object common data properties EXPECT_EQ(static_cast(msg_object_common_data._object_type), object_common_data["obj_type"].GetUint()); EXPECT_EQ(msg_object_common_data._classification_confidence, object_common_data["obj_type_cfd"].GetUint()); EXPECT_EQ(msg_object_common_data._object_id, object_common_data["object_id"].GetUint()); @@ -364,7 +364,7 @@ TEST(sensor_data_sharing_msg_json_serialization_test, confirm_optional_vru_human ASSERT_TRUE(object.HasMember("detected_object_optional_data")); ASSERT_TRUE(object["detected_object_optional_data"].HasMember("detected_vru_data") ); auto msg_object_optional_data = std::get(msg._objects[0]._detected_object_optional_data.value()); - auto option_vru_json_data = object["detected_object_optional_data"]["detected_vru_data"].GetObject(); + auto option_vru_json_data = object["detected_object_optional_data"]["detected_vru_data"].GetObject(); EXPECT_EQ( msg_object_optional_data._attachment.value(), attachment_from_int( option_vru_json_data["attachment"].GetUint())); EXPECT_EQ( msg_object_optional_data._attachment_radius.value(), option_vru_json_data["radius"].GetUint()); EXPECT_EQ( msg_object_optional_data._personal_device_user_type.value(), personal_device_user_type_from_int(option_vru_json_data["basic_type"].GetUint())); @@ -383,7 +383,7 @@ TEST(sensor_data_sharing_msg_json_serialization_test, confirm_optional_vru_anima msg._time_stamp.hour= 0; msg._time_stamp.day = 0; msg._time_stamp.month = 0; - msg._time_stamp.year = 0; + msg._time_stamp.year = 0; msg._ref_position_confidence._semi_major_axis_accuracy = 0; msg._ref_position_confidence._semi_minor_axis_accuracy = 0; msg._ref_position_confidence._semi_major_axis_orientation = 0; @@ -436,7 +436,7 @@ TEST(sensor_data_sharing_msg_json_serialization_test, confirm_optional_vru_anima // Confirm result has msg information in desired structure auto result = parse_json(json); - // Confirm timestamp data, Should fail at assert statements since if this require property is + // Confirm timestamp data, Should fail at assert statements since if this require property is // not available the other calls will fail ASSERT_TRUE(result.HasMember("sdsm_time_stamp")); ASSERT_TRUE(result.FindMember("sdsm_time_stamp")->value.IsObject()); @@ -460,7 +460,7 @@ TEST(sensor_data_sharing_msg_json_serialization_test, confirm_optional_vru_anima EXPECT_TRUE( result.HasMember("ref_pos_el_conf")); // Confirm ref position ASSERT_TRUE(result.HasMember("ref_pos")); - ASSERT_TRUE(result.FindMember("ref_pos")->value.IsObject()); + ASSERT_TRUE(result.FindMember("ref_pos")->value.IsObject()); EXPECT_EQ( msg._ref_positon._longitude, result["ref_pos"]["long"].GetInt64()); EXPECT_EQ( msg._ref_positon._latitude, result["ref_pos"]["lat"].GetInt64()); // Confirm optional elevation parameter is present @@ -479,9 +479,9 @@ TEST(sensor_data_sharing_msg_json_serialization_test, confirm_optional_vru_anima ASSERT_TRUE(object.FindMember("detected_object_common_data")->value.IsObject()); // Retreive Object common data auto object_common_data = object["detected_object_common_data"].GetObject(); - // Retreive Object common data from + // Retreive Object common data from auto msg_object_common_data = msg._objects[0]._detected_object_common_data; - // Confirm Object common data properties + // Confirm Object common data properties EXPECT_EQ(static_cast(msg_object_common_data._object_type), object_common_data["obj_type"].GetUint()); EXPECT_EQ(msg_object_common_data._classification_confidence, object_common_data["obj_type_cfd"].GetUint()); EXPECT_EQ(msg_object_common_data._object_id, object_common_data["object_id"].GetUint()); @@ -509,7 +509,7 @@ TEST(sensor_data_sharing_msg_json_serialization_test, confirm_optional_vru_anima ASSERT_TRUE(object.HasMember("detected_object_optional_data")); ASSERT_TRUE(object["detected_object_optional_data"].HasMember("detected_vru_data") ); auto msg_object_optional_data = std::get(msg._objects[0]._detected_object_optional_data.value()); - auto option_vru_json_data = object["detected_object_optional_data"]["detected_vru_data"].GetObject(); + auto option_vru_json_data = object["detected_object_optional_data"]["detected_vru_data"].GetObject(); EXPECT_EQ( msg_object_optional_data._attachment.value(), attachment_from_int( option_vru_json_data["attachment"].GetUint())); EXPECT_EQ( msg_object_optional_data._attachment_radius.value(), option_vru_json_data["radius"].GetUint()); EXPECT_EQ( msg_object_optional_data._personal_device_user_type.value(), personal_device_user_type_from_int(option_vru_json_data["basic_type"].GetUint())); @@ -530,7 +530,7 @@ TEST(sensor_data_sharing_msg_json_serialization_test, confirm_optional_vru_motor msg._time_stamp.hour= 0; msg._time_stamp.day = 0; msg._time_stamp.month = 0; - msg._time_stamp.year = 0; + msg._time_stamp.year = 0; msg._ref_position_confidence._semi_major_axis_accuracy = 0; msg._ref_position_confidence._semi_minor_axis_accuracy = 0; msg._ref_position_confidence._semi_major_axis_orientation = 0; @@ -582,7 +582,7 @@ TEST(sensor_data_sharing_msg_json_serialization_test, confirm_optional_vru_motor // Confirm result has msg information in desired structure auto result = parse_json(json); - // Confirm timestamp data, Should fail at assert statements since if this require property is + // Confirm timestamp data, Should fail at assert statements since if this require property is // not available the other calls will fail ASSERT_TRUE(result.HasMember("sdsm_time_stamp")); ASSERT_TRUE(result.FindMember("sdsm_time_stamp")->value.IsObject()); @@ -606,7 +606,7 @@ TEST(sensor_data_sharing_msg_json_serialization_test, confirm_optional_vru_motor EXPECT_TRUE( result.HasMember("ref_pos_el_conf")); // Confirm ref position ASSERT_TRUE(result.HasMember("ref_pos")); - ASSERT_TRUE(result.FindMember("ref_pos")->value.IsObject()); + ASSERT_TRUE(result.FindMember("ref_pos")->value.IsObject()); EXPECT_EQ( msg._ref_positon._longitude, result["ref_pos"]["long"].GetInt64()); EXPECT_EQ( msg._ref_positon._latitude, result["ref_pos"]["lat"].GetInt64()); // Confirm optional elevation parameter is present @@ -625,9 +625,9 @@ TEST(sensor_data_sharing_msg_json_serialization_test, confirm_optional_vru_motor ASSERT_TRUE(object.FindMember("detected_object_common_data")->value.IsObject()); // Retreive Object common data auto object_common_data = object["detected_object_common_data"].GetObject(); - // Retreive Object common data from + // Retreive Object common data from auto msg_object_common_data = msg._objects[0]._detected_object_common_data; - // Confirm Object common data properties + // Confirm Object common data properties EXPECT_EQ(static_cast(msg_object_common_data._object_type), object_common_data["obj_type"].GetUint()); EXPECT_EQ(msg_object_common_data._classification_confidence, object_common_data["obj_type_cfd"].GetUint()); EXPECT_EQ(msg_object_common_data._object_id, object_common_data["object_id"].GetUint()); @@ -655,7 +655,7 @@ TEST(sensor_data_sharing_msg_json_serialization_test, confirm_optional_vru_motor ASSERT_TRUE(object.HasMember("detected_object_optional_data")); ASSERT_TRUE(object["detected_object_optional_data"].HasMember("detected_vru_data") ); auto msg_object_optional_data = std::get(msg._objects[0]._detected_object_optional_data.value()); - auto option_vru_json_data = object["detected_object_optional_data"]["detected_vru_data"].GetObject(); + auto option_vru_json_data = object["detected_object_optional_data"]["detected_vru_data"].GetObject(); EXPECT_EQ( msg_object_optional_data._attachment.value(), attachment_from_int( option_vru_json_data["attachment"].GetUint())); EXPECT_EQ( msg_object_optional_data._attachment_radius.value(), option_vru_json_data["radius"].GetUint()); EXPECT_EQ( msg_object_optional_data._personal_device_user_type.value(), personal_device_user_type_from_int(option_vru_json_data["basic_type"].GetUint())); @@ -675,7 +675,7 @@ TEST(sensor_data_sharing_msg_json_serialization_test, confirm_optional_obstacle_ msg._time_stamp.hour= 0; msg._time_stamp.day = 0; msg._time_stamp.month = 0; - msg._time_stamp.year = 0; + msg._time_stamp.year = 0; msg._ref_position_confidence._semi_major_axis_accuracy = 0; msg._ref_position_confidence._semi_minor_axis_accuracy = 0; msg._ref_position_confidence._semi_major_axis_orientation = 0; @@ -699,7 +699,7 @@ TEST(sensor_data_sharing_msg_json_serialization_test, confirm_optional_obstacle_ detected_obstacle._size._height = 1023; detected_obstacle._size._width = 1023; detected_obstacle._size._length = 1023; - + obstacle_size_confidence _size_confidence; detected_obstacle._size_confidence._height_confidence = size_value_confidence::SIZE_0_01; detected_obstacle._size_confidence._width_confidence = size_value_confidence::SIZE_0_01; @@ -715,7 +715,7 @@ TEST(sensor_data_sharing_msg_json_serialization_test, confirm_optional_obstacle_ // Confirm result has msg information in desired structure auto result = parse_json(json); - // Confirm timestamp data, Should fail at assert statements since if this require property is + // Confirm timestamp data, Should fail at assert statements since if this require property is // not available the other calls will fail ASSERT_TRUE(result.HasMember("sdsm_time_stamp")); ASSERT_TRUE(result.FindMember("sdsm_time_stamp")->value.IsObject()); @@ -739,7 +739,7 @@ TEST(sensor_data_sharing_msg_json_serialization_test, confirm_optional_obstacle_ EXPECT_FALSE( result.HasMember("ref_pos_el_conf")); // Confirm ref position ASSERT_TRUE(result.HasMember("ref_pos")); - ASSERT_TRUE(result.FindMember("ref_pos")->value.IsObject()); + ASSERT_TRUE(result.FindMember("ref_pos")->value.IsObject()); EXPECT_EQ( msg._ref_positon._longitude, result["ref_pos"]["long"].GetInt64()); EXPECT_EQ( msg._ref_positon._latitude, result["ref_pos"]["lat"].GetInt64()); // Optional parameter is not present @@ -757,9 +757,9 @@ TEST(sensor_data_sharing_msg_json_serialization_test, confirm_optional_obstacle_ ASSERT_TRUE(object.FindMember("detected_object_common_data")->value.IsObject()); // Retreive Object common data auto object_common_data = object["detected_object_common_data"].GetObject(); - // Retreive Object common data from + // Retreive Object common data from auto msg_object_common_data = msg._objects[0]._detected_object_common_data; - // Confirm Object common data properties + // Confirm Object common data properties EXPECT_EQ(static_cast(msg_object_common_data._object_type), object_common_data["obj_type"].GetUint()); EXPECT_EQ(msg_object_common_data._classification_confidence, object_common_data["obj_type_cfd"].GetUint()); EXPECT_EQ(msg_object_common_data._object_id, object_common_data["object_id"].GetUint()); @@ -781,7 +781,7 @@ TEST(sensor_data_sharing_msg_json_serialization_test, confirm_optional_obstacle_ ASSERT_TRUE(object["detected_object_optional_data"].HasMember("detected_obstacle_data") ); auto msg_object_optional_data = std::get(msg._objects[0]._detected_object_optional_data.value()); auto option_obstacle_json_data = object["detected_object_optional_data"]["detected_obstacle_data"].GetObject(); - // Confirm Size + // Confirm Size EXPECT_EQ( msg_object_optional_data._size._length, option_obstacle_json_data["obst_size"]["length"].GetUint()); EXPECT_EQ( msg_object_optional_data._size._width, option_obstacle_json_data["obst_size"]["width"].GetUint()); EXPECT_EQ( msg_object_optional_data._size._height, option_obstacle_json_data["obst_size"]["height"].GetUint()); @@ -789,7 +789,7 @@ TEST(sensor_data_sharing_msg_json_serialization_test, confirm_optional_obstacle_ EXPECT_EQ( msg_object_optional_data._size_confidence._length_confidence, size_value_confidence_from_int(option_obstacle_json_data["obst_size_confidence"]["length_confidence"].GetUint())); EXPECT_EQ( msg_object_optional_data._size_confidence._width_confidence, size_value_confidence_from_int(option_obstacle_json_data["obst_size_confidence"]["width_confidence"].GetUint())); EXPECT_EQ( msg_object_optional_data._size_confidence._height_confidence.value(), size_value_confidence_from_int(option_obstacle_json_data["obst_size_confidence"]["height_confidence"].GetUint())); - + } @@ -806,7 +806,7 @@ TEST(sensor_data_sharing_msg_json_serialization_test, confirm_optional_vehicle_p msg._time_stamp.hour= 0; msg._time_stamp.day = 0; msg._time_stamp.month = 0; - msg._time_stamp.year = 0; + msg._time_stamp.year = 0; msg._ref_position_confidence._semi_major_axis_accuracy = 0; msg._ref_position_confidence._semi_minor_axis_accuracy = 0; msg._ref_position_confidence._semi_major_axis_orientation = 0; @@ -834,19 +834,19 @@ TEST(sensor_data_sharing_msg_json_serialization_test, confirm_optional_vehicle_p detected_vehicle._vehicle_height = 127; vehicle_size_confidence _vehicle_size_confidence; - + _vehicle_size_confidence._height_confidence = size_value_confidence::SIZE_0_01; _vehicle_size_confidence._width_confidence = size_value_confidence::SIZE_0_01; _vehicle_size_confidence._length_confidence = size_value_confidence::SIZE_0_01; detected_vehicle._size_confidence = _vehicle_size_confidence; detected_vehicle.exterior_lights = "11110000"; - + angular_velocity_set _angular_velocity_set; _angular_velocity_set._pitch_rate = 32767; _angular_velocity_set._roll_rate = 32767; detected_vehicle._angular_velocity = _angular_velocity_set; - + angular_velocity_confidence_set _angular_velocity_confidence_set; _angular_velocity_confidence_set._pitch_rate_confidence = angular_velocity_confidence::DEGSEC_01; @@ -877,7 +877,7 @@ TEST(sensor_data_sharing_msg_json_serialization_test, confirm_optional_vehicle_p // Confirm result has msg information in desired structure auto result = parse_json(json); - // Confirm timestamp data, Should fail at assert statements since if this require property is + // Confirm timestamp data, Should fail at assert statements since if this require property is // not available the other calls will fail ASSERT_TRUE(result.HasMember("sdsm_time_stamp")); ASSERT_TRUE(result.FindMember("sdsm_time_stamp")->value.IsObject()); @@ -901,7 +901,7 @@ TEST(sensor_data_sharing_msg_json_serialization_test, confirm_optional_vehicle_p EXPECT_FALSE( result.HasMember("ref_pos_el_conf")); // Confirm ref position ASSERT_TRUE(result.HasMember("ref_pos")); - ASSERT_TRUE(result.FindMember("ref_pos")->value.IsObject()); + ASSERT_TRUE(result.FindMember("ref_pos")->value.IsObject()); EXPECT_EQ( msg._ref_positon._longitude, result["ref_pos"]["long"].GetInt64()); EXPECT_EQ( msg._ref_positon._latitude, result["ref_pos"]["lat"].GetInt64()); // Optional parameter is not present @@ -919,9 +919,9 @@ TEST(sensor_data_sharing_msg_json_serialization_test, confirm_optional_vehicle_p ASSERT_TRUE(object.FindMember("detected_object_common_data")->value.IsObject()); // Retreive Object common data auto object_common_data = object["detected_object_common_data"].GetObject(); - // Retreive Object common data from + // Retreive Object common data from auto msg_object_common_data = msg._objects[0]._detected_object_common_data; - // Confirm Object common data properties + // Confirm Object common data properties EXPECT_EQ(static_cast(msg_object_common_data._object_type), object_common_data["obj_type"].GetUint()); EXPECT_EQ(msg_object_common_data._classification_confidence, object_common_data["obj_type_cfd"].GetUint()); EXPECT_EQ(msg_object_common_data._object_id, object_common_data["object_id"].GetUint()); @@ -971,6 +971,5 @@ TEST(sensor_data_sharing_msg_json_serialization_test, confirm_optional_vehicle_p EXPECT_EQ( msg_object_optional_data._vehicle_class, option_vehicle_json_data["vehicle_class"].GetUint()); // Confirm classification confidence EXPECT_EQ( msg_object_optional_data._classification_confidence, option_vehicle_json_data["vehicle_class_conf"].GetUint()); - -} +} From 6b891d457f26c0505ce9f01e24f4e917e1725f10 Mon Sep 17 00:00:00 2001 From: Anish_deva <51463994+adev4a@users.noreply.github.com> Date: Tue, 21 Nov 2023 13:12:23 -0500 Subject: [PATCH 46/80] cdar 390 deserialize detected objects (#367) * add detected objects message definition * update variable names * update namespace * fix namespace * update array var to vector and add missing parameter * update readme for Detected Objects * fix comment * Add test * fix unit tests * update detected objects example * fix code smells --- .../DetectedObjectsMessage.md | 64 +++++------ .../detected_obj_msg_deserializer.hpp | 40 +++++++ .../detected_obj_msg_deserializer.cpp | 107 ++++++++++++++++++ .../detected_obj_msg_deserialization_test.cpp | 74 ++++++++++++ 4 files changed, 253 insertions(+), 32 deletions(-) create mode 100644 streets_utils/streets_messages/include/deserializers/detected_obj_msg_deserializer.hpp create mode 100644 streets_utils/streets_messages/src/deserializers/detected_obj_msg_deserializer.cpp create mode 100644 streets_utils/streets_messages/test/deserializers/detected_obj_msg_deserialization_test.cpp diff --git a/streets_utils/streets_messages/DetectedObjectsMessage.md b/streets_utils/streets_messages/DetectedObjectsMessage.md index fe0ae50ad..0c4132ad5 100644 --- a/streets_utils/streets_messages/DetectedObjectsMessage.md +++ b/streets_utils/streets_messages/DetectedObjectsMessage.md @@ -19,41 +19,41 @@ This CARMA-Streets library contains the Detected Objects Message as well as the }, "positionCovariance": [ [ - 1, - 0, - 0 + 1.0, + 0.0, + 0.0 ], [ - 1, - 0, - 0 + 1.0, + 0.0, + 0.0 ], [ - 1, - 0, - 0 + 1.0, + 0.0, + 0.0 ] ], "velocity": { - "x": 1, - "y": 1, - "z": 1 + "x": 1.0, + "y": 1.0, + "z": 1.0 }, "velocityCovariance": [ [ - 1, - 0, - 0 + 1.0, + 0.0, + 0.0 ], [ - 1, - 0, - 0 + 1.0, + 0.0, + 0.0 ], [ - 1, - 0, - 0 + 1.0, + 0.0, + 0.0 ] ], "angularVelocity": { @@ -63,24 +63,24 @@ This CARMA-Streets library contains the Detected Objects Message as well as the }, "angularVelocityCovariance": [ [ - 1, - 0, - 0 + 1.0, + 0.0, + 0.0 ], [ - 1, - 0, - 0 + 1.0, + 0.0, + 0.0 ], [ - 1, - 0, - 0 + 1.0, + 0.0, + 0.0 ] ], "size": { - "length": 2, - "height": 1, + "length": 2.0, + "height": 1.0, "width": 0.5 } } diff --git a/streets_utils/streets_messages/include/deserializers/detected_obj_msg_deserializer.hpp b/streets_utils/streets_messages/include/deserializers/detected_obj_msg_deserializer.hpp new file mode 100644 index 000000000..27b96822e --- /dev/null +++ b/streets_utils/streets_messages/include/deserializers/detected_obj_msg_deserializer.hpp @@ -0,0 +1,40 @@ +// Copyright 2023 Leidos +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#pragma once +#include +#include +#include +#include +#include +#include +#include +#include + + +namespace streets_utils::messages::detected_objects_msg { + + + detected_objects_msg from_json( const std::string &val); + + cartesian_point parse_cartesian_3d(const rapidjson::Value &val); + + std::vector> parse_covariance(const rapidjson::Value::ConstArray &val); + + vector_3d parse_vector3d(const rapidjson::Value &val); + + size parse_size(const rapidjson::Value &val); + + +} \ No newline at end of file diff --git a/streets_utils/streets_messages/src/deserializers/detected_obj_msg_deserializer.cpp b/streets_utils/streets_messages/src/deserializers/detected_obj_msg_deserializer.cpp new file mode 100644 index 000000000..19bb32e42 --- /dev/null +++ b/streets_utils/streets_messages/src/deserializers/detected_obj_msg_deserializer.cpp @@ -0,0 +1,107 @@ +// Copyright 2019-2023 Leidos +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +#include "deserializers/detected_obj_msg_deserializer.hpp" + +#include + +namespace streets_utils::messages::detected_objects_msg { + + detected_objects_msg from_json( const std::string &json) + { + // Deserializes the incoming json message + rapidjson::Document document = streets_utils::json_utils::parse_json(json); + detected_objects_msg msg; + + msg._type = streets_utils::json_utils::parse_string_member("type",document, true).value(); + msg._confidence = streets_utils::json_utils::parse_double_member("confidence", document, true).value(); + msg._sensor_id = streets_utils::json_utils::parse_string_member("sensorId", document, true).value(); + msg._proj_string = streets_utils::json_utils::parse_string_member("projString", document, true).value(); + msg._object_id = streets_utils::json_utils::parse_string_member("objectId", document, true).value(); + + auto position_obj = streets_utils::json_utils::parse_object_member("position", document, true).value(); + msg._position = parse_cartesian_3d(position_obj); + + auto position_cov_obj = streets_utils::json_utils::parse_array_member("positionCovariance", document, true).value(); + msg._position_covariance = parse_covariance(position_cov_obj); + + auto velocity_obj = streets_utils::json_utils::parse_object_member("velocity", document, true).value(); + msg._velocity = parse_vector3d(velocity_obj); + + auto velocity_cov_obj = streets_utils::json_utils::parse_array_member("velocityCovariance", document, true).value(); + msg._velocity_covariance = parse_covariance(velocity_cov_obj); + + auto angular_velocity_obj = streets_utils::json_utils::parse_object_member("angularVelocity", document, true).value(); + msg._angular_velocity = parse_vector3d(angular_velocity_obj); + + auto angular_velocity_cov_obj = streets_utils::json_utils::parse_array_member("angularVelocityCovariance", document, true).value(); + msg._angular_velocity_covariance = parse_covariance(angular_velocity_cov_obj); + + auto size_obj = streets_utils::json_utils::parse_object_member("size", document, true).value(); + msg._size = parse_size(size_obj); + + + return msg; + } + + cartesian_point parse_cartesian_3d(const rapidjson::Value &val) + { + cartesian_point point; + point._x = streets_utils::json_utils::parse_double_member("x", val, true).value(); + point._y = streets_utils::json_utils::parse_double_member("y", val, true).value(); + point._z = streets_utils::json_utils::parse_double_member("z", val, true).value(); + return point; + } + + vector_3d parse_vector3d(const rapidjson::Value &val) + { + vector_3d vector; + vector._x = streets_utils::json_utils::parse_double_member("x", val, true).value(); + vector._y = streets_utils::json_utils::parse_double_member("y", val, true).value(); + vector._z = streets_utils::json_utils::parse_double_member("z", val, true).value(); + + return vector; + + } + + std::vector> parse_covariance(const rapidjson::Value::ConstArray &val) + { + // Initialize 3x3 covariance matrix + std::vector> covariance; + + for (rapidjson::SizeType i = 0; i < val.Size(); i++) + { + const rapidjson::Value& row = val[i]; + std::vector val_row; + + for (rapidjson::SizeType j = 0; j < row.Size(); j++) + { + val_row.push_back(val[i][j].GetDouble()); + } + covariance.push_back(val_row); + } + return covariance; + } + + size parse_size(const rapidjson::Value &val) + { + size size_obj; + size_obj._length = streets_utils::json_utils::parse_double_member("length", val, true).value(); + size_obj._height = streets_utils::json_utils::parse_double_member("height", val, true).value(); + size_obj._width = streets_utils::json_utils::parse_double_member("width", val, true).value(); + + return size_obj; + } + + +} \ No newline at end of file diff --git a/streets_utils/streets_messages/test/deserializers/detected_obj_msg_deserialization_test.cpp b/streets_utils/streets_messages/test/deserializers/detected_obj_msg_deserialization_test.cpp new file mode 100644 index 000000000..1acde3ce8 --- /dev/null +++ b/streets_utils/streets_messages/test/deserializers/detected_obj_msg_deserialization_test.cpp @@ -0,0 +1,74 @@ +// Copyright 2023 Leidos +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include +#include +#include + +#include + +TEST(detected_obj_msg_deserializer_test, deserialize) +{ + std::string json_prediction = "{\"type\":\"CAR\",\"confidence\":0.7,\"sensorId\":\"sensor1\",\"projString\":\"projectionString2\",\"objectId\":\"Object7\",\"position\":{\"x\":-1.1,\"y\":-2.0,\"z\":-3.2},\"positionCovariance\":[[1.0,0.0,0.0],[1.0,0.0,0.0],[1.0,0.0,0.0]],\"velocity\":{\"x\":1.0,\"y\":1.0,\"z\":1.0},\"velocityCovariance\":[[1.0,0.0,0.0],[1.0,0.0,0.0],[1.0,0.0,0.0]],\"angularVelocity\":{\"x\":0.1,\"y\":0.2,\"z\":0.3},\"angularVelocityCovariance\":[[1.0,0.0,0.0],[1.0,0.0,0.0],[1.0,0.0,0.0]],\"size\":{\"length\":2.0,\"height\":1.0,\"width\":0.5}}"; + + auto msg = streets_utils::messages::detected_objects_msg::from_json(json_prediction); + EXPECT_EQ(msg._type, "CAR"); + EXPECT_NEAR(0.7,msg._confidence, 0.01); + EXPECT_EQ(msg._sensor_id, "sensor1"); + EXPECT_EQ(msg._object_id, "Object7"); + EXPECT_NEAR(msg._position._x, -1.1, 0.001); + EXPECT_NEAR(msg._position._y, -2.0, 0.001); + EXPECT_NEAR(msg._position._z, -3.2, 0.001); + // Position Covariance + std::vector> pose_cov = {{1,0,0},{1,0,0},{1,0,0}}; + for (size_t i = 0 ; i < msg._position_covariance.size();i++) + { + for (size_t j=0; j< msg._position_covariance[i].size();j++) + { + EXPECT_NEAR(msg._position_covariance[i][j], pose_cov[i][j], 0.001); + } + + } + + EXPECT_NEAR(msg._velocity._x, 1.0, 0.001); + EXPECT_NEAR(msg._velocity._y, 1.0, 0.001); + EXPECT_NEAR(msg._velocity._z,1.0, 0.001); + // Velocity Covariance + std::vector> vel_cov = {{1,0,0},{1,0,0},{1,0,0}}; + for (size_t i = 0 ; i < msg._velocity_covariance.size();i++) + { + for (size_t j=0; j< msg._velocity_covariance[i].size();j++) + { + EXPECT_NEAR(msg._velocity_covariance[i][j], vel_cov[i][j], 0.001); + } + + } + EXPECT_NEAR(msg._angular_velocity._x,0.1, 0.001); + EXPECT_NEAR(msg._angular_velocity._y,0.2, 0.001); + EXPECT_NEAR(msg._angular_velocity._z,0.3, 0.001); + // AngularVelocity Covariance + std::vector> ang_vel_cov = {{1,0,0},{1,0,0},{1,0,0}}; + for (size_t i = 0 ; i < msg._angular_velocity_covariance.size();i++) + { + for (size_t j=0; j< msg._angular_velocity_covariance[i].size();j++) + { + EXPECT_NEAR(msg._angular_velocity_covariance[i][j], ang_vel_cov[i][j], 0.001); + } + + } + EXPECT_NEAR(msg._size._length,2.0, 0.001); + EXPECT_NEAR(msg._size._height,1.0, 0.001); + EXPECT_NEAR(msg._size._width,0.5, 0.001); + +} \ No newline at end of file From 39e0d407cca137b2deafd413b6944ca948049e3a Mon Sep 17 00:00:00 2001 From: paulbourelly999 <77466294+paulbourelly999@users.noreply.github.com> Date: Wed, 22 Nov 2023 10:34:17 -0500 Subject: [PATCH 47/80] Dev Container setup for CARMA Streets (#368) * Dev Container Setup * Updates * Dev Container updates * Update documentation * Renamed devcontainer docker compose file * Update documentation * Add comment to dev container json --- .devcontainer/README.md | 26 ++++++++++++ .devcontainer/devcontainer.json | 42 +++++++++++++++++++ .devcontainer/docker-compose-devcontainer.yml | 32 ++++++++++++++ README.md | 5 ++- 4 files changed, 104 insertions(+), 1 deletion(-) create mode 100644 .devcontainer/README.md create mode 100644 .devcontainer/devcontainer.json create mode 100644 .devcontainer/docker-compose-devcontainer.yml diff --git a/.devcontainer/README.md b/.devcontainer/README.md new file mode 100644 index 000000000..c7f0646d2 --- /dev/null +++ b/.devcontainer/README.md @@ -0,0 +1,26 @@ +# CARMA Streets Dev Container Setup +## Introduction +This is the setup for the VSCode extension [devcontainer](https://code.visualstudio.com/docs/devcontainers/containers) extension, which allows developers to standup a containerized development environment with the VS Code IDE. The current configuration includes the installation of common VSCode plugins for C++, CMake, Python, Mark Down and more. A complete list of VS Code installed plugins can be found in the `./devcontainer/devcontainer.json` under `"customizations.vscode.extensions` attribute. The setup also stands up the CARMA Streets [docker compose file](../docker-compose.yml) , which provides your development environment with easy access to Kafka and CARMA Streets services for integration testing of functionality. +## Suggested Structure of CARMA Street Service +```bash +├── +│ ├── src/ # Source Code +│ ├── include/ # Headers +│ ├── manifest.json # CARMA Streets Service configuration file +│ ├── CMakeLists.txt # CMake build file +│ ├── README.md # Documentation about Service +│ ├── build.sh # Build script to build streets_util libraries for service and service itself +| ├── install_dependencies.sh # Optional file to install external dependencies not included in base image +│ └── Dockerfile # Dockerfile to build streets serivce +``` +## Setup for new CARMA Streets Service +* Select a streets base image ([streets base images](../README.md#base-images)) and uncomment appropriate lines in `.devcontainer/devcontainer.json` and `.devcontainer/docker-compose-devcontainer.yml` (See comments in files) +* Use ctrl-shift-P (Command Pallette) and select *Dev Containers: Open Folder in Container*. +* Use VS Code terminals to build new streets service, preferably using a `build.sh` script under your new service directory(see [Sensor Data Sharing Service](../sensor_data_sharing_service/README.md)). +## Setup for existing CARMA Streets Service +* Select a streets base image ([streets base images](../README.md#base-images)) and uncomment appropriate lines in `.devcontainer/devcontainer.json` and `.devcontainer/docker-compose-devcontainer.yml` (See comments in files) +* Comment out existing service inside `docker-compose.yml` +* Use ctrl-shift-P (Command Pallette) and select *Dev Containers: Open Folder in Container*. +* Use VS Code terminals to build existing streets service, preferably using a `build.sh` script under your new service directory(see [Sensor Data Sharing Service](../sensor_data_sharing_service/README.md)). + + diff --git a/.devcontainer/devcontainer.json b/.devcontainer/devcontainer.json new file mode 100644 index 000000000..746e4759b --- /dev/null +++ b/.devcontainer/devcontainer.json @@ -0,0 +1,42 @@ +{ + "name": "Existing Docker Compose (Extend)", + // First "../docker-compose.yml launches all of CARMA Street including Kafka, V2X-Hub and existing CARMA Streets service + // Second "docker-compose-devcontainer.yml" lauches dev container for developing and integration testing new and existing + // CARMA Streets services + "dockerComposeFile": [ + "../docker-compose.yml", + "docker-compose-devcontainer.yml" + ], + + // Uncomment for Streets Service Base dev environment + "service": "streets_service_base", + // Uncomment for Streets Service Base Lanelet aware dev environment + // "service": "streets_service_base_lanelet_aware", + + // The optional 'workspaceFolder' property is the path VS Code should open by default when + // connected. This is typically a file mount in .devcontainer/docker-compose.yml + "workspaceFolder": "/home/${localWorkspaceFolderBasename}", + + // Uncomment for Sensor Data Sharing Service . + // "postCreateCommand": "/home/carma-streets/sensor_data_sharing_service/build.sh", + + // Configure tool-specific properties. + "customizations": { + "vscode": { + "extensions": [ + "ms-vscode.cpptools-extension-pack", + "github.vscode-github-actions", + "yzhang.markdown-all-in-one", + "ms-python.python", + "ms-python.black-formatter", + "ms-python.isort", + "streetsidesoftware.code-spell-checker", + "sonarsource.sonarlint-vscode", + "esbenp.prettier-vscode" + ] + } + }, + + "shutdownAction": "stopCompose" + +} diff --git a/.devcontainer/docker-compose-devcontainer.yml b/.devcontainer/docker-compose-devcontainer.yml new file mode 100644 index 000000000..389f553d7 --- /dev/null +++ b/.devcontainer/docker-compose-devcontainer.yml @@ -0,0 +1,32 @@ +version: '3.7' +services: + # Uncomment for streets service base dev environment + streets_service_base: + build: + context: . + dockerfile: ./streets_service_base/Dockerfile + args: + UBUNTU_CODENAME: bionic + network_mode: host + + # Uncomment for streets service base lanelet aware dev environment + # streets_service_base_lanelet_aware: + # build: + # context: . + # dockerfile: ./streets_service_base_lanelet_aware/Dockerfile + # network_mode: host + + + volumes: + # Update this to wherever you want VS Code to mount the folder of your project + - ..:/home:cached + + # Uncomment the next four lines if you will use a ptrace-based debugger like C++, Go, and Rust. + # cap_add: + # - SYS_PTRACE + # security_opt: + # - seccomp:unconfined + + # Overrides default command so things don't shut down after the process ends. + command: /bin/sh -c "while sleep 1000; do :; done" + diff --git a/README.md b/README.md index 4c9653804..77b1dde63 100644 --- a/README.md +++ b/README.md @@ -22,7 +22,10 @@ CARMA Streets is a component of CARMA ecosystem, which enables such a coordinati CARMA Streets architecture is based on a scalable services and layered architecture pattern that allows for easy deployment. Service components are packaged to contain one or more modules (classes) that represent a specific reusable function (e.g., decode a particular ASN.1 message) or an independently deployable business function (e.g., control interface to a signal controller). Services interact with each other via lightweight messaging service (e.g., Kafka) which allows for them be deployed either together or distributed for scalability and performance. A high-level abstract view of the architecture to communicate the design pattern is shown in Upcoming Figure. A more detailed Unified Modeling Language class and packaging diagrams to define the interfaces between services and layers and their interactions will be developed and documented here during implementation following an Agile Development Methodology. ## Deployment -Docker is the primary deployment mechanism to containerize one or more services. The CARMA Streets application and other major frameworks such as Kafka will run in their own separate containers. This document will be updated with a detailed Docker deployment strategy during later design phases. +Docker is the primary deployment mechanism to containerize one or more services. The CARMA Streets application and other major frameworks such as Kafka will run in their own separate containers. This document will be updated with a detailed Docker deployment strategy during later design phases. + +## Development +This repository includes configurations for [devcontainer](https://code.visualstudio.com/docs/devcontainers/containers) VSCode extension. This extension allows us to standup a containerized development environment. More information about the CARMA Streets Dev Container Setup can be found [here](.devcontainer/README.md). ## Base Images To make creating new CARMA Streets services easier and to make our CI/CD more efficient, we have introduced new CARMA Streets base images. These images can be used as a starting point and common build/runtime environment for CARMA Streets services. There are currently two CARMA Streets base images , for which documentation and Dockerfiles can be found [Streets Service Base](streets_service_base/README.md) and [Streets Service Base Lanelet Aware](streets_service_base_lanelet_aware/README.md). From 18ad7f6d4e45d6993c288befbb7b27649ec3ea0f Mon Sep 17 00:00:00 2001 From: paulbourelly999 <77466294+paulbourelly999@users.noreply.github.com> Date: Wed, 29 Nov 2023 10:34:28 -0500 Subject: [PATCH 48/80] Fix Dev Container source code volume (#370) * Fix Dev Container source code volume * Update --- .devcontainer/devcontainer.json | 2 +- .devcontainer/docker-compose-devcontainer.yml | 7 ++++++- 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/.devcontainer/devcontainer.json b/.devcontainer/devcontainer.json index 746e4759b..f436634ec 100644 --- a/.devcontainer/devcontainer.json +++ b/.devcontainer/devcontainer.json @@ -15,7 +15,7 @@ // The optional 'workspaceFolder' property is the path VS Code should open by default when // connected. This is typically a file mount in .devcontainer/docker-compose.yml - "workspaceFolder": "/home/${localWorkspaceFolderBasename}", + "workspaceFolder": "/home/carma-streets/", // Uncomment for Sensor Data Sharing Service . // "postCreateCommand": "/home/carma-streets/sensor_data_sharing_service/build.sh", diff --git a/.devcontainer/docker-compose-devcontainer.yml b/.devcontainer/docker-compose-devcontainer.yml index 389f553d7..e30a98adf 100644 --- a/.devcontainer/docker-compose-devcontainer.yml +++ b/.devcontainer/docker-compose-devcontainer.yml @@ -2,6 +2,9 @@ version: '3.7' services: # Uncomment for streets service base dev environment streets_service_base: + # Replace bionic with distro of choice + image: usdotfhwastoldev/streets_service_base:bionic + # Remove build parameter to force pull from dockerhub build: context: . dockerfile: ./streets_service_base/Dockerfile @@ -11,6 +14,8 @@ services: # Uncomment for streets service base lanelet aware dev environment # streets_service_base_lanelet_aware: + # image: usdotfhwastoldev/streets_service_base_lanelet_aware:bionic + # # Remove build parameter to force pull from dockerhub # build: # context: . # dockerfile: ./streets_service_base_lanelet_aware/Dockerfile @@ -19,7 +24,7 @@ services: volumes: # Update this to wherever you want VS Code to mount the folder of your project - - ..:/home:cached + - .:/home/carma-streets:cached # Uncomment the next four lines if you will use a ptrace-based debugger like C++, Go, and Rust. # cap_add: From 1a81b77d86918a5fc7c913166c19ab38414e2ef5 Mon Sep 17 00:00:00 2001 From: paulbourelly999 <77466294+paulbourelly999@users.noreply.github.com> Date: Wed, 29 Nov 2023 10:35:19 -0500 Subject: [PATCH 49/80] Fix/ci build (#369) * Update CI * Updates to CI * Update * Updates * Updates * Updates * Updates * Updates * Updates * Updates * Update CI to work * Fix script * Update to script * update CMake * Update to use root bashrc * Update build script * Updates to include intersection_model and message_services * Fix build script * Update coverage script to run message_services and intersection_model tests * Remove old commented out steps in workflow * Using cmake arguement instead of CMakeList command * Update CI script --- .../workflows/build_streets_base_images.yml | 42 ------- .github/workflows/ci.yml | 108 ++++++------------ .sonarqube/sonar-scanner.properties | 30 +++-- build.sh | 16 +-- .../install_lanelet2_dependencies.sh | 15 +-- .../install_rest_server_dependencies.sh | 2 +- coverage.sh | 30 ++--- intersection_model/CMakeLists.txt | 6 +- message_services/CMakeLists.txt | 1 - .../CMakeLists.txt | 2 + streets_utils/streets_snmp_cmd/CMakeLists.txt | 2 + .../streets_vehicle_scheduler/CMakeLists.txt | 3 + 12 files changed, 89 insertions(+), 168 deletions(-) delete mode 100644 .github/workflows/build_streets_base_images.yml diff --git a/.github/workflows/build_streets_base_images.yml b/.github/workflows/build_streets_base_images.yml deleted file mode 100644 index 9b37bc698..000000000 --- a/.github/workflows/build_streets_base_images.yml +++ /dev/null @@ -1,42 +0,0 @@ -name: Build and Push Base Images -on: - pull_request: - types: [opened, synchronize, reopened] - push: - branches: [develop, master] -jobs: - docker-build: - strategy: - matrix: - include: - - ubuntu-codename: bionic - build_lanelet_aware: true - # Currently install_lanelet2_dependencies.sh script only works for bionic. Disabled lanelet_aware build on newer distributions pending script updates. - - ubuntu-codename: focal - build_lanelet_aware: false - # Currently install_lanelet2_dependencies.sh script only works for bionic. Disabled lanelet_aware build on newer distributions pending script updates. - - ubuntu-codename: jammy - build_lanelet_aware: false - runs-on: ubuntu-latest - steps: - - name: Log in to the Container registry - uses: docker/login-action@v2 - with: - username: ${{ secrets.DOCKERHUB_USERNAME }} - password: ${{ secrets.DOCKERHUB_TOKEN }} - - name: Docker Build Streets Service Base - uses: docker/build-push-action@v3 - with: - push: true - build-args: | - UBUNTU_CODENAME=${{ matrix.ubuntu-codename }} - tags: usdotfhwastoldev/streets_service_base:${{ matrix.ubuntu-codename }} - file: ./streets_service_base/Dockerfile - - name: Docker Build Streets Service Base Lanelet Aware - if: ${{ matrix.build_lanelet_aware }} - uses: docker/build-push-action@v3 - with: - push: true - tags: usdotfhwastoldev/streets_service_base_lanelet_aware:bionic - file: ./streets_service_base_lanelet_aware/Dockerfile - \ No newline at end of file diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index f5c4dab1e..8f8acfac6 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -5,16 +5,50 @@ on: push: branches: [develop, master] jobs: + docker-build: + strategy: + matrix: + include: + - ubuntu-codename: bionic + build_lanelet_aware: true + # Currently install_lanelet2_dependencies.sh script only works for bionic. Disabled lanelet_aware build on newer distributions pending script updates. + - ubuntu-codename: focal + build_lanelet_aware: false + # Currently install_lanelet2_dependencies.sh script only works for bionic. Disabled lanelet_aware build on newer distributions pending script updates. + - ubuntu-codename: jammy + build_lanelet_aware: false + runs-on: ubuntu-latest + steps: + - name: Log in to the Container registry + uses: docker/login-action@v2 + with: + username: ${{ secrets.DOCKERHUB_USERNAME }} + password: ${{ secrets.DOCKERHUB_TOKEN }} + - name: Docker Build Streets Service Base + uses: docker/build-push-action@v3 + with: + push: true + build-args: | + UBUNTU_CODENAME=${{ matrix.ubuntu-codename }} + tags: usdotfhwastoldev/streets_service_base:${{ matrix.ubuntu-codename }} + file: ./streets_service_base/Dockerfile + - name: Docker Build Streets Service Base Lanelet Aware + if: ${{ matrix.build_lanelet_aware }} + uses: docker/build-push-action@v3 + with: + push: true + tags: usdotfhwastoldev/streets_service_base_lanelet_aware:${{ matrix.ubuntu-codename }} + file: ./streets_service_base_lanelet_aware/Dockerfile build: + needs: docker-build defaults: run: shell: bash runs-on: ubuntu-latest-8-cores container: - image: ubuntu:jammy-20230126 + image: usdotfhwastoldev/streets_service_base_lanelet_aware:bionic env: DEBIAN_FRONTEND: noninteractive - INIT_ENV: "/home/carma-streets/.base-image/init-env.sh" SONAR_SCANNER_VERSION: "5.0.1.3006" TERM: xterm options: "--user root" @@ -36,33 +70,10 @@ jobs: run: mv $GITHUB_WORKSPACE/${{ github.event.repository.name }} /home/carma-streets - name: Install dependencies run: | - mkdir /home/carma-streets/.base-image - touch /home/carma-streets/.base-image/init-env.sh cd /home/carma-streets/build_scripts - ./install_dependencies.sh ./install_test_dependencies.sh mkdir -p /home/carma-streets/ext ./install_rest_server_dependencies.sh - - - name: Install librdkafka - run: | - cd /home/carma-streets/ext/ - git clone --depth 1 https://github.com/confluentinc/librdkafka.git /home/carma-streets/ext/librdkafka - cd /home/carma-streets/ext/librdkafka/ - cmake -H. -B_cmake_build - cmake --build _cmake_build - cmake --build _cmake_build --target install - - name: Install rapidjson - run: | - cd /home/carma-streets/ext/ - git clone https://github.com/Tencent/rapidjson /home/carma-streets/ext/rapidjson - cd /home/carma-streets/ext/rapidjson - git checkout a95e013b97ca6523f32da23f5095fcc9dd6067e5 - mkdir -p /home/carma-streets/ext/rapidjson/build - cd /home/carma-streets/ext/rapidjson/build - cmake .. - make -j - make install - name: Install net-snmp run: | cd /home/carma-streets/ext/ @@ -73,52 +84,6 @@ jobs: ./configure --with-default-snmp-version="1" --with-sys-contact="@@no.where" --with-sys-location="Unknown" --with-logfile="/var/log/snmpd.log" --with-persistent-directory="/var/net-snmp" make -j make install - # - name: Install PROJ for coordinate transformations - # run: | - # git clone --depth 1 https://github.com/OSGeo/PROJ.git /home/carma-streets/PROJ --branch 6.2.1 - # cd /home/carma-streets/PROJ - # ./autogen.sh - # ./configure - # make -j - # make install - # - name: Download a cmake module for PROJ - # run: | - # cd /usr/share/cmake-3.10/Modules - # curl -O https://raw.githubusercontent.com/mloskot/cmake-modules/master/modules/FindPROJ4.cmake - # - name: Install ROS melodic - # run: | - # apt install -y lsb-release - # sh -c 'echo "deb http://packages.ros.org/ros/ubuntu $(lsb_release -sc) main" > /etc/apt/sources.list.d/ros-latest.list' - # curl -s https://raw.githubusercontent.com/ros/rosdistro/master/ros.asc |apt-key add - - # apt-get update - # apt-get install -y ros-melodic-catkin - # cd /opt/ros/melodic/ - # ls -a - # mkdir -p /home/carma-streets/.base-image - # echo "source /opt/ros/melodic/setup.bash" > "$INIT_ENV" - # - name: Install carma_lanelet2 - # run: | - # mkdir -p /home/carma-streets/carma_lanelet2/src - # cd /home/carma-streets/carma_lanelet2/src - # git init - # echo "temp" - # git remote add origin -f https://github.com/usdot-fhwa-stol/autoware.ai.git - # git config core.sparsecheckout true - # echo "common/hardcoded_params/*" >> .git/info/sparse-checkout - # echo "common/lanelet2_extension/*" >> .git/info/sparse-checkout - # echo "lanelet2/*" >> .git/info/sparse-checkout - # echo "mrt_cmake_modules/*" >> .git/info/sparse-checkout - # git pull --depth 1 origin refactor_lanelet2_extension - # git checkout refactor_lanelet2_extension - # rm -r lanelet2/lanelet2_python - # rm -r lanelet2/lanelet2_examples - # cd /home/carma-streets/carma_lanelet2 - # source /opt/ros/melodic/setup.bash - # apt-get install -y libeigen3-dev python-rospkg - # ROS_VERSION=1 LANELET2_EXTENSION_LOGGER_TYPE=1 catkin_make install - # cd /home/carma-streets/carma_lanelet2/install/ - # ls -a - # echo "source /home/carma-streets/carma_lanelet2/install/setup.bash" >> "$INIT_ENV" - name: Set up JDK 17 uses: actions/setup-java@v3 # The setup-java action provides the functionality for GitHub Actions runners for Downloading and setting up a requested version of Java with: @@ -152,7 +117,6 @@ jobs: echo $JAVA_HOME - name: Build run: | - source ${INIT_ENV} cd /home/carma-streets/ build-wrapper-linux-x86-64 --out-dir /home/carma-streets/bw-output ./build.sh - name: Tests diff --git a/.sonarqube/sonar-scanner.properties b/.sonarqube/sonar-scanner.properties index 7f09ea1da..53b79ea79 100644 --- a/.sonarqube/sonar-scanner.properties +++ b/.sonarqube/sonar-scanner.properties @@ -37,11 +37,9 @@ sonar.coverageReportPaths= /home/carma-streets/kafka_clients/coverage/coverage.x /home/carma-streets/streets_utils/streets_signal_optimization/coverage/coverage.xml, \ /home/carma-streets/streets_utils/streets_snmp_cmd/coverage/coverage.xml, \ /home/carma-streets/streets_utils/streets_messages/coverage/coverage.xml, \ -/home/carma-streets/streets_utils/json_utils/coverage/coverage.xml - - -# TODO : /home/carma-streets/intersection_model/coverage/coverage.xml, \ -# TODO: /home/carma-streets/message_services/coverage/coverage.xml, \ +/home/carma-streets/streets_utils/json_utils/coverage/coverage.xml, \ +/home/carma-streets/intersection_model/coverage/coverage.xml, \ +/home/carma-streets/message_services/coverage/coverage.xml #Encoding of the source code. Default is default system encoding sonar.sourceEncoding=UTF-8 @@ -81,16 +79,16 @@ streets_desired_phase_plan, \ streets_signal_optimization, \ streets_snmp_cmd, \ streets_messages, \ -json_utils +json_utils, \ +message_services, \ +intersection_model -# TODO message_services -# TODO intersection_model kafka_clients.sonar.projectBaseDir=/home/carma-streets/kafka_clients/ scheduling_service.sonar.projectBaseDir=/home/carma-streets/scheduling_service -# message_services.sonar.projectBaseDir=/home/carma-streets/message_services/ +message_services.sonar.projectBaseDir=/home/carma-streets/message_services/ signal_opt_service.sonar.projectBaseDir=/home/carma-streets/signal_opt_service/ -# intersection_model.sonar.projectBaseDir=/home/carma-streets/intersection_model/ +intersection_model.sonar.projectBaseDir=/home/carma-streets/intersection_model/ streets_service_configuration.sonar.projectBaseDir=/home/carma-streets/streets_utils/streets_service_configuration streets_service_base.sonar.projectBaseDir=/home/carma-streets/streets_utils/streets_service_base streets_vehicle_list.sonar.projectBaseDir=/home/carma-streets/streets_utils/streets_vehicle_list @@ -115,12 +113,12 @@ kafka_clients.sonar.sources =src/,include/ kafka_clients.sonar.exclusions =test/** scheduling_service.sonar.sources =src/,include/ scheduling_service.sonar.exclusions =test/** -# message_services.sonar.sources =src/,include/,lib/ -# message_services.sonar.exclusions =test/** +message_services.sonar.sources =src/,include/,lib/ +message_services.sonar.exclusions =test/** signal_opt_service.sonar.sources =src/,include/ signal_opt_service.sonar.exclusions =test/** -# intersection_model.sonar.sources =src/,include/ -# intersection_model.sonar.exclusions =src/server/**,test/** +intersection_model.sonar.sources =src/,include/ +intersection_model.sonar.exclusions =src/server/**,test/** streets_service_base.sonar.sources =src/,include/ streets_service_base.sonar.exclusions =test/** streets_service_configuration.sonar.sources =src/,include/ @@ -154,9 +152,9 @@ json_utils.sonar.exclusions =test/** # Note: For C++ setting this field does not cause test analysis to occur. It only allows the test source code to be evaluated. kafka_clients.sonar.tests=test/ scheduling_service.sonar.tests=test/ -# message_services.sonar.tests=test/ +message_services.sonar.tests=test/ signal_opt_service.sonar.tests=test/ -# intersection_model.sonar.tests=test/ +intersection_model.sonar.tests=test/ streets_service_configuration.sonar.tests=test/ streets_service_base.sonar.tests=test/ streets_vehicle_list.sonar.tests=test/ diff --git a/build.sh b/build.sh index 2ed93113c..792b458de 100755 --- a/build.sh +++ b/build.sh @@ -16,6 +16,9 @@ # script executes all kafka_clients and scheduling service build and coverage steps so that they can be singularly # wrapped by the sonarcloud build-wrapper set -e +# For lanelet aware streets services like message_services and intersection_model +source /opt/ros/melodic/setup.bash +source /opt/carma_lanelet2/setup.bash COVERAGE_FLAGS="-g --coverage -fprofile-arcs -ftest-coverage" @@ -42,20 +45,19 @@ MAKE_INSTALL_DIRS=( # only make for these subdirectories MAKE_ONLY_DIRS=( "scheduling_service" - # "intersection_model" - # "message_services" + "intersection_model" + "message_services" "signal_opt_service" "tsc_client_service" ) for DIR in "${MAKE_INSTALL_DIRS[@]}" "${MAKE_ONLY_DIRS[@]}"; do - mkdir /home/carma-streets/"$DIR"/build - cd /home/carma-streets/"$DIR"/build - cmake -DCMAKE_CXX_FLAGS="${COVERAGE_FLAGS}" -DCMAKE_C_FLAGS="${COVERAGE_FLAGS}" -DCMAKE_BUILD_TYPE="Debug" .. - make -j + cd /home/carma-streets/"$DIR" + cmake -Bbuild -DCMAKE_CXX_FLAGS="${COVERAGE_FLAGS}" -DCMAKE_C_FLAGS="${COVERAGE_FLAGS}" -DCMAKE_BUILD_TYPE="Debug" -DCMAKE_PREFIX_PATH="/opt/carma/cmake/;/opt/carma_lanelet2/" + cmake --build build for MAKE_INSTALL_DIR in "${MAKE_INSTALL_DIRS[@]}"; do if [ "$DIR" == "$MAKE_INSTALL_DIR" ]; then - make -j install + cmake --install build fi done done diff --git a/build_scripts/install_lanelet2_dependencies.sh b/build_scripts/install_lanelet2_dependencies.sh index a0d02f0e0..5c0a62ca6 100755 --- a/build_scripts/install_lanelet2_dependencies.sh +++ b/build_scripts/install_lanelet2_dependencies.sh @@ -37,7 +37,7 @@ rm -r PROJ # Download a cmake module for PROJ wget -P /usr/local/share/cmake-3.27/Modules https://raw.githubusercontent.com/mloskot/cmake-modules/master/modules/FindPROJ4.cmake - +cd /tmp # Once catkin is installed only the required lanelet2 packages will be pulled in from carma # NOTE: The lanelet2_python package requires additional dependencies that have not yet been installed so it is removed for now mkdir carma_lanelet2 @@ -59,12 +59,9 @@ git pull --depth 1 origin refactor_lanelet2_extension git checkout refactor_lanelet2_extension rm -r lanelet2/lanelet2_python rm -r lanelet2/lanelet2_examples -cd .. +cd /tmp/carma_lanelet2/ +source /opt/ros/melodic/setup.bash +ROS_VERSION=1 LANELET2_EXTENSION_LOGGER_TYPE=1 catkin_make install -DCMAKE_INSTALL_PREFIX=/opt/carma_lanelet2 -DCATKIN_DEVEL_PREFIX=/tmp/carma_lanelet2/src +rm -r /tmp/carma_lanelet2 + -# In order to trick lanelet2 into building the ROS_VERSION environment variable must be set -# In order to fully decouple lanelet2_extension from ros the LANELET2_EXTENSION_LOGGER_TYPE environment variable must be set -# shellcheck source=/dev/null -source /opt/ros/melodic/setup.bash -ROS_VERSION=1 LANELET2_EXTENSION_LOGGER_TYPE=1 catkin_make install -cd .. -rm -r carma_lanelet2 \ No newline at end of file diff --git a/build_scripts/install_rest_server_dependencies.sh b/build_scripts/install_rest_server_dependencies.sh index 5d909f629..002639bf8 100755 --- a/build_scripts/install_rest_server_dependencies.sh +++ b/build_scripts/install_rest_server_dependencies.sh @@ -14,7 +14,7 @@ DEPENDENCIES=( ) # install all things needed for deployment, always done -apt-get install -y $DEPENDENCIES +apt-get install -y "${DEPENDENCIES[@]}" cd /home/carma-streets/ext git clone https://github.com/etherealjoy/qhttpengine.git diff --git a/coverage.sh b/coverage.sh index f50ee43d3..43559741e 100755 --- a/coverage.sh +++ b/coverage.sh @@ -125,21 +125,21 @@ mkdir coverage cd /home/carma-streets/ gcovr --sonarqube streets_utils/streets_snmp_cmd/coverage/coverage.xml -s -f streets_utils/streets_snmp_cmd/ -r . -# cd /home/carma-streets/message_services/build/ -# # Currently only running a subset of message_services tests. TODO: Fix the remaining test cases. -# ./message_services_test --gtest_output=xml:../../test_results/ -# cd /home/carma-streets/message_services/ -# mkdir coverage -# cd /home/carma-streets/ -# gcovr --sonarqube message_services/coverage/coverage.xml -s -f message_services/ -r . - - -# cd /home/carma-streets/intersection_model/build/ -# ./intersection_model_test ---gtest_output=xml:../../test_results/ -# cd /home/carma-streets/intersection_model/ -# mkdir coverage -# cd /home/carma-streets/ -# gcovr --exclude=intersection_model/src/server/ --exclude=intersection_model/test/ --exclude=intersection_model/build/src/ --sonarqube intersection_model/coverage/coverage.xml -s -f intersection_model/ -r . +cd /home/carma-streets/message_services/build/ +# Currently only running a subset of message_services tests. TODO: Fix the remaining test cases. +./message_services_test --gtest_output=xml:../../test_results/ +cd /home/carma-streets/message_services/ +mkdir coverage +cd /home/carma-streets/ +gcovr --sonarqube message_services/coverage/coverage.xml -s -f message_services/ -r . + + +cd /home/carma-streets/intersection_model/build/ +./intersection_model_test ---gtest_output=xml:../../test_results/ +cd /home/carma-streets/intersection_model/ +mkdir coverage +cd /home/carma-streets/ +gcovr --exclude=intersection_model/src/server/ --exclude=intersection_model/test/ --exclude=intersection_model/build/src/ --sonarqube intersection_model/coverage/coverage.xml -s -f intersection_model/ -r . cd /home/carma-streets/signal_opt_service/build/ diff --git a/intersection_model/CMakeLists.txt b/intersection_model/CMakeLists.txt index 3b19364cd..befd6a876 100644 --- a/intersection_model/CMakeLists.txt +++ b/intersection_model/CMakeLists.txt @@ -1,10 +1,6 @@ cmake_minimum_required(VERSION 3.10.2) project(intersection_model) -link_directories( - "/usr/lib" - "/usr/local/lib" - ${catkin_LIBRARY_DIRS} - ${catkin_INCLUDE_DIRS}) + find_package(Boost COMPONENTS thread filesystem system REQUIRED) find_package(spdlog REQUIRED) add_definitions(-DSPDLOG_ACTIVE_LEVEL=SPDLOG_LEVEL_TRACE) diff --git a/message_services/CMakeLists.txt b/message_services/CMakeLists.txt index 28f54bcc5..01f453cf3 100644 --- a/message_services/CMakeLists.txt +++ b/message_services/CMakeLists.txt @@ -8,7 +8,6 @@ set(CMAKE_CXX_STANDARD 17) set(CMAKE_CXX_STANDARD_REQUIRED ON) set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fPIC") -link_directories(${catkin_LIBRARY_DIRS} ${catkin_INCLUDE_DIRS}) find_package(PROJ4) find_package(Eigen3 3.3 NO_MODULE) find_package(catkin COMPONENTS diff --git a/streets_utils/streets_signal_optimization/CMakeLists.txt b/streets_utils/streets_signal_optimization/CMakeLists.txt index aa9b0892f..357ee1e4d 100644 --- a/streets_utils/streets_signal_optimization/CMakeLists.txt +++ b/streets_utils/streets_signal_optimization/CMakeLists.txt @@ -4,6 +4,8 @@ project(streets_signal_optimization) # Find Dependent Packages and set configurations ######################################################## set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fPIC ") +# std::shared_mutex and std::map::try_emplace +set(CMAKE_CXX_STANDARD 17) find_package(spdlog REQUIRED) add_definitions(-DSPDLOG_ACTIVE_LEVEL=SPDLOG_LEVEL_TRACE) find_package(Boost COMPONENTS system filesystem thread REQUIRED) diff --git a/streets_utils/streets_snmp_cmd/CMakeLists.txt b/streets_utils/streets_snmp_cmd/CMakeLists.txt index 68609de13..bb9d082c7 100644 --- a/streets_utils/streets_snmp_cmd/CMakeLists.txt +++ b/streets_utils/streets_snmp_cmd/CMakeLists.txt @@ -4,6 +4,8 @@ project(streets_snmp_cmd) # Find Dependent Packages and set configurations ######################################################## set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fPIC") +# std::map::try_emplace +set(CMAKE_CXX_STANDARD 17) find_package(spdlog REQUIRED) find_package(GTest REQUIRED CONFIG) add_definitions(-DSPDLOG_ACTIVE_LEVEL=SPDLOG_LEVEL_TRACE) diff --git a/streets_utils/streets_vehicle_scheduler/CMakeLists.txt b/streets_utils/streets_vehicle_scheduler/CMakeLists.txt index beaef9b18..79292fd91 100644 --- a/streets_utils/streets_vehicle_scheduler/CMakeLists.txt +++ b/streets_utils/streets_vehicle_scheduler/CMakeLists.txt @@ -4,6 +4,9 @@ project(streets_vehicle_scheduler) # Find Dependent Packages and set configurations ######################################################## set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fPIC ") +# std::shared_mutex and std::map::try_emplace +set(CMAKE_CXX_STANDARD 17) + find_package(spdlog REQUIRED) find_package(RapidJSON REQUIRED) add_definitions(-DSPDLOG_ACTIVE_LEVEL=SPDLOG_LEVEL_TRACE) From c5b3fb35bb237d9b831f7a25c2527873260fc01d Mon Sep 17 00:00:00 2001 From: paulbourelly999 <77466294+paulbourelly999@users.noreply.github.com> Date: Thu, 30 Nov 2023 10:40:25 -0500 Subject: [PATCH 50/80] Initial commit for sensor data sharing service. (#363) * Initial commit for sensor data sharing service. * Create buildable sds_service * Add to sonar scanner * Updates for sdsm namespace and including lanelet depedencies. * Peer Review updates * Add sensor_data_sharing_service build to ci * Update coverage script * PR Updates * PR Updates * PR Updates * Updates * Include License Header comments * Include unit test coverage * PR Updates * Disable catkin testing to avoid rebuild gtest/gmock and linker issues * Update disable catkin testing * PR Updates --- .sonarqube/sonar-scanner.properties | 11 +- build.sh | 5 +- ...stall_streets_service_base_dependencies.sh | 4 +- coverage.sh | 6 + scripts/detected_object.json | 78 +++++++++++++ scripts/simulate_detected_object.py | 30 +++++ sensor_data_sharing_service/CMakeLists.txt | 82 +++++++++++++ sensor_data_sharing_service/Dockerfile | 17 +++ sensor_data_sharing_service/README.md | 7 ++ sensor_data_sharing_service/build.sh | 47 ++++++++ .../include/sensor_data_sharing_service.hpp | 95 +++++++++++++++ sensor_data_sharing_service/manifest.json | 30 +++++ sensor_data_sharing_service/src/main.cpp | 38 ++++++ .../src/sensor_data_sharing_service.cpp | 110 ++++++++++++++++++ .../test/sensor_data_sharing_service_test.cpp | 72 ++++++++++++ .../detected_obj_msg_deserializer.cpp | 41 ++++--- 16 files changed, 653 insertions(+), 20 deletions(-) create mode 100644 scripts/detected_object.json create mode 100644 scripts/simulate_detected_object.py create mode 100644 sensor_data_sharing_service/CMakeLists.txt create mode 100644 sensor_data_sharing_service/Dockerfile create mode 100644 sensor_data_sharing_service/README.md create mode 100755 sensor_data_sharing_service/build.sh create mode 100644 sensor_data_sharing_service/include/sensor_data_sharing_service.hpp create mode 100644 sensor_data_sharing_service/manifest.json create mode 100644 sensor_data_sharing_service/src/main.cpp create mode 100644 sensor_data_sharing_service/src/sensor_data_sharing_service.cpp create mode 100644 sensor_data_sharing_service/test/sensor_data_sharing_service_test.cpp diff --git a/.sonarqube/sonar-scanner.properties b/.sonarqube/sonar-scanner.properties index 53b79ea79..11ed0cffc 100644 --- a/.sonarqube/sonar-scanner.properties +++ b/.sonarqube/sonar-scanner.properties @@ -39,7 +39,8 @@ sonar.coverageReportPaths= /home/carma-streets/kafka_clients/coverage/coverage.x /home/carma-streets/streets_utils/streets_messages/coverage/coverage.xml, \ /home/carma-streets/streets_utils/json_utils/coverage/coverage.xml, \ /home/carma-streets/intersection_model/coverage/coverage.xml, \ -/home/carma-streets/message_services/coverage/coverage.xml +/home/carma-streets/message_services/coverage/coverage.xml, \ +/home/carma-streets/sensor_data_sharing_service/coverage/coverage.xml #Encoding of the source code. Default is default system encoding sonar.sourceEncoding=UTF-8 @@ -81,7 +82,8 @@ streets_snmp_cmd, \ streets_messages, \ json_utils, \ message_services, \ -intersection_model +intersection_model, \ +sensor_data_sharing_service kafka_clients.sonar.projectBaseDir=/home/carma-streets/kafka_clients/ @@ -103,6 +105,7 @@ streets_timing_plan.sonar.projectBaseDir=/home/carma-streets/streets_utils/stree streets_snmp_cmd.sonar.projectBaseDir=/home/carma-streets/streets_utils/streets_snmp_cmd streets_messages.sonar.projectBaseDir=/home/carma-streets/streets_utils/streets_messages json_utils.sonar.projectBaseDir=/home/carma-streets/streets_utils/json_utils +sensor_data_sharing_service.sonar.projectBaseDir=/home/carma-streets/sensor_data_sharing_service @@ -147,7 +150,8 @@ streets_messages.sonar.sources =src/,include/ streets_messages.sonar.exclusions =test/** json_utils.sonar.sources =src/,include/ json_utils.sonar.exclusions =test/** - +sensor_data_sharing_service.sonar.sources =src/,include/ +sensor_data_sharing_service.sonar.exclusions =test/** #Tests # Note: For C++ setting this field does not cause test analysis to occur. It only allows the test source code to be evaluated. kafka_clients.sonar.tests=test/ @@ -169,5 +173,6 @@ streets_timing_plan.sonar.tests=test/ streets_snmp_cmd.sonar.tests=test/ streets_messages.sonar.tests=test/ json_utils.sonar.tests=test/ +sensor_data_sharing_service.sonar.tests=test/ diff --git a/build.sh b/build.sh index 792b458de..ba3660b47 100755 --- a/build.sh +++ b/build.sh @@ -49,11 +49,14 @@ MAKE_ONLY_DIRS=( "message_services" "signal_opt_service" "tsc_client_service" + "sensor_data_sharing_service" ) for DIR in "${MAKE_INSTALL_DIRS[@]}" "${MAKE_ONLY_DIRS[@]}"; do cd /home/carma-streets/"$DIR" - cmake -Bbuild -DCMAKE_CXX_FLAGS="${COVERAGE_FLAGS}" -DCMAKE_C_FLAGS="${COVERAGE_FLAGS}" -DCMAKE_BUILD_TYPE="Debug" -DCMAKE_PREFIX_PATH="/opt/carma/cmake/;/opt/carma_lanelet2/" + # Avoid catkin rebuilding gtest/gmock executables + # Build on libraries position independent + cmake -Bbuild -DCMAKE_CXX_FLAGS="${COVERAGE_FLAGS}" -DCMAKE_C_FLAGS="${COVERAGE_FLAGS}" -DCMAKE_BUILD_TYPE="Debug" -DCMAKE_POSITION_INDEPENDENT_CODE=ON -DCATKIN_ENABLE_TESTING=OFF -DCMAKE_PREFIX_PATH="/opt/carma/cmake/;/opt/carma_lanelet2/" cmake --build build for MAKE_INSTALL_DIR in "${MAKE_INSTALL_DIRS[@]}"; do if [ "$DIR" == "$MAKE_INSTALL_DIR" ]; then diff --git a/build_scripts/install_streets_service_base_dependencies.sh b/build_scripts/install_streets_service_base_dependencies.sh index 7a29a57ab..baea02b32 100755 --- a/build_scripts/install_streets_service_base_dependencies.sh +++ b/build_scripts/install_streets_service_base_dependencies.sh @@ -31,7 +31,7 @@ cd /tmp git clone https://github.com/Tencent/rapidjson cd rapidjson/ git checkout a95e013b97ca6523f32da23f5095fcc9dd6067e5 -cmake -Bbuild +cmake -Bbuild -DCMAKE_POSITION_INDEPENDENT_CODE=ON cmake --build build cmake --install build cd .. @@ -42,7 +42,7 @@ echo " ------> Install spdlog... " cd /tmp git clone https://github.com/gabime/spdlog.git -b v1.12.0 cd spdlog -cmake -Bbuild +cmake -Bbuild -DCMAKE_POSITION_INDEPENDENT_CODE=ON cmake --build build cmake --install build cd .. diff --git a/coverage.sh b/coverage.sh index 43559741e..0d0dd70a3 100755 --- a/coverage.sh +++ b/coverage.sh @@ -156,3 +156,9 @@ mkdir coverage cd /home/carma-streets/ gcovr --sonarqube tsc_client_service/coverage/coverage.xml -s -f tsc_client_service/ -r . +cd /home/carma-streets/sensor_data_sharing_service/build/ +./sensor_data_sharing_service_test --gtest_output=xml:../../test_results/ +cd /home/carma-streets/sensor_data_sharing_service/ +mkdir coverage +cd /home/carma-streets/ +gcovr --sonarqube sensor_data_sharing_service/coverage/coverage.xml -s -f sensor_data_sharing_service/ -r . diff --git a/scripts/detected_object.json b/scripts/detected_object.json new file mode 100644 index 000000000..7a0c01e47 --- /dev/null +++ b/scripts/detected_object.json @@ -0,0 +1,78 @@ +{ + "type": "CAR", + "confidence": 0.7, + "sensorId": "sensor1", + "projString": "projection String2", + "objectId": "Object7", + "position": { + "x": -1.1, + "y": -2.0, + "z": -3.2 + }, + "positionCovariance": [ + [ + 1.0, + 0.0, + 0.0 + ], + [ + 1.0, + 0.0, + 0.0 + ], + [ + 1.0, + 0.0, + 0.0 + ] + ], + "velocity": { + "x": 1.0, + "y": 1.0, + "z": 1.0 + }, + "velocityCovariance": [ + [ + 1.0, + 0.0, + 0.0 + ], + [ + 1.0, + 0.0, + 0.0 + ], + [ + 1.0, + 0.0, + 0.0 + ] + ], + "angularVelocity": { + "x": 0.1, + "y": 0.2, + "z": 0.3 + }, + "angularVelocityCovariance": [ + [ + 1.0, + 0.0, + 0.0 + ], + [ + 1.0, + 0.0, + 0.0 + ], + [ + 1.0, + 0.0, + 0.0 + ] + ], + "size": { + "length": 2.0, + "height": 1.0, + "width": 0.5 + } + } \ No newline at end of file diff --git a/scripts/simulate_detected_object.py b/scripts/simulate_detected_object.py new file mode 100644 index 000000000..a6edf7401 --- /dev/null +++ b/scripts/simulate_detected_object.py @@ -0,0 +1,30 @@ +from os import read +from kafka import KafkaProducer +import time +from time import sleep +import json + + +def read_json(json_name): + data = {} + with open(json_name, "r") as json_file: + data = json.load(json_file) + return data + + +if __name__ == "__main__": + producer = KafkaProducer(bootstrap_servers=["127.0.0.1:9092"], + value_serializer=lambda x: json.dumps(x).encode('utf-8')) + obj = time.gmtime(0) + epoch = time.asctime(obj) + print("The epoch is:",epoch) + + # Send a new schedule + curr_time = round(time.time()*1000) + data = read_json('detected_object.json') + producer.send('detections', value=data) + print(f'Sent a detection.{data}') + producer.flush() + + + \ No newline at end of file diff --git a/sensor_data_sharing_service/CMakeLists.txt b/sensor_data_sharing_service/CMakeLists.txt new file mode 100644 index 000000000..d07d930ec --- /dev/null +++ b/sensor_data_sharing_service/CMakeLists.txt @@ -0,0 +1,82 @@ +# Copyright 2019-2023 Leidos +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http:www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +cmake_minimum_required(VERSION 3.10.2) +project(sensor_data_sharing_service LANGUAGES CXX) +# shared_mutex +set(CMAKE_CXX_STANDARD 17) +# GNU standard installation directories +include(GNUInstallDirs) +# Find Packages +find_package(spdlog REQUIRED) +find_package(RapidJSON REQUIRED) +find_package(GTest REQUIRED) +find_package(streets_messages_lib REQUIRED) +find_package(streets_service_base_lib REQUIRED) +find_package(PROJ4) +find_package(Eigen3 3.3 NO_MODULE) +find_package(catkin COMPONENTS + lanelet2_core + lanelet2_projection + lanelet2_io REQUIRED) +find_package(carma-clock REQUIRED) +# Add definition for rapidjson to include std::string +add_definitions(-DRAPIDJSON_HAS_STDSTRING=1) +add_definitions(-DSPDLOG_ACTIVE_LEVEL=SPDLOG_LEVEL_TRACE) + +set(SERVICE_NAME ${PROJECT_NAME}) +set(LIBRARY_NAME ${PROJECT_NAME}_lib) + +######################################################## +# Build Library +######################################################## +add_library(${LIBRARY_NAME} src/sensor_data_sharing_service.cpp ) +target_include_directories(${LIBRARY_NAME} + PUBLIC + $ + ${catkin_INCLUDE_DIRS} + ${catkin_LIBRARY_DIRS} +) +target_link_libraries( ${LIBRARY_NAME} + PUBLIC + spdlog::spdlog + rapidjson + streets_service_base_lib::streets_service_base_lib + streets_utils::streets_messages_lib + ${catkin_LIBRARIES} + Eigen3::Eigen + lanelet2_core +) + +add_executable(${SERVICE_NAME} src/main.cpp) + +target_link_libraries( ${SERVICE_NAME} + PUBLIC + ${LIBRARY_NAME} +) + + +######################## +# Setup Test executable +######################## +enable_testing() +set(TEST_NAME ${SERVICE_NAME}_test) +file(GLOB_RECURSE TEST_SOURCES LIST_DIRECTORIES false test/*.hpp test/*.cpp) +set(SOURCES ${SERVICE_NAME} WORKING_DIRECTORY ${PROJECT_SOURCE_DIR}/test) +add_executable(${TEST_NAME} ${TEST_SOURCES}) +target_link_libraries(${TEST_NAME} + PRIVATE + ${LIBRARY_NAME} + GTest::Main + ) + diff --git a/sensor_data_sharing_service/Dockerfile b/sensor_data_sharing_service/Dockerfile new file mode 100644 index 000000000..7c0767a19 --- /dev/null +++ b/sensor_data_sharing_service/Dockerfile @@ -0,0 +1,17 @@ +FROM usdotfhwastoldev/streets_service_base_lanelet_aware +WORKDIR /home/carma-streets/ +COPY ./sensor_data_sharing_service/ /home/carma-streets/sensor_data_sharing_service +RUN /home/carma-streets/sensor_data_sharing_service/build.sh +LABEL org.label-schema.schema-version="1.0" +LABEL org.label-schema.name="sensor_data_sharing_service" +LABEL org.label-schema.description="Image for Sensor Data Sharing Service" +LABEL org.label-schema.vendor="Leidos" +LABEL org.label-schema.version="${VERSION}" +LABEL org.label-schema.url="https://highways.dot.gov/research/research-programs/operations" +LABEL org.label-schema.vcs-url="https://github.com/usdot-fhwa-stol/carma-streets" +LABEL org.label-schema.vcs-ref=${VCS_REF} +LABEL org.label-schema.build-date=${BUILD_DATE} + + + +CMD ["/home/carma-streets/sensor_data_sharing_service/build/sensor_data_sharing_service"] \ No newline at end of file diff --git a/sensor_data_sharing_service/README.md b/sensor_data_sharing_service/README.md new file mode 100644 index 000000000..d7e990125 --- /dev/null +++ b/sensor_data_sharing_service/README.md @@ -0,0 +1,7 @@ +# Sensor Data Sharing Service +## Introduction +This is the **CARMA Streets** service responsible for consuming [detected_objects_msg](../streets_utils/streets_messages/DetectedObjectsMessage.md) and publishing [sensor_data_sharing_msg](../streets_utils/streets_messages/SensorDataSharingMessage.md) in accordance with the J3224 specification. This service is built on the [streets_service_base_lanelet_aware](../streets_service_base_lanelet_aware/README.md) base image to include lanelet2 dependencies used for map coordinate frame translations. +## Configuration Parameters +**TODO** Saving this part for once I finalize the implementation +> [!IMPORTANT]\ +> Initial implementation of this service will not support the BSM functionality described in the J3224 specification and will also not support any data fusion from multiple infrastructure sensors. \ No newline at end of file diff --git a/sensor_data_sharing_service/build.sh b/sensor_data_sharing_service/build.sh new file mode 100755 index 000000000..7513459e1 --- /dev/null +++ b/sensor_data_sharing_service/build.sh @@ -0,0 +1,47 @@ +#!/bin/bash +# Copyright (C) 2018-2020 LEIDOS. +# +# Licensed under the Apache License, Version 2.0 (the "License"); you may not +# use this file except in compliance with the License. You may obtain a copy of +# the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations under +# the License. + +# Build script to build Sensor Data Sharing Service +set -e +# Source ros and lanelet2 for lanelet aware services +source /opt/ros/melodic/setup.bash +source /opt/carma_lanelet2/setup.bash + +COVERAGE_FLAGS="" + +# make install for these subdirectories +MAKE_INSTALL_DIRS=( + "streets_utils/json_utils" + "streets_utils/streets_messages" + "streets_utils/streets_service_configuration" + "kafka_clients" + "streets_utils/streets_service_base" +) + +# only make for these subdirectories +MAKE_ONLY_DIRS=( + "sensor_data_sharing_service" +) + +for DIR in "${MAKE_INSTALL_DIRS[@]}" "${MAKE_ONLY_DIRS[@]}"; do + cd /home/carma-streets/"$DIR" + cmake -Bbuild -DCMAKE_CXX_FLAGS="${COVERAGE_FLAGS}" -DCMAKE_C_FLAGS="${COVERAGE_FLAGS}" -DCMAKE_POSITION_INDEPENDENT_CODE=ON -DCATKIN_ENABLE_TESTING=OFF -DCMAKE_BUILD_TYPE="Debug" -DCMAKE_PREFIX_PATH="/opt/carma/cmake/;/opt/carma_lanelet2/" + cmake --build build + for MAKE_INSTALL_DIR in "${MAKE_INSTALL_DIRS[@]}"; do + if [ "$DIR" == "$MAKE_INSTALL_DIR" ]; then + cmake --install build + fi + done +done \ No newline at end of file diff --git a/sensor_data_sharing_service/include/sensor_data_sharing_service.hpp b/sensor_data_sharing_service/include/sensor_data_sharing_service.hpp new file mode 100644 index 000000000..90979a00a --- /dev/null +++ b/sensor_data_sharing_service/include/sensor_data_sharing_service.hpp @@ -0,0 +1,95 @@ +// Copyright 2023 Leidos +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +#pragma once + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + + +namespace sensor_data_sharing_service { + + class sds_service : public streets_service::streets_service { + private: + /** + * @brief Kafka producer for SDSM JSON + */ + std::shared_ptr sdsm_producer; + /* + * @brief Kafka consumer for consuming Detected Object JSON + */ + std::shared_ptr detection_consumer; + /** + * @brief Map of detected objects. New detections of existing objects will replace old detections. + */ + std::map> detected_objects; + /** + * @brief Mutex for thread safe operations on detected objects map. + */ + std::shared_mutex detected_objects_lock; + /** + * @brief Initialize Kafka consumers and producers for sensor data sharing service. + * @return true if successful and false if unsuccessful. + */ + bool initialize_kafka_consumers_producers( ); + /** + * @brief Loop to consume detections from kafka detection consumer. Will terminate if kafka consumer + * is no longer running + * + * @throws std::runtime exception if detection_consumer == nullptr + */ + void consume_detections(); + /** + * @brief Loop to produce sensor data sharing messages from detected_objects. Loop will populate sensor data sharing message with + * most recent detection information, publish message and clear detected object map. Will terminate if kafka producer is no longer + * running. + * + * @throws std::runtime exception if sdsm_producer == nullptr + */ + void produce_sdsms(); + + + + + public: + sds_service() = default; + + ~sds_service(); + + /** + * @brief Method to initialize the sds_service. + * + * @return true if successful. + * @return false if not successful. + */ + bool initialize() override; + + /** + * @brief Method to start all threads included in the tsc_service. + */ + void start() override; + + FRIEND_TEST(sensorDataSharingServiceTest, consumeDetections); + FRIEND_TEST(sensorDataSharingServiceTest, produceSdsms); + }; +} \ No newline at end of file diff --git a/sensor_data_sharing_service/manifest.json b/sensor_data_sharing_service/manifest.json new file mode 100644 index 000000000..5093e62d2 --- /dev/null +++ b/sensor_data_sharing_service/manifest.json @@ -0,0 +1,30 @@ +{ + "service_name": "sensor_data_sharing_service", + "loglevel": "debug", + "configurations": [ + { + "name": "sdsm_producer_topic", + "value": "sdsm", + "description": "Kafka topic to which the sensor data sharing service will produce SDSMs.", + "type": "STRING" + }, + { + "name": "detection_consumer_topic", + "value": "detections", + "description":"Kafka topic from which the sensor data sharing service will consume Detected Objects.", + "type": "STRING" + }, + { + "name": "sensor_configuration_file_path", + "value": "/home/carma-streets/sensor_configurations/sensors.json", + "description": "Path to sensor configuration information that includes sensor location and orientation.", + "type": "STRING" + }, + { + "name": "bootstrap_server", + "value": "127.0.0.1:9092", + "description": "Kafka Broker Server Address.", + "type": "STRING" + } + ] +} \ No newline at end of file diff --git a/sensor_data_sharing_service/src/main.cpp b/sensor_data_sharing_service/src/main.cpp new file mode 100644 index 000000000..09d151ed2 --- /dev/null +++ b/sensor_data_sharing_service/src/main.cpp @@ -0,0 +1,38 @@ +// Copyright 2023 Leidos +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +#include +#include +#include +#include "streets_configuration.h" +#include "sensor_data_sharing_service.hpp" + +int main(int argc, char **argv) +{ + + sensor_data_sharing_service::sds_service service; + try { + if (service.initialize()){ + service.start(); + } + else { + SPDLOG_ERROR("TSC Service Initialization failed!"); + } + } + catch ( const std::exception &e) { + SPDLOG_ERROR("Exception Encountered : {0}" , e.what()); + exit(1); + } + + return 0; +} \ No newline at end of file diff --git a/sensor_data_sharing_service/src/sensor_data_sharing_service.cpp b/sensor_data_sharing_service/src/sensor_data_sharing_service.cpp new file mode 100644 index 000000000..329cf94d3 --- /dev/null +++ b/sensor_data_sharing_service/src/sensor_data_sharing_service.cpp @@ -0,0 +1,110 @@ +// Copyright 2023 Leidos +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +#include "sensor_data_sharing_service.hpp" + +namespace sensor_data_sharing_service { + + namespace ss = streets_service; + namespace sdsm = streets_utils::messages::sdsm; + namespace detected_objects_message = streets_utils::messages::detected_objects_msg; + sds_service::~sds_service() { + + if (sdsm_producer) + { + SPDLOG_WARN("Stopping SDSM producer!"); + sdsm_producer->stop(); + } + + if(detection_consumer) + { + SPDLOG_WARN("Stopping Detection consumer!"); + detection_consumer->stop(); + } + } + + bool sds_service::initialize() { + if (!streets_service::initialize()) { + return false; + } + SPDLOG_DEBUG("Intializing Sensor Data Sharing Service"); + + // Initialize SDSM Kafka producer + std::string sdsm_topic = ss::streets_configuration::get_string_config("sdsm_producer_topic"); + std::string detection_topic = ss::streets_configuration::get_string_config("detection_consumer_topic"); + + return initialize_kafka_producer(sdsm_topic, sdsm_producer) && initialize_kafka_consumer(detection_topic, detection_consumer); + } + + void sds_service::consume_detections(){ + if ( !detection_consumer ) { + throw std::runtime_error("Detection consumer is null!"); + } + SPDLOG_DEBUG("Attempting to consume detections ..."); + detection_consumer->subscribe(); + while ( detection_consumer->is_running() ) { + try{ + const std::string payload = detection_consumer->consume(1000); + if (payload.length() != 0) + { + auto detected_object = streets_utils::messages::detected_objects_msg::from_json(payload); + std::unique_lock lock(detected_objects_lock); + detected_objects[detected_object._object_id] = detected_object; + SPDLOG_DEBUG("Detected Object List Size {0} after consumed: {1}", detected_objects.size(), payload); + + } + } + catch (const streets_utils::json_utils::json_parse_exception &e) { + SPDLOG_ERROR("Exception occured consuming detection message : {0}", e.what()); + } + } + SPDLOG_ERROR("Something went wrong, no longer consuming detections." ); + + } + + void sds_service::produce_sdsms() { + if ( !sdsm_producer ) { + throw std::runtime_error("SDSM consumer is null!"); + } + SPDLOG_INFO("Starting SDSM Producer!"); + while ( sdsm_producer->is_running() ) { + try{ + if ( !detected_objects.empty() ) { + std::unique_lock lock(detected_objects_lock); + streets_utils::messages::sdsm::sensor_data_sharing_msg msg; + // TODO: Populate SDSM with detected objects + const std::string json_msg = streets_utils::messages::sdsm::to_json(msg); + SPDLOG_DEBUG("Sending SDSM : {0}", json_msg); + sdsm_producer->send(json_msg); + // Clear detected object + detected_objects.clear(); + } + } + catch( const streets_utils::json_utils::json_parse_exception &e) { + SPDLOG_ERROR("Exception occurred producing SDSM : {0}", e.what()); + } + ss::streets_clock_singleton::sleep_for(1000); // Sleep for 10 second between publish + } + + } + + + void sds_service::start() { + SPDLOG_DEBUG("Starting Sensor Data Sharing Service"); + streets_service::start(); + std::thread detection_thread(&sds_service::consume_detections, this); + std::thread sdsm_thread(&sds_service::produce_sdsms, this); + detection_thread.join(); + sdsm_thread.join(); + } +} \ No newline at end of file diff --git a/sensor_data_sharing_service/test/sensor_data_sharing_service_test.cpp b/sensor_data_sharing_service/test/sensor_data_sharing_service_test.cpp new file mode 100644 index 000000000..b969c6400 --- /dev/null +++ b/sensor_data_sharing_service/test/sensor_data_sharing_service_test.cpp @@ -0,0 +1,72 @@ +// Copyright 2019-2023 Leidos +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + + +using testing::_; +using testing::Return; +using testing::AnyNumber; +namespace sensor_data_sharing_service { + + TEST(sensorDataSharingServiceTest, consumeDetections) { + + sds_service serv; + // If consumer null expect runtime error + EXPECT_THROW(serv.consume_detections(), std::runtime_error); + + serv.detection_consumer = std::make_shared(); + EXPECT_CALL(dynamic_cast(*serv.detection_consumer),subscribe()).Times(1); + EXPECT_CALL(dynamic_cast(*serv.detection_consumer),is_running()).Times(4).WillOnce(Return(true)) + .WillOnce(Return(true)) + .WillOnce(Return(true)) + .WillRepeatedly(Return(false)); + EXPECT_CALL(dynamic_cast(*serv.detection_consumer), consume(_)).Times(3).WillOnce(Return("")) + .WillOnce(Return("NOT JSON")) + .WillOnce(Return( + "{\"type\":\"CAR\",\"confidence\":0.7,\"sensorId\":\"sensor1\",\"projString\":\"projectionString2\",\"objectId\":\"Object7\",\"position\":{\"x\":-1.1,\"y\":-2.0,\"z\":-3.2},\"positionCovariance\":[[1.0,0.0,0.0],[1.0,0.0,0.0],[1.0,0.0,0.0]],\"velocity\":{\"x\":1.0,\"y\":1.0,\"z\":1.0},\"velocityCovariance\":[[1.0,0.0,0.0],[1.0,0.0,0.0],[1.0,0.0,0.0]],\"angularVelocity\":{\"x\":0.1,\"y\":0.2,\"z\":0.3},\"angularVelocityCovariance\":[[1.0,0.0,0.0],[1.0,0.0,0.0],[1.0,0.0,0.0]],\"size\":{\"length\":2.0,\"height\":1.0,\"width\":0.5}}" + )); + serv.consume_detections(); + EXPECT_EQ(serv.detected_objects.size(), 1); + } + + TEST(sensorDataSharingServiceTest, produceSdsms) { + + sds_service serv; + // Initialize streets_clock in non simulation mode + streets_service::streets_clock_singleton::create(false); + // If producer null expect runtime error + + EXPECT_THROW(serv.produce_sdsms(), std::runtime_error); + const std::string detected_object_json ="{\"type\":\"CAR\",\"confidence\":0.7,\"sensorId\":\"sensor1\",\"projString\":\"projectionString2\",\"objectId\":\"Object7\",\"position\":{\"x\":-1.1,\"y\":-2.0,\"z\":-3.2},\"positionCovariance\":[[1.0,0.0,0.0],[1.0,0.0,0.0],[1.0,0.0,0.0]],\"velocity\":{\"x\":1.0,\"y\":1.0,\"z\":1.0},\"velocityCovariance\":[[1.0,0.0,0.0],[1.0,0.0,0.0],[1.0,0.0,0.0]],\"angularVelocity\":{\"x\":0.1,\"y\":0.2,\"z\":0.3},\"angularVelocityCovariance\":[[1.0,0.0,0.0],[1.0,0.0,0.0],[1.0,0.0,0.0]],\"size\":{\"length\":2.0,\"height\":1.0,\"width\":0.5}}"; + auto detected_object = streets_utils::messages::detected_objects_msg::from_json(detected_object_json); + serv.detected_objects[detected_object._object_id] =detected_object; + serv.sdsm_producer = std::make_shared(); + EXPECT_CALL(dynamic_cast(*serv.sdsm_producer),is_running()).Times(3).WillOnce(Return(true)) + .WillOnce(Return(true)) + .WillRepeatedly(Return(false)); + const std::string sdsm_json ="{\"msg_cnt\":0,\"source_id\":\"\",\"equipment_type\":0,\"sdsm_time_stamp\":{\"second\":0,\"minute\":0,\"hour\":0,\"day\":0,\"month\":0,\"year\":0,\"offset\":0},\"ref_pos\":{\"long\":0,\"lat\":0},\"ref_pos_xy_conf\":{\"semi_major\":0,\"semi_minor\":0,\"orientation\":0},\"objects\":[]}"; + EXPECT_CALL(dynamic_cast(*serv.sdsm_producer), send(sdsm_json)).Times(1); + serv.produce_sdsms(); + EXPECT_EQ(serv.detected_objects.size(), 0); + } +} \ No newline at end of file diff --git a/streets_utils/streets_messages/src/deserializers/detected_obj_msg_deserializer.cpp b/streets_utils/streets_messages/src/deserializers/detected_obj_msg_deserializer.cpp index 19bb32e42..e4dab8aa3 100644 --- a/streets_utils/streets_messages/src/deserializers/detected_obj_msg_deserializer.cpp +++ b/streets_utils/streets_messages/src/deserializers/detected_obj_msg_deserializer.cpp @@ -55,21 +55,30 @@ namespace streets_utils::messages::detected_objects_msg { } cartesian_point parse_cartesian_3d(const rapidjson::Value &val) - { + { cartesian_point point; - point._x = streets_utils::json_utils::parse_double_member("x", val, true).value(); - point._y = streets_utils::json_utils::parse_double_member("y", val, true).value(); - point._z = streets_utils::json_utils::parse_double_member("z", val, true).value(); - return point; + try { + point._x = streets_utils::json_utils::parse_double_member("x", val, true).value(); + point._y = streets_utils::json_utils::parse_double_member("y", val, true).value(); + point._z = streets_utils::json_utils::parse_double_member("z", val, true).value(); + return point; + } + catch (const streets_utils::json_utils::json_parse_exception &e) { + throw streets_utils::json_utils::json_parse_exception("Parsing error occured during parsing of cartesian_3d: " + std::string(e.what())); + } } vector_3d parse_vector3d(const rapidjson::Value &val) { vector_3d vector; - vector._x = streets_utils::json_utils::parse_double_member("x", val, true).value(); - vector._y = streets_utils::json_utils::parse_double_member("y", val, true).value(); - vector._z = streets_utils::json_utils::parse_double_member("z", val, true).value(); - + try { + vector._x = streets_utils::json_utils::parse_double_member("x", val, true).value(); + vector._y = streets_utils::json_utils::parse_double_member("y", val, true).value(); + vector._z = streets_utils::json_utils::parse_double_member("z", val, true).value(); + } + catch (const streets_utils::json_utils::json_parse_exception &e) { + throw streets_utils::json_utils::json_parse_exception("Parsing error occured during parsing of vector3d: " + std::string(e.what()) ); + } return vector; } @@ -94,12 +103,16 @@ namespace streets_utils::messages::detected_objects_msg { } size parse_size(const rapidjson::Value &val) - { + { size size_obj; - size_obj._length = streets_utils::json_utils::parse_double_member("length", val, true).value(); - size_obj._height = streets_utils::json_utils::parse_double_member("height", val, true).value(); - size_obj._width = streets_utils::json_utils::parse_double_member("width", val, true).value(); - + try { + size_obj._length = streets_utils::json_utils::parse_double_member("length", val, true).value(); + size_obj._height = streets_utils::json_utils::parse_double_member("height", val, true).value(); + size_obj._width = streets_utils::json_utils::parse_double_member("width", val, true).value(); + } + catch (const streets_utils::json_utils::json_parse_exception &e) { + throw streets_utils::json_utils::json_parse_exception("Parsing error occured during parsing of size: " + std::string(e.what())); + } return size_obj; } From c744bb26b1a8f3056b93818d6e6d32c73b56b464 Mon Sep 17 00:00:00 2001 From: paulbourelly999 <77466294+paulbourelly999@users.noreply.github.com> Date: Fri, 1 Dec 2023 09:44:37 -0500 Subject: [PATCH 51/80] Sensor Data Sharing Service Docker Image Build (#372) * Sensor Data Sharing Service Docker Image Build * Update name * Update CI * Update docker-compose to include new service * Update badge --- .../workflows/{ci.yml => develop_master.yml} | 4 +- .github/workflows/feature_branch.yml | 99 +++++++++++++++++++ README.md | 6 +- docker-compose.yml | 22 +++++ sensor_data_sharing_service/CMakeLists.txt | 2 - sensor_data_sharing_service/Dockerfile | 5 +- 6 files changed, 128 insertions(+), 10 deletions(-) rename .github/workflows/{ci.yml => develop_master.yml} (98%) create mode 100644 .github/workflows/feature_branch.yml diff --git a/.github/workflows/ci.yml b/.github/workflows/develop_master.yml similarity index 98% rename from .github/workflows/ci.yml rename to .github/workflows/develop_master.yml index 8f8acfac6..240292443 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/develop_master.yml @@ -1,7 +1,5 @@ -name: CI +name: develop_master on: - pull_request: - types: [opened, synchronize, reopened] push: branches: [develop, master] jobs: diff --git a/.github/workflows/feature_branch.yml b/.github/workflows/feature_branch.yml new file mode 100644 index 000000000..08e2bbe6e --- /dev/null +++ b/.github/workflows/feature_branch.yml @@ -0,0 +1,99 @@ +name: feature_branch +on: + pull_request: + types: [opened, synchronize, reopened] +jobs: + build: + defaults: + run: + shell: bash + runs-on: ubuntu-latest-8-cores + container: + image: usdotfhwastoldev/streets_service_base_lanelet_aware:bionic + env: + DEBIAN_FRONTEND: noninteractive + SONAR_SCANNER_VERSION: "5.0.1.3006" + TERM: xterm + options: "--user root" + steps: + # Bionic's git version is not sufficient for actions/checkout 0 fetch-depth, + # remove this step after rebasing carma-streets to newer Ubuntu release + - name: Install newer git for checkout + run: | + apt-get update + apt-get install -y software-properties-common + add-apt-repository -u ppa:git-core/ppa + apt-get install -y git + - name: Checkout ${{ github.event.repository.name }} + uses: actions/checkout@v3.3.0 + with: + path: ${{ github.event.repository.name }} + fetch-depth: 0 + - name: Move source code + run: mv $GITHUB_WORKSPACE/${{ github.event.repository.name }} /home/carma-streets + - name: Install dependencies + run: | + cd /home/carma-streets/build_scripts + ./install_test_dependencies.sh + mkdir -p /home/carma-streets/ext + ./install_rest_server_dependencies.sh + - name: Install net-snmp + run: | + cd /home/carma-streets/ext/ + apt-get install -y libperl-dev curl + curl -k -L -O http://sourceforge.net/projects/net-snmp/files/net-snmp/5.9.1/net-snmp-5.9.1.tar.gz + tar -xvzf /home/carma-streets/ext/net-snmp-5.9.1.tar.gz + cd net-snmp-5.9.1/ + ./configure --with-default-snmp-version="1" --with-sys-contact="@@no.where" --with-sys-location="Unknown" --with-logfile="/var/log/snmpd.log" --with-persistent-directory="/var/net-snmp" + make -j + make install + - name: Set up JDK 17 + uses: actions/setup-java@v3 # The setup-java action provides the functionality for GitHub Actions runners for Downloading and setting up a requested version of Java + with: + java-version: 17 + distribution: "temurin" + - name: Install Sonar + run: | + SONAR_DIR=/opt/sonarqube + mkdir $SONAR_DIR + curl -o $SONAR_DIR/sonar-scanner.zip https://binaries.sonarsource.com/Distribution/sonar-scanner-cli/sonar-scanner-cli-${SONAR_SCANNER_VERSION}-linux.zip + curl -o $SONAR_DIR/build-wrapper.zip https://sonarcloud.io/static/cpp/build-wrapper-linux-x86.zip + curl -sL https://deb.nodesource.com/setup_16.x | bash - + apt-get install -y nodejs unzip + # Set the JAVA_HOME to a compatible version of Java, e.g., Java 17 + export JAVA_HOME=$GITHUB_WORKSPACE/java-17 + cd $SONAR_DIR + for ZIP in *.zip; do + unzip "$ZIP" -d . + rm "$ZIP" + done + mv $(ls $SONAR_DIR | grep "sonar-scanner-") $SONAR_DIR/sonar-scanner/ + mv $(ls $SONAR_DIR | grep "build-wrapper-") $SONAR_DIR/build-wrapper/ + echo $SONAR_DIR/sonar-scanner/bin >> $GITHUB_PATH + echo $SONAR_DIR/build-wrapper >> $GITHUB_PATH + env: + JAVA_HOME: $GITHUB_WORKSPACE/java-17 + + - name: Check Java Version + run: | + java -version + echo $JAVA_HOME + - name: Build + run: | + cd /home/carma-streets/ + build-wrapper-linux-x86-64 --out-dir /home/carma-streets/bw-output ./build.sh + - name: Tests + run: | + cd /home/carma-streets/ + ldconfig + ./coverage.sh + - name: Archive test results + uses: actions/upload-artifact@v3 + with: + name: Test Results + path: /home/carma-streets/test_results + - name: Run SonarScanner + uses: usdot-fhwa-stol/actions/sonar-scanner@main + with: + sonar-token: ${{ secrets.SONAR_TOKEN }} + working-dir: /home/carma-streets diff --git a/README.md b/README.md index 77b1dde63..c8d37b940 100644 --- a/README.md +++ b/README.md @@ -10,9 +10,9 @@ |----|----|----|----|----| [![Docker Cloud Build Status](https://img.shields.io/docker/cloud/build/usdotfhwastolcandidate/scheduling_service?label=tsc%20service&logoColor=%232496ED)](https://hub.docker.com/repository/docker/usdotfhwastolcandidate/scheduling_service) | [![Docker Cloud Build Status](https://img.shields.io/docker/cloud/build/usdotfhwastolcandidate/message_services?label=message%20services)](https://hub.docker.com/repository/docker/usdotfhwastolcandidate/message_services) | [ ![Docker Cloud Build Status](https://img.shields.io/docker/cloud/build/usdotfhwastolcandidate/intersection_model?label=intersection%20model)](https://hub.docker.com/repository/docker/usdotfhwastolcandidate/intersection_model) | [![Docker Cloud Build Status](https://img.shields.io/docker/cloud/build/usdotfhwastolcandidate/signal_opt_service?label=signal%20opt%20service)](https://hub.docker.com/repository/docker/usdotfhwastolcandidate/signal_opt_service) | [![Docker Cloud Build Status](https://img.shields.io/docker/cloud/build/usdotfhwastolcandidate/tsc_service?label=tsc%20service&logoColor=%232496ED)](https://hub.docker.com/repository/docker/usdotfhwastolcandidate/tsc_service) | # DockerHub Develop Builds -| Scheduling Service | Message Services | Intersection Model | Signal Opt Service | Tsc Service | -|-----|-----|-----|-----|-----| -[![Docker Cloud Build Status](https://img.shields.io/docker/cloud/build/usdotfhwastoldev/scheduling_service?label=scheduling%20service)](https://hub.docker.com/repository/docker/usdotfhwastoldev/scheduling_service) | [![Docker Cloud Build Status](https://img.shields.io/docker/cloud/build/usdotfhwastoldev/message_services?label=message%20services)](https://hub.docker.com/repository/docker/usdotfhwastoldev/message_services) | [ ![Docker Cloud Build Status](https://img.shields.io/docker/cloud/build/usdotfhwastoldev/intersection_model?label=intersection%20model)](https://hub.docker.com/repository/docker/usdotfhwastoldev/intersection_model) | [![Docker Cloud Build Status](https://img.shields.io/docker/cloud/build/usdotfhwastoldev/signal_opt_service?label=signal%20opt%20service)](https://hub.docker.com/repository/docker/usdotfhwastoldev/signal_opt_service) | [![Docker Cloud Build Status](https://img.shields.io/docker/cloud/build/usdotfhwastoldev/tsc_service?label=tsc%20service&logoColor=%232496ED)](https://hub.docker.com/repository/docker/usdotfhwastoldev/tsc_service) | +| Scheduling Service | Message Services | Intersection Model | Signal Opt Service | Tsc Service | Sensor Data Sharing Service +|-----|-----|-----|-----|-----|-----| +[![Docker Cloud Build Status](https://img.shields.io/docker/cloud/build/usdotfhwastoldev/scheduling_service?label=scheduling%20service)](https://hub.docker.com/repository/docker/usdotfhwastoldev/scheduling_service) | [![Docker Cloud Build Status](https://img.shields.io/docker/cloud/build/usdotfhwastoldev/message_services?label=message%20services)](https://hub.docker.com/repository/docker/usdotfhwastoldev/message_services) | [ ![Docker Cloud Build Status](https://img.shields.io/docker/cloud/build/usdotfhwastoldev/intersection_model?label=intersection%20model)](https://hub.docker.com/repository/docker/usdotfhwastoldev/intersection_model) | [![Docker Cloud Build Status](https://img.shields.io/docker/cloud/build/usdotfhwastoldev/signal_opt_service?label=signal%20opt%20service)](https://hub.docker.com/repository/docker/usdotfhwastoldev/signal_opt_service) | [![Docker Cloud Build Status](https://img.shields.io/docker/cloud/build/usdotfhwastoldev/tsc_service?label=tsc%20service&logoColor=%232496ED)](https://hub.docker.com/repository/docker/usdotfhwastoldev/tsc_service) | ![Docker Cloud Build Status](https://img.shields.io/docker/cloud/build/usdotfhwastoldev/sensor_data_sharing_service?label=sensor_data_sharing_%20service&logoColor=%232496ED)| # CARMA Streets Base Image Builds [![Build and Push Base Images](https://github.com/usdot-fhwa-stol/carma-streets/actions/workflows/build_streets_base_images.yml/badge.svg)](https://github.com/usdot-fhwa-stol/carma-streets/actions/workflows/build_streets_base_images.yml) diff --git a/docker-compose.yml b/docker-compose.yml index 4671af0b6..19ff7c240 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -208,6 +208,28 @@ services: - ./tsc_client_service/logs/:/home/carma-streets/tsc_client_service/logs/ - /etc/localtime:/etc/localtime:ro - /etc/timezone:/etc/timezone:ro + sensor_data_sharing_service: + image: usdotfhwastoldev/sensor_data_sharing_service:develop + build: + context: . + dockerfile: sensor_data_sharing_service/Dockerfile + container_name: sensor_data_sharing_service + restart: always + network_mode: host + depends_on: + - kafka + environment: + SIMULATION_MODE: FALSE + CONFIG_FILE_PATH: ../manifest.json + TIME_SYNC_TOPIC: time_sync + LOGS_DIRECTORY: /home/carma-streets/sensor_data_sharing_service/logs/ + LANELET2_MAP: /home/carma-streets/MAP/Intersection.osm + volumes: + - ./sensor_data_sharing_service/manifest.json:/home/carma-streets/sensor_data_sharing_service/manifest.json + - ./sensor_data_sharing_service/logs/:/home/carma-streets/sensor_data_sharing_service/logs/ + - ./MAP/:/home/carma-streets/intersection_model/MAP/ + - /etc/localtime:/etc/localtime:ro + - /etc/timezone:/etc/timezone:ro secrets: mysql_password: file: ./secrets/mysql_password.txt diff --git a/sensor_data_sharing_service/CMakeLists.txt b/sensor_data_sharing_service/CMakeLists.txt index d07d930ec..c7c7cf1f8 100644 --- a/sensor_data_sharing_service/CMakeLists.txt +++ b/sensor_data_sharing_service/CMakeLists.txt @@ -45,7 +45,6 @@ target_include_directories(${LIBRARY_NAME} PUBLIC $ ${catkin_INCLUDE_DIRS} - ${catkin_LIBRARY_DIRS} ) target_link_libraries( ${LIBRARY_NAME} PUBLIC @@ -55,7 +54,6 @@ target_link_libraries( ${LIBRARY_NAME} streets_utils::streets_messages_lib ${catkin_LIBRARIES} Eigen3::Eigen - lanelet2_core ) add_executable(${SERVICE_NAME} src/main.cpp) diff --git a/sensor_data_sharing_service/Dockerfile b/sensor_data_sharing_service/Dockerfile index 7c0767a19..c72be2e93 100644 --- a/sensor_data_sharing_service/Dockerfile +++ b/sensor_data_sharing_service/Dockerfile @@ -1,6 +1,7 @@ -FROM usdotfhwastoldev/streets_service_base_lanelet_aware -WORKDIR /home/carma-streets/ +FROM usdotfhwastoldev/streets_service_base_lanelet_aware:bionic COPY ./sensor_data_sharing_service/ /home/carma-streets/sensor_data_sharing_service +COPY ./streets_utils/ /home/carma-streets/streets_utils +COPY ./kafka_clients/ /home/carma-streets/kafka_clients RUN /home/carma-streets/sensor_data_sharing_service/build.sh LABEL org.label-schema.schema-version="1.0" LABEL org.label-schema.name="sensor_data_sharing_service" From 5f49bdceec28bd531269aabb1c2f65c627cb51be Mon Sep 17 00:00:00 2001 From: paulbourelly999 <77466294+paulbourelly999@users.noreply.github.com> Date: Mon, 11 Dec 2023 17:17:35 -0500 Subject: [PATCH 52/80] Feature/sdsm generation (#373) * Initial commit * Updates * Update unit tests * Update build script to not source ros or lanelet2 setup * Updates for processing detection messages * Update to coverage script for running lanelet2 depedent tests Update to resolve sonar scanner report bugs * Function for translating object type * Sonar Scanner code smells addressed * Added unit tests and fixed issues * Update unit test * Add license header * Update docker-compose to include sensor config file and volu,e * PR Updates * PR Updates * Updated Unit tests * Unit test updates * Updates * PR Updates * PR Updates * Fix timestamp bug * Update * Updates --- coverage.sh | 4 + docker-compose.yml | 10 ++- scripts/detected_object.json | 4 +- sensor_data_sharing_service/CMakeLists.txt | 44 ++++++--- sensor_data_sharing_service/build.sh | 6 +- .../detected_object_to_sdsm_converter.hpp | 63 +++++++++++++ .../include/sensor_configuration_parser.hpp | 47 ++++++++++ .../include/sensor_data_sharing_service.hpp | 59 +++++++++++- sensor_data_sharing_service/manifest.json | 12 +++ .../src/detected_object_to_sdsm_converter.cpp | 89 +++++++++++++++++++ .../src/sensor_configuration_parser.cpp | 45 ++++++++++ .../src/sensor_data_sharing_service.cpp | 74 +++++++++++++-- ...detected_object_to_sdsm_converter_test.cpp | 71 +++++++++++++++ .../test/sensor_configuration_parser_test.cpp | 26 ++++++ .../test/sensor_data_sharing_service_test.cpp | 72 ++++++++++++++- .../test/test_files/sensors.json | 16 ++++ .../include/json_document_parse_error.hpp | 13 +++ .../json_utils/include/json_utils.hpp | 12 +++ streets_utils/json_utils/src/json_utils.cpp | 18 ++++ .../json_utils/test/json_utils_test.cpp | 21 +++++ .../json_utils/test/test_files/invalid.json | 16 ++++ .../json_utils/test/test_files/valid.json | 16 ++++ ...sor_data_sharing_msg_json_deserializer.cpp | 27 +++--- ...ensor_data_sharing_msg_json_serializer.cpp | 6 +- 24 files changed, 723 insertions(+), 48 deletions(-) create mode 100644 sensor_data_sharing_service/include/detected_object_to_sdsm_converter.hpp create mode 100644 sensor_data_sharing_service/include/sensor_configuration_parser.hpp create mode 100644 sensor_data_sharing_service/src/detected_object_to_sdsm_converter.cpp create mode 100644 sensor_data_sharing_service/src/sensor_configuration_parser.cpp create mode 100644 sensor_data_sharing_service/test/detected_object_to_sdsm_converter_test.cpp create mode 100644 sensor_data_sharing_service/test/sensor_configuration_parser_test.cpp create mode 100644 sensor_data_sharing_service/test/test_files/sensors.json create mode 100644 streets_utils/json_utils/include/json_document_parse_error.hpp create mode 100644 streets_utils/json_utils/test/test_files/invalid.json create mode 100644 streets_utils/json_utils/test/test_files/valid.json diff --git a/coverage.sh b/coverage.sh index 0d0dd70a3..20c5be619 100755 --- a/coverage.sh +++ b/coverage.sh @@ -16,6 +16,10 @@ # script to run tests, generate test-coverage, and store coverage reports in a place # easily accessible to sonar. Test names should follow convention runTests +# For lanelet aware streets services like message_services and intersection_model +source /opt/ros/melodic/setup.bash +source /opt/carma_lanelet2/setup.bash + cd /home/carma-streets mkdir test_results diff --git a/docker-compose.yml b/docker-compose.yml index 19ff7c240..2cc6d8e88 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -19,7 +19,7 @@ services: environment: DOCKER_HOST_IP: ${DOCKER_HOST_IP} KAFKA_ADVERTISED_HOST_NAME: ${DOCKER_HOST_IP} - KAFKA_CREATE_TOPICS: "v2xhub_scheduling_plan_sub:1:1,v2xhub_bsm_in:1:1,v2xhub_mobility_operation_in:1:1,v2xhub_mobility_path_in:1:1,vehicle_status_intent_output:1:1,v2xhub_map_msg_in:1:1,modified_spat:1:1,desired_phase_plan:1:1, tsc_config_state:1:1,phase_control_schedule:1:1" + KAFKA_CREATE_TOPICS: "v2xhub_scheduling_plan_sub:1:1,v2xhub_bsm_in:1:1,v2xhub_mobility_operation_in:1:1,v2xhub_mobility_path_in:1:1,vehicle_status_intent_output:1:1,v2xhub_map_msg_in:1:1,modified_spat:1:1,desired_phase_plan:1:1, tsc_config_state:1:1,phase_control_schedule:1:1,detections:1:1,sdsm_out:1:1" KAFKA_ZOOKEEPER_CONNECT: zookeeper:2181 KAFKA_LOG_DIRS: "/kafka/kafka-logs" volumes: @@ -85,7 +85,7 @@ services: - TIME_SYNC_PORT=7575 - SIM_V2X_PORT=5757 - V2X_PORT=8686 - - INFRASTRUCTURE_ID=carma-streets_1 + - INFRASTRUCTURE_ID=rsu_123 - KAFKA_BROKER_ADDRESS=127.0.0.1:9092 secrets: - mysql_password @@ -220,14 +220,18 @@ services: - kafka environment: SIMULATION_MODE: FALSE - CONFIG_FILE_PATH: ../manifest.json + CONFIG_FILE_PATH: /home/carma-streets/sensor_data_sharing_service/manifest.json TIME_SYNC_TOPIC: time_sync LOGS_DIRECTORY: /home/carma-streets/sensor_data_sharing_service/logs/ LANELET2_MAP: /home/carma-streets/MAP/Intersection.osm + INFRASTRUCTURE_ID: "rsu_123" + SENSOR_JSON_FILE_PATH: /home/carma-streets/sensor_configurations/sensors.json + volumes: - ./sensor_data_sharing_service/manifest.json:/home/carma-streets/sensor_data_sharing_service/manifest.json - ./sensor_data_sharing_service/logs/:/home/carma-streets/sensor_data_sharing_service/logs/ - ./MAP/:/home/carma-streets/intersection_model/MAP/ + - ./sensor_configurations/:/home/carma-streets/sensor_configurations/ - /etc/localtime:/etc/localtime:ro - /etc/timezone:/etc/timezone:ro secrets: diff --git a/scripts/detected_object.json b/scripts/detected_object.json index 7a0c01e47..f72f08340 100644 --- a/scripts/detected_object.json +++ b/scripts/detected_object.json @@ -1,9 +1,9 @@ { - "type": "CAR", + "type": "BICYCLE", "confidence": 0.7, "sensorId": "sensor1", "projString": "projection String2", - "objectId": "Object7", + "objectId": "1", "position": { "x": -1.1, "y": -2.0, diff --git a/sensor_data_sharing_service/CMakeLists.txt b/sensor_data_sharing_service/CMakeLists.txt index c7c7cf1f8..fb7bee0b1 100644 --- a/sensor_data_sharing_service/CMakeLists.txt +++ b/sensor_data_sharing_service/CMakeLists.txt @@ -23,37 +23,52 @@ find_package(RapidJSON REQUIRED) find_package(GTest REQUIRED) find_package(streets_messages_lib REQUIRED) find_package(streets_service_base_lib REQUIRED) -find_package(PROJ4) +find_package(PROJ4 REQUIRED) find_package(Eigen3 3.3 NO_MODULE) -find_package(catkin COMPONENTS - lanelet2_core - lanelet2_projection - lanelet2_io REQUIRED) +find_package(lanelet2_core REQUIRED) +find_package(lanelet2_projection REQUIRED) +find_package(lanelet2_io REQUIRED) +find_package(lanelet2_extension REQUIRED) find_package(carma-clock REQUIRED) # Add definition for rapidjson to include std::string add_definitions(-DRAPIDJSON_HAS_STDSTRING=1) +# Add to make trace level logging available add_definitions(-DSPDLOG_ACTIVE_LEVEL=SPDLOG_LEVEL_TRACE) - -set(SERVICE_NAME ${PROJECT_NAME}) +# Service Library set(LIBRARY_NAME ${PROJECT_NAME}_lib) - +# Service Executable +set(SERVICE_NAME ${PROJECT_NAME}) ######################################################## -# Build Library +# Build Library and Service Executable ######################################################## -add_library(${LIBRARY_NAME} src/sensor_data_sharing_service.cpp ) + +add_library(${LIBRARY_NAME} + src/sensor_data_sharing_service.cpp + src/sensor_configuration_parser.cpp + src/detected_object_to_sdsm_converter.cpp) + target_include_directories(${LIBRARY_NAME} PUBLIC $ - ${catkin_INCLUDE_DIRS} + ${lanelet2_core_INCLUDE_DIRS} + ${lanelet2_io_INCLUDE_DIRS} + ${lanelet2_projection_INCLUDE_DIRS} + ${lanelet2_extension_lib_INCLUDE_DIRS} ) + + target_link_libraries( ${LIBRARY_NAME} PUBLIC spdlog::spdlog rapidjson streets_service_base_lib::streets_service_base_lib streets_utils::streets_messages_lib - ${catkin_LIBRARIES} Eigen3::Eigen + ${PROJ4_LIBRARIES} + ${lanelet2_core_LIBRARIES} + ${lanelet2_io_LIBRARIES} + ${lanelet2_projection_LIBRARIES} + ${lanelet2_extension_LIBRARIES} ) add_executable(${SERVICE_NAME} src/main.cpp) @@ -77,4 +92,9 @@ target_link_libraries(${TEST_NAME} ${LIBRARY_NAME} GTest::Main ) +include(GoogleTest) +gtest_discover_tests( ${TEST_NAME} + DISCOVERY_MODE PRE_TEST + WORKING_DIRECTORY ${PROJECT_SOURCE_DIR}/test +) diff --git a/sensor_data_sharing_service/build.sh b/sensor_data_sharing_service/build.sh index 7513459e1..a81bccb94 100755 --- a/sensor_data_sharing_service/build.sh +++ b/sensor_data_sharing_service/build.sh @@ -15,9 +15,7 @@ # Build script to build Sensor Data Sharing Service set -e -# Source ros and lanelet2 for lanelet aware services -source /opt/ros/melodic/setup.bash -source /opt/carma_lanelet2/setup.bash + COVERAGE_FLAGS="" @@ -37,7 +35,7 @@ MAKE_ONLY_DIRS=( for DIR in "${MAKE_INSTALL_DIRS[@]}" "${MAKE_ONLY_DIRS[@]}"; do cd /home/carma-streets/"$DIR" - cmake -Bbuild -DCMAKE_CXX_FLAGS="${COVERAGE_FLAGS}" -DCMAKE_C_FLAGS="${COVERAGE_FLAGS}" -DCMAKE_POSITION_INDEPENDENT_CODE=ON -DCATKIN_ENABLE_TESTING=OFF -DCMAKE_BUILD_TYPE="Debug" -DCMAKE_PREFIX_PATH="/opt/carma/cmake/;/opt/carma_lanelet2/" + cmake -Bbuild -DCMAKE_CXX_FLAGS="${COVERAGE_FLAGS}" -DCMAKE_C_FLAGS="${COVERAGE_FLAGS}" -DCMAKE_POSITION_INDEPENDENT_CODE=ON -DCMAKE_BUILD_TYPE="Debug" -DCMAKE_PREFIX_PATH="/opt/carma/cmake/;/opt/carma_lanelet2/" cmake --build build for MAKE_INSTALL_DIR in "${MAKE_INSTALL_DIRS[@]}"; do if [ "$DIR" == "$MAKE_INSTALL_DIR" ]; then diff --git a/sensor_data_sharing_service/include/detected_object_to_sdsm_converter.hpp b/sensor_data_sharing_service/include/detected_object_to_sdsm_converter.hpp new file mode 100644 index 000000000..2684b24c7 --- /dev/null +++ b/sensor_data_sharing_service/include/detected_object_to_sdsm_converter.hpp @@ -0,0 +1,63 @@ +// Copyright 2023 Leidos +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +#pragma once + +#include +#include +#include //include all types plus i/o +#include +#include +#include + +namespace sensor_data_sharing_service { + /** + * @brief Map of detection types to SDSM object types. + */ + inline const std::map> sdsm_object_types = { + {"CAR",streets_utils::messages::sdsm::object_type::VEHICLE }, + {"VAN",streets_utils::messages::sdsm::object_type::VEHICLE }, + {"TRUCK",streets_utils::messages::sdsm::object_type::VEHICLE }, + {"PEDESTRIAN",streets_utils::messages::sdsm::object_type::VRU }, + {"CYCLIST",streets_utils::messages::sdsm::object_type::VRU }, + {"MOTORCYCLE",streets_utils::messages::sdsm::object_type::VRU }, + }; + + inline const int MILLISECONDS_TO_MICROSECONDS = 1000; + + inline const int SECONDS_TO_MILLISECONDS = 1000; + + inline const int METERS_TO_CM = 100; + + inline const int METERS_TO_5_CM = 20; + + inline const int METERS_TO_10_CM = 10; + + inline const int METERS_PER_SECOND_TO_2_CM_PER_SECOND = 50; + /** + * @brief convert epoch millisecond timestamp to sdsm timestamp object + * @return streets_utils::messages::sdsm::time_stamp. + */ + streets_utils::messages::sdsm::time_stamp to_sdsm_timestamp(const uint64_t _epoch_time_ms); + /** + * @brief convert detected_object_msg to sdsm detection_object_data. + * @return streets_utils::messages::sdsm::detected_object_data. + */ + streets_utils::messages::sdsm::detected_object_data to_detected_object_data(const streets_utils::messages::detected_objects_msg::detected_objects_msg &msg); + /** + * @brief convert string detection type to sdsm object_type. + * @param detection_type string detection classification for detection message. + * @return streets_utils::messages::sdsm::object_type + */ + streets_utils::messages::sdsm::object_type to_object_type(const std::string &detection_type); +} \ No newline at end of file diff --git a/sensor_data_sharing_service/include/sensor_configuration_parser.hpp b/sensor_data_sharing_service/include/sensor_configuration_parser.hpp new file mode 100644 index 000000000..8c4c49e9b --- /dev/null +++ b/sensor_data_sharing_service/include/sensor_configuration_parser.hpp @@ -0,0 +1,47 @@ +// Copyright 2023 Leidos +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +#pragma once + +#include +// Lanelet2 libraries +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +namespace sensor_data_sharing_service{ + /** + * @brief Method to parse sensor configuration json file. + * @param filepath absolute or relative file path. + * @param sensor_id ID of sensor to get location from. + * @return cartesian location of sensor described in json configuration file. + */ + lanelet::BasicPoint3d parse_sensor_location( const std::string &filepath, const std::string &sensor_id ); +} \ No newline at end of file diff --git a/sensor_data_sharing_service/include/sensor_data_sharing_service.hpp b/sensor_data_sharing_service/include/sensor_data_sharing_service.hpp index 90979a00a..0a05507dc 100644 --- a/sensor_data_sharing_service/include/sensor_data_sharing_service.hpp +++ b/sensor_data_sharing_service/include/sensor_data_sharing_service.hpp @@ -23,10 +23,30 @@ #include #include #include +// Lanelet2 libraries +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include #include #include +#include "sensor_configuration_parser.hpp" +#include "detected_object_to_sdsm_converter.hpp" + + namespace sensor_data_sharing_service { class sds_service : public streets_service::streets_service { @@ -47,6 +67,32 @@ namespace sensor_data_sharing_service { * @brief Mutex for thread safe operations on detected objects map. */ std::shared_mutex detected_objects_lock; + /** + * @brief Lanelet2 Map pointer + */ + lanelet::LaneletMapPtr map_ptr; + + /** + * @brief WSG84 Map projection + */ + std::unique_ptr map_projector; + + /** + * @brief Location of sensor.This is also the sensor's coordinate frame orignin meaning all offsets + * are interpreted relative to this location. + */ + lanelet::GPSPoint sdsm_reference_point; + + /** + * @brief Infrastructure ID used as source ID for SDSM broadcast + */ + std::string _infrastructure_id; + + /** + * @brief Message count for SDSM + */ + uint8_t _message_count = 0; + /** * @brief Initialize Kafka consumers and producers for sensor data sharing service. * @return true if successful and false if unsuccessful. @@ -67,8 +113,18 @@ namespace sensor_data_sharing_service { * @throws std::runtime exception if sdsm_producer == nullptr */ void produce_sdsms(); - + /** + * @brief Method to read lanelet2 map. + * @param filepath to lanelet2 osm file. + * @return true if successful. + */ + bool read_lanelet_map(const std::string &filepath); + /** + * @brief Method to create SDSM from detected objects. + * @return sensor_data_sharing_msg created from detected objects. + */ + streets_utils::messages::sdsm::sensor_data_sharing_msg create_sdsm(); public: @@ -91,5 +147,6 @@ namespace sensor_data_sharing_service { FRIEND_TEST(sensorDataSharingServiceTest, consumeDetections); FRIEND_TEST(sensorDataSharingServiceTest, produceSdsms); + FRIEND_TEST(sensorDataSharingServiceTest, readLanelet2Map); }; } \ No newline at end of file diff --git a/sensor_data_sharing_service/manifest.json b/sensor_data_sharing_service/manifest.json index 5093e62d2..235ec35e2 100644 --- a/sensor_data_sharing_service/manifest.json +++ b/sensor_data_sharing_service/manifest.json @@ -25,6 +25,18 @@ "value": "127.0.0.1:9092", "description": "Kafka Broker Server Address.", "type": "STRING" + }, + { + "name": "sensor_id", + "value": "sensor_1", + "description": "Unique id of sensor for which to publish SDSMS", + "type": "STRING" + }, + { + "name": "sdsm_geo_reference", + "value": "+proj=tmerc +lat_0=38.95197911150576 +lon_0=-77.14835128349988 +k=1 +x_0=0 +y_0=0 +datum=WGS84 +units=m +vunits=m +no_defs", + "description": "Geo reference used for reference frame for Sensor Data Sharing messges broadcast", + "type": "STRING" } ] } \ No newline at end of file diff --git a/sensor_data_sharing_service/src/detected_object_to_sdsm_converter.cpp b/sensor_data_sharing_service/src/detected_object_to_sdsm_converter.cpp new file mode 100644 index 000000000..661bd3463 --- /dev/null +++ b/sensor_data_sharing_service/src/detected_object_to_sdsm_converter.cpp @@ -0,0 +1,89 @@ +// Copyright 2019-2023 Leidos +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +#include "detected_object_to_sdsm_converter.hpp" + +namespace sensor_data_sharing_service{ + + streets_utils::messages::sdsm::time_stamp to_sdsm_timestamp(const uint64_t _epoch_time_ms) { + streets_utils::messages::sdsm::time_stamp sdsm_timestamp; + + // From millisecond time stamp + boost::posix_time::ptime posix_time = boost::posix_time::from_time_t(_epoch_time_ms/SECONDS_TO_MILLISECONDS) + + boost::posix_time::millisec( _epoch_time_ms % SECONDS_TO_MILLISECONDS); + sdsm_timestamp.year = posix_time.date().year(); + sdsm_timestamp.month = posix_time.date().month(); + sdsm_timestamp.day = posix_time.date().day(); + + sdsm_timestamp.hour = (unsigned int) posix_time.time_of_day().hours(); + sdsm_timestamp.minute = (unsigned int) posix_time.time_of_day().minutes(); + // Milliseconds of the current minute. The SDSM field is named seconds but is in the unit of milliseconds (see DDateTime from J2735). + // Fractional_seconds returns microseconds from the current second since default time resolution is microseconds + sdsm_timestamp.second = (unsigned int) (posix_time.time_of_day().seconds()*SECONDS_TO_MILLISECONDS + posix_time.time_of_day().fractional_seconds()/MILLISECONDS_TO_MICROSECONDS); + return sdsm_timestamp; + } + + streets_utils::messages::sdsm::detected_object_data to_detected_object_data(const streets_utils::messages::detected_objects_msg::detected_objects_msg &msg) { + streets_utils::messages::sdsm::detected_object_data detected_object; + detected_object._detected_object_common_data._object_type = to_object_type(msg._type); + if (detected_object._detected_object_common_data._object_type == streets_utils::messages::sdsm::object_type::VEHICLE ) { + streets_utils::messages::sdsm::detected_vehicle_data optional_data; + // Size in cm + streets_utils::messages::sdsm::vehicle_size veh_size; + veh_size._length= static_cast(msg._size._length*METERS_TO_CM); + veh_size._width= static_cast(msg._size._width*METERS_TO_CM); + optional_data._size = veh_size; + // Height in 5 cm + optional_data._vehicle_height = static_cast(msg._size._height * METERS_TO_5_CM); + + + detected_object._detected_object_optional_data = optional_data; + } + else if ( detected_object._detected_object_common_data._object_type == streets_utils::messages::sdsm::object_type::VRU ) { + streets_utils::messages::sdsm::detected_vru_data optional_data; + // Populate Optional VRU data + detected_object._detected_object_optional_data = optional_data; + } + else if (detected_object._detected_object_common_data._object_type == streets_utils::messages::sdsm::object_type::UNKNOWN ){ + streets_utils::messages::sdsm::detected_obstacle_data optional_data; + // size dimensions in units of 0.1 m + streets_utils::messages::sdsm::obstacle_size obs_size; + obs_size._length = static_cast(msg._size._length*METERS_TO_10_CM); + obs_size._width = static_cast(msg._size._width*METERS_TO_10_CM); + obs_size._height = static_cast(msg._size._height*METERS_TO_10_CM); + optional_data._size = obs_size; + + detected_object._detected_object_optional_data = optional_data; + + } + detected_object._detected_object_common_data._classification_confidence = static_cast(msg._confidence*100); + // TODO: Change Detected Object ID to int + detected_object._detected_object_common_data._object_id = std::stoi(msg._object_id); + // Units are 0.1 m + detected_object._detected_object_common_data._position_offset._offset_x = static_cast(msg._position._x*METERS_TO_10_CM); + detected_object._detected_object_common_data._position_offset._offset_y = static_cast(msg._position._y*METERS_TO_10_CM); + detected_object._detected_object_common_data._position_offset._offset_z = static_cast(msg._position._z*METERS_TO_10_CM); + // Units are 0.02 m/s + detected_object._detected_object_common_data._speed = static_cast(std::hypot( msg._velocity._x* METERS_PER_SECOND_TO_2_CM_PER_SECOND, msg._velocity._y* METERS_PER_SECOND_TO_2_CM_PER_SECOND)); + detected_object._detected_object_common_data._speed_z = static_cast(msg._velocity._z* METERS_PER_SECOND_TO_2_CM_PER_SECOND); + return detected_object; + } + + streets_utils::messages::sdsm::object_type to_object_type(const std::string &detection_type){ + if ( sdsm_object_types.find(detection_type) != sdsm_object_types.end()) { + return sdsm_object_types.at(detection_type); + } + return streets_utils::messages::sdsm::object_type::UNKNOWN; + } + +} \ No newline at end of file diff --git a/sensor_data_sharing_service/src/sensor_configuration_parser.cpp b/sensor_data_sharing_service/src/sensor_configuration_parser.cpp new file mode 100644 index 000000000..c8de50679 --- /dev/null +++ b/sensor_data_sharing_service/src/sensor_configuration_parser.cpp @@ -0,0 +1,45 @@ +// Copyright 2019-2023 Leidos +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +#include "sensor_configuration_parser.hpp" + +namespace sensor_data_sharing_service { + + lanelet::BasicPoint3d parse_sensor_location( const std::string &filepath , const std::string &sensor_id ){ + // Parse JSON configuration file + auto doc = streets_utils::json_utils::parse_json_file(filepath); + if (!doc.IsArray()) { + throw streets_utils::json_utils::json_parse_exception("Invadid format for sensor configuration file " + filepath + + ". Sensor configuration file should contain an array of json sensor configurations!"); + } + bool found = false; + lanelet::BasicPoint3d sensor_location; + for (auto itr = doc.Begin(); itr != doc.End(); ++itr) { + // Access the data in the object + auto sensor_config_id = streets_utils::json_utils::parse_string_member("sensorId", itr->GetObject(), true ).value(); + if ( sensor_config_id == sensor_id ) { + found = true; + auto location = streets_utils::json_utils::parse_object_member("location", itr->GetObject(), true ).value(); + sensor_location = lanelet::BasicPoint3d{ + streets_utils::json_utils::parse_double_member("x", location, true ).value(), + streets_utils::json_utils::parse_double_member("y", location, true ).value(), + streets_utils::json_utils::parse_double_member("z", location, true ).value() + }; + } + } + if (!found) { + throw streets_utils::json_utils::json_parse_exception("Did not find sensor with id " + sensor_id + " in sensor configuration file " + filepath + "!"); + } + return sensor_location; + } +} \ No newline at end of file diff --git a/sensor_data_sharing_service/src/sensor_data_sharing_service.cpp b/sensor_data_sharing_service/src/sensor_data_sharing_service.cpp index 329cf94d3..1349db380 100644 --- a/sensor_data_sharing_service/src/sensor_data_sharing_service.cpp +++ b/sensor_data_sharing_service/src/sensor_data_sharing_service.cpp @@ -34,18 +34,56 @@ namespace sensor_data_sharing_service { } bool sds_service::initialize() { - if (!streets_service::initialize()) { + if (!streets_service::initialize() ) { + SPDLOG_ERROR("Failed to initialize streets service base!"); return false; } SPDLOG_DEBUG("Intializing Sensor Data Sharing Service"); + const std::string lanlet2_map = streets_service::get_system_config("LANELET2_MAP", "/home/carma-streets/MAP/Intersection.osm"); + if (!read_lanelet_map(lanlet2_map)){ + SPDLOG_ERROR("Failed to read lanlet2 map {0} !", lanlet2_map); + return false; + } + // Read sensor configuration file and get WSG84 location/origin reference frame. + const std::string sensor_config_file = streets_service::get_system_config("SENSOR_JSON_FILE_PATH", "/home/carma-streets/sensor_configurations/sensors.json"); + const std::string sensor_id = ss::streets_configuration::get_string_config("sensor_id"); + const lanelet::BasicPoint3d pose = parse_sensor_location(sensor_config_file, sensor_id); + this->sdsm_reference_point = this->map_projector->reverse(pose); // Initialize SDSM Kafka producer - std::string sdsm_topic = ss::streets_configuration::get_string_config("sdsm_producer_topic"); - std::string detection_topic = ss::streets_configuration::get_string_config("detection_consumer_topic"); - + const std::string sdsm_topic = ss::streets_configuration::get_string_config("sdsm_producer_topic"); + const std::string detection_topic = ss::streets_configuration::get_string_config("detection_consumer_topic"); + const std::string sdsm_geo_reference = ss::streets_configuration::get_string_config("sdsm_geo_reference"); + // Get Infrastructure ID for SDSM messages + this->_infrastructure_id = streets_service::get_system_config("INFRASTRUCTURE_ID", ""); return initialize_kafka_producer(sdsm_topic, sdsm_producer) && initialize_kafka_consumer(detection_topic, detection_consumer); } + bool sds_service::read_lanelet_map(const std::string &filepath) { + + try + { + int projector_type = 1; + std::string target_frame; + lanelet::ErrorMessages errors; + // Parse geo reference info from the lanelet map (.osm) + lanelet::io_handlers::AutowareOsmParser::parseMapParams(filepath, &projector_type, &target_frame); + this->map_projector = std::make_unique(target_frame.c_str()); + this->map_ptr = lanelet::load(filepath, *map_projector.get(), &errors); + // + + if (!this->map_ptr->empty()) + { + return true; + } + } + catch (const lanelet::ParseError &ex) + { + SPDLOG_ERROR("Cannot read osm file {0}. Error message: {1} .", filepath, ex.what()); + } + return false; + } + void sds_service::consume_detections(){ if ( !detection_consumer ) { throw std::runtime_error("Detection consumer is null!"); @@ -58,6 +96,7 @@ namespace sensor_data_sharing_service { if (payload.length() != 0) { auto detected_object = streets_utils::messages::detected_objects_msg::from_json(payload); + // Write Lock std::unique_lock lock(detected_objects_lock); detected_objects[detected_object._object_id] = detected_object; SPDLOG_DEBUG("Detected Object List Size {0} after consumed: {1}", detected_objects.size(), payload); @@ -80,12 +119,11 @@ namespace sensor_data_sharing_service { while ( sdsm_producer->is_running() ) { try{ if ( !detected_objects.empty() ) { - std::unique_lock lock(detected_objects_lock); - streets_utils::messages::sdsm::sensor_data_sharing_msg msg; - // TODO: Populate SDSM with detected objects + streets_utils::messages::sdsm::sensor_data_sharing_msg msg = create_sdsm(); const std::string json_msg = streets_utils::messages::sdsm::to_json(msg); SPDLOG_DEBUG("Sending SDSM : {0}", json_msg); sdsm_producer->send(json_msg); + this->_message_count++; // Clear detected object detected_objects.clear(); } @@ -98,6 +136,26 @@ namespace sensor_data_sharing_service { } + streets_utils::messages::sdsm::sensor_data_sharing_msg sds_service::create_sdsm() { + streets_utils::messages::sdsm::sensor_data_sharing_msg msg; + // Read lock + uint64_t timestamp = ss::streets_clock_singleton::time_in_ms(); + msg._time_stamp = to_sdsm_timestamp(timestamp); + // Populate with rolling counter + msg._msg_count = this->_message_count; + // Populate with infrastructure id + msg._source_id = this->_infrastructure_id; + msg._equipment_type = sdsm::equipment_type::RSU; + std::shared_lock lock(detected_objects_lock); + for (const auto &[object_id, object] : detected_objects){ + auto detected_object_data = to_detected_object_data(object); + // TODO: Update time offset. Currently CARMA-Streets detected object message does not support timestamp + // This is a bug and needs to be addressed. + msg._objects.push_back(detected_object_data); + } + return msg; + } + void sds_service::start() { SPDLOG_DEBUG("Starting Sensor Data Sharing Service"); @@ -107,4 +165,6 @@ namespace sensor_data_sharing_service { detection_thread.join(); sdsm_thread.join(); } + + } \ No newline at end of file diff --git a/sensor_data_sharing_service/test/detected_object_to_sdsm_converter_test.cpp b/sensor_data_sharing_service/test/detected_object_to_sdsm_converter_test.cpp new file mode 100644 index 000000000..e0715b314 --- /dev/null +++ b/sensor_data_sharing_service/test/detected_object_to_sdsm_converter_test.cpp @@ -0,0 +1,71 @@ +// Copyright 2019-2023 Leidos +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +#include +#include +#include +#include +#include + +namespace sensor_data_sharing_service { + TEST(detected_object_to_sdsm_converter_test, test_to_object_type){ + EXPECT_EQ(streets_utils::messages::sdsm::object_type::VEHICLE, to_object_type("CAR")); + EXPECT_EQ(streets_utils::messages::sdsm::object_type::VRU, to_object_type("CYCLIST")); + EXPECT_EQ(streets_utils::messages::sdsm::object_type::UNKNOWN, to_object_type("TREE")); + } + + TEST(detected_object_to_sdsm_converter_test, test_to_detected_object_data) { + // Create detected object + streets_utils::messages::detected_objects_msg::detected_objects_msg msg; + msg._object_id = "123"; + msg._type = "TREE"; + msg._sensor_id = "sensor_1"; + msg._proj_string = "some string"; + msg._confidence = 0.9; + msg._velocity = {0.1, 2.3, 5.2}; + msg._angular_velocity = {0.1, 2.3, 5.2}; + msg._position = {0.1, 2.3, 5.2}; + msg._position_covariance = {{0.1, 2.3, 5.2},{0.1, 2.3, 5.2},{0.1, 2.3, 5.2}}; + msg._angular_velocity_covariance = {{0.1, 2.3, 5.2},{0.1, 2.3, 5.2},{0.1, 2.3, 5.2}}; + msg._position_covariance = {{0.1, 2.3, 5.2},{0.1, 2.3, 5.2},{0.1, 2.3, 5.2}}; + msg._size._length =1.1; + msg._size._width =1.1; + msg._size._height = 10; + + auto data = to_detected_object_data(msg); + EXPECT_EQ(std::stoi(msg._object_id), data._detected_object_common_data._object_id); + EXPECT_EQ(streets_utils::messages::sdsm::object_type::UNKNOWN, data._detected_object_common_data._object_type); + EXPECT_EQ(90, data._detected_object_common_data._classification_confidence); + EXPECT_EQ(static_cast(msg._position._x*10), data._detected_object_common_data._position_offset._offset_x); + EXPECT_EQ(static_cast(msg._position._y*10), data._detected_object_common_data._position_offset._offset_y); + EXPECT_EQ(static_cast(msg._position._y*10), data._detected_object_common_data._position_offset._offset_y); + + EXPECT_EQ(static_cast(std::hypot(msg._velocity._x*50, msg._velocity._y*50)), data._detected_object_common_data._speed); + EXPECT_EQ(static_cast(msg._velocity._z*50), data._detected_object_common_data._speed_z); + EXPECT_EQ(static_cast(msg._size._length*10), std::get(data._detected_object_optional_data.value())._size._length); + + + } + + TEST(detected_object_to_sdsm_converter_test, to_sdsm_timestamp_test) { + // Time 2023-12-11T19:07:44.075Z + uint64_t epoch_timestamp = 1702321664075; + auto sdsm_timestamp = to_sdsm_timestamp(epoch_timestamp); + EXPECT_EQ(2023, sdsm_timestamp.year); + EXPECT_EQ(12, sdsm_timestamp.month); + EXPECT_EQ(11, sdsm_timestamp.day); + EXPECT_EQ(19, sdsm_timestamp.hour); + EXPECT_EQ(7, sdsm_timestamp.minute); + EXPECT_EQ(44075, sdsm_timestamp.second); + } +} \ No newline at end of file diff --git a/sensor_data_sharing_service/test/sensor_configuration_parser_test.cpp b/sensor_data_sharing_service/test/sensor_configuration_parser_test.cpp new file mode 100644 index 000000000..c30f107a8 --- /dev/null +++ b/sensor_data_sharing_service/test/sensor_configuration_parser_test.cpp @@ -0,0 +1,26 @@ +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +#include +#include +#include + + +namespace sensor_data_sharing_service { + TEST(sensor_configuration_parser_test, parse_sensor_location_test) { + auto pose = parse_sensor_location("/home/carma-streets/sensor_data_sharing_service/test/test_files/sensors.json", "sensor_1"); + EXPECT_NEAR(pose.x(), 1.0 , 0.01 ); + EXPECT_NEAR(pose.y(), 2.0 , 0.01 ); + EXPECT_NEAR(pose.z(), -3.2 , 0.01 ); + + } +} \ No newline at end of file diff --git a/sensor_data_sharing_service/test/sensor_data_sharing_service_test.cpp b/sensor_data_sharing_service/test/sensor_data_sharing_service_test.cpp index b969c6400..fde324a90 100644 --- a/sensor_data_sharing_service/test/sensor_data_sharing_service_test.cpp +++ b/sensor_data_sharing_service/test/sensor_data_sharing_service_test.cpp @@ -20,12 +20,16 @@ #include #include #include -#include +#include +#include +#include using testing::_; using testing::Return; using testing::AnyNumber; +using testing::SaveArg; +using testing::DoAll; namespace sensor_data_sharing_service { TEST(sensorDataSharingServiceTest, consumeDetections) { @@ -52,21 +56,81 @@ namespace sensor_data_sharing_service { TEST(sensorDataSharingServiceTest, produceSdsms) { sds_service serv; + serv._infrastructure_id = "rsu_1234"; // Initialize streets_clock in non simulation mode streets_service::streets_clock_singleton::create(false); // If producer null expect runtime error EXPECT_THROW(serv.produce_sdsms(), std::runtime_error); - const std::string detected_object_json ="{\"type\":\"CAR\",\"confidence\":0.7,\"sensorId\":\"sensor1\",\"projString\":\"projectionString2\",\"objectId\":\"Object7\",\"position\":{\"x\":-1.1,\"y\":-2.0,\"z\":-3.2},\"positionCovariance\":[[1.0,0.0,0.0],[1.0,0.0,0.0],[1.0,0.0,0.0]],\"velocity\":{\"x\":1.0,\"y\":1.0,\"z\":1.0},\"velocityCovariance\":[[1.0,0.0,0.0],[1.0,0.0,0.0],[1.0,0.0,0.0]],\"angularVelocity\":{\"x\":0.1,\"y\":0.2,\"z\":0.3},\"angularVelocityCovariance\":[[1.0,0.0,0.0],[1.0,0.0,0.0],[1.0,0.0,0.0]],\"size\":{\"length\":2.0,\"height\":1.0,\"width\":0.5}}"; + const std::string detected_object_json = + R"( + { + "type":"CAR", + "confidence":0.7, + "sensorId":"sensor1", + "projString":"projectionString2", + "objectId":"1", + "position":{ + "x":-1.1, + "y":-2.0, + "z":-3.2 + }, + "positionCovariance":[[1.0,0.0,0.0],[1.0,0.0,0.0],[1.0,0.0,0.0]], + "velocity":{ + "x":1.0, + "y":1.0, + "z":1.0 + }, + "velocityCovariance":[[1.0,0.0,0.0],[1.0,0.0,0.0],[1.0,0.0,0.0]], + "angularVelocity":{ + "x":0.1, + "y":0.2, + "z":0.3 + }, + "angularVelocityCovariance":[[1.0,0.0,0.0],[1.0,0.0,0.0],[1.0,0.0,0.0]], + "size":{ + "length":2.0, + "height":1.0, + "width":0.5 + } + } + )"; auto detected_object = streets_utils::messages::detected_objects_msg::from_json(detected_object_json); serv.detected_objects[detected_object._object_id] =detected_object; serv.sdsm_producer = std::make_shared(); EXPECT_CALL(dynamic_cast(*serv.sdsm_producer),is_running()).Times(3).WillOnce(Return(true)) .WillOnce(Return(true)) .WillRepeatedly(Return(false)); - const std::string sdsm_json ="{\"msg_cnt\":0,\"source_id\":\"\",\"equipment_type\":0,\"sdsm_time_stamp\":{\"second\":0,\"minute\":0,\"hour\":0,\"day\":0,\"month\":0,\"year\":0,\"offset\":0},\"ref_pos\":{\"long\":0,\"lat\":0},\"ref_pos_xy_conf\":{\"semi_major\":0,\"semi_minor\":0,\"orientation\":0},\"objects\":[]}"; - EXPECT_CALL(dynamic_cast(*serv.sdsm_producer), send(sdsm_json)).Times(1); + std::string sdsm_json; + EXPECT_CALL(dynamic_cast(*serv.sdsm_producer), send(_)).WillOnce( DoAll(SaveArg<0>(&sdsm_json)) ); serv.produce_sdsms(); + + // Verify produced json + SPDLOG_INFO("Produces SDMS {0}", sdsm_json); + streets_utils::messages::sdsm::sensor_data_sharing_msg msg = streets_utils::messages::sdsm::from_json(sdsm_json); + EXPECT_EQ( msg._equipment_type, streets_utils::messages::sdsm::equipment_type::RSU); + EXPECT_EQ( msg._source_id, serv._infrastructure_id ); + // http://en.cppreference.com/w/cpp/chrono/c/time + const std::time_t now = std::time(nullptr) ; // get the current time point + + // convert it to (local) calendar time + // http://en.cppreference.com/w/cpp/chrono/c/localtime + const std::tm calendar_time = *std::localtime( std::addressof(now) ) ; + EXPECT_EQ(calendar_time.tm_year+1900, msg._time_stamp.year); + EXPECT_EQ(calendar_time.tm_mon+1, msg._time_stamp.month); + EXPECT_EQ(calendar_time.tm_mday, msg._time_stamp.day); + EXPECT_EQ(calendar_time.tm_hour, msg._time_stamp.hour); + // Account for runs near close of minute + EXPECT_NEAR(calendar_time.tm_min, msg._time_stamp.minute, 1); + EXPECT_EQ(1 , msg._objects.size()); + EXPECT_EQ(streets_utils::messages::sdsm::object_type::VEHICLE, msg._objects[0]._detected_object_common_data._object_type); EXPECT_EQ(serv.detected_objects.size(), 0); } + + TEST(sensorDataSharingServiceTest,readLanelet2Map) { + sds_service serv; + EXPECT_TRUE(serv.read_lanelet_map("/home/carma-streets/sample_map/town01_vector_map_test.osm")); + EXPECT_NE(nullptr, serv.map_ptr); + } + } \ No newline at end of file diff --git a/sensor_data_sharing_service/test/test_files/sensors.json b/sensor_data_sharing_service/test/test_files/sensors.json new file mode 100644 index 000000000..162728b45 --- /dev/null +++ b/sensor_data_sharing_service/test/test_files/sensors.json @@ -0,0 +1,16 @@ +[ + { + "sensorId": "sensor_1", + "type": "SemanticLidar", + "location": { + "x": 1.0, + "y": 2.0, + "z": -3.2 + }, + "orientation": { + "yaw": 0.0, + "pitch": 0.0, + "roll": 0.0 + } + } +] \ No newline at end of file diff --git a/streets_utils/json_utils/include/json_document_parse_error.hpp b/streets_utils/json_utils/include/json_document_parse_error.hpp new file mode 100644 index 000000000..b92430193 --- /dev/null +++ b/streets_utils/json_utils/include/json_document_parse_error.hpp @@ -0,0 +1,13 @@ +#pragma once + +#include +#include + +namespace streets_utils::json_utils { + class json_document_parse_error : public std::runtime_error { + public: + explicit json_document_parse_error(const std::string &msg, const rapidjson::Document &doc ) : + std::runtime_error(msg + "\nError :" + std::to_string(doc.GetParseError()) + " Offset: " + std::to_string(doc.GetErrorOffset())){}; + + }; +} \ No newline at end of file diff --git a/streets_utils/json_utils/include/json_utils.hpp b/streets_utils/json_utils/include/json_utils.hpp index f4086190c..7eb4c64bd 100644 --- a/streets_utils/json_utils/include/json_utils.hpp +++ b/streets_utils/json_utils/include/json_utils.hpp @@ -7,7 +7,10 @@ #include #include #include +#include +#include #include "json_utils_exception.hpp" +#include "json_document_parse_error.hpp" namespace streets_utils::json_utils { @@ -20,6 +23,15 @@ namespace streets_utils::json_utils { * @return resulting `rapidjson::Document` */ rapidjson::Document parse_json( const std::string &json ); +/** + * @brief Function to parse file containing JSON with [RapidJSON](https://miloyip.github.io/rapidjson/index.html) with + * [DOM parsing](https://miloyip.github.io/rapidjson/md_obj_dom.html). Throws `json_parse_exception` if string is + * not valid json. Otherwise returns `rapidjson::Document` + * @param json std::string JSON to parse + * @throws json_parse_exception if passed string is not valid json. + * @return resulting `rapidjson::Document` + */ + rapidjson::Document parse_json_file(const std::string &filepath); /** * @brief Functions to retrieve int member values from JSON object [RapidJSON](https://miloyip.github.io/rapidjson/index.html) * with [DOM parsing](https://miloyip.github.io/rapidjson/md_doc_dom.html). Functions will return unintialized diff --git a/streets_utils/json_utils/src/json_utils.cpp b/streets_utils/json_utils/src/json_utils.cpp index 56793c501..d9a45ad20 100644 --- a/streets_utils/json_utils/src/json_utils.cpp +++ b/streets_utils/json_utils/src/json_utils.cpp @@ -7,10 +7,28 @@ namespace streets_utils::json_utils { obj.Parse(json); if (obj.HasParseError()) { + // TODO: Change to json_document_parse_exception. Requires changes to services and unit tests throw json_parse_exception("Message JSON is misformatted. JSON parsing failed!"); } return obj; } + + rapidjson::Document parse_json_file(const std::string &filepath) { + // Parse JSON configuration file + std::ifstream file(filepath); + if (!file.is_open()) { + throw std::invalid_argument("Unable to open Streets configuration file " + filepath + " due to : " + strerror(errno) + "."); + } + // Add file contents to stream and parse stream into Document + rapidjson::IStreamWrapper isw(file); + rapidjson::Document doc; + doc.ParseStream(isw); + if (doc.HasParseError()){ + throw json_document_parse_error("Encounter document parse error while attempting to parse sensor configuration file " + filepath + "!", doc); + } + file.close(); + return doc; + } std::optional parse_int_member(const std::string &member_name, const rapidjson::Value &obj, bool required ){ std::optional member; if (obj.HasMember(member_name.c_str()) && obj.FindMember(member_name.c_str())->value.IsInt64()) diff --git a/streets_utils/json_utils/test/json_utils_test.cpp b/streets_utils/json_utils/test/json_utils_test.cpp index 5de160d62..9842f95e2 100644 --- a/streets_utils/json_utils/test/json_utils_test.cpp +++ b/streets_utils/json_utils/test/json_utils_test.cpp @@ -22,6 +22,27 @@ TEST(JsonUtilsTest, testParseJsonValidJson) { auto parsed_doc = parse_json(valid_json); EXPECT_FALSE(parsed_doc.HasParseError()); } + +TEST(JsonUtilsTest, testParseJsonValidJsonFile) { + // Correct JSON + std::string file_path = "/home/carma-streets/streets_utils/json_utils/test/test_files/valid.json"; + auto parsed_doc = parse_json_file(file_path); + EXPECT_FALSE(parsed_doc.HasParseError()); + +} + +TEST(JsonUtilsTest, testParseJsonInvalidJsonFilePath) { + // Correct JSON + std::string file_path = "/home/invalid/filepath.json"; + EXPECT_THROW(parse_json_file(file_path), std::invalid_argument); +} + +TEST(JsonUtilsTest, testParseJsonInvalidJsonFile) { + // Correct JSON + std::string file_path = "/home/carma-streets/streets_utils/json_utils/test/test_files/invalid.json"; + EXPECT_THROW(parse_json_file(file_path), json_document_parse_error); +} + //---------------------test parse_uint_member--------------------- TEST(JsonUtilsTest, testGetJsonUintRequiredPropertyPresent){ // Test with required property present diff --git a/streets_utils/json_utils/test/test_files/invalid.json b/streets_utils/json_utils/test/test_files/invalid.json new file mode 100644 index 000000000..4b1721cba --- /dev/null +++ b/streets_utils/json_utils/test/test_files/invalid.json @@ -0,0 +1,16 @@ +[ + { + "sensorId": "sensor_1", + "type": "SemanticLidar",, + "location": { + "x": 1.0, + "y": 2.0, + "z": -3.2 + }, + "orientation": { + "yaw": 0.0, + "pitch": 0.0, + "roll": 0.0 + } + } +] \ No newline at end of file diff --git a/streets_utils/json_utils/test/test_files/valid.json b/streets_utils/json_utils/test/test_files/valid.json new file mode 100644 index 000000000..162728b45 --- /dev/null +++ b/streets_utils/json_utils/test/test_files/valid.json @@ -0,0 +1,16 @@ +[ + { + "sensorId": "sensor_1", + "type": "SemanticLidar", + "location": { + "x": 1.0, + "y": 2.0, + "z": -3.2 + }, + "orientation": { + "yaw": 0.0, + "pitch": 0.0, + "roll": 0.0 + } + } +] \ No newline at end of file diff --git a/streets_utils/streets_messages/src/deserializers/sensor_data_sharing_msg_json_deserializer.cpp b/streets_utils/streets_messages/src/deserializers/sensor_data_sharing_msg_json_deserializer.cpp index 291db160a..510809ec2 100644 --- a/streets_utils/streets_messages/src/deserializers/sensor_data_sharing_msg_json_deserializer.cpp +++ b/streets_utils/streets_messages/src/deserializers/sensor_data_sharing_msg_json_deserializer.cpp @@ -104,19 +104,20 @@ namespace streets_utils::messages::sdsm { } _detected_object_common_data._heading = parse_uint_member("heading", val, true).value(); _detected_object_common_data._heading_confidence = heading_confidence_from_int(parse_uint_member("heading_conf", val, true).value()); - - _detected_object_common_data._acceleration_4_way = parse_acceleration_4_way(parse_object_member("accel_4_way", val, true ).value()); - if ( val.HasMember("acc_cfd_x")) { - _detected_object_common_data._lateral_acceleration_confidence = acceleration_confidence_from_int(parse_uint_member("acc_cfd_x", val, true).value()); - } - if ( val.HasMember("acc_cfd_y")) { - _detected_object_common_data._longitudinal_acceleration_confidence = acceleration_confidence_from_int(parse_uint_member("acc_cfd_y", val, true).value()); - } - if ( val.HasMember("acc_cfd_z")) { - _detected_object_common_data._vertical_accelaration_confidence = acceleration_confidence_from_int(parse_uint_member("acc_cfd_z", val, true).value()); - } - if ( val.HasMember("acc_cfd_yaw")) { - _detected_object_common_data._yaw_rate_confidence = angular_velocity_confidence_from_int(parse_uint_member("acc_cfd_yaw", val, true).value()); + if ( val.HasMember("accel_4_way") ) { + _detected_object_common_data._acceleration_4_way = parse_acceleration_4_way(parse_object_member("accel_4_way", val, false ).value()); + if ( val.HasMember("acc_cfd_x")) { + _detected_object_common_data._lateral_acceleration_confidence = acceleration_confidence_from_int(parse_uint_member("acc_cfd_x", val, true).value()); + } + if ( val.HasMember("acc_cfd_y")) { + _detected_object_common_data._longitudinal_acceleration_confidence = acceleration_confidence_from_int(parse_uint_member("acc_cfd_y", val, true).value()); + } + if ( val.HasMember("acc_cfd_z")) { + _detected_object_common_data._vertical_accelaration_confidence = acceleration_confidence_from_int(parse_uint_member("acc_cfd_z", val, true).value()); + } + if ( val.HasMember("acc_cfd_yaw")) { + _detected_object_common_data._yaw_rate_confidence = angular_velocity_confidence_from_int(parse_uint_member("acc_cfd_yaw", val, true).value()); + } } return _detected_object_common_data; } diff --git a/streets_utils/streets_messages/src/serializers/sensor_data_sharing_msg_json_serializer.cpp b/streets_utils/streets_messages/src/serializers/sensor_data_sharing_msg_json_serializer.cpp index 7341ba711..83be1d7c4 100644 --- a/streets_utils/streets_messages/src/serializers/sensor_data_sharing_msg_json_serializer.cpp +++ b/streets_utils/streets_messages/src/serializers/sensor_data_sharing_msg_json_serializer.cpp @@ -81,14 +81,16 @@ namespace streets_utils::messages::sdsm{ rapidjson::Value create_detected_object_data(const detected_object_data &val, rapidjson::Document::AllocatorType &allocator){ rapidjson::Value detected_object_data_json(rapidjson::kObjectType); + rapidjson::Value object_data_json(rapidjson::kObjectType); // Create Common Data - detected_object_data_json.AddMember("detected_object_common_data",create_detected_object_data_common(val._detected_object_common_data,allocator), allocator ); + object_data_json.AddMember("detected_object_common_data",create_detected_object_data_common(val._detected_object_common_data,allocator), allocator ); // Create Optional Data if ( val._detected_object_optional_data.has_value() ) - detected_object_data_json.AddMember( + object_data_json.AddMember( "detected_object_optional_data", create_detected_object_data_optional(val._detected_object_optional_data.value(), allocator), allocator); + detected_object_data_json.AddMember("detected_object_data", object_data_json , allocator); return detected_object_data_json; } From 112221a36c4f536b512dcedf4210686b6315917e Mon Sep 17 00:00:00 2001 From: paulbourelly999 <77466294+paulbourelly999@users.noreply.github.com> Date: Tue, 12 Dec 2023 15:30:41 -0500 Subject: [PATCH 53/80] Fix unit tests for sdsm JSON serialization (#377) * Fix unit tests for sdsm JSON serialization * Update documentation --- .../SensorDataSharingMessage.md | 104 +++++++++--------- ...ta_sharing_msg_json_serialization_test.cpp | 15 +-- 2 files changed, 61 insertions(+), 58 deletions(-) diff --git a/streets_utils/streets_messages/SensorDataSharingMessage.md b/streets_utils/streets_messages/SensorDataSharingMessage.md index 061b0e358..4d0665086 100644 --- a/streets_utils/streets_messages/SensorDataSharingMessage.md +++ b/streets_utils/streets_messages/SensorDataSharingMessage.md @@ -6,7 +6,7 @@ This CARMA-Streets library contains the Sensor Data Sharing Object as well as th > [!IMPORTANT]\ > The first implementation of the SDSM generator will not include functionality to filter out detections that are likely self reporting actors publishing BSMs like described in the J3224 202208 document. ## Example JSON payload -``` +```json { "msg_cnt": 1, "source_id": "00000001", @@ -31,58 +31,60 @@ This CARMA-Streets library contains the Sensor Data Sharing Object as well as th }, "objects": [ { - "detected_object_common_data": { - "obj_type": 1, - "object_id": 0, - "obj_type_cfd": 0, - "measurement_time": -1500, - "time_confidence": 39, - "pos": { - "offset_x": -32767, - "offset_y": -32767, - "offset_z": -32767 - }, - "pos_confidence": { - "pos": 1, - "elevation": 15 - }, - "speed": 0, - "speed_confidence": 21845, - "heading": 0, - "heading_conf": 7 - }, - "detected_object_optional_data": { - "detected_vehicle_data": { - "lights": "11110000", - "veh_attitude": { - "pitch": 72000, - "roll": 14400, - "yaw": 14400 - }, - "veh_attitude_confidence": { - "pitch_confidence": 5, - "roll_confidence": 1, - "yaw_confidence": 3 + "detected_object_data": { + "detected_object_common_data": { + "obj_type": 1, + "object_id": 0, + "obj_type_cfd": 0, + "measurement_time": -1500, + "time_confidence": 39, + "pos": { + "offset_x": -32767, + "offset_y": -32767, + "offset_z": -32767 }, - "veh_ang_vel": { - "pitch_rate": 32767, - "roll_rate": 32767 + "pos_confidence": { + "pos": 1, + "elevation": 15 }, - "veh_ang_vel_confidence": { - "pitch_rate_confidence": 4 - }, - "size": { - "width": 4095, - "length": 4095 - }, - "vehicle_size_confidence": { - "vehicle_width_confidence": 13, - "vehicle_length_confidence": 13, - "vehicle_height_confidence": 13 - }, - "height": 127, - "vehicle_class": 23, - "class_conf": 101 + "speed": 0, + "speed_confidence": 21845, + "heading": 0, + "heading_conf": 7 + }, + "detected_object_optional_data": { + "detected_vehicle_data": { + "lights": "11110000", + "veh_attitude": { + "pitch": 72000, + "roll": 14400, + "yaw": 14400 + }, + "veh_attitude_confidence": { + "pitch_confidence": 5, + "roll_confidence": 1, + "yaw_confidence": 3 + }, + "veh_ang_vel": { + "pitch_rate": 32767, + "roll_rate": 32767 + }, + "veh_ang_vel_confidence": { + "pitch_rate_confidence": 4 + }, + "size": { + "width": 4095, + "length": 4095 + }, + "vehicle_size_confidence": { + "vehicle_width_confidence": 13, + "vehicle_length_confidence": 13, + "vehicle_height_confidence": 13 + }, + "height": 127, + "vehicle_class": 23, + "class_conf": 101 + } } } } diff --git a/streets_utils/streets_messages/test/serializers/sensor_data_sharing_msg_json_serialization_test.cpp b/streets_utils/streets_messages/test/serializers/sensor_data_sharing_msg_json_serialization_test.cpp index 332427dbd..af368e19f 100644 --- a/streets_utils/streets_messages/test/serializers/sensor_data_sharing_msg_json_serialization_test.cpp +++ b/streets_utils/streets_messages/test/serializers/sensor_data_sharing_msg_json_serialization_test.cpp @@ -93,9 +93,10 @@ TEST(sensor_data_sharing_msg_json_serialization_test, confirm_required_propertie ASSERT_EQ(1,result.FindMember("objects")->value.GetArray().Size()); ASSERT_TRUE(result["objects"].GetArray()[0].IsObject()); // Confirm object properties - auto object = result["objects"].GetArray()[0].GetObject(); + auto object = result["objects"].GetArray()[0].GetObject()["detected_object_data"].GetObject(); // Assert Object has common data ASSERT_TRUE(object.HasMember("detected_object_common_data")); + ASSERT_TRUE(object.FindMember("detected_object_common_data")->value.IsObject()); ASSERT_FALSE(object.HasMember("detected_object_optional_data")); // Retreive Object common data @@ -197,7 +198,7 @@ TEST(sensor_data_sharing_msg_json_serialization_test, confirm_required_propertie ASSERT_EQ(1,result.FindMember("objects")->value.GetArray().Size()); ASSERT_TRUE(result["objects"].GetArray()[0].IsObject()); // Confirm object properties - auto object = result["objects"].GetArray()[0].GetObject(); + auto object = result["objects"].GetArray()[0].GetObject()["detected_object_data"].GetObject(); // Assert Object has common data ASSERT_TRUE(object.HasMember("detected_object_common_data")); ASSERT_TRUE(object.FindMember("detected_object_common_data")->value.IsObject()); @@ -328,7 +329,7 @@ TEST(sensor_data_sharing_msg_json_serialization_test, confirm_optional_vru_human ASSERT_EQ(1,result.FindMember("objects")->value.GetArray().Size()); ASSERT_TRUE(result["objects"].GetArray()[0].IsObject()); // Confirm object properties - auto object = result["objects"].GetArray()[0].GetObject(); + auto object = result["objects"].GetArray()[0].GetObject()["detected_object_data"].GetObject(); // Assert Object has common data ASSERT_TRUE(object.HasMember("detected_object_common_data")); ASSERT_TRUE(object.FindMember("detected_object_common_data")->value.IsObject()); @@ -473,7 +474,7 @@ TEST(sensor_data_sharing_msg_json_serialization_test, confirm_optional_vru_anima ASSERT_EQ(1,result.FindMember("objects")->value.GetArray().Size()); ASSERT_TRUE(result["objects"].GetArray()[0].IsObject()); // Confirm object properties - auto object = result["objects"].GetArray()[0].GetObject(); + auto object = result["objects"].GetArray()[0].GetObject()["detected_object_data"].GetObject(); // Assert Object has common data ASSERT_TRUE(object.HasMember("detected_object_common_data")); ASSERT_TRUE(object.FindMember("detected_object_common_data")->value.IsObject()); @@ -619,7 +620,7 @@ TEST(sensor_data_sharing_msg_json_serialization_test, confirm_optional_vru_motor ASSERT_EQ(1,result.FindMember("objects")->value.GetArray().Size()); ASSERT_TRUE(result["objects"].GetArray()[0].IsObject()); // Confirm object properties - auto object = result["objects"].GetArray()[0].GetObject(); + auto object = result["objects"].GetArray()[0].GetObject()["detected_object_data"].GetObject(); // Assert Object has common data ASSERT_TRUE(object.HasMember("detected_object_common_data")); ASSERT_TRUE(object.FindMember("detected_object_common_data")->value.IsObject()); @@ -751,7 +752,7 @@ TEST(sensor_data_sharing_msg_json_serialization_test, confirm_optional_obstacle_ ASSERT_EQ(1,result.FindMember("objects")->value.GetArray().Size()); ASSERT_TRUE(result["objects"].GetArray()[0].IsObject()); // Confirm object properties - auto object = result["objects"].GetArray()[0].GetObject(); + auto object = result["objects"].GetArray()[0].GetObject()["detected_object_data"].GetObject(); // Assert Object has common data ASSERT_TRUE(object.HasMember("detected_object_common_data")); ASSERT_TRUE(object.FindMember("detected_object_common_data")->value.IsObject()); @@ -913,7 +914,7 @@ TEST(sensor_data_sharing_msg_json_serialization_test, confirm_optional_vehicle_p ASSERT_EQ(1,result.FindMember("objects")->value.GetArray().Size()); ASSERT_TRUE(result["objects"].GetArray()[0].IsObject()); // Confirm object properties - auto object = result["objects"].GetArray()[0].GetObject(); + auto object = result["objects"].GetArray()[0].GetObject()["detected_object_data"].GetObject(); // Assert Object has common data ASSERT_TRUE(object.HasMember("detected_object_common_data")); ASSERT_TRUE(object.FindMember("detected_object_common_data")->value.IsObject()); From 2cc8b1eab7a635bffed2d3dad5dcc96b26e8e1e3 Mon Sep 17 00:00:00 2001 From: paulbourelly999 <77466294+paulbourelly999@users.noreply.github.com> Date: Tue, 12 Dec 2023 15:39:05 -0500 Subject: [PATCH 54/80] Fix Detected Object message (#376) * Add timestamp to detected object msg Update object ID to int from string * Update sample JSON in documentation * Add json type to markdown for syntax highlighting * Remove transparent comparator --- .../include/sensor_data_sharing_service.hpp | 2 +- .../src/detected_object_to_sdsm_converter.cpp | 2 +- ...detected_object_to_sdsm_converter_test.cpp | 4 +- .../test/sensor_data_sharing_service_test.cpp | 40 +++++++++++++++++-- .../DetectedObjectsMessage.md | 5 ++- .../detected_object_msg.hpp | 3 +- .../detected_obj_msg_deserializer.cpp | 3 +- .../detected_obj_msg_deserialization_test.cpp | 37 ++++++++++++++++- 8 files changed, 83 insertions(+), 13 deletions(-) diff --git a/sensor_data_sharing_service/include/sensor_data_sharing_service.hpp b/sensor_data_sharing_service/include/sensor_data_sharing_service.hpp index 0a05507dc..7d8f01f4a 100644 --- a/sensor_data_sharing_service/include/sensor_data_sharing_service.hpp +++ b/sensor_data_sharing_service/include/sensor_data_sharing_service.hpp @@ -62,7 +62,7 @@ namespace sensor_data_sharing_service { /** * @brief Map of detected objects. New detections of existing objects will replace old detections. */ - std::map> detected_objects; + std::map detected_objects; /** * @brief Mutex for thread safe operations on detected objects map. */ diff --git a/sensor_data_sharing_service/src/detected_object_to_sdsm_converter.cpp b/sensor_data_sharing_service/src/detected_object_to_sdsm_converter.cpp index 661bd3463..c26d3c44f 100644 --- a/sensor_data_sharing_service/src/detected_object_to_sdsm_converter.cpp +++ b/sensor_data_sharing_service/src/detected_object_to_sdsm_converter.cpp @@ -68,7 +68,7 @@ namespace sensor_data_sharing_service{ } detected_object._detected_object_common_data._classification_confidence = static_cast(msg._confidence*100); // TODO: Change Detected Object ID to int - detected_object._detected_object_common_data._object_id = std::stoi(msg._object_id); + detected_object._detected_object_common_data._object_id = msg._object_id; // Units are 0.1 m detected_object._detected_object_common_data._position_offset._offset_x = static_cast(msg._position._x*METERS_TO_10_CM); detected_object._detected_object_common_data._position_offset._offset_y = static_cast(msg._position._y*METERS_TO_10_CM); diff --git a/sensor_data_sharing_service/test/detected_object_to_sdsm_converter_test.cpp b/sensor_data_sharing_service/test/detected_object_to_sdsm_converter_test.cpp index e0715b314..0952b6d99 100644 --- a/sensor_data_sharing_service/test/detected_object_to_sdsm_converter_test.cpp +++ b/sensor_data_sharing_service/test/detected_object_to_sdsm_converter_test.cpp @@ -27,7 +27,7 @@ namespace sensor_data_sharing_service { TEST(detected_object_to_sdsm_converter_test, test_to_detected_object_data) { // Create detected object streets_utils::messages::detected_objects_msg::detected_objects_msg msg; - msg._object_id = "123"; + msg._object_id = 123; msg._type = "TREE"; msg._sensor_id = "sensor_1"; msg._proj_string = "some string"; @@ -43,7 +43,7 @@ namespace sensor_data_sharing_service { msg._size._height = 10; auto data = to_detected_object_data(msg); - EXPECT_EQ(std::stoi(msg._object_id), data._detected_object_common_data._object_id); + EXPECT_EQ(msg._object_id, data._detected_object_common_data._object_id); EXPECT_EQ(streets_utils::messages::sdsm::object_type::UNKNOWN, data._detected_object_common_data._object_type); EXPECT_EQ(90, data._detected_object_common_data._classification_confidence); EXPECT_EQ(static_cast(msg._position._x*10), data._detected_object_common_data._position_offset._offset_x); diff --git a/sensor_data_sharing_service/test/sensor_data_sharing_service_test.cpp b/sensor_data_sharing_service/test/sensor_data_sharing_service_test.cpp index fde324a90..696393db9 100644 --- a/sensor_data_sharing_service/test/sensor_data_sharing_service_test.cpp +++ b/sensor_data_sharing_service/test/sensor_data_sharing_service_test.cpp @@ -47,7 +47,39 @@ namespace sensor_data_sharing_service { EXPECT_CALL(dynamic_cast(*serv.detection_consumer), consume(_)).Times(3).WillOnce(Return("")) .WillOnce(Return("NOT JSON")) .WillOnce(Return( - "{\"type\":\"CAR\",\"confidence\":0.7,\"sensorId\":\"sensor1\",\"projString\":\"projectionString2\",\"objectId\":\"Object7\",\"position\":{\"x\":-1.1,\"y\":-2.0,\"z\":-3.2},\"positionCovariance\":[[1.0,0.0,0.0],[1.0,0.0,0.0],[1.0,0.0,0.0]],\"velocity\":{\"x\":1.0,\"y\":1.0,\"z\":1.0},\"velocityCovariance\":[[1.0,0.0,0.0],[1.0,0.0,0.0],[1.0,0.0,0.0]],\"angularVelocity\":{\"x\":0.1,\"y\":0.2,\"z\":0.3},\"angularVelocityCovariance\":[[1.0,0.0,0.0],[1.0,0.0,0.0],[1.0,0.0,0.0]],\"size\":{\"length\":2.0,\"height\":1.0,\"width\":0.5}}" + R"( + { + "type":"CAR", + "confidence":0.7, + "sensorId":"sensor1", + "projString":"projectionString2", + "objectId":123, + "position":{ + "x":-1.1, + "y":-2.0, + "z":-3.2 + }, + "positionCovariance":[[1.0,0.0,0.0],[1.0,0.0,0.0],[1.0,0.0,0.0]], + "velocity":{ + "x":1.0, + "y":1.0, + "z":1.0 + }, + "velocityCovariance":[[1.0,0.0,0.0],[1.0,0.0,0.0],[1.0,0.0,0.0]], + "angularVelocity":{ + "x":0.1, + "y":0.2, + "z":0.3 + }, + "angularVelocityCovariance":[[1.0,0.0,0.0],[1.0,0.0,0.0],[1.0,0.0,0.0]], + "size":{ + "length":2.0, + "height":1.0, + "width":0.5 + }, + "timestamp":1000 + } + )" )); serv.consume_detections(); EXPECT_EQ(serv.detected_objects.size(), 1); @@ -69,7 +101,7 @@ namespace sensor_data_sharing_service { "confidence":0.7, "sensorId":"sensor1", "projString":"projectionString2", - "objectId":"1", + "objectId":1, "position":{ "x":-1.1, "y":-2.0, @@ -92,7 +124,9 @@ namespace sensor_data_sharing_service { "length":2.0, "height":1.0, "width":0.5 - } + }, + "timestamp":1000 + } )"; auto detected_object = streets_utils::messages::detected_objects_msg::from_json(detected_object_json); diff --git a/streets_utils/streets_messages/DetectedObjectsMessage.md b/streets_utils/streets_messages/DetectedObjectsMessage.md index 0c4132ad5..a4ee05e6e 100644 --- a/streets_utils/streets_messages/DetectedObjectsMessage.md +++ b/streets_utils/streets_messages/DetectedObjectsMessage.md @@ -11,7 +11,7 @@ This CARMA-Streets library contains the Detected Objects Message as well as the "confidence": 0.7, "sensorId": "sensor1", "projString": "projection String2", - "objectId": "Object7", + "objectId": 123, "position": { "x": -1.1, "y": -2, @@ -82,6 +82,7 @@ This CARMA-Streets library contains the Detected Objects Message as well as the "length": 2.0, "height": 1.0, "width": 0.5 - } + }, + "timestamp":1200 } ``` diff --git a/streets_utils/streets_messages/include/detected_object_msg/detected_object_msg.hpp b/streets_utils/streets_messages/include/detected_object_msg/detected_object_msg.hpp index 15d7c5683..f1918a479 100644 --- a/streets_utils/streets_messages/include/detected_object_msg/detected_object_msg.hpp +++ b/streets_utils/streets_messages/include/detected_object_msg/detected_object_msg.hpp @@ -32,7 +32,7 @@ namespace streets_utils::messages::detected_objects_msg { double _confidence; std::string _sensor_id; std::string _proj_string; - std::string _object_id; + unsigned int _object_id; cartesian_point _position; std::vector> _position_covariance; vector_3d _velocity; @@ -40,6 +40,7 @@ namespace streets_utils::messages::detected_objects_msg { vector_3d _angular_velocity; std::vector> _angular_velocity_covariance; size _size; + uint64_t _timestamp; }; diff --git a/streets_utils/streets_messages/src/deserializers/detected_obj_msg_deserializer.cpp b/streets_utils/streets_messages/src/deserializers/detected_obj_msg_deserializer.cpp index e4dab8aa3..9ee3ae821 100644 --- a/streets_utils/streets_messages/src/deserializers/detected_obj_msg_deserializer.cpp +++ b/streets_utils/streets_messages/src/deserializers/detected_obj_msg_deserializer.cpp @@ -27,7 +27,7 @@ namespace streets_utils::messages::detected_objects_msg { msg._confidence = streets_utils::json_utils::parse_double_member("confidence", document, true).value(); msg._sensor_id = streets_utils::json_utils::parse_string_member("sensorId", document, true).value(); msg._proj_string = streets_utils::json_utils::parse_string_member("projString", document, true).value(); - msg._object_id = streets_utils::json_utils::parse_string_member("objectId", document, true).value(); + msg._object_id = streets_utils::json_utils::parse_uint_member("objectId", document, true).value(); auto position_obj = streets_utils::json_utils::parse_object_member("position", document, true).value(); msg._position = parse_cartesian_3d(position_obj); @@ -49,6 +49,7 @@ namespace streets_utils::messages::detected_objects_msg { auto size_obj = streets_utils::json_utils::parse_object_member("size", document, true).value(); msg._size = parse_size(size_obj); + msg._timestamp = streets_utils::json_utils::parse_uint_member("timestamp", document, true).value(); return msg; diff --git a/streets_utils/streets_messages/test/deserializers/detected_obj_msg_deserialization_test.cpp b/streets_utils/streets_messages/test/deserializers/detected_obj_msg_deserialization_test.cpp index 1acde3ce8..675054ed3 100644 --- a/streets_utils/streets_messages/test/deserializers/detected_obj_msg_deserialization_test.cpp +++ b/streets_utils/streets_messages/test/deserializers/detected_obj_msg_deserialization_test.cpp @@ -20,13 +20,45 @@ TEST(detected_obj_msg_deserializer_test, deserialize) { - std::string json_prediction = "{\"type\":\"CAR\",\"confidence\":0.7,\"sensorId\":\"sensor1\",\"projString\":\"projectionString2\",\"objectId\":\"Object7\",\"position\":{\"x\":-1.1,\"y\":-2.0,\"z\":-3.2},\"positionCovariance\":[[1.0,0.0,0.0],[1.0,0.0,0.0],[1.0,0.0,0.0]],\"velocity\":{\"x\":1.0,\"y\":1.0,\"z\":1.0},\"velocityCovariance\":[[1.0,0.0,0.0],[1.0,0.0,0.0],[1.0,0.0,0.0]],\"angularVelocity\":{\"x\":0.1,\"y\":0.2,\"z\":0.3},\"angularVelocityCovariance\":[[1.0,0.0,0.0],[1.0,0.0,0.0],[1.0,0.0,0.0]],\"size\":{\"length\":2.0,\"height\":1.0,\"width\":0.5}}"; + std::string json_prediction = R"( + { + "type":"CAR", + "confidence":0.7, + "sensorId":"sensor1", + "projString":"projectionString2", + "objectId":27, + "position":{ + "x":-1.1, + "y":-2.0, + "z":-3.2 + }, + "positionCovariance":[[1.0,0.0,0.0],[1.0,0.0,0.0],[1.0,0.0,0.0]], + "velocity":{ + "x":1.0, + "y":1.0, + "z":1.0 + }, + "velocityCovariance":[[1.0,0.0,0.0],[1.0,0.0,0.0],[1.0,0.0,0.0]], + "angularVelocity":{ + "x":0.1, + "y":0.2, + "z":0.3 + }, + "angularVelocityCovariance":[[1.0,0.0,0.0],[1.0,0.0,0.0],[1.0,0.0,0.0]], + "size":{ + "length":2.0, + "height":1.0, + "width":0.5 + }, + "timestamp":1702335309 + } + )"; auto msg = streets_utils::messages::detected_objects_msg::from_json(json_prediction); EXPECT_EQ(msg._type, "CAR"); EXPECT_NEAR(0.7,msg._confidence, 0.01); EXPECT_EQ(msg._sensor_id, "sensor1"); - EXPECT_EQ(msg._object_id, "Object7"); + EXPECT_EQ(msg._object_id, 27); EXPECT_NEAR(msg._position._x, -1.1, 0.001); EXPECT_NEAR(msg._position._y, -2.0, 0.001); EXPECT_NEAR(msg._position._z, -3.2, 0.001); @@ -70,5 +102,6 @@ TEST(detected_obj_msg_deserializer_test, deserialize) EXPECT_NEAR(msg._size._length,2.0, 0.001); EXPECT_NEAR(msg._size._height,1.0, 0.001); EXPECT_NEAR(msg._size._width,0.5, 0.001); + EXPECT_EQ(msg._timestamp, 1702335309); } \ No newline at end of file From caa419c710ff93b03a4e5b97f7c6e80fc4a2fe1e Mon Sep 17 00:00:00 2001 From: paulbourelly999 <77466294+paulbourelly999@users.noreply.github.com> Date: Fri, 15 Dec 2023 15:53:49 -0500 Subject: [PATCH 55/80] Fix failing tests (#378) * Fix failing tests * Fix failing signal opt service unit test * Update streets utils signal opt lib to not build tests into library --- coverage.sh | 2 +- message_services/test/test_vehicle_status_intent_service.cpp | 2 ++ signal_opt_service/CMakeLists.txt | 3 +-- .../test/test_signal_opt_processing_worker.cpp | 3 +++ streets_utils/streets_signal_optimization/CMakeLists.txt | 5 +++-- .../test/test_streets_desired_phase_plan_arbitrator.cpp | 4 +--- .../test/test_streets_desired_phase_plan_generator.cpp | 2 ++ .../test/test_signalized_scenario.cpp | 1 + 8 files changed, 14 insertions(+), 8 deletions(-) diff --git a/coverage.sh b/coverage.sh index 20c5be619..f15b5c31d 100755 --- a/coverage.sh +++ b/coverage.sh @@ -15,7 +15,7 @@ # script to run tests, generate test-coverage, and store coverage reports in a place # easily accessible to sonar. Test names should follow convention runTests - +set -e # For lanelet aware streets services like message_services and intersection_model source /opt/ros/melodic/setup.bash source /opt/carma_lanelet2/setup.bash diff --git a/message_services/test/test_vehicle_status_intent_service.cpp b/message_services/test/test_vehicle_status_intent_service.cpp index 03aab60e6..47b6e4d14 100644 --- a/message_services/test/test_vehicle_status_intent_service.cpp +++ b/message_services/test/test_vehicle_status_intent_service.cpp @@ -4,5 +4,7 @@ TEST(test_vehicle_status_intent_service, initialize) { message_services::services::vehicle_status_intent_service service; + streets_service::streets_configuration::create("/home/carma-streets/message_services/manifest.json"); + ASSERT_FALSE(service.initialize()); } \ No newline at end of file diff --git a/signal_opt_service/CMakeLists.txt b/signal_opt_service/CMakeLists.txt index a76ee77c3..5a5229320 100644 --- a/signal_opt_service/CMakeLists.txt +++ b/signal_opt_service/CMakeLists.txt @@ -71,12 +71,11 @@ target_link_libraries(${PROJECT_NAME} PUBLIC ${PROJECT_NAME}_lib ) SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -pthread") set(BINARY ${PROJECT_NAME}_test) file(GLOB_RECURSE TEST_SOURCES LIST_DIRECTORIES false test/*.h test/*.cpp) -set(SOURCES ${TEST_SOURCES} WORKING_DIRECTORY ${PROJECT_SOURCE_DIR}/test) add_executable(${BINARY} ${TEST_SOURCES}) add_test(NAME ${BINARY} COMMAND ${BINARY}) target_include_directories(${BINARY} PUBLIC ${PROJECT_SOURCE_DIR}/include) target_link_libraries(${BINARY} - PUBLIC + PRIVATE ${PROJECT_NAME}_lib spdlog::spdlog rapidjson diff --git a/signal_opt_service/test/test_signal_opt_processing_worker.cpp b/signal_opt_service/test/test_signal_opt_processing_worker.cpp index f7b96de78..314b0232e 100644 --- a/signal_opt_service/test/test_signal_opt_processing_worker.cpp +++ b/signal_opt_service/test/test_signal_opt_processing_worker.cpp @@ -19,6 +19,8 @@ namespace signal_opt_service protected: void SetUp() override { + streets_service::streets_configuration::create("/home/carma-streets/message_services/manifest.json"); + streets_service::streets_clock_singleton::create(false); tsc_state = std::make_shared(); std::chrono::system_clock::time_point now = std::chrono::system_clock::now(); std::chrono::milliseconds epochMs = std::chrono::duration_cast(now.time_since_epoch()); @@ -247,6 +249,7 @@ namespace signal_opt_service TEST_F(test_signal_opt_processing_worker, select_optimal_dpp) { + auto so_processing_worker = std::make_shared(); std::chrono::system_clock::time_point now = std::chrono::system_clock::now(); std::chrono::milliseconds epochMs = std::chrono::duration_cast(now.time_since_epoch()); diff --git a/streets_utils/streets_signal_optimization/CMakeLists.txt b/streets_utils/streets_signal_optimization/CMakeLists.txt index 357ee1e4d..0ba39572b 100644 --- a/streets_utils/streets_signal_optimization/CMakeLists.txt +++ b/streets_utils/streets_signal_optimization/CMakeLists.txt @@ -23,7 +23,7 @@ find_package(carma-clock REQUIRED) ######################################################## # Build Library ######################################################## -file(GLOB_RECURSE SOURCES ${CMAKE_CURRENT_SOURCE_DIR}/src/ *.cpp) +file(GLOB_RECURSE SOURCES ${CMAKE_CURRENT_SOURCE_DIR}/src/*.cpp) add_library(${PROJECT_NAME}_lib ${SOURCES} ) target_include_directories(${PROJECT_NAME}_lib PUBLIC @@ -83,9 +83,10 @@ file(GLOB_RECURSE TEST_SOURCES LIST_DIRECTORIES false test/*.h test/*.cpp) set(SOURCES ${TEST_SOURCES} WORKING_DIRECTORY ${PROJECT_SOURCE_DIR}/test) add_executable(${BINARY} ${TEST_SOURCES} ) add_test(NAME ${BINARY} COMMAND ${BINARY}) +message(status "Test sources ${TEST_SOURCES}") target_include_directories(${BINARY} PUBLIC ${PROJECT_SOURCE_DIR}/include) target_link_libraries(${BINARY} - PUBLIC + PRIVATE ${PROJECT_NAME}_lib rapidjson ) diff --git a/streets_utils/streets_signal_optimization/test/test_streets_desired_phase_plan_arbitrator.cpp b/streets_utils/streets_signal_optimization/test/test_streets_desired_phase_plan_arbitrator.cpp index 3ec5133c7..2be1651a4 100644 --- a/streets_utils/streets_signal_optimization/test/test_streets_desired_phase_plan_arbitrator.cpp +++ b/streets_utils/streets_signal_optimization/test/test_streets_desired_phase_plan_arbitrator.cpp @@ -31,9 +31,7 @@ namespace streets_signal_optimization protected: void SetUp() override { - - - + streets_service::streets_clock_singleton::create(false); tsc_state = std::make_shared(); std::chrono::system_clock::time_point now = std::chrono::system_clock::now(); std::chrono::milliseconds epochMs = std::chrono::duration_cast(now.time_since_epoch()); diff --git a/streets_utils/streets_signal_optimization/test/test_streets_desired_phase_plan_generator.cpp b/streets_utils/streets_signal_optimization/test/test_streets_desired_phase_plan_generator.cpp index fbcd9555f..bbcf25985 100644 --- a/streets_utils/streets_signal_optimization/test/test_streets_desired_phase_plan_generator.cpp +++ b/streets_utils/streets_signal_optimization/test/test_streets_desired_phase_plan_generator.cpp @@ -69,6 +69,7 @@ TEST(test_streets_desired_phase_plan_generator, test_set_configuration) { * red-clearance for 2 seconds. */ TEST(test_streets_desired_phase_plan_generator, test_convert_spat_to_dpp) { + streets_service::streets_clock_singleton::create(false); /** * spat: @@ -276,6 +277,7 @@ TEST(test_streets_desired_phase_plan_generator, test_signal_group_entry_lane_map namespace streets_signal_optimization { TEST(test_streets_desired_phase_plan_generator, test_generate_desired_phase_plan_list) { + streets_service::streets_clock_singleton::create(false); streets_desired_phase_plan_generator generator; diff --git a/streets_utils/streets_vehicle_scheduler/test/test_signalized_scenario.cpp b/streets_utils/streets_vehicle_scheduler/test/test_signalized_scenario.cpp index a663075d9..fef280ff7 100644 --- a/streets_utils/streets_vehicle_scheduler/test/test_signalized_scenario.cpp +++ b/streets_utils/streets_vehicle_scheduler/test/test_signalized_scenario.cpp @@ -31,6 +31,7 @@ namespace { */ void SetUp() override { + streets_service::streets_clock_singleton::create(false); schedule = std::make_shared(); // the schedule timestamp is set to 10000 tenths of seconds from the current hour. uint64_t hour_tenth_secs = 10000; From acccf7673cee2e435d3c4a6b5914ac36096e8807 Mon Sep 17 00:00:00 2001 From: paulbourelly999 <77466294+paulbourelly999@users.noreply.github.com> Date: Mon, 18 Dec 2023 13:39:04 -0500 Subject: [PATCH 56/80] Fix SDSM JSON serialization (#380) * Fix SDSM JSON serialization * Update message count to not exceed 127 * Remove unnecessary constructor call --- .../src/sensor_data_sharing_service.cpp | 9 +++++++-- .../sensor_data_sharing_msg/position_confidence_set.hpp | 4 ++-- 2 files changed, 9 insertions(+), 4 deletions(-) diff --git a/sensor_data_sharing_service/src/sensor_data_sharing_service.cpp b/sensor_data_sharing_service/src/sensor_data_sharing_service.cpp index 1349db380..62ca8d3fe 100644 --- a/sensor_data_sharing_service/src/sensor_data_sharing_service.cpp +++ b/sensor_data_sharing_service/src/sensor_data_sharing_service.cpp @@ -123,7 +123,12 @@ namespace sensor_data_sharing_service { const std::string json_msg = streets_utils::messages::sdsm::to_json(msg); SPDLOG_DEBUG("Sending SDSM : {0}", json_msg); sdsm_producer->send(json_msg); - this->_message_count++; + // Message Count max is 127, reset after max value + if ( this->_message_count <= 127) { + this->_message_count++; + }else { + this->_message_count = 0; + } // Clear detected object detected_objects.clear(); } @@ -131,7 +136,7 @@ namespace sensor_data_sharing_service { catch( const streets_utils::json_utils::json_parse_exception &e) { SPDLOG_ERROR("Exception occurred producing SDSM : {0}", e.what()); } - ss::streets_clock_singleton::sleep_for(1000); // Sleep for 10 second between publish + ss::streets_clock_singleton::sleep_for(100); // Sleep for 100 ms between publish } } diff --git a/streets_utils/streets_messages/include/sensor_data_sharing_msg/position_confidence_set.hpp b/streets_utils/streets_messages/include/sensor_data_sharing_msg/position_confidence_set.hpp index 47a77892b..a66dbaa4c 100644 --- a/streets_utils/streets_messages/include/sensor_data_sharing_msg/position_confidence_set.hpp +++ b/streets_utils/streets_messages/include/sensor_data_sharing_msg/position_confidence_set.hpp @@ -17,8 +17,8 @@ namespace streets_utils::messages::sdsm { struct position_confidence_set{ - position_confidence _position_confidence; - position_confidence _elevation_confidence; + position_confidence _position_confidence = position_confidence::UNAVAILABLE; + position_confidence _elevation_confidence = position_confidence::UNAVAILABLE; }; } \ No newline at end of file From 12c3fafef2efeeb830761b9847b6216e210d323d Mon Sep 17 00:00:00 2001 From: paulbourelly999 <77466294+paulbourelly999@users.noreply.github.com> Date: Mon, 18 Dec 2023 15:24:09 -0500 Subject: [PATCH 57/80] Remove unreachable break statements (#381) --- .../streets_snmp_cmd/src/models/streets_snmp_cmd_converter.cpp | 1 - tsc_client_service/src/spat_projection_mode.cpp | 3 --- 2 files changed, 4 deletions(-) diff --git a/streets_utils/streets_snmp_cmd/src/models/streets_snmp_cmd_converter.cpp b/streets_utils/streets_snmp_cmd/src/models/streets_snmp_cmd_converter.cpp index 33595236a..681bf92bf 100644 --- a/streets_utils/streets_snmp_cmd/src/models/streets_snmp_cmd_converter.cpp +++ b/streets_utils/streets_snmp_cmd/src/models/streets_snmp_cmd_converter.cpp @@ -375,7 +375,6 @@ namespace streets_snmp_cmd break; default: throw streets_snmp_cmd_exception("Phase control schedule command type does not have a mapping SNMP phase control type."); - break; } return result; } diff --git a/tsc_client_service/src/spat_projection_mode.cpp b/tsc_client_service/src/spat_projection_mode.cpp index b3da700d4..a2b9f7011 100644 --- a/tsc_client_service/src/spat_projection_mode.cpp +++ b/tsc_client_service/src/spat_projection_mode.cpp @@ -6,13 +6,10 @@ namespace traffic_signal_controller_service{ { case 0: return SPAT_PROJECTION_MODE::NO_PROJECTION; - break; case 1: return SPAT_PROJECTION_MODE::DPP_PROJECTION; - break; case 2: return SPAT_PROJECTION_MODE::FIXED_TIMING_PROJECTION; - break; default: return SPAT_PROJECTION_MODE::NO_PROJECTION; } From ce82d6bc5be696cb205090ee584fba033e54a49d Mon Sep 17 00:00:00 2001 From: paulbourelly999 <77466294+paulbourelly999@users.noreply.github.com> Date: Tue, 19 Dec 2023 14:18:29 -0500 Subject: [PATCH 58/80] Fix sonar bug garage initialized value (#383) * Fix sonar bug garage initialized value * Update to test whether bug still exists * Fix typo --- .../vehicle/vehicle_size_confidence.hpp | 2 +- .../sensor_data_sharing_msg_json_deserializer.cpp | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/streets_utils/streets_messages/include/sensor_data_sharing_msg/vehicle/vehicle_size_confidence.hpp b/streets_utils/streets_messages/include/sensor_data_sharing_msg/vehicle/vehicle_size_confidence.hpp index 1ae7b1b63..afa8e2007 100644 --- a/streets_utils/streets_messages/include/sensor_data_sharing_msg/vehicle/vehicle_size_confidence.hpp +++ b/streets_utils/streets_messages/include/sensor_data_sharing_msg/vehicle/vehicle_size_confidence.hpp @@ -15,7 +15,7 @@ #include "sensor_data_sharing_msg/size_value_confidence.hpp" #include namespace streets_utils::messages::sdsm{ - struct vehicle_size_confidence{ + struct vehicle_size_confidence { /** * @brief Confidence in reported width */ diff --git a/streets_utils/streets_messages/src/deserializers/sensor_data_sharing_msg_json_deserializer.cpp b/streets_utils/streets_messages/src/deserializers/sensor_data_sharing_msg_json_deserializer.cpp index 510809ec2..6e749b85a 100644 --- a/streets_utils/streets_messages/src/deserializers/sensor_data_sharing_msg_json_deserializer.cpp +++ b/streets_utils/streets_messages/src/deserializers/sensor_data_sharing_msg_json_deserializer.cpp @@ -171,8 +171,8 @@ namespace streets_utils::messages::sdsm { detected_vehicle_data parse_detected_vehicle_data(const rapidjson::Value &val){ detected_vehicle_data data; - data.exterior_lights = parse_string_member("lights", val, false); { - if ( val.HasMember("veh_attitude")) + data.exterior_lights = parse_string_member("lights", val, false); + if ( val.HasMember("veh_attitude")) { data._veh_attitude = parse_vehicle_attitude(parse_object_member("veh_attitude", val, false).value()); } if ( val.HasMember("veh_attitude_confidence")) { From 84be5d2e9d35e436d795c9b0fa37e49deea591e9 Mon Sep 17 00:00:00 2001 From: paulbourelly999 <77466294+paulbourelly999@users.noreply.github.com> Date: Wed, 20 Dec 2023 15:39:21 -0500 Subject: [PATCH 59/80] Detection mocking test scripts (#382) --- scripts/{readme.md => README.md} | 18 +++-- ...cted_object.json => detected_cyclist.json} | 7 +- scripts/detected_pedestrian.json | 79 +++++++++++++++++++ scripts/detected_truck.json | 79 +++++++++++++++++++ scripts/simulate_detected_object.py | 17 +++- 5 files changed, 188 insertions(+), 12 deletions(-) rename scripts/{readme.md => README.md} (62%) rename scripts/{detected_object.json => detected_cyclist.json} (93%) create mode 100644 scripts/detected_pedestrian.json create mode 100644 scripts/detected_truck.json diff --git a/scripts/readme.md b/scripts/README.md similarity index 62% rename from scripts/readme.md rename to scripts/README.md index 3a26cc60a..7649c6d26 100644 --- a/scripts/readme.md +++ b/scripts/README.md @@ -1,8 +1,16 @@ +# Testing Scripts +## Introduction +The python scripts and json files in this directory are used for mocking message communication to provide an environment in which CARMA Streets Service functionality can be integration tested in isolation. Directions for using the scripts should be included below under the appropriate heading. -Requirements: - pip3 install -r requirements.txt - - +## Prerequisites +Please add any script dependencies into requirements.txt so that they can be easily installed using +```sh +pip3 install -r requirements.txt +``` +## simulate_detections.py +### Description +This script is used to mock Detected Object message sent from sensor integrated with CARMA Streets. Currently this only includes CARLA Lidar sensor integrated under the Vunerable Road User Use Case 1 project. It will currently read `detected_cyclist.json`, `detected_pedestrian.json`, and `detected_truck.json` and publish those messages at 10 hz to the detections kafka topic. This script is used to provide input to the **Sensor Data Sharing Service** to generate J2735 SDSMs. To add new detections, create a new json file and update the script to send its content on a given interval. For json format of the [Detected Object Message](../streets_utils/streets_messages/DetectedObjectsMessage.md). +## simulate_vehicle_status_intent_pub_uc1.py Instructions for sending sample status and intent messages for UC1 scheduling logic: - In the simulate_vehicle_status_intent_pub_uc1.py, change the input of the read_json() method on line 23 to @@ -19,7 +27,7 @@ Instructions for sending sample status and intent messages for UC1 scheduling lo * Note: One of the limitation of the python script is that the number of vehicles in each set of updates shall be the same. Otherwise, the time gap between two set of updates will not be correctly applied. - +## simulate_vehicle_status_intent_pub_uc3.py Instructions for sending sample status and intent messages for UC3 scheduling logic: - In the simulate_vehicle_status_intent_pub_uc3.py, change the input of the read_json() method on line 23 to diff --git a/scripts/detected_object.json b/scripts/detected_cyclist.json similarity index 93% rename from scripts/detected_object.json rename to scripts/detected_cyclist.json index f72f08340..b489f576a 100644 --- a/scripts/detected_object.json +++ b/scripts/detected_cyclist.json @@ -1,9 +1,9 @@ { - "type": "BICYCLE", + "type": "CYCLIST", "confidence": 0.7, "sensorId": "sensor1", "projString": "projection String2", - "objectId": "1", + "objectId": 1, "position": { "x": -1.1, "y": -2.0, @@ -74,5 +74,6 @@ "length": 2.0, "height": 1.0, "width": 0.5 - } + }, + "timestamp": 1230 } \ No newline at end of file diff --git a/scripts/detected_pedestrian.json b/scripts/detected_pedestrian.json new file mode 100644 index 000000000..27ff66afe --- /dev/null +++ b/scripts/detected_pedestrian.json @@ -0,0 +1,79 @@ +{ + "type": "PEDESTRIAN", + "confidence": 0.7, + "sensorId": "sensor1", + "projString": "projection String2", + "objectId": 2, + "position": { + "x": -1.1, + "y": -2.0, + "z": -3.2 + }, + "positionCovariance": [ + [ + 1.0, + 0.0, + 0.0 + ], + [ + 1.0, + 0.0, + 0.0 + ], + [ + 1.0, + 0.0, + 0.0 + ] + ], + "velocity": { + "x": 0.2, + "y": 0.5, + "z": 0.0 + }, + "velocityCovariance": [ + [ + 1.0, + 0.0, + 0.0 + ], + [ + 1.0, + 0.0, + 0.0 + ], + [ + 1.0, + 0.0, + 0.0 + ] + ], + "angularVelocity": { + "x": 0.1, + "y": 0.2, + "z": 0.3 + }, + "angularVelocityCovariance": [ + [ + 1.0, + 0.0, + 0.0 + ], + [ + 1.0, + 0.0, + 0.0 + ], + [ + 1.0, + 0.0, + 0.0 + ] + ], + "size": { + "length": 0.5, + "height": 2.0, + "width": 0.5 + }, + "timestamp": 1230 + } \ No newline at end of file diff --git a/scripts/detected_truck.json b/scripts/detected_truck.json new file mode 100644 index 000000000..df3caebb4 --- /dev/null +++ b/scripts/detected_truck.json @@ -0,0 +1,79 @@ +{ + "type": "TRUCK", + "confidence": 0.7, + "sensorId": "sensor1", + "projString": "projection String2", + "objectId": 3, + "position": { + "x": -1.1, + "y": -2.0, + "z": -3.2 + }, + "positionCovariance": [ + [ + 1.0, + 0.0, + 0.0 + ], + [ + 1.0, + 0.0, + 0.0 + ], + [ + 1.0, + 0.0, + 0.0 + ] + ], + "velocity": { + "x": 0.2, + "y": 0.5, + "z": 0.0 + }, + "velocityCovariance": [ + [ + 1.0, + 0.0, + 0.0 + ], + [ + 1.0, + 0.0, + 0.0 + ], + [ + 1.0, + 0.0, + 0.0 + ] + ], + "angularVelocity": { + "x": 0.1, + "y": 0.2, + "z": 0.3 + }, + "angularVelocityCovariance": [ + [ + 1.0, + 0.0, + 0.0 + ], + [ + 1.0, + 0.0, + 0.0 + ], + [ + 1.0, + 0.0, + 0.0 + ] + ], + "size": { + "length": 0.5, + "height": 2.0, + "width": 0.5 + }, + "timestamp": 1230 + } \ No newline at end of file diff --git a/scripts/simulate_detected_object.py b/scripts/simulate_detected_object.py index a6edf7401..67e08b7e7 100644 --- a/scripts/simulate_detected_object.py +++ b/scripts/simulate_detected_object.py @@ -21,10 +21,19 @@ def read_json(json_name): # Send a new schedule curr_time = round(time.time()*1000) - data = read_json('detected_object.json') - producer.send('detections', value=data) - print(f'Sent a detection.{data}') - producer.flush() + while True : + data = read_json('detected_pedestrian.json') + producer.send('detections', value=data) + print(f'Sent a detection.{data}') + producer.flush() + data = read_json('detected_cyclist.json') + producer.send('detections', value=data) + print(f'Sent a detection.{data}') + data = read_json('detected_truck.json') + producer.send('detections', value=data) + print(f'Sent a detection.{data}') + producer.flush() + time.sleep(0.1) \ No newline at end of file From dff51d21699615a06fdcc43f7f26594cee628da6 Mon Sep 17 00:00:00 2001 From: paulbourelly999 <77466294+paulbourelly999@users.noreply.github.com> Date: Fri, 22 Dec 2023 14:46:35 -0500 Subject: [PATCH 60/80] Fix Sensor Data Sharing Serivice Docker Image (#384) --- sensor_data_sharing_service/Dockerfile | 5 +++-- sensor_data_sharing_service/build.sh | 4 +++- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/sensor_data_sharing_service/Dockerfile b/sensor_data_sharing_service/Dockerfile index c72be2e93..3bb08cfe0 100644 --- a/sensor_data_sharing_service/Dockerfile +++ b/sensor_data_sharing_service/Dockerfile @@ -3,6 +3,8 @@ COPY ./sensor_data_sharing_service/ /home/carma-streets/sensor_data_sharing_serv COPY ./streets_utils/ /home/carma-streets/streets_utils COPY ./kafka_clients/ /home/carma-streets/kafka_clients RUN /home/carma-streets/sensor_data_sharing_service/build.sh +RUN chmod u+x /opt/carma_lanelet2/setup.bash + LABEL org.label-schema.schema-version="1.0" LABEL org.label-schema.name="sensor_data_sharing_service" LABEL org.label-schema.description="Image for Sensor Data Sharing Service" @@ -14,5 +16,4 @@ LABEL org.label-schema.vcs-ref=${VCS_REF} LABEL org.label-schema.build-date=${BUILD_DATE} - -CMD ["/home/carma-streets/sensor_data_sharing_service/build/sensor_data_sharing_service"] \ No newline at end of file +ENTRYPOINT ["/bin/bash", "-c", "source /opt/carma_lanelet2/setup.bash && /home/carma-streets/sensor_data_sharing_service/build/sensor_data_sharing_service"] \ No newline at end of file diff --git a/sensor_data_sharing_service/build.sh b/sensor_data_sharing_service/build.sh index a81bccb94..7510f071b 100755 --- a/sensor_data_sharing_service/build.sh +++ b/sensor_data_sharing_service/build.sh @@ -15,6 +15,7 @@ # Build script to build Sensor Data Sharing Service set -e +source /opt/carma_lanelet2/setup.bash COVERAGE_FLAGS="" @@ -42,4 +43,5 @@ for DIR in "${MAKE_INSTALL_DIRS[@]}" "${MAKE_ONLY_DIRS[@]}"; do cmake --install build fi done -done \ No newline at end of file +done +ldconfig \ No newline at end of file From 4bc92ca931d3ab792f2595f47538aa912590ca3a Mon Sep 17 00:00:00 2001 From: paulbourelly999 <77466294+paulbourelly999@users.noreply.github.com> Date: Wed, 27 Dec 2023 17:36:27 -0500 Subject: [PATCH 61/80] Adding conversion logic covariance to accuracy/confidence measures (#385) * Adding conversion logic covariance to accuracy/confidence measures * Add velocity to heading calculation * Add velocity covariance to speed confidence translation * Added yaw rate confidence * Include yaw rate conversion from radians per second to 100th of degree per second * Update documentation * Update documentation --- sensor_data_sharing_service/README.md | 11 +- sensor_data_sharing_service/docs/image.png | Bin 0 -> 69131 bytes .../detected_object_to_sdsm_converter.hpp | 63 ++++++ .../src/detected_object_to_sdsm_converter.cpp | 187 ++++++++++++++++++ ...detected_object_to_sdsm_converter_test.cpp | 130 +++++++++++- 5 files changed, 386 insertions(+), 5 deletions(-) create mode 100644 sensor_data_sharing_service/docs/image.png diff --git a/sensor_data_sharing_service/README.md b/sensor_data_sharing_service/README.md index d7e990125..57114c8e2 100644 --- a/sensor_data_sharing_service/README.md +++ b/sensor_data_sharing_service/README.md @@ -4,4 +4,13 @@ This is the **CARMA Streets** service responsible for consuming [detected_object ## Configuration Parameters **TODO** Saving this part for once I finalize the implementation > [!IMPORTANT]\ -> Initial implementation of this service will not support the BSM functionality described in the J3224 specification and will also not support any data fusion from multiple infrastructure sensors. \ No newline at end of file +> Initial implementation of this service will not support the BSM functionality described in the J3224 specification and will also not support any data fusion from multiple infrastructure sensors. +## Covariance to confidence conversion +![image](https://github.com/usdot-fhwa-stol/carma-streets/assets/77466294/b5f4d827-7768-40ba-8674-3115ff4a338c) + +For covariance to confidence/accuracy translation assumed the following. All confidence/accuracy measurements in SDSM assume 95% confidence interval ( from J3224 documentation). Assuming normal distribution of measurements. Diagonal in covariance matrix is variance for each component. SD (Standard deviation) is square root of variance. 2 standard deviations covers 95 % of sample in normal distribution +![image](https://github.com/usdot-fhwa-stol/carma-streets/assets/77466294/4a6e9875-2f87-4a8f-b1a0-33a6769439ac) + +## Sensor Data Sharing speed and heading +For heading and speed we use following coordinate frame as described in the J3224 specifications. +![Alt text](docs/image.png) \ No newline at end of file diff --git a/sensor_data_sharing_service/docs/image.png b/sensor_data_sharing_service/docs/image.png new file mode 100644 index 0000000000000000000000000000000000000000..9d5fca349174eb2e45bf1d750ac486ffd691f30e GIT binary patch literal 69131 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%0Akja*-Nvnk; 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 #include #include +#include namespace sensor_data_sharing_service { /** @@ -60,4 +61,66 @@ namespace sensor_data_sharing_service { * @return streets_utils::messages::sdsm::object_type */ streets_utils::messages::sdsm::object_type to_object_type(const std::string &detection_type); + /** + * @brief Convert XY velocity into 2 dimensional heading measured in 0.0125 degree units + * @param velocity vector of velocity + * @return 2 dimensional heading in units of 0.0125 degrees + */ + unsigned int to_heading(const streets_utils::messages::detected_objects_msg::vector_3d &velocity); + /** + * @brief Convert position covariance to position confidence set which includes positionXY confidence + * and position z confidence. + * @param _position_covariance + * @return position confidence set. + */ + streets_utils::messages::sdsm::position_confidence_set to_position_confidence_set( const std::vector> &_position_covariance); + /** + * @brief Convert velocity covariance to xy speed confidence. + * @param velocity_covariance + * @return xy speed confidence + */ + streets_utils::messages::sdsm::speed_confidence to_xy_speed_confidence(const std::vector> &velocity_covariance); + /** + * @brief Convert velocity covariance to z speed confidence. + * @param velocity_covariance + * @return z speed confidence + */ + streets_utils::messages::sdsm::speed_confidence to_z_speed_confidence(const std::vector> &velocity_covariance); + /** + * @brief Convert accuracy value to speed confidence enumeration. To minimize error in conversion, method + * rounds up/down to the nearest enum value + * @param accuracy + * @return speed confidence enumeration + */ + streets_utils::messages::sdsm::speed_confidence to_speed_confidence(const double accuracy); + /** + * @brief Convert angular velocity covariance to angular velocity confidence. + * @param angular_velocity_covariance + * @return angular velocity confidence + */ + streets_utils::messages::sdsm::angular_velocity_confidence to_yaw_rate_confidence( const std::vector> &angular_velocity_covariance ); + /** + * @brief Convert accuracy value to position confidence enumeration. To minimize error in conversion, method + * rounds up/down to the nearest enum value + * @param accuracy + * @return position confidence enumeration + */ + streets_utils::messages::sdsm::position_confidence to_position_confidence(const double accuracy); + /** + * @brief Convert accuracy value to angular velocity confidence enumeration. To minimize error in conversion, method + * rounds up/down to the nearest enum value + * @param accuracy + * @return angular velocity confidence + */ + streets_utils::messages::sdsm::angular_velocity_confidence to_angular_velocity_confidence(const double accuracy); + + /** + * @brief Convert yaw rate in radians per second to 100ths of a degree per second + * @param yaw_rate_radians_per_second + * @return yaw rate in 0.01 degrees/second. + */ + int to_yaw_rate( const double yaw_rate_radians_per_second ); + + + } \ No newline at end of file diff --git a/sensor_data_sharing_service/src/detected_object_to_sdsm_converter.cpp b/sensor_data_sharing_service/src/detected_object_to_sdsm_converter.cpp index c26d3c44f..0557ae5c1 100644 --- a/sensor_data_sharing_service/src/detected_object_to_sdsm_converter.cpp +++ b/sensor_data_sharing_service/src/detected_object_to_sdsm_converter.cpp @@ -73,9 +73,28 @@ namespace sensor_data_sharing_service{ detected_object._detected_object_common_data._position_offset._offset_x = static_cast(msg._position._x*METERS_TO_10_CM); detected_object._detected_object_common_data._position_offset._offset_y = static_cast(msg._position._y*METERS_TO_10_CM); detected_object._detected_object_common_data._position_offset._offset_z = static_cast(msg._position._z*METERS_TO_10_CM); + // Position Confidence + detected_object._detected_object_common_data._pos_confidence = to_position_confidence_set(msg._position_covariance); // Units are 0.02 m/s detected_object._detected_object_common_data._speed = static_cast(std::hypot( msg._velocity._x* METERS_PER_SECOND_TO_2_CM_PER_SECOND, msg._velocity._y* METERS_PER_SECOND_TO_2_CM_PER_SECOND)); + // Speed confidence + detected_object._detected_object_common_data._speed_confidence = to_xy_speed_confidence(msg._velocity_covariance); + // Speed Z detected_object._detected_object_common_data._speed_z = static_cast(msg._velocity._z* METERS_PER_SECOND_TO_2_CM_PER_SECOND); + // Speed Z confidence + detected_object._detected_object_common_data._speed_z_confidence = to_z_speed_confidence(msg._velocity_covariance); + // Heading + detected_object._detected_object_common_data._heading = to_heading(msg._velocity); + // TODO: how to calculate heading confidence without orientation covariance + // Possible approximation is velocity covariance since we are using that currently + // Currently hard coding value + detected_object._detected_object_common_data._heading_confidence = streets_utils::messages::sdsm::heading_confidence::PREC_10_deg; + // Yaw rate + detected_object._detected_object_common_data._acceleration_4_way->_yaw_rate = to_yaw_rate(msg._angular_velocity._z); + // Yaw rate confidence + detected_object._detected_object_common_data._yaw_rate_confidence = to_yaw_rate_confidence(msg._angular_velocity_covariance); + + return detected_object; } @@ -86,4 +105,172 @@ namespace sensor_data_sharing_service{ return streets_utils::messages::sdsm::object_type::UNKNOWN; } + streets_utils::messages::sdsm::position_confidence_set to_position_confidence_set(const std::vector> &_position_covariance) { + auto x_variance = _position_covariance[0][0]; + auto y_variance = _position_covariance[1][1]; + auto z_variance = _position_covariance[2][2]; + // Assuming normal distribution and 95 % confidence interval (From J3224 Specification documentation of all accuracy/confidence measures) + // +/- 2*variance to achieve 95% confidence interval. + // TODO: If position variance x is different from y how do we handle this? For now assuming X variance is the same as Y and justing using + // X + // Multiply variance by 2 get 95% confidence interval for normal distribution + auto xy_accuracy = sqrt(x_variance) * 2 ; + auto z_accuracy = sqrt(z_variance) * 2; + streets_utils::messages::sdsm::position_confidence_set position_confidence_set; + position_confidence_set._position_confidence = to_position_confidence(xy_accuracy); + position_confidence_set._elevation_confidence = to_position_confidence(z_accuracy); + return position_confidence_set; + } + + streets_utils::messages::sdsm::position_confidence to_position_confidence(const double accuracy) { + // Check to see if accuracy is greater than or equal to half way between to confidence enumeration + if ( accuracy >= 350) { + return streets_utils::messages::sdsm::position_confidence::A_500M; + } + else if (accuracy >= 150) { + return streets_utils::messages::sdsm::position_confidence::A_200M; + } + else if (accuracy >= 75) { + return streets_utils::messages::sdsm::position_confidence::A_100M; + } + else if (accuracy >= 35) { + return streets_utils::messages::sdsm::position_confidence::A_50M; + } + else if (accuracy >= 15) { + return streets_utils::messages::sdsm::position_confidence::A_20M; + } + else if (accuracy >= 7.5) { + return streets_utils::messages::sdsm::position_confidence::A_10M; + } + else if (accuracy >= 3.5) { + return streets_utils::messages::sdsm::position_confidence::A_5M; + } + else if (accuracy >= 1.5) { + return streets_utils::messages::sdsm::position_confidence::A_2M; + } + else if (accuracy >= 0.75) { + return streets_utils::messages::sdsm::position_confidence::A_1M; + } + else if (accuracy >= 0.35) { + return streets_utils::messages::sdsm::position_confidence::A_50CM; + } + else if (accuracy >= 0.15) { + return streets_utils::messages::sdsm::position_confidence::A_20CM; + } + else if (accuracy >= 0.075) { + return streets_utils::messages::sdsm::position_confidence::A_10CM; + } + else if (accuracy >= 0.035) { + return streets_utils::messages::sdsm::position_confidence::A_5CM; + } + else if (accuracy >= 0.015) { + return streets_utils::messages::sdsm::position_confidence::A_2CM; + } + else { + // This is the lowest position confidence the SDSM can reflect + return streets_utils::messages::sdsm::position_confidence::A_1CM; + } + + } + + unsigned int to_heading(const streets_utils::messages::detected_objects_msg::vector_3d &velocity){ + auto heading_radians = std::atan2(velocity._y,velocity._x); + auto heading_degrees = heading_radians*(180/M_PI); + // If angle is negative 360 + (-negative angle) + if ( heading_degrees < 0 ) { + heading_degrees = 360 + heading_degrees; + } + // in units (x) = 0.0125 degrees + // 28800 x = 360 degrees + return static_cast(heading_degrees * (28800/360)); + } + + streets_utils::messages::sdsm::speed_confidence to_xy_speed_confidence(const std::vector> &velocity_covariance) { + auto x_variance = velocity_covariance[0][0]; + auto y_variance = velocity_covariance[1][1]; + // Assuming normal distribution and 95 % confidence interval (From J3224 Specification documentation of all accuracy/confidence measures) + // +/- 2*variance to achieve 95% confidence interval. + // TODO: If position variance x is different from y how do we handle this? For now assuming X variance is the same as Y and justing using + // X + // Multiply variance by 2 get 95% confidence interval for normal distribution + auto xy_accuracy = sqrt(x_variance) * 2 ; + return to_speed_confidence(xy_accuracy); + } + + streets_utils::messages::sdsm::speed_confidence to_z_speed_confidence(const std::vector> &velocity_covariance) { + auto z_variance = velocity_covariance[2][2]; + // Assuming normal distribution and 95 % confidence interval (From J3224 Specification documentation of all accuracy/confidence measures) + // +/- 2*variance to achieve 95% confidence interval. + + // Multiply variance by 2 get 95% confidence interval for normal distribution + auto z_accuracy = sqrt(z_variance) * 2 ; + return to_speed_confidence(z_accuracy); + } + + streets_utils::messages::sdsm::speed_confidence to_speed_confidence(const double accuracy) { + // Check to see if accuracy is greater than or equal to half way between to confidence enumeration + if ( accuracy >= 55) { + return streets_utils::messages::sdsm::speed_confidence::PREC_100ms; + } + else if (accuracy >= 7.5) { + return streets_utils::messages::sdsm::speed_confidence::PREC_10ms; + } + else if (accuracy >= 3) { + return streets_utils::messages::sdsm::speed_confidence::PREC_5ms; + } + else if (accuracy >= .55) { + return streets_utils::messages::sdsm::speed_confidence::PREC_1ms; + } + else if (accuracy >= .075) { + return streets_utils::messages::sdsm::speed_confidence::PREC_0_1ms; + } + else if (accuracy >= .03) { + return streets_utils::messages::sdsm::speed_confidence::PREC_0_05ms; + } + else { + // This is the lowest speed confidence the SDSM can reflect + return streets_utils::messages::sdsm::speed_confidence::PREC_0_01ms; + } + } + + + streets_utils::messages::sdsm::angular_velocity_confidence to_yaw_rate_confidence( const std::vector> &angular_velocity_covariance ){ + auto z_variance = angular_velocity_covariance[2][2]; + auto z_accuracy = sqrt(z_variance) * 2 ; + return to_angular_velocity_confidence(z_accuracy); + } + + + streets_utils::messages::sdsm::angular_velocity_confidence to_angular_velocity_confidence(const double accuracy) { + if ( accuracy >= 55) { + return streets_utils::messages::sdsm::angular_velocity_confidence::DEGSEC_100; + } + else if (accuracy >= 7.5) { + return streets_utils::messages::sdsm::angular_velocity_confidence::DEGSEC_10; + } + else if (accuracy >= 3) { + return streets_utils::messages::sdsm::angular_velocity_confidence::DEGSEC_05; + } + else if (accuracy >= .55) { + return streets_utils::messages::sdsm::angular_velocity_confidence::DEGSEC_01; + } + else if (accuracy >= .075) { + return streets_utils::messages::sdsm::angular_velocity_confidence::DEGSEC_0_1; + } + else if (accuracy >= .03) { + return streets_utils::messages::sdsm::angular_velocity_confidence::DEGSEC_0_05; + } + else { + // This is the lowest speed confidence the SDSM can reflect + return streets_utils::messages::sdsm::angular_velocity_confidence::DEGSEC_0_01; + } + } + + int to_yaw_rate( const double yaw_rate_radians_per_second ) { + // Return in units of 0.01 degrees per second; + return static_cast( yaw_rate_radians_per_second * (180/(M_PI)) * 100); + } + + + } \ No newline at end of file diff --git a/sensor_data_sharing_service/test/detected_object_to_sdsm_converter_test.cpp b/sensor_data_sharing_service/test/detected_object_to_sdsm_converter_test.cpp index 0952b6d99..86b04d96c 100644 --- a/sensor_data_sharing_service/test/detected_object_to_sdsm_converter_test.cpp +++ b/sensor_data_sharing_service/test/detected_object_to_sdsm_converter_test.cpp @@ -18,13 +18,13 @@ #include namespace sensor_data_sharing_service { - TEST(detected_object_to_sdsm_converter_test, test_to_object_type){ + TEST(detectedObjectToSdsmConverterTest, testToObjectType){ EXPECT_EQ(streets_utils::messages::sdsm::object_type::VEHICLE, to_object_type("CAR")); EXPECT_EQ(streets_utils::messages::sdsm::object_type::VRU, to_object_type("CYCLIST")); EXPECT_EQ(streets_utils::messages::sdsm::object_type::UNKNOWN, to_object_type("TREE")); } - TEST(detected_object_to_sdsm_converter_test, test_to_detected_object_data) { + TEST(detectedObjectToSdsmConverterTest, testToDetectedObjectData) { // Create detected object streets_utils::messages::detected_objects_msg::detected_objects_msg msg; msg._object_id = 123; @@ -36,8 +36,8 @@ namespace sensor_data_sharing_service { msg._angular_velocity = {0.1, 2.3, 5.2}; msg._position = {0.1, 2.3, 5.2}; msg._position_covariance = {{0.1, 2.3, 5.2},{0.1, 2.3, 5.2},{0.1, 2.3, 5.2}}; + msg._velocity_covariance = {{0.1, 2.3, 5.2},{0.1, 2.3, 5.2},{0.1, 2.3, 5.2}}; msg._angular_velocity_covariance = {{0.1, 2.3, 5.2},{0.1, 2.3, 5.2},{0.1, 2.3, 5.2}}; - msg._position_covariance = {{0.1, 2.3, 5.2},{0.1, 2.3, 5.2},{0.1, 2.3, 5.2}}; msg._size._length =1.1; msg._size._width =1.1; msg._size._height = 10; @@ -57,7 +57,7 @@ namespace sensor_data_sharing_service { } - TEST(detected_object_to_sdsm_converter_test, to_sdsm_timestamp_test) { + TEST(detectedObjectToSdsmConverterTest, toSdsmTimestampTest) { // Time 2023-12-11T19:07:44.075Z uint64_t epoch_timestamp = 1702321664075; auto sdsm_timestamp = to_sdsm_timestamp(epoch_timestamp); @@ -68,4 +68,126 @@ namespace sensor_data_sharing_service { EXPECT_EQ(7, sdsm_timestamp.minute); EXPECT_EQ(44075, sdsm_timestamp.second); } + + TEST(detectedObjectToSdsmConverterTest, toPositionConfidenceSet) { + // Covariance matrix x variance 0.4m variance =0.4m and z variance 0.4m (diagonal) + std::vector> position_covariance = {{0.2, 2.3, 5.2},{0.1, 0.2, 5.2},{0.1, 2.3, 0.2}}; + auto position_confidence_set = to_position_confidence_set(position_covariance); + // 0.4m * 2 for 95 % confidence interval is closer to 1 m than to 50 cm + EXPECT_EQ( streets_utils::messages::sdsm::position_confidence::A_1M ,position_confidence_set._position_confidence ); + EXPECT_EQ( streets_utils::messages::sdsm::position_confidence::A_1M ,position_confidence_set._elevation_confidence ); + + } + + TEST(detectedObjectToSdsmConvertTest, toPositionConfidence) { + EXPECT_EQ(streets_utils::messages::sdsm::position_confidence::A_500M, to_position_confidence(550)); + EXPECT_EQ(streets_utils::messages::sdsm::position_confidence::A_500M, to_position_confidence(375)); + EXPECT_EQ(streets_utils::messages::sdsm::position_confidence::A_500M, to_position_confidence(350)); + EXPECT_EQ(streets_utils::messages::sdsm::position_confidence::A_200M, to_position_confidence(349)); + EXPECT_EQ(streets_utils::messages::sdsm::position_confidence::A_200M, to_position_confidence(150)); + EXPECT_EQ(streets_utils::messages::sdsm::position_confidence::A_100M, to_position_confidence(149)); + EXPECT_EQ(streets_utils::messages::sdsm::position_confidence::A_100M, to_position_confidence(75)); + EXPECT_EQ(streets_utils::messages::sdsm::position_confidence::A_50M, to_position_confidence(74)); + EXPECT_EQ(streets_utils::messages::sdsm::position_confidence::A_50M, to_position_confidence(35)); + EXPECT_EQ(streets_utils::messages::sdsm::position_confidence::A_20M, to_position_confidence(34)); + + EXPECT_EQ(streets_utils::messages::sdsm::position_confidence::A_500M, to_position_confidence(500)); + EXPECT_EQ(streets_utils::messages::sdsm::position_confidence::A_200M, to_position_confidence(200)); + EXPECT_EQ(streets_utils::messages::sdsm::position_confidence::A_100M, to_position_confidence(100)); + EXPECT_EQ(streets_utils::messages::sdsm::position_confidence::A_50M, to_position_confidence(50)); + EXPECT_EQ(streets_utils::messages::sdsm::position_confidence::A_20M, to_position_confidence(20)); + EXPECT_EQ(streets_utils::messages::sdsm::position_confidence::A_10M, to_position_confidence(10)); + EXPECT_EQ(streets_utils::messages::sdsm::position_confidence::A_5M, to_position_confidence(5)); + EXPECT_EQ(streets_utils::messages::sdsm::position_confidence::A_2M, to_position_confidence(2)); + EXPECT_EQ(streets_utils::messages::sdsm::position_confidence::A_1M, to_position_confidence(1)); + EXPECT_EQ(streets_utils::messages::sdsm::position_confidence::A_50CM, to_position_confidence(.50)); + EXPECT_EQ(streets_utils::messages::sdsm::position_confidence::A_20CM, to_position_confidence(.20)); + EXPECT_EQ(streets_utils::messages::sdsm::position_confidence::A_10CM, to_position_confidence(.10)); + EXPECT_EQ(streets_utils::messages::sdsm::position_confidence::A_5CM, to_position_confidence(.05)); + EXPECT_EQ(streets_utils::messages::sdsm::position_confidence::A_2CM, to_position_confidence(.02)); + EXPECT_EQ(streets_utils::messages::sdsm::position_confidence::A_1CM, to_position_confidence(.01)); + EXPECT_EQ(streets_utils::messages::sdsm::position_confidence::A_1CM, to_position_confidence(.001)); + + } + + + TEST(detectedObjectToSdsmConvertTest, toHeading) { + streets_utils::messages::detected_objects_msg::vector_3d velocity; + velocity._x = 1; + velocity._y= 0; + EXPECT_EQ(0, to_heading(velocity)); + velocity._x = 1; + velocity._y = 1; + EXPECT_EQ(3600, to_heading(velocity)); + velocity._x = 0; + velocity._y = 1; + EXPECT_EQ(7200, to_heading(velocity)); + velocity._x = -1; + velocity._y = 1; + EXPECT_EQ(10800, to_heading(velocity)); + velocity._x = -1; + velocity._y = 0; + EXPECT_EQ(14400, to_heading(velocity)); + velocity._x = -1; + velocity._y = -1; + EXPECT_EQ(18000, to_heading(velocity)); + velocity._x = 0; + velocity._y = -1; + EXPECT_EQ(21600, to_heading(velocity)); + velocity._x = 1; + velocity._y = -1; + EXPECT_EQ(25200, to_heading(velocity)); + + + } + + TEST(detectedObjectToSdsmConvertTest, toSpeed_XYZ_Confidence) { + std::vector> velocity_covariance = {{4, 2.3, 5.2},{0.1, 4, 5.2},{0.1, 2.3, 9}}; + auto speed_confidence = to_xy_speed_confidence(velocity_covariance); + auto speed_z_confidence = to_z_speed_confidence(velocity_covariance); + EXPECT_EQ(streets_utils::messages::sdsm::speed_confidence::PREC_5ms,speed_z_confidence); + EXPECT_EQ(streets_utils::messages::sdsm::speed_confidence::PREC_5ms,speed_confidence); + + } + + TEST(detectedObjectToSdsmConvertTest, toSpeedConfidence) { + + EXPECT_EQ(streets_utils::messages::sdsm::speed_confidence::PREC_100ms,to_speed_confidence(100)); + EXPECT_EQ(streets_utils::messages::sdsm::speed_confidence::PREC_10ms,to_speed_confidence(10)); + EXPECT_EQ(streets_utils::messages::sdsm::speed_confidence::PREC_5ms,to_speed_confidence(5)); + EXPECT_EQ(streets_utils::messages::sdsm::speed_confidence::PREC_1ms,to_speed_confidence(1)); + EXPECT_EQ(streets_utils::messages::sdsm::speed_confidence::PREC_0_1ms,to_speed_confidence(0.1)); + EXPECT_EQ(streets_utils::messages::sdsm::speed_confidence::PREC_0_05ms,to_speed_confidence(0.05)); + EXPECT_EQ(streets_utils::messages::sdsm::speed_confidence::PREC_0_01ms,to_speed_confidence(0.01)); + + + } + + TEST(detectedObjectToSdsmConvertTest, toYawRateConfidence) { + std::vector> angular_velocity_covariance = {{4, 2.3, 5.2},{0.1, 4, 5.2},{0.1, 2.3, 9}}; + auto speed_confidence = to_yaw_rate_confidence(angular_velocity_covariance); + EXPECT_EQ(streets_utils::messages::sdsm::angular_velocity_confidence::DEGSEC_05,speed_confidence); + + } + + TEST(detectedObjectToSdsmConvertTest, toAngularVelocityConfidence) { + + EXPECT_EQ(streets_utils::messages::sdsm::angular_velocity_confidence::DEGSEC_100,to_angular_velocity_confidence(100)); + EXPECT_EQ(streets_utils::messages::sdsm::angular_velocity_confidence::DEGSEC_10,to_angular_velocity_confidence(10)); + EXPECT_EQ(streets_utils::messages::sdsm::angular_velocity_confidence::DEGSEC_05,to_angular_velocity_confidence(5)); + EXPECT_EQ(streets_utils::messages::sdsm::angular_velocity_confidence::DEGSEC_01,to_angular_velocity_confidence(1)); + EXPECT_EQ(streets_utils::messages::sdsm::angular_velocity_confidence::DEGSEC_0_1,to_angular_velocity_confidence(0.1)); + EXPECT_EQ(streets_utils::messages::sdsm::angular_velocity_confidence::DEGSEC_0_05,to_angular_velocity_confidence(0.05)); + EXPECT_EQ(streets_utils::messages::sdsm::angular_velocity_confidence::DEGSEC_0_01,to_angular_velocity_confidence(0.01)); + + } + + TEST(detectedObjectToSdsmConvertTest, toYawRate) { + EXPECT_NEAR(2939, to_yaw_rate(0.513), 1); + EXPECT_NEAR(-2939, to_yaw_rate(-0.513), 1); + + } + + + } \ No newline at end of file From 7ceb5ec12322c8e225bb710a51e2b487349b2bf0 Mon Sep 17 00:00:00 2001 From: paulbourelly999 <77466294+paulbourelly999@users.noreply.github.com> Date: Fri, 29 Dec 2023 13:17:28 -0500 Subject: [PATCH 62/80] Fix velocity to speed conversion to account for negative velocities (#387) --- .../src/detected_object_to_sdsm_converter.cpp | 2 +- ...detected_object_to_sdsm_converter_test.cpp | 36 +++++++++++++++++-- 2 files changed, 35 insertions(+), 3 deletions(-) diff --git a/sensor_data_sharing_service/src/detected_object_to_sdsm_converter.cpp b/sensor_data_sharing_service/src/detected_object_to_sdsm_converter.cpp index 0557ae5c1..9beee7d1d 100644 --- a/sensor_data_sharing_service/src/detected_object_to_sdsm_converter.cpp +++ b/sensor_data_sharing_service/src/detected_object_to_sdsm_converter.cpp @@ -80,7 +80,7 @@ namespace sensor_data_sharing_service{ // Speed confidence detected_object._detected_object_common_data._speed_confidence = to_xy_speed_confidence(msg._velocity_covariance); // Speed Z - detected_object._detected_object_common_data._speed_z = static_cast(msg._velocity._z* METERS_PER_SECOND_TO_2_CM_PER_SECOND); + detected_object._detected_object_common_data._speed_z = static_cast(fabs(msg._velocity._z)* METERS_PER_SECOND_TO_2_CM_PER_SECOND); // Speed Z confidence detected_object._detected_object_common_data._speed_z_confidence = to_z_speed_confidence(msg._velocity_covariance); // Heading diff --git a/sensor_data_sharing_service/test/detected_object_to_sdsm_converter_test.cpp b/sensor_data_sharing_service/test/detected_object_to_sdsm_converter_test.cpp index 86b04d96c..9126be0b2 100644 --- a/sensor_data_sharing_service/test/detected_object_to_sdsm_converter_test.cpp +++ b/sensor_data_sharing_service/test/detected_object_to_sdsm_converter_test.cpp @@ -50,8 +50,40 @@ namespace sensor_data_sharing_service { EXPECT_EQ(static_cast(msg._position._y*10), data._detected_object_common_data._position_offset._offset_y); EXPECT_EQ(static_cast(msg._position._y*10), data._detected_object_common_data._position_offset._offset_y); - EXPECT_EQ(static_cast(std::hypot(msg._velocity._x*50, msg._velocity._y*50)), data._detected_object_common_data._speed); - EXPECT_EQ(static_cast(msg._velocity._z*50), data._detected_object_common_data._speed_z); + EXPECT_EQ(115, data._detected_object_common_data._speed); + EXPECT_EQ(260, data._detected_object_common_data._speed_z); + EXPECT_EQ(static_cast(msg._size._length*10), std::get(data._detected_object_optional_data.value())._size._length); + + + } + + TEST(detectedObjectToSdsmConverterTest, testToDetectedObjectDataWithNegativeVelocity) { + // Create detected object + streets_utils::messages::detected_objects_msg::detected_objects_msg msg; + msg._object_id = 123; + msg._type = "TREE"; + msg._sensor_id = "sensor_1"; + msg._proj_string = "some string"; + msg._confidence = 0.9; + msg._velocity = {-0.1, -2.3, -5.2}; + msg._angular_velocity = {0.1, 2.3, 5.2}; + msg._position = {0.1, 2.3, 5.2}; + msg._position_covariance = {{0.1, 2.3, 5.2},{0.1, 2.3, 5.2},{0.1, 2.3, 5.2}}; + msg._velocity_covariance = {{0.1, 2.3, 5.2},{0.1, 2.3, 5.2},{0.1, 2.3, 5.2}}; + msg._angular_velocity_covariance = {{0.1, 2.3, 5.2},{0.1, 2.3, 5.2},{0.1, 2.3, 5.2}}; + msg._size._length =1.1; + msg._size._width =1.1; + msg._size._height = 10; + + auto data = to_detected_object_data(msg); + EXPECT_EQ(msg._object_id, data._detected_object_common_data._object_id); + EXPECT_EQ(streets_utils::messages::sdsm::object_type::UNKNOWN, data._detected_object_common_data._object_type); + EXPECT_EQ(90, data._detected_object_common_data._classification_confidence); + EXPECT_EQ(static_cast(msg._position._x*10), data._detected_object_common_data._position_offset._offset_x); + EXPECT_EQ(static_cast(msg._position._y*10), data._detected_object_common_data._position_offset._offset_y); + EXPECT_EQ(static_cast(msg._position._y*10), data._detected_object_common_data._position_offset._offset_y); + EXPECT_EQ(115, data._detected_object_common_data._speed); + EXPECT_EQ(260, data._detected_object_common_data._speed_z); EXPECT_EQ(static_cast(msg._size._length*10), std::get(data._detected_object_optional_data.value())._size._length); From 7c8ba0efccd188e556331123700ee2854a00e31d Mon Sep 17 00:00:00 2001 From: paulbourelly999 <77466294+paulbourelly999@users.noreply.github.com> Date: Fri, 5 Jan 2024 09:00:56 -0500 Subject: [PATCH 63/80] Add method to set sdsm reference location (#388) --- .../include/detected_object_to_sdsm_converter.hpp | 1 + .../include/sensor_data_sharing_service.hpp | 2 ++ .../src/detected_object_to_sdsm_converter.cpp | 1 + .../src/sensor_data_sharing_service.cpp | 12 ++++++++++++ .../test/sensor_data_sharing_service_test.cpp | 8 ++++++++ 5 files changed, 24 insertions(+) diff --git a/sensor_data_sharing_service/include/detected_object_to_sdsm_converter.hpp b/sensor_data_sharing_service/include/detected_object_to_sdsm_converter.hpp index f411eb1c8..68199f60a 100644 --- a/sensor_data_sharing_service/include/detected_object_to_sdsm_converter.hpp +++ b/sensor_data_sharing_service/include/detected_object_to_sdsm_converter.hpp @@ -123,4 +123,5 @@ namespace sensor_data_sharing_service { + } \ No newline at end of file diff --git a/sensor_data_sharing_service/include/sensor_data_sharing_service.hpp b/sensor_data_sharing_service/include/sensor_data_sharing_service.hpp index 7d8f01f4a..479a44a04 100644 --- a/sensor_data_sharing_service/include/sensor_data_sharing_service.hpp +++ b/sensor_data_sharing_service/include/sensor_data_sharing_service.hpp @@ -149,4 +149,6 @@ namespace sensor_data_sharing_service { FRIEND_TEST(sensorDataSharingServiceTest, produceSdsms); FRIEND_TEST(sensorDataSharingServiceTest, readLanelet2Map); }; + streets_utils::messages::sdsm::position_3d to_position_3d(const lanelet::GPSPoint &ref_position); + } \ No newline at end of file diff --git a/sensor_data_sharing_service/src/detected_object_to_sdsm_converter.cpp b/sensor_data_sharing_service/src/detected_object_to_sdsm_converter.cpp index 9beee7d1d..510ac1668 100644 --- a/sensor_data_sharing_service/src/detected_object_to_sdsm_converter.cpp +++ b/sensor_data_sharing_service/src/detected_object_to_sdsm_converter.cpp @@ -271,6 +271,7 @@ namespace sensor_data_sharing_service{ return static_cast( yaw_rate_radians_per_second * (180/(M_PI)) * 100); } + } \ No newline at end of file diff --git a/sensor_data_sharing_service/src/sensor_data_sharing_service.cpp b/sensor_data_sharing_service/src/sensor_data_sharing_service.cpp index 62ca8d3fe..912b2e0dc 100644 --- a/sensor_data_sharing_service/src/sensor_data_sharing_service.cpp +++ b/sensor_data_sharing_service/src/sensor_data_sharing_service.cpp @@ -150,7 +150,10 @@ namespace sensor_data_sharing_service { msg._msg_count = this->_message_count; // Populate with infrastructure id msg._source_id = this->_infrastructure_id; + // Populate equipement type msg._equipment_type = sdsm::equipment_type::RSU; + // Polulate ref position + msg._ref_positon = to_position_3d(this->sdsm_reference_point); std::shared_lock lock(detected_objects_lock); for (const auto &[object_id, object] : detected_objects){ auto detected_object_data = to_detected_object_data(object); @@ -171,5 +174,14 @@ namespace sensor_data_sharing_service { sdsm_thread.join(); } + streets_utils::messages::sdsm::position_3d to_position_3d(const lanelet::GPSPoint &ref_position) { + streets_utils::messages::sdsm::position_3d position; + // Convert to 1/10 of microdegrees + position._longitude = static_cast(ref_position.lon * 1e7); + position._latitude = static_cast(ref_position.lat *1e7); + // Convert 0.1 meters + position._elevation = static_cast(ref_position.ele * 10); + return position; + } } \ No newline at end of file diff --git a/sensor_data_sharing_service/test/sensor_data_sharing_service_test.cpp b/sensor_data_sharing_service/test/sensor_data_sharing_service_test.cpp index 696393db9..695a4ec28 100644 --- a/sensor_data_sharing_service/test/sensor_data_sharing_service_test.cpp +++ b/sensor_data_sharing_service/test/sensor_data_sharing_service_test.cpp @@ -167,4 +167,12 @@ namespace sensor_data_sharing_service { EXPECT_NE(nullptr, serv.map_ptr); } + TEST(sensorDataSharingServiceTest, toPosition3d) { + lanelet::GPSPoint point{38.9551605829,-77.14701567,1}; + streets_utils::messages::sdsm::position_3d position = to_position_3d(point); + EXPECT_EQ(389551605, position._latitude ); + EXPECT_EQ(-771470156, position._longitude); + EXPECT_EQ(10, position._elevation); + } + } \ No newline at end of file From 4d094c3d524467081c40756d8dd9982850450acc Mon Sep 17 00:00:00 2001 From: paulbourelly999 <77466294+paulbourelly999@users.noreply.github.com> Date: Mon, 22 Jan 2024 17:24:27 -0500 Subject: [PATCH 64/80] Adding pip3 depedencies (#390) * Adding pip3 depedencies * Update libxslt1-dev package name * Updates to install lxml through apt to ensure we get its depedencies * Update script to bash and use bash list * Updates --- build_scripts/install_test_dependencies.sh | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/build_scripts/install_test_dependencies.sh b/build_scripts/install_test_dependencies.sh index 20e9001f7..b56098ecd 100755 --- a/build_scripts/install_test_dependencies.sh +++ b/build_scripts/install_test_dependencies.sh @@ -1,4 +1,4 @@ -#!/bin/sh +#!/bin/bash # exit on errors set -e @@ -7,10 +7,14 @@ set -e apt-get update -# NOTE: libwebsockets-dev from Ubuntu 20 on is sufficient -DEPENDENCIES="python3-pip " +# NOTE: lxml python library dependes on libxml2 and libxslt1-dev. lxml is a dependency of gcovr + +DEPENDENCIES=( + python3-pip + python3-lxml +) # install all things needed for deployment, always done -apt-get install -y $DEPENDENCIES -pip3 install gcovr +DEBIAN_FRONTEND=noninteractive apt-get install --yes --quiet --no-install-recommends "${DEPENDENCIES[@]}" +python3 -m pip install gcovr From ae449181ae72c4cb82952edcfea44d6f9b68d994 Mon Sep 17 00:00:00 2001 From: paulbourelly999 <77466294+paulbourelly999@users.noreply.github.com> Date: Tue, 23 Jan 2024 10:38:42 -0500 Subject: [PATCH 65/80] Fix SDSM serialization (#389) * CDAR-738:Fix SDSM serialization Added curly braces to if statements Added unit test to cover yaw rate confidence serialization Fixed SDSM deserialization to not require acceleration data to write yaw rate confidence * Remove unnecessary space --- ...detected_object_to_sdsm_converter_test.cpp | 4 +- .../test/sensor_data_sharing_service_test.cpp | 46 ++++++++++--------- ...sor_data_sharing_msg_json_deserializer.cpp | 22 ++++----- ...ensor_data_sharing_msg_json_serializer.cpp | 15 ++++-- 4 files changed, 48 insertions(+), 39 deletions(-) diff --git a/sensor_data_sharing_service/test/detected_object_to_sdsm_converter_test.cpp b/sensor_data_sharing_service/test/detected_object_to_sdsm_converter_test.cpp index 9126be0b2..f9e8a5e00 100644 --- a/sensor_data_sharing_service/test/detected_object_to_sdsm_converter_test.cpp +++ b/sensor_data_sharing_service/test/detected_object_to_sdsm_converter_test.cpp @@ -199,6 +199,9 @@ namespace sensor_data_sharing_service { std::vector> angular_velocity_covariance = {{4, 2.3, 5.2},{0.1, 4, 5.2},{0.1, 2.3, 9}}; auto speed_confidence = to_yaw_rate_confidence(angular_velocity_covariance); EXPECT_EQ(streets_utils::messages::sdsm::angular_velocity_confidence::DEGSEC_05,speed_confidence); + angular_velocity_covariance = {{0.01, 0.0, 0.0},{0.0, 0.01, 0.0},{0.0, 0.0, 0.01}}; + speed_confidence = to_yaw_rate_confidence(angular_velocity_covariance); + EXPECT_EQ(streets_utils::messages::sdsm::angular_velocity_confidence::DEGSEC_0_1,speed_confidence); } @@ -221,5 +224,4 @@ namespace sensor_data_sharing_service { } - } \ No newline at end of file diff --git a/sensor_data_sharing_service/test/sensor_data_sharing_service_test.cpp b/sensor_data_sharing_service/test/sensor_data_sharing_service_test.cpp index 695a4ec28..2f8353d64 100644 --- a/sensor_data_sharing_service/test/sensor_data_sharing_service_test.cpp +++ b/sensor_data_sharing_service/test/sensor_data_sharing_service_test.cpp @@ -97,36 +97,35 @@ namespace sensor_data_sharing_service { const std::string detected_object_json = R"( { - "type":"CAR", - "confidence":0.7, - "sensorId":"sensor1", - "projString":"projectionString2", - "objectId":1, + "type":"TRUCK", + "confidence":1.0, + "sensorId":"IntersectionLidar", + "projString":"+proj=tmerc +lat_0=0 +lon_0=0 +k=1 +x_0=0 +y_0=0 +datum=WGS84 +units=m +geoidgrids=egm96_15.gtx +vunits=m +no_defs", + "objectId":222, "position":{ - "x":-1.1, - "y":-2.0, - "z":-3.2 + "x":-23.70157158620998, + "y":-4.561758806718842, + "z":-9.991932254782586 }, - "positionCovariance":[[1.0,0.0,0.0],[1.0,0.0,0.0],[1.0,0.0,0.0]], + "positionCovariance":[[0.04000000000000001,0.0,0.0],[0.0,0.04000000000000001,0.0],[0.0,0.0,0.04000000000000001]], "velocity":{ - "x":1.0, - "y":1.0, - "z":1.0 + "x":0.0, + "y":0.0, + "z":0.0 }, - "velocityCovariance":[[1.0,0.0,0.0],[1.0,0.0,0.0],[1.0,0.0,0.0]], + "velocityCovariance":[[0.04000000000000001,0.0,0.0],[0.0,0.04000000000000001,0.0],[0.0,0.0,0.04000000000000001]], "angularVelocity":{ - "x":0.1, - "y":0.2, - "z":0.3 + "x":0.0, + "y":0.0, + "z":0.0 }, - "angularVelocityCovariance":[[1.0,0.0,0.0],[1.0,0.0,0.0],[1.0,0.0,0.0]], + "angularVelocityCovariance":[[0.010000000000000002,0.0,0.0],[0.0,0.010000000000000002,0.0],[0.0,0.0,0.010000000000000002]], "size":{ - "length":2.0, - "height":1.0, - "width":0.5 + "length":2.601919174194336, + "height":1.3072861433029175, + "width":1.2337223291397095 }, - "timestamp":1000 - + "timestamp":41343 } )"; auto detected_object = streets_utils::messages::detected_objects_msg::from_json(detected_object_json); @@ -158,6 +157,8 @@ namespace sensor_data_sharing_service { EXPECT_NEAR(calendar_time.tm_min, msg._time_stamp.minute, 1); EXPECT_EQ(1 , msg._objects.size()); EXPECT_EQ(streets_utils::messages::sdsm::object_type::VEHICLE, msg._objects[0]._detected_object_common_data._object_type); + EXPECT_TRUE(msg._objects[0]._detected_object_common_data._yaw_rate_confidence.has_value()); + EXPECT_EQ(streets_utils::messages::sdsm::angular_velocity_confidence::DEGSEC_0_1 , msg._objects[0]._detected_object_common_data._yaw_rate_confidence); EXPECT_EQ(serv.detected_objects.size(), 0); } @@ -175,4 +176,5 @@ namespace sensor_data_sharing_service { EXPECT_EQ(10, position._elevation); } + } \ No newline at end of file diff --git a/streets_utils/streets_messages/src/deserializers/sensor_data_sharing_msg_json_deserializer.cpp b/streets_utils/streets_messages/src/deserializers/sensor_data_sharing_msg_json_deserializer.cpp index 6e749b85a..075410690 100644 --- a/streets_utils/streets_messages/src/deserializers/sensor_data_sharing_msg_json_deserializer.cpp +++ b/streets_utils/streets_messages/src/deserializers/sensor_data_sharing_msg_json_deserializer.cpp @@ -106,18 +106,18 @@ namespace streets_utils::messages::sdsm { _detected_object_common_data._heading_confidence = heading_confidence_from_int(parse_uint_member("heading_conf", val, true).value()); if ( val.HasMember("accel_4_way") ) { _detected_object_common_data._acceleration_4_way = parse_acceleration_4_way(parse_object_member("accel_4_way", val, false ).value()); - if ( val.HasMember("acc_cfd_x")) { + } + if ( val.HasMember("acc_cfd_x")) { _detected_object_common_data._lateral_acceleration_confidence = acceleration_confidence_from_int(parse_uint_member("acc_cfd_x", val, true).value()); - } - if ( val.HasMember("acc_cfd_y")) { - _detected_object_common_data._longitudinal_acceleration_confidence = acceleration_confidence_from_int(parse_uint_member("acc_cfd_y", val, true).value()); - } - if ( val.HasMember("acc_cfd_z")) { - _detected_object_common_data._vertical_accelaration_confidence = acceleration_confidence_from_int(parse_uint_member("acc_cfd_z", val, true).value()); - } - if ( val.HasMember("acc_cfd_yaw")) { - _detected_object_common_data._yaw_rate_confidence = angular_velocity_confidence_from_int(parse_uint_member("acc_cfd_yaw", val, true).value()); - } + } + if ( val.HasMember("acc_cfd_y")) { + _detected_object_common_data._longitudinal_acceleration_confidence = acceleration_confidence_from_int(parse_uint_member("acc_cfd_y", val, true).value()); + } + if ( val.HasMember("acc_cfd_z")) { + _detected_object_common_data._vertical_accelaration_confidence = acceleration_confidence_from_int(parse_uint_member("acc_cfd_z", val, true).value()); + } + if ( val.HasMember("acc_cfd_yaw")) { + _detected_object_common_data._yaw_rate_confidence = angular_velocity_confidence_from_int(parse_uint_member("acc_cfd_yaw", val, true).value()); } return _detected_object_common_data; } diff --git a/streets_utils/streets_messages/src/serializers/sensor_data_sharing_msg_json_serializer.cpp b/streets_utils/streets_messages/src/serializers/sensor_data_sharing_msg_json_serializer.cpp index 83be1d7c4..a8e1a473f 100644 --- a/streets_utils/streets_messages/src/serializers/sensor_data_sharing_msg_json_serializer.cpp +++ b/streets_utils/streets_messages/src/serializers/sensor_data_sharing_msg_json_serializer.cpp @@ -111,13 +111,15 @@ namespace streets_utils::messages::sdsm{ detected_object_data_common_json.AddMember("pos_confidence", create_position_confidence_set(val._pos_confidence, allocator), allocator ); detected_object_data_common_json.AddMember("speed", val._speed, allocator); detected_object_data_common_json.AddMember("speed_confidence", static_cast(val._speed_confidence), allocator); - if ( val._speed_z.has_value()) + if ( val._speed_z.has_value()) { detected_object_data_common_json.AddMember("speed_z", val._speed_z.value(), allocator); - if ( val._speed_z_confidence.has_value()) + } + if ( val._speed_z_confidence.has_value()) { detected_object_data_common_json.AddMember( "speed_confidence_z", static_cast(val._speed_z_confidence.value()), allocator); + } detected_object_data_common_json.AddMember("heading", val._heading, allocator); detected_object_data_common_json.AddMember("heading_conf", static_cast(val._heading_confidence), allocator); if (val._lateral_acceleration_confidence.has_value() ) { @@ -125,17 +127,20 @@ namespace streets_utils::messages::sdsm{ "acc_cfd_x", static_cast(val._lateral_acceleration_confidence.value()), allocator); - if (val._longitudinal_acceleration_confidence.has_value() ) + } + if (val._longitudinal_acceleration_confidence.has_value() ) { detected_object_data_common_json.AddMember( "acc_cfd_y", static_cast(val._longitudinal_acceleration_confidence.value()), allocator); - if (val._vertical_accelaration_confidence.has_value()) + } + if (val._vertical_accelaration_confidence.has_value()){ detected_object_data_common_json.AddMember( "acc_cfd_z", static_cast(val._vertical_accelaration_confidence.value()), allocator); - if (val._yaw_rate_confidence.has_value()) + } + if (val._yaw_rate_confidence.has_value()) { detected_object_data_common_json.AddMember( "acc_cfd_yaw", static_cast(val._yaw_rate_confidence.value()), From b92673664907abdb051a0a1a61a96ef39ce75dd5 Mon Sep 17 00:00:00 2001 From: paulbourelly999 <77466294+paulbourelly999@users.noreply.github.com> Date: Wed, 24 Jan 2024 11:00:43 -0500 Subject: [PATCH 66/80] Update devcontainer setup to allow for configuration of background services to run (#375) --- .devcontainer/devcontainer.json | 4 ++-- .devcontainer/docker-compose-devcontainer.yml | 16 +++++++++------- 2 files changed, 11 insertions(+), 9 deletions(-) diff --git a/.devcontainer/devcontainer.json b/.devcontainer/devcontainer.json index f436634ec..e0c683c73 100644 --- a/.devcontainer/devcontainer.json +++ b/.devcontainer/devcontainer.json @@ -12,7 +12,8 @@ "service": "streets_service_base", // Uncomment for Streets Service Base Lanelet aware dev environment // "service": "streets_service_base_lanelet_aware", - + // Specify the services you want to run + "runServices": ["php", "v2xhub", "db", "zookeeper", "kafka", "kowl"], // The optional 'workspaceFolder' property is the path VS Code should open by default when // connected. This is typically a file mount in .devcontainer/docker-compose.yml "workspaceFolder": "/home/carma-streets/", @@ -36,7 +37,6 @@ ] } }, - "shutdownAction": "stopCompose" } diff --git a/.devcontainer/docker-compose-devcontainer.yml b/.devcontainer/docker-compose-devcontainer.yml index e30a98adf..1e7681695 100644 --- a/.devcontainer/docker-compose-devcontainer.yml +++ b/.devcontainer/docker-compose-devcontainer.yml @@ -11,15 +11,17 @@ services: args: UBUNTU_CODENAME: bionic network_mode: host + command: /bin/sh -c "while sleep 1000; do :; done" + # Uncomment for streets service base lanelet aware dev environment - # streets_service_base_lanelet_aware: - # image: usdotfhwastoldev/streets_service_base_lanelet_aware:bionic - # # Remove build parameter to force pull from dockerhub - # build: - # context: . - # dockerfile: ./streets_service_base_lanelet_aware/Dockerfile - # network_mode: host + streets_service_base_lanelet_aware: + image: usdotfhwastoldev/streets_service_base_lanelet_aware:bionic + # Remove build parameter to force pull from dockerhub + build: + context: . + dockerfile: ./streets_service_base_lanelet_aware/Dockerfile + network_mode: host volumes: From ffd6ec861cf7f057fb503141e8e023f84757d80d Mon Sep 17 00:00:00 2001 From: paulbourelly999 <77466294+paulbourelly999@users.noreply.github.com> Date: Thu, 25 Jan 2024 09:35:19 -0500 Subject: [PATCH 67/80] Adding working directory to sensor_data_sharing_service (#392) * Adding working directory to allow for relative path creation of log file * Included comment --- sensor_data_sharing_service/Dockerfile | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/sensor_data_sharing_service/Dockerfile b/sensor_data_sharing_service/Dockerfile index 3bb08cfe0..d86a30858 100644 --- a/sensor_data_sharing_service/Dockerfile +++ b/sensor_data_sharing_service/Dockerfile @@ -4,6 +4,10 @@ COPY ./streets_utils/ /home/carma-streets/streets_utils COPY ./kafka_clients/ /home/carma-streets/kafka_clients RUN /home/carma-streets/sensor_data_sharing_service/build.sh RUN chmod u+x /opt/carma_lanelet2/setup.bash +# Required due to log file hardcoded relative path "../logs" +# (see https://github.com/usdot-fhwa-stol/carma-streets/issues/391 & +# https://github.com/usdot-fhwa-stol/carma-streets/issues/342) +WORKDIR /home/carma-streets/sensor_data_sharing_service/build LABEL org.label-schema.schema-version="1.0" LABEL org.label-schema.name="sensor_data_sharing_service" @@ -15,5 +19,4 @@ LABEL org.label-schema.vcs-url="https://github.com/usdot-fhwa-stol/carma-streets LABEL org.label-schema.vcs-ref=${VCS_REF} LABEL org.label-schema.build-date=${BUILD_DATE} - ENTRYPOINT ["/bin/bash", "-c", "source /opt/carma_lanelet2/setup.bash && /home/carma-streets/sensor_data_sharing_service/build/sensor_data_sharing_service"] \ No newline at end of file From 610402c5a38cfb2fdb2ad43392b69903ddc4524f Mon Sep 17 00:00:00 2001 From: paulbourelly999 <77466294+paulbourelly999@users.noreply.github.com> Date: Mon, 29 Jan 2024 11:30:51 -0500 Subject: [PATCH 68/80] Convert detections from ENU to NED coordinate frame. (#393) * CDAR-751:Convert detections from assumed ENU to SDSM NED coordinate frame * Updated documentation * Add method documentation * Update comments * Added license header comment * Peer Review comments Removed intermediate variables Using EXPECT_DOUBLE_EQ and EXPECT_NEAR * Updates * Add comment --- sensor_data_sharing_service/CMakeLists.txt | 3 +- sensor_data_sharing_service/README.md | 10 +++- sensor_data_sharing_service/docs/ned_enu.png | Bin 0 -> 7078 bytes .../detected_object_enu_to_ned_converter.hpp | 25 ++++++++++ .../detected_object_to_sdsm_converter.hpp | 3 +- .../include/sensor_data_sharing_service.hpp | 1 + .../detected_object_enu_to_ned_converter.cpp | 29 +++++++++++ .../src/detected_object_to_sdsm_converter.cpp | 9 ++-- .../src/sensor_data_sharing_service.cpp | 3 +- ...ected_object_enu_to_ned_converter_test.cpp | 45 ++++++++++++++++++ ...detected_object_to_sdsm_converter_test.cpp | 20 ++++---- .../test/sensor_data_sharing_service_test.cpp | 6 ++- 12 files changed, 136 insertions(+), 18 deletions(-) create mode 100644 sensor_data_sharing_service/docs/ned_enu.png create mode 100644 sensor_data_sharing_service/include/detected_object_enu_to_ned_converter.hpp create mode 100644 sensor_data_sharing_service/src/detected_object_enu_to_ned_converter.cpp create mode 100644 sensor_data_sharing_service/test/detected_object_enu_to_ned_converter_test.cpp diff --git a/sensor_data_sharing_service/CMakeLists.txt b/sensor_data_sharing_service/CMakeLists.txt index fb7bee0b1..c78bef0f0 100644 --- a/sensor_data_sharing_service/CMakeLists.txt +++ b/sensor_data_sharing_service/CMakeLists.txt @@ -45,7 +45,8 @@ set(SERVICE_NAME ${PROJECT_NAME}) add_library(${LIBRARY_NAME} src/sensor_data_sharing_service.cpp src/sensor_configuration_parser.cpp - src/detected_object_to_sdsm_converter.cpp) + src/detected_object_to_sdsm_converter.cpp + src/detected_object_enu_to_ned_converter.cpp) target_include_directories(${LIBRARY_NAME} PUBLIC diff --git a/sensor_data_sharing_service/README.md b/sensor_data_sharing_service/README.md index 57114c8e2..32ebde926 100644 --- a/sensor_data_sharing_service/README.md +++ b/sensor_data_sharing_service/README.md @@ -12,5 +12,11 @@ For covariance to confidence/accuracy translation assumed the following. All con ![image](https://github.com/usdot-fhwa-stol/carma-streets/assets/77466294/4a6e9875-2f87-4a8f-b1a0-33a6769439ac) ## Sensor Data Sharing speed and heading -For heading and speed we use following coordinate frame as described in the J3224 specifications. -![Alt text](docs/image.png) \ No newline at end of file +For heading and speed we use following coordinate frame (NED) as described in the J3224 specifications. + +![Alt text](docs/image.png) + +We assume incoming detections are in the ENU cordinate frame. The `detected_object_enu_to_ned_converter` handles conversion of position and velocity in detection to NED coordinate frame. + +![Alt text](docs/ned_enu.png) + diff --git a/sensor_data_sharing_service/docs/ned_enu.png b/sensor_data_sharing_service/docs/ned_enu.png new file mode 100644 index 0000000000000000000000000000000000000000..c4e246e89a94373cf81d0685f2cbdff196adc5b1 GIT binary patch literal 7078 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 C-i*Tl literal 0 HcmV?d00001 diff --git a/sensor_data_sharing_service/include/detected_object_enu_to_ned_converter.hpp b/sensor_data_sharing_service/include/detected_object_enu_to_ned_converter.hpp new file mode 100644 index 000000000..9ad8a034f --- /dev/null +++ b/sensor_data_sharing_service/include/detected_object_enu_to_ned_converter.hpp @@ -0,0 +1,25 @@ +// Copyright 2023 Leidos +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +#pragma once + +#include + +namespace sensor_data_sharing_service { + /** + * @brief Converts detected_objects_msg from ENU coordinate frame to NED. + * @param msg + * @return Returns detected_objects_msg in NED coordinate frame. + */ + streets_utils::messages::detected_objects_msg::detected_objects_msg detected_object_enu_to_ned(const streets_utils::messages::detected_objects_msg::detected_objects_msg &msg ); +} \ No newline at end of file diff --git a/sensor_data_sharing_service/include/detected_object_to_sdsm_converter.hpp b/sensor_data_sharing_service/include/detected_object_to_sdsm_converter.hpp index 68199f60a..b103366b0 100644 --- a/sensor_data_sharing_service/include/detected_object_to_sdsm_converter.hpp +++ b/sensor_data_sharing_service/include/detected_object_to_sdsm_converter.hpp @@ -51,7 +51,8 @@ namespace sensor_data_sharing_service { */ streets_utils::messages::sdsm::time_stamp to_sdsm_timestamp(const uint64_t _epoch_time_ms); /** - * @brief convert detected_object_msg to sdsm detection_object_data. + * @brief convert detected_object_msg to sdsm detection_object_data. + * @note method assumes detected_object_msg is in NED coordinate frame (NED is required for SDSM based on J3223 specification) * @return streets_utils::messages::sdsm::detected_object_data. */ streets_utils::messages::sdsm::detected_object_data to_detected_object_data(const streets_utils::messages::detected_objects_msg::detected_objects_msg &msg); diff --git a/sensor_data_sharing_service/include/sensor_data_sharing_service.hpp b/sensor_data_sharing_service/include/sensor_data_sharing_service.hpp index 479a44a04..b69cd953a 100644 --- a/sensor_data_sharing_service/include/sensor_data_sharing_service.hpp +++ b/sensor_data_sharing_service/include/sensor_data_sharing_service.hpp @@ -45,6 +45,7 @@ #include "sensor_configuration_parser.hpp" #include "detected_object_to_sdsm_converter.hpp" +#include "detected_object_enu_to_ned_converter.hpp" namespace sensor_data_sharing_service { diff --git a/sensor_data_sharing_service/src/detected_object_enu_to_ned_converter.cpp b/sensor_data_sharing_service/src/detected_object_enu_to_ned_converter.cpp new file mode 100644 index 000000000..a2c3264fe --- /dev/null +++ b/sensor_data_sharing_service/src/detected_object_enu_to_ned_converter.cpp @@ -0,0 +1,29 @@ +// Copyright 2023 Leidos +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +#include "detected_object_enu_to_ned_converter.hpp" + +namespace sensor_data_sharing_service { + + streets_utils::messages::detected_objects_msg::detected_objects_msg detected_object_enu_to_ned(const streets_utils::messages::detected_objects_msg::detected_objects_msg &msg ) { + streets_utils::messages::detected_objects_msg::detected_objects_msg ned_detection(msg); + ned_detection._position._x = msg._position._y; + ned_detection._position._y = msg._position._x; + ned_detection._position._z = -msg._position._z; + ned_detection._velocity._x = msg._velocity._y; + ned_detection._velocity._y = msg._velocity._x; + ned_detection._velocity._z = -msg._velocity._z; + return ned_detection; + } +} + diff --git a/sensor_data_sharing_service/src/detected_object_to_sdsm_converter.cpp b/sensor_data_sharing_service/src/detected_object_to_sdsm_converter.cpp index 510ac1668..5c22ba262 100644 --- a/sensor_data_sharing_service/src/detected_object_to_sdsm_converter.cpp +++ b/sensor_data_sharing_service/src/detected_object_to_sdsm_converter.cpp @@ -69,7 +69,7 @@ namespace sensor_data_sharing_service{ detected_object._detected_object_common_data._classification_confidence = static_cast(msg._confidence*100); // TODO: Change Detected Object ID to int detected_object._detected_object_common_data._object_id = msg._object_id; - // Units are 0.1 m + // Units are 0.1 m detected_object._detected_object_common_data._position_offset._offset_x = static_cast(msg._position._x*METERS_TO_10_CM); detected_object._detected_object_common_data._position_offset._offset_y = static_cast(msg._position._y*METERS_TO_10_CM); detected_object._detected_object_common_data._position_offset._offset_z = static_cast(msg._position._z*METERS_TO_10_CM); @@ -88,7 +88,7 @@ namespace sensor_data_sharing_service{ // TODO: how to calculate heading confidence without orientation covariance // Possible approximation is velocity covariance since we are using that currently // Currently hard coding value - detected_object._detected_object_common_data._heading_confidence = streets_utils::messages::sdsm::heading_confidence::PREC_10_deg; + detected_object._detected_object_common_data._heading_confidence = streets_utils::messages::sdsm::heading_confidence::PREC_0_1_deg; // Yaw rate detected_object._detected_object_common_data._acceleration_4_way->_yaw_rate = to_yaw_rate(msg._angular_velocity._z); // Yaw rate confidence @@ -174,12 +174,15 @@ namespace sensor_data_sharing_service{ } unsigned int to_heading(const streets_utils::messages::detected_objects_msg::vector_3d &velocity){ + if ( std::abs(velocity._x) < 0.01 && std::abs(velocity._y) < 0.01) { + return 0; + } auto heading_radians = std::atan2(velocity._y,velocity._x); auto heading_degrees = heading_radians*(180/M_PI); // If angle is negative 360 + (-negative angle) if ( heading_degrees < 0 ) { heading_degrees = 360 + heading_degrees; - } + } // in units (x) = 0.0125 degrees // 28800 x = 360 degrees return static_cast(heading_degrees * (28800/360)); diff --git a/sensor_data_sharing_service/src/sensor_data_sharing_service.cpp b/sensor_data_sharing_service/src/sensor_data_sharing_service.cpp index 912b2e0dc..5e23a0704 100644 --- a/sensor_data_sharing_service/src/sensor_data_sharing_service.cpp +++ b/sensor_data_sharing_service/src/sensor_data_sharing_service.cpp @@ -156,7 +156,8 @@ namespace sensor_data_sharing_service { msg._ref_positon = to_position_3d(this->sdsm_reference_point); std::shared_lock lock(detected_objects_lock); for (const auto &[object_id, object] : detected_objects){ - auto detected_object_data = to_detected_object_data(object); + auto ned_object = detected_object_enu_to_ned(object); + auto detected_object_data = to_detected_object_data(ned_object); // TODO: Update time offset. Currently CARMA-Streets detected object message does not support timestamp // This is a bug and needs to be addressed. msg._objects.push_back(detected_object_data); diff --git a/sensor_data_sharing_service/test/detected_object_enu_to_ned_converter_test.cpp b/sensor_data_sharing_service/test/detected_object_enu_to_ned_converter_test.cpp new file mode 100644 index 000000000..e2a22a959 --- /dev/null +++ b/sensor_data_sharing_service/test/detected_object_enu_to_ned_converter_test.cpp @@ -0,0 +1,45 @@ +// Copyright 2019-2023 Leidos +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +#include +#include +#include +#include +namespace sensor_data_sharing_service { + TEST(DetectedObjectToENUToNEDConverterTest, TestConversion) { + streets_utils::messages::detected_objects_msg::detected_objects_msg enu_detection; + auto pos_x = 4.3; + auto pos_y = -6.3; + auto pos_z = 10; + auto vel_x = -3.2; + auto vel_y = 0.7; + auto vel_z = -5.0; + + enu_detection._position._x = pos_x; + enu_detection._position._y = pos_y; + enu_detection._position._z = pos_z; + enu_detection._velocity._x = vel_x; + enu_detection._velocity._y = vel_y; + enu_detection._velocity._z = vel_z; + + auto ned_detection = detected_object_enu_to_ned(enu_detection); + EXPECT_DOUBLE_EQ( ned_detection._position._x, pos_y); + EXPECT_DOUBLE_EQ( ned_detection._position._y, pos_x); + EXPECT_DOUBLE_EQ( ned_detection._position._z, -pos_z); + + EXPECT_DOUBLE_EQ( ned_detection._velocity._x, vel_y); + EXPECT_DOUBLE_EQ( ned_detection._velocity._y, vel_x); + EXPECT_DOUBLE_EQ( ned_detection._velocity._z, -vel_z); + + } +} \ No newline at end of file diff --git a/sensor_data_sharing_service/test/detected_object_to_sdsm_converter_test.cpp b/sensor_data_sharing_service/test/detected_object_to_sdsm_converter_test.cpp index f9e8a5e00..5f26996c3 100644 --- a/sensor_data_sharing_service/test/detected_object_to_sdsm_converter_test.cpp +++ b/sensor_data_sharing_service/test/detected_object_to_sdsm_converter_test.cpp @@ -144,31 +144,35 @@ namespace sensor_data_sharing_service { TEST(detectedObjectToSdsmConvertTest, toHeading) { + // to_heading converts velocity to heading (2 dimensional heading in units of 0.0125 degrees) streets_utils::messages::detected_objects_msg::vector_3d velocity; + velocity._x = 0; + velocity._y= 0; + EXPECT_NEAR(0, to_heading(velocity),1); velocity._x = 1; velocity._y= 0; - EXPECT_EQ(0, to_heading(velocity)); + EXPECT_NEAR(0, to_heading(velocity),1); velocity._x = 1; velocity._y = 1; - EXPECT_EQ(3600, to_heading(velocity)); + EXPECT_NEAR(3600, to_heading(velocity),1); velocity._x = 0; velocity._y = 1; - EXPECT_EQ(7200, to_heading(velocity)); + EXPECT_NEAR(7200, to_heading(velocity), 1); velocity._x = -1; velocity._y = 1; - EXPECT_EQ(10800, to_heading(velocity)); + EXPECT_NEAR(10800, to_heading(velocity),1); velocity._x = -1; velocity._y = 0; - EXPECT_EQ(14400, to_heading(velocity)); + EXPECT_NEAR(14400, to_heading(velocity),1); velocity._x = -1; velocity._y = -1; - EXPECT_EQ(18000, to_heading(velocity)); + EXPECT_NEAR(18000, to_heading(velocity),1); velocity._x = 0; velocity._y = -1; - EXPECT_EQ(21600, to_heading(velocity)); + EXPECT_NEAR(21600, to_heading(velocity),1); velocity._x = 1; velocity._y = -1; - EXPECT_EQ(25200, to_heading(velocity)); + EXPECT_NEAR(25200, to_heading(velocity),1); } diff --git a/sensor_data_sharing_service/test/sensor_data_sharing_service_test.cpp b/sensor_data_sharing_service/test/sensor_data_sharing_service_test.cpp index 2f8353d64..62480aa56 100644 --- a/sensor_data_sharing_service/test/sensor_data_sharing_service_test.cpp +++ b/sensor_data_sharing_service/test/sensor_data_sharing_service_test.cpp @@ -109,7 +109,7 @@ namespace sensor_data_sharing_service { }, "positionCovariance":[[0.04000000000000001,0.0,0.0],[0.0,0.04000000000000001,0.0],[0.0,0.0,0.04000000000000001]], "velocity":{ - "x":0.0, + "x":1.0, "y":0.0, "z":0.0 }, @@ -143,7 +143,7 @@ namespace sensor_data_sharing_service { streets_utils::messages::sdsm::sensor_data_sharing_msg msg = streets_utils::messages::sdsm::from_json(sdsm_json); EXPECT_EQ( msg._equipment_type, streets_utils::messages::sdsm::equipment_type::RSU); EXPECT_EQ( msg._source_id, serv._infrastructure_id ); - // http://en.cppreference.com/w/cpp/chrono/c/time + // http://en.cppreference.com/w/cpp/chrono/c/time const std::time_t now = std::time(nullptr) ; // get the current time point // convert it to (local) calendar time @@ -160,6 +160,8 @@ namespace sensor_data_sharing_service { EXPECT_TRUE(msg._objects[0]._detected_object_common_data._yaw_rate_confidence.has_value()); EXPECT_EQ(streets_utils::messages::sdsm::angular_velocity_confidence::DEGSEC_0_1 , msg._objects[0]._detected_object_common_data._yaw_rate_confidence); EXPECT_EQ(serv.detected_objects.size(), 0); + // SDSM assumes NED coordinate frame. Incoming detection is ENU. 1,0 in ENU is 0,1 in NED and is a 90 degree heading (heading is calculated from velocity) + EXPECT_NEAR( msg._objects[0]._detected_object_common_data._heading, 7200, 2); } TEST(sensorDataSharingServiceTest,readLanelet2Map) { From 757e6fe90691fb7ea2cf15db86292f50e2e2dfe4 Mon Sep 17 00:00:00 2001 From: paulbourelly999 <77466294+paulbourelly999@users.noreply.github.com> Date: Wed, 7 Feb 2024 08:51:47 -0500 Subject: [PATCH 69/80] Skip detection data with large delay (#396) * CDAR-756: Skip detection data with large delay * Fix compilation errors * Added logging * Updates --- sensor_data_sharing_service/src/main.cpp | 4 ++++ .../src/sensor_data_sharing_service.cpp | 16 ++++++++++++++++ 2 files changed, 20 insertions(+) diff --git a/sensor_data_sharing_service/src/main.cpp b/sensor_data_sharing_service/src/main.cpp index 09d151ed2..1ddd585ae 100644 --- a/sensor_data_sharing_service/src/main.cpp +++ b/sensor_data_sharing_service/src/main.cpp @@ -33,6 +33,10 @@ int main(int argc, char **argv) SPDLOG_ERROR("Exception Encountered : {0}" , e.what()); exit(1); } + catch ( ... ) { + SPDLOG_CRITICAL("Unknown error occured."); + exit(1); + } return 0; } \ No newline at end of file diff --git a/sensor_data_sharing_service/src/sensor_data_sharing_service.cpp b/sensor_data_sharing_service/src/sensor_data_sharing_service.cpp index 5e23a0704..1706a96e7 100644 --- a/sensor_data_sharing_service/src/sensor_data_sharing_service.cpp +++ b/sensor_data_sharing_service/src/sensor_data_sharing_service.cpp @@ -96,6 +96,19 @@ namespace sensor_data_sharing_service { if (payload.length() != 0) { auto detected_object = streets_utils::messages::detected_objects_msg::from_json(payload); + // Get delay of detected object + auto delay = static_cast(ss::streets_clock_singleton::time_in_ms()) - static_cast(detected_object._timestamp); + SPDLOG_DEBUG("Detection Delay : {0}ms!", delay); + // if delay is greater than 500 ms skip detection to get more recent data + if ( delay >= 500 ) { + SPDLOG_WARN("Skipping incoming detection at {0}ms is not current or has invalid timestamp of {1}ms!" , ss::streets_clock_singleton::time_in_ms(), detected_object._timestamp ); + continue; + } + // if delay is negative, detection message was processed before time sync message. Wait on time sync message + else if ( delay < 0 ) { + SPDLOG_WARN("Current sim time {0} waiting for sim time {1}ms from detection ...",ss::streets_clock_singleton::time_in_ms(), detected_object._timestamp ); + ss::streets_clock_singleton::sleep_for(abs(delay)); + } // Write Lock std::unique_lock lock(detected_objects_lock); detected_objects[detected_object._object_id] = detected_object; @@ -129,6 +142,8 @@ namespace sensor_data_sharing_service { }else { this->_message_count = 0; } + // Write Lock + std::unique_lock lock(detected_objects_lock); // Clear detected object detected_objects.clear(); } @@ -138,6 +153,7 @@ namespace sensor_data_sharing_service { } ss::streets_clock_singleton::sleep_for(100); // Sleep for 100 ms between publish } + SPDLOG_CRITICAL("SDSM Producers no longer running."); } From 1b282dee752d0cda865558fab3aaa3e778bc9c6e Mon Sep 17 00:00:00 2001 From: Saikrishna Bairamoni <84093461+SaikrishnaBairamoni@users.noreply.github.com> Date: Sat, 10 Feb 2024 13:15:10 -0500 Subject: [PATCH 70/80] update docker compose file to point candidate lavida images --- docker-compose.yml | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/docker-compose.yml b/docker-compose.yml index 2cc6d8e88..7d81f719c 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -60,7 +60,7 @@ services: - ./mysql/localhost.sql:/docker-entrypoint-initdb.d/localhost.sql - mysql-datavolume:/var/lib/mysql php: - image: usdotfhwaops/php:latest + image: usdotfhwaops/php:lavida container_name: php network_mode: host depends_on: @@ -69,7 +69,7 @@ services: tty: true v2xhub: - image: usdotfhwaops/v2xhubamd:latest + image: usdotfhwaops/v2xhubamd:lavida container_name: v2xhub network_mode: host restart: always @@ -95,7 +95,7 @@ services: - /etc/localtime:/etc/localtime:ro - /etc/timezone:/etc/timezone:ro scheduling_service: - image: usdotfhwastoldev/scheduling_service:develop + image: usdotfhwastolcandidate/scheduling_service:lavida command: sh -c "/wait && /home/carma-streets/scheduling_service/build/scheduling_service" build: context: . @@ -120,7 +120,7 @@ services: - /etc/timezone:/etc/timezone:ro message_services: - image: usdotfhwastoldev/message_services:develop + image: usdotfhwastolcandidate/message_services:lavida build: context: . dockerfile: message_services/Dockerfile @@ -138,7 +138,7 @@ services: - /etc/localtime:/etc/localtime:ro - /etc/timezone:/etc/timezone:ro intersection_model: - image: usdotfhwastoldev/intersection_model:develop + image: usdotfhwastolcandidate/intersection_model:lavida build: context: . dockerfile: intersection_model/Dockerfile @@ -156,7 +156,7 @@ services: - /etc/localtime:/etc/localtime:ro - /etc/timezone:/etc/timezone:ro signal_opt_service: - image: usdotfhwastoldev/signal_opt_service:develop + image: usdotfhwastolcandidate/signal_opt_service:lavida command: sh -c "/wait && /home/carma-streets/signal_opt_service/build/signal_opt_service" build: context: . @@ -182,7 +182,7 @@ services: - /etc/localtime:/etc/localtime:ro - /etc/timezone:/etc/timezone:ro tsc_service: - image: usdotfhwastoldev/tsc_service:develop + image: usdotfhwastolcandidate/tsc_service:lavida command: sh -c "/wait && /home/carma-streets/tsc_client_service/build/traffic_signal_controller_service" build: context: . @@ -209,7 +209,7 @@ services: - /etc/localtime:/etc/localtime:ro - /etc/timezone:/etc/timezone:ro sensor_data_sharing_service: - image: usdotfhwastoldev/sensor_data_sharing_service:develop + image: usdotfhwastolcandidate/sensor_data_sharing_service:lavida build: context: . dockerfile: sensor_data_sharing_service/Dockerfile From ceb2a3ec65b78c814662b195e069d86f59e0c970 Mon Sep 17 00:00:00 2001 From: paulbourelly999 <77466294+paulbourelly999@users.noreply.github.com> Date: Wed, 21 Feb 2024 14:06:44 -0500 Subject: [PATCH 71/80] CDAR-790:Fix kafka data collection script (#397) * CDAR-790:Fix kafka data collection script + update script to consume data from new topics * Update docker-compose with health-check Update collection script * Update collect service logs script and include documentation * Updates * Shell check updates * Fix errors * Fix failing unit test * Address sonar code smells * Replace os zip and rm calls with shutil * Use Pathlib instead of os for mkdir and file checks * Fix type --- README.md | 41 +++++++ collect_kafka_logs.py | 60 ++++++---- collect_service_logs.sh | 113 +++++++++++++++++- docker-compose.yml | 69 ++++++++--- sensor_data_sharing_service/manifest.json | 4 +- .../src/sensor_data_sharing_service.cpp | 15 ++- .../test/sensor_data_sharing_service_test.cpp | 10 +- 7 files changed, 263 insertions(+), 49 deletions(-) diff --git a/README.md b/README.md index c8d37b940..ddeb59872 100644 --- a/README.md +++ b/README.md @@ -34,6 +34,47 @@ To make creating new CARMA Streets services easier and to make our CI/CD more ef The primary carma-streets repository can be found [here](https://github.com/usdot-fhwa-stol/carma-streets) and is part of the [USDOT FHWA STOL](https://github.com/usdot-fhwa-stol/) github organization. Documentation on how the carma-streets functions, how it will evolve over time, and how you can contribute can be found at the above links, as well as on the [Doxygen Source Code Documentation](https://usdot-fhwa-stol.github.io/documentation/carma-streets/). +## Data Collection +**CARMA Streets** services have several source from which data about usecase performance can be pulled. **Kafka Message Topics** can be pulled using the `collect_kafka_logs.py` script and includes message traffic on a configurable list of topics. Service log files can be found in a given **CARMA Streets** service `logs/` directory. The `collect_service_logs.sh` adds all log files in this directory to a zip file and then deletes the originals. Some **CARMA Streets** services can configurable log performance in `.csv` file in the `logs/` directory. + +### Collect Kafka Logs +This script uses `docker exec` to ssh into a running kafka container. Then using kafka container scripts to read all kafka data from a list of provided topics. +``` +usage: collect_kafka_logs.py [-h] [--start_timestamp START_TIMESTAMP] [--end_timestamp END_TIMESTAMP] [--start_hours_ago START_HOURS_AGO] [--end_hours_ago END_HOURS_AGO] + [--topics TOPICS [TOPICS ...]] [--timeout TIMEOUT] [--zip ZIP] + outdir + +Script to grab data from kafka + +positional arguments: + outdir Folder name for the resulting folder logs are placed in + +options: + -h, --help show this help message and exit + --start_timestamp START_TIMESTAMP + Unix timestamp (seconds) for the first message to grab. Exclusive with start_hours_ago. + --end_timestamp END_TIMESTAMP + Unix timestamp (seconds) for the last message to grab. Exclusive with end_hours_ago. + --start_hours_ago START_HOURS_AGO + float hours before current time to grab first message. Exclusive with start_timestamp. + --end_hours_ago END_HOURS_AGO + float hours before current time to grab last message. Exclusive with start_timestamp. + --topics TOPICS [TOPICS ...] + list of topics to grab data from + --timeout TIMEOUT timeout for receiving messages on a topic, default is 5 seconds + --zip ZIP bool flag. When set to true, folder is compressed into a zip file. +``` +### Collection Service Logs +This script collects all **CARMA Streets** service log files, adds them to a zip file. +``` +usage: collect_service_logs.sh [-h | --help] [--output .zip] + [-c | --clear] + +options: +-h, --help Show usage +--output, -o Name of the resulting zip file container CARMA Streets Service logs +--clear, -c Adding this flag will delete log directories after creating zip. +``` ## Contribution Welcome to the CARMA contributing guide. Please read this guide to learn about our development process, how to propose pull requests and improvements, and how to build and test your changes to this project. [CARMA Contributing Guide](https://github.com/usdot-fhwa-stol/carma-platform/blob/develop/Contributing.md) diff --git a/collect_kafka_logs.py b/collect_kafka_logs.py index e822fcdb5..d6809cbee 100644 --- a/collect_kafka_logs.py +++ b/collect_kafka_logs.py @@ -1,9 +1,25 @@ +#!/usr/bin/python3 +# Copyright (C) 2024 LEIDOS. +# +# Licensed under the Apache License, Version 2.0 (the "License"); you may not +# use this file except in compliance with the License. You may obtain a copy of +# the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations under +# the License. import os import time import re import signal import subprocess import argparse +from pathlib import Path +import shutil ## Python script to download and store kafka logs @@ -71,37 +87,39 @@ def store_kafka_topic(topic, dir, timeout, start_time, end_time): def main(): # Default list of topics, aka all topics from https://usdot-carma.atlassian.net/wiki/spaces/CRMSRT/pages/2317549999/CARMA-Streets+Messaging - # topic desire_phase_plan is in the list from confluence, but doesn't exist in kafka as of 2022/12/05 topics = ['v2xhub_scheduling_plan_sub' ,'v2xhub_bsm_in', 'v2xhub_mobility_operation_in', 'v2xhub_mobility_path_in', - 'vehicle_status_intent_output', 'v2xhub_map_msg_in', 'modified_spat', 'tsc_config_state', 'desired_phase_plan'] + 'vehicle_status_intent_output', 'v2xhub_map_msg_in', 'modified_spat', 'tsc_config_state', 'desired_phase_plan', + 'v2xhub_sdsm_sub', 'v2xhub_sim_sensor_detected_object', 'v2xhub_sdsm_tra', 'desire_phase_plan'] timeout = 5 # Get arguments # The idea here was to give the user the bare minimum options, and make the default condition the most used. parser = argparse.ArgumentParser(description='Script to grab data from kafka') - parser.add_argument('outfile', help='Filename for the resulting zip file', type=str) # Required argument + parser.add_argument('outdir', help='Folder name for the resulting folder logs are placed in', type=str) # Required argument start_group = parser.add_mutually_exclusive_group() end_group = parser.add_mutually_exclusive_group() start_group.add_argument('--start_timestamp', help='Unix timestamp (seconds) for the first message to grab. Exclusive with start_hours_ago. ', type=int) end_group.add_argument('--end_timestamp', help='Unix timestamp (seconds) for the last message to grab. Exclusive with end_hours_ago. ', type=int) start_group.add_argument('--start_hours_ago', help='float hours before current time to grab first message. Exclusive with start_timestamp. ', type=float) end_group.add_argument('--end_hours_ago', help='float hours before current time to grab last message. Exclusive with start_timestamp. ', type=float) - parser.add_argument('--topics', type=str, nargs='+', help=f'list of topics to grab data from') - parser.add_argument('--timeout', type=float, help=f'timeout for receiving messages on a topic, default is 5 seconds') + parser.add_argument('--topics', type=str, nargs='+', help='list of topics to grab data from') + parser.add_argument('--timeout', type=float, help='timeout for receiving messages on a topic, default is 5 seconds') + parser.add_argument('--zip', type=bool,help='bool flag. When set to true, folder is compressed into a zip file.', default=False) args = parser.parse_args() # Correct and validate outfile name - if args.outfile[-4:] == '.zip': - outfile = args.outfile[:-4] + if args.outdir[-4:] == '.zip': + outdir = args.outdir[:-4] else: - outfile = args.outfile - - if os.path.isdir(f'{os.getcwd()}/{outfile}'): - print(f'folder {outfile} exists, please remove or rename') + outdir = args.outdir + # Check if output directory already exists + if Path(Path.cwd()/outdir).is_dir(): + print(f'folder {outdir} exists, please remove or rename') return - elif os.path.isfile(f'{os.getcwd()}/{outfile}.zip'): - print(f'zip file {outfile}.zip exists, please remove or rename') + # Check if zip file already exists + elif Path(Path.cwd()/f'{outdir}.zip').is_file(): + print(f'zip file {outdir}.zip exists, please remove or rename') return # Convert given time to unix timestamp (in ms) @@ -123,17 +141,17 @@ def main(): topics = args.topics if args.timeout: timeout = args.timeout - - os.system(f'mkdir {outfile}') + Path(outdir).mkdir(exist_ok=False) for topic in topics: - ret = store_kafka_topic(topic, outfile, timeout, start_time, end_time) + ret = store_kafka_topic(topic, outdir, timeout, start_time, end_time) if ret != 0: - print('received error, stopping execution') - return + print(f'received error on topic {topic}, going to next topic') - print('Available logs collected, zipping and removing the temporary folder') - os.system(f'zip -r {outfile}.zip {outfile}') - os.system(f'rm -r {outfile}') + print('Available logs collected') + if args.zip: + print('Zipping and removing the temporary folder') + shutil.make_archive(base_name=outdir, format='zip', root_dir=outdir) + shutil.rmtree(path=outdir) if __name__ == "__main__": diff --git a/collect_service_logs.sh b/collect_service_logs.sh index 2ff76338a..70752fc98 100755 --- a/collect_service_logs.sh +++ b/collect_service_logs.sh @@ -1,11 +1,116 @@ +#!/bin/bash +# Copyright (C) 2024 LEIDOS. +# +# Licensed under the Apache License, Version 2.0 (the "License"); you may not +# use this file except in compliance with the License. You may obtain a copy of +# the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations under # the License. - -# script collects all service logs files and zips them into an archive and then removes the source files. +####################################### +# Prints the script's argument descriptions +# Globals: +# None +# Arguments: +# None +# Returns: +# 0 +####################################### set -e -zip -r service_logs.zip ./scheduling_service/logs ./intersection_model/logs ./tsc_client_service/logs ./message_services/logs ./signal_opt_service/logs -sudo rm ./scheduling_service/logs/* ./intersection_model/logs/* ./tsc_client_service/logs/* ./message_services/logs/* ./signal_opt_service/logs/* +function print_help() { + command cat <<-HELP +options: +-h, --help Show usage +--output, -o Name of the resulting zip file container CARMA Streets Service logs +--clear, -c Adding this flag will delete log directories after creating zip. + +HELP +} +####################################### +# Prints a string to standard error +# Globals: +# None +# Arguments: +# String to print +# Returns: +# 0 +####################################### +function err() { + echo "$*" >&2 +} +####################################### +# Prints the script's usage +# Globals: +# None +# Arguments: +# None +# Returns: +# 0 +####################################### +function print_usage() { + command cat <<-USAGE +usage: collect_service_logs.sh [-h | --help] [--output .zip] + [-c | --clear] +USAGE +} +####################################### +# Collects all service logs files and zips them into an archive and then configurable removes the source files. +# Globals: +# None +# Arguments: +# None +# Returns: +# 0 +####################################### +function main() { + while :; do + case "$1" in + -h|--help) + print_usage + echo "" + print_help + exit 0 + ;; + -c|--clear) + echo "Clearing log directories after zip file is created!" + clear=true + ;; + --output|-o) + shift + if [ "$#" -eq 0 ]; then + err "error: option 'output' requires a value" + print_usage + exit 129 + fi + output="$1" + ;; + *) + if [[ "$1" == -* ]]; then + err "unknown option '$1'" + print_usage + exit 129 + fi + break + esac + shift + done + if [ "$output" ]; + then + echo "Writing zip file ${output}.zip" + zip -rq ./"${output}".zip ./scheduling_service/logs ./intersection_model/logs ./tsc_client_service/logs ./message_services/logs ./signal_opt_service/logs ./sensor_data_sharing_service/logs + else + filename=$(date "+%FT%H%M%S") + echo "Writing zip file ${filename}.zip" + zip -rq ./"${filename}".zip ./scheduling_service/logs ./intersection_model/logs ./tsc_client_service/logs ./message_services/logs ./signal_opt_service/logs ./sensor_data_sharing_service/logs + fi + if [ "$clear" = true ]; + then + rm ./scheduling_service/logs/* ./intersection_model/logs/* ./tsc_client_service/logs/* ./message_services/logs/* ./signal_opt_service/logs/* ./sensor_data_sharing_service/logs/* + fi +} +main "$@" \ No newline at end of file diff --git a/docker-compose.yml b/docker-compose.yml index 7d81f719c..690562eda 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -16,10 +16,31 @@ services: - zookeeper ports: - "9092:9092" + # Health check to confirm kafka server is healthy (script is a client) and all topics + # have been created (time_sync is last topic). + healthcheck: + test: ["CMD", "kafka-topics.sh", "--describe", "--bootstrap-server", "127.0.0.1:9092", "--topic", "time_sync"] + interval: 2s + timeout: 10s + retries: 10 environment: DOCKER_HOST_IP: ${DOCKER_HOST_IP} KAFKA_ADVERTISED_HOST_NAME: ${DOCKER_HOST_IP} - KAFKA_CREATE_TOPICS: "v2xhub_scheduling_plan_sub:1:1,v2xhub_bsm_in:1:1,v2xhub_mobility_operation_in:1:1,v2xhub_mobility_path_in:1:1,vehicle_status_intent_output:1:1,v2xhub_map_msg_in:1:1,modified_spat:1:1,desired_phase_plan:1:1, tsc_config_state:1:1,phase_control_schedule:1:1,detections:1:1,sdsm_out:1:1" + KAFKA_CREATE_TOPICS: "\ + v2xhub_scheduling_plan_sub:1:1,\ + v2xhub_bsm_in:1:1,\ + v2xhub_mobility_operation_in:1:1,\ + v2xhub_mobility_path_in:1:1,\ + vehicle_status_intent_output:1:1,\ + v2xhub_map_msg_in:1:1,\ + modified_spat:1:1,\ + desired_phase_plan:1:1,\ + tsc_config_state:1:1,\ + v2xhub_sim_sensor_detected_object:1:1,\ + v2xhub_sdsm_sub:1:1,\ + desire_phase_plan:1:1,\ + v2xhub_sdsm_tra:1:1,\ + time_sync:1:1" KAFKA_ZOOKEEPER_CONNECT: zookeeper:2181 KAFKA_LOG_DIRS: "/kafka/kafka-logs" volumes: @@ -38,7 +59,8 @@ services: restart: on-failure network_mode: host depends_on: - - kafka + kafka: + condition: service_healthy environment: CONFIG_FILEPATH: /etc/kowl/kowl.yaml volumes: @@ -103,10 +125,13 @@ services: container_name: scheduling_service restart: always network_mode: host - depends_on: - - kafka - - intersection_model - - message_services + depends_on: + kafka: + condition: service_healthy + intersection_model: + condition: service_started + message_services: + condition: service_started environment: DOCKER_HOST_IP: ${DOCKER_HOST_IP} WAIT_HOSTS: localhost:8080 @@ -127,8 +152,9 @@ services: container_name: message_services restart: always network_mode: host - depends_on: - - kafka + depends_on: + kafka: + condition: service_healthy environment: DOCKER_HOST_IP: ${DOCKER_HOST_IP} volumes: @@ -146,7 +172,8 @@ services: restart: always network_mode: host depends_on: - - kafka + kafka: + condition: service_healthy environment: DOCKER_HOST_IP: ${DOCKER_HOST_IP} volumes: @@ -165,11 +192,16 @@ services: restart: always network_mode: host depends_on: - - kafka - - intersection_model - - message_services - - scheduling_service - - tsc_service + kafka: + condition: service_healthy + intersection_model: + condition: service_started + message_services: + condition: service_started + scheduling_service: + condition: service_started + tsc_service: + condition: service_started environment: DOCKER_HOST_IP: ${DOCKER_HOST_IP} WAIT_HOSTS: localhost:8080 @@ -191,8 +223,10 @@ services: restart: always network_mode: host depends_on: - - kafka - - intersection_model + kafka: + condition: service_healthy + intersection_model: + condition: service_started environment: DOCKER_HOST_IP: ${DOCKER_HOST_IP} WAIT_HOSTS: localhost:8080 @@ -217,7 +251,8 @@ services: restart: always network_mode: host depends_on: - - kafka + kafka: + condition: service_healthy environment: SIMULATION_MODE: FALSE CONFIG_FILE_PATH: /home/carma-streets/sensor_data_sharing_service/manifest.json diff --git a/sensor_data_sharing_service/manifest.json b/sensor_data_sharing_service/manifest.json index 235ec35e2..eb7baf7f8 100644 --- a/sensor_data_sharing_service/manifest.json +++ b/sensor_data_sharing_service/manifest.json @@ -4,13 +4,13 @@ "configurations": [ { "name": "sdsm_producer_topic", - "value": "sdsm", + "value": "v2xhub_sdsm_sub", "description": "Kafka topic to which the sensor data sharing service will produce SDSMs.", "type": "STRING" }, { "name": "detection_consumer_topic", - "value": "detections", + "value": "v2xhub_sim_sensor_detected_object", "description":"Kafka topic from which the sensor data sharing service will consume Detected Objects.", "type": "STRING" }, diff --git a/sensor_data_sharing_service/src/sensor_data_sharing_service.cpp b/sensor_data_sharing_service/src/sensor_data_sharing_service.cpp index 1706a96e7..c1233354e 100644 --- a/sensor_data_sharing_service/src/sensor_data_sharing_service.cpp +++ b/sensor_data_sharing_service/src/sensor_data_sharing_service.cpp @@ -104,11 +104,22 @@ namespace sensor_data_sharing_service { SPDLOG_WARN("Skipping incoming detection at {0}ms is not current or has invalid timestamp of {1}ms!" , ss::streets_clock_singleton::time_in_ms(), detected_object._timestamp ); continue; } - // if delay is negative, detection message was processed before time sync message. Wait on time sync message - else if ( delay < 0 ) { + // if delay is negative, and service is in simulation mode + // the detection message was processed before time sync message. Wait on time sync message + else if (is_simulation_mode() && delay < 0 ) { SPDLOG_WARN("Current sim time {0} waiting for sim time {1}ms from detection ...",ss::streets_clock_singleton::time_in_ms(), detected_object._timestamp ); ss::streets_clock_singleton::sleep_for(abs(delay)); } + // If delay is negative and service is not in simulation mode + // indicates sensor and service are not time sychronized. + else if( delay < 0 ) { + SPDLOG_WARN( + R"(Current time is {0}ms and detection time stamp is {1}ms. Sensor Data Sharing Service and sensor producing detections to not appear to be time synchronized)", + ss::streets_clock_singleton::time_in_ms(), + detected_object._timestamp ); + + } + // Write Lock std::unique_lock lock(detected_objects_lock); detected_objects[detected_object._object_id] = detected_object; diff --git a/sensor_data_sharing_service/test/sensor_data_sharing_service_test.cpp b/sensor_data_sharing_service/test/sensor_data_sharing_service_test.cpp index 62480aa56..f4b729e45 100644 --- a/sensor_data_sharing_service/test/sensor_data_sharing_service_test.cpp +++ b/sensor_data_sharing_service/test/sensor_data_sharing_service_test.cpp @@ -25,6 +25,7 @@ #include + using testing::_; using testing::Return; using testing::AnyNumber; @@ -33,14 +34,17 @@ using testing::DoAll; namespace sensor_data_sharing_service { TEST(sensorDataSharingServiceTest, consumeDetections) { - + // Set simulation mode to false + setenv(streets_service::SIMULATION_MODE_ENV.c_str(), "FALSE", 1); sds_service serv; + + serv.initialize(); // If consumer null expect runtime error EXPECT_THROW(serv.consume_detections(), std::runtime_error); - serv.detection_consumer = std::make_shared(); EXPECT_CALL(dynamic_cast(*serv.detection_consumer),subscribe()).Times(1); - EXPECT_CALL(dynamic_cast(*serv.detection_consumer),is_running()).Times(4).WillOnce(Return(true)) + EXPECT_CALL(dynamic_cast(*serv.detection_consumer),is_running()).Times(4) + .WillOnce(Return(true)) .WillOnce(Return(true)) .WillOnce(Return(true)) .WillRepeatedly(Return(false)); From bb41ff9dd4a671be3ef34f83e71a1acfcf3da07d Mon Sep 17 00:00:00 2001 From: paulbourelly999 <77466294+paulbourelly999@users.noreply.github.com> Date: Wed, 21 Feb 2024 17:52:40 -0500 Subject: [PATCH 72/80] CDAR:790: Add time_sync topic to default list (#399) --- collect_kafka_logs.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/collect_kafka_logs.py b/collect_kafka_logs.py index d6809cbee..d4df815a1 100644 --- a/collect_kafka_logs.py +++ b/collect_kafka_logs.py @@ -89,7 +89,7 @@ def main(): # Default list of topics, aka all topics from https://usdot-carma.atlassian.net/wiki/spaces/CRMSRT/pages/2317549999/CARMA-Streets+Messaging topics = ['v2xhub_scheduling_plan_sub' ,'v2xhub_bsm_in', 'v2xhub_mobility_operation_in', 'v2xhub_mobility_path_in', 'vehicle_status_intent_output', 'v2xhub_map_msg_in', 'modified_spat', 'tsc_config_state', 'desired_phase_plan', - 'v2xhub_sdsm_sub', 'v2xhub_sim_sensor_detected_object', 'v2xhub_sdsm_tra', 'desire_phase_plan'] + 'v2xhub_sdsm_sub', 'v2xhub_sim_sensor_detected_object', 'v2xhub_sdsm_tra', 'desire_phase_plan', 'time_sync'] timeout = 5 # Get arguments From fc142ceb491097a21d5adb827e3cdb8ee0a8e525 Mon Sep 17 00:00:00 2001 From: paulbourelly999 <77466294+paulbourelly999@users.noreply.github.com> Date: Fri, 1 Mar 2024 09:36:43 -0500 Subject: [PATCH 73/80] CDA-819: Add timestamp information to detection data in SDSM (#400) * CDA-819: Add timestamp information to detection data in SDSM * Correct message timestamp member name * Fix unit tests * Fix syntax errors * Address sonar code smell * Remove const from primitive parameter pass by value * Updates * Fix syntax error --- .../include/detected_object_to_sdsm_converter.hpp | 2 +- .../src/detected_object_to_sdsm_converter.cpp | 5 ++++- .../src/sensor_data_sharing_service.cpp | 2 +- .../test/detected_object_to_sdsm_converter_test.cpp | 9 ++++++--- 4 files changed, 12 insertions(+), 6 deletions(-) diff --git a/sensor_data_sharing_service/include/detected_object_to_sdsm_converter.hpp b/sensor_data_sharing_service/include/detected_object_to_sdsm_converter.hpp index b103366b0..36d6861c5 100644 --- a/sensor_data_sharing_service/include/detected_object_to_sdsm_converter.hpp +++ b/sensor_data_sharing_service/include/detected_object_to_sdsm_converter.hpp @@ -55,7 +55,7 @@ namespace sensor_data_sharing_service { * @note method assumes detected_object_msg is in NED coordinate frame (NED is required for SDSM based on J3223 specification) * @return streets_utils::messages::sdsm::detected_object_data. */ - streets_utils::messages::sdsm::detected_object_data to_detected_object_data(const streets_utils::messages::detected_objects_msg::detected_objects_msg &msg); + streets_utils::messages::sdsm::detected_object_data to_detected_object_data(const streets_utils::messages::detected_objects_msg::detected_objects_msg &msg, uint64_t sdsm_message_timestamp); /** * @brief convert string detection type to sdsm object_type. * @param detection_type string detection classification for detection message. diff --git a/sensor_data_sharing_service/src/detected_object_to_sdsm_converter.cpp b/sensor_data_sharing_service/src/detected_object_to_sdsm_converter.cpp index 5c22ba262..0ae64ff47 100644 --- a/sensor_data_sharing_service/src/detected_object_to_sdsm_converter.cpp +++ b/sensor_data_sharing_service/src/detected_object_to_sdsm_converter.cpp @@ -33,7 +33,7 @@ namespace sensor_data_sharing_service{ return sdsm_timestamp; } - streets_utils::messages::sdsm::detected_object_data to_detected_object_data(const streets_utils::messages::detected_objects_msg::detected_objects_msg &msg) { + streets_utils::messages::sdsm::detected_object_data to_detected_object_data(const streets_utils::messages::detected_objects_msg::detected_objects_msg &msg, uint64_t sdsm_message_timestamp ) { streets_utils::messages::sdsm::detected_object_data detected_object; detected_object._detected_object_common_data._object_type = to_object_type(msg._type); if (detected_object._detected_object_common_data._object_type == streets_utils::messages::sdsm::object_type::VEHICLE ) { @@ -66,6 +66,9 @@ namespace sensor_data_sharing_service{ detected_object._detected_object_optional_data = optional_data; } + // Used to convey an offset in time relative to the sDSMTimeStamp associated with the reference position. Negative values indicate + // the provided detected object characteristics refer to a point in time after the sDSMTimeStamp + detected_object._detected_object_common_data._time_measurement_offset = static_cast(sdsm_message_timestamp - msg._timestamp); detected_object._detected_object_common_data._classification_confidence = static_cast(msg._confidence*100); // TODO: Change Detected Object ID to int detected_object._detected_object_common_data._object_id = msg._object_id; diff --git a/sensor_data_sharing_service/src/sensor_data_sharing_service.cpp b/sensor_data_sharing_service/src/sensor_data_sharing_service.cpp index c1233354e..675b14a65 100644 --- a/sensor_data_sharing_service/src/sensor_data_sharing_service.cpp +++ b/sensor_data_sharing_service/src/sensor_data_sharing_service.cpp @@ -184,7 +184,7 @@ namespace sensor_data_sharing_service { std::shared_lock lock(detected_objects_lock); for (const auto &[object_id, object] : detected_objects){ auto ned_object = detected_object_enu_to_ned(object); - auto detected_object_data = to_detected_object_data(ned_object); + auto detected_object_data = to_detected_object_data(ned_object,timestamp); // TODO: Update time offset. Currently CARMA-Streets detected object message does not support timestamp // This is a bug and needs to be addressed. msg._objects.push_back(detected_object_data); diff --git a/sensor_data_sharing_service/test/detected_object_to_sdsm_converter_test.cpp b/sensor_data_sharing_service/test/detected_object_to_sdsm_converter_test.cpp index 5f26996c3..a0d81e818 100644 --- a/sensor_data_sharing_service/test/detected_object_to_sdsm_converter_test.cpp +++ b/sensor_data_sharing_service/test/detected_object_to_sdsm_converter_test.cpp @@ -41,8 +41,9 @@ namespace sensor_data_sharing_service { msg._size._length =1.1; msg._size._width =1.1; msg._size._height = 10; + msg._timestamp = 100; - auto data = to_detected_object_data(msg); + auto data = to_detected_object_data(msg, 200); EXPECT_EQ(msg._object_id, data._detected_object_common_data._object_id); EXPECT_EQ(streets_utils::messages::sdsm::object_type::UNKNOWN, data._detected_object_common_data._object_type); EXPECT_EQ(90, data._detected_object_common_data._classification_confidence); @@ -53,7 +54,7 @@ namespace sensor_data_sharing_service { EXPECT_EQ(115, data._detected_object_common_data._speed); EXPECT_EQ(260, data._detected_object_common_data._speed_z); EXPECT_EQ(static_cast(msg._size._length*10), std::get(data._detected_object_optional_data.value())._size._length); - + EXPECT_EQ(100, data._detected_object_common_data._time_measurement_offset); } @@ -74,8 +75,9 @@ namespace sensor_data_sharing_service { msg._size._length =1.1; msg._size._width =1.1; msg._size._height = 10; + msg._timestamp = 200; - auto data = to_detected_object_data(msg); + auto data = to_detected_object_data(msg, 100); EXPECT_EQ(msg._object_id, data._detected_object_common_data._object_id); EXPECT_EQ(streets_utils::messages::sdsm::object_type::UNKNOWN, data._detected_object_common_data._object_type); EXPECT_EQ(90, data._detected_object_common_data._classification_confidence); @@ -86,6 +88,7 @@ namespace sensor_data_sharing_service { EXPECT_EQ(260, data._detected_object_common_data._speed_z); EXPECT_EQ(static_cast(msg._size._length*10), std::get(data._detected_object_optional_data.value())._size._length); + EXPECT_EQ(-100, data._detected_object_common_data._time_measurement_offset); } From f466541a7ed30d7b4aa56e2e885aa603cf6f459e Mon Sep 17 00:00:00 2001 From: Misheel Bayartsengel Date: Thu, 21 Mar 2024 15:25:25 -0400 Subject: [PATCH 74/80] add time sync log (#403) * add time sync log * move to debug * add comment --- .../src/streets_service.cpp | 25 +++++++++++++------ 1 file changed, 18 insertions(+), 7 deletions(-) diff --git a/streets_utils/streets_service_base/src/streets_service.cpp b/streets_utils/streets_service_base/src/streets_service.cpp index f63f8deb1..dc27467f3 100644 --- a/streets_utils/streets_service_base/src/streets_service.cpp +++ b/streets_utils/streets_service_base/src/streets_service.cpp @@ -1,4 +1,5 @@ #include "streets_service.h" +#include namespace streets_service { @@ -37,7 +38,7 @@ namespace streets_service { } - std::shared_ptr streets_service::create_daily_logger(const std::string &name, const std::string &extension, + std::shared_ptr streets_service::create_daily_logger(const std::string &name, const std::string &extension, const std::string &pattern, const spdlog::level::level_enum &level) const { auto logger = spdlog::daily_logger_mt( @@ -53,7 +54,7 @@ namespace streets_service { return logger; } bool streets_service::initialize_kafka_producer( const std::string &producer_topic, std::shared_ptr &producer ) { - + if ( !_kafka_client ) { _kafka_client = std::make_unique(); } @@ -68,11 +69,11 @@ namespace streets_service { SPDLOG_DEBUG("Initialized Kafka producer on topic {0}!", producer_topic); return true; } - + bool streets_service::initialize_kafka_consumer(const std::string &consumer_topic, std::shared_ptr &kafka_consumer ){ if ( !_kafka_client ) { _kafka_client = std::make_unique(); - } + } std::string bootstrap_server = streets_configuration::get_string_config("bootstrap_server"); kafka_consumer = _kafka_client->create_consumer(bootstrap_server, consumer_topic, _service_name); if (!kafka_consumer->init()) @@ -87,7 +88,7 @@ namespace streets_service { void streets_service::consume_time_sync_message() const { _time_consumer->subscribe(); while (_time_consumer->is_running()) - { + { const std::string payload = _time_consumer->consume(1000); if (payload.length() != 0) { @@ -96,14 +97,24 @@ namespace streets_service { simulation::time_sync_message msg; msg.fromJson(payload); streets_clock_singleton::update(msg.timestep); + // A script to validate time synchronization of tools in CDASim currently relies on the following + // log line. TODO: This line is meant to be removed in the future upon completion of this work: + // https://github.com/usdot-fhwa-stol/carma-analytics-fotda/pull/43 + if (spdlog::get_level() == spdlog::level::debug) + { + auto time_now = std::chrono::system_clock::now(); + auto epoch = time_now.time_since_epoch(); + auto milliseconds = std::chrono::duration_cast(epoch); + SPDLOG_DEBUG("Simulation Time: {0} where current system time is: {1}", msg.timestep, milliseconds.count()); + } } catch( const std::runtime_error &e) { SPDLOG_WARN( "{0} exception occured will consuming {1} msg! Skipping message!", e.what(), payload); } - + } - } + } } From b88043b0269b0b0cdfaef206fd13bfbf3bf38a98 Mon Sep 17 00:00:00 2001 From: paulbourelly999 <77466294+paulbourelly999@users.noreply.github.com> Date: Thu, 21 Mar 2024 15:25:41 -0400 Subject: [PATCH 75/80] Update unit test to expect 0 detections (#405) * Update unit test to expect 0 detections since incoming detection is not up to date * Fix typo --- .../test/sensor_data_sharing_service_test.cpp | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/sensor_data_sharing_service/test/sensor_data_sharing_service_test.cpp b/sensor_data_sharing_service/test/sensor_data_sharing_service_test.cpp index f4b729e45..0097b1dc6 100644 --- a/sensor_data_sharing_service/test/sensor_data_sharing_service_test.cpp +++ b/sensor_data_sharing_service/test/sensor_data_sharing_service_test.cpp @@ -85,8 +85,11 @@ namespace sensor_data_sharing_service { } )" )); + serv.consume_detections(); - EXPECT_EQ(serv.detected_objects.size(), 1); + // Consumed detection timestamp is more than 500 ms older than current real wall time. + // TODO: Create test case that covers condition for consuming up to date detection data. + EXPECT_EQ(serv.detected_objects.size(), 0); } TEST(sensorDataSharingServiceTest, produceSdsms) { From 986b2b1c870aa6128d61c714c3a6ffb3fae9a802 Mon Sep 17 00:00:00 2001 From: paulbourelly999 <77466294+paulbourelly999@users.noreply.github.com> Date: Thu, 21 Mar 2024 15:57:55 -0400 Subject: [PATCH 76/80] Include CI badge for Sensor Data Sharing Service (#406) * Include CI badge for Sensor Data Sharing Service * Fix typo --- README.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index ddeb59872..1deb67120 100644 --- a/README.md +++ b/README.md @@ -6,9 +6,9 @@ |-----|-----|-----|-----|-----| [![Docker Cloud Build Status](https://img.shields.io/docker/cloud/build/usdotfhwastol/scheduling_service?label=scheduling%20service)](https://hub.docker.com/repository/docker/usdotfhwastol/scheduling_service) | [![Docker Cloud Build Status](https://img.shields.io/docker/cloud/build/usdotfhwastol/message_services?label=message%20services)](https://hub.docker.com/repository/docker/usdotfhwastol/message_services) | [ ![Docker Cloud Build Status](https://img.shields.io/docker/cloud/build/usdotfhwastol/intersection_model?label=intersection%20model)](https://hub.docker.com/repository/docker/usdotfhwastol/intersection_model) | [![Docker Cloud Build Status](https://img.shields.io/docker/cloud/build/usdotfhwastol/signal_opt_service?label=signal%20opt%20service)](https://hub.docker.com/repository/docker/usdotfhwastol/signal_opt_service) | [![Docker Cloud Build Status](https://img.shields.io/docker/cloud/build/usdotfhwastol/tsc_service?label=tsc%20service&logoColor=%232496ED)](https://hub.docker.com/repository/docker/usdotfhwastol/tsc_service) | # DockerHub Release Candidate Builds -| Scheduling Service | Message Services | Intersection Model | Signal Opt Service | Tsc Service | -|----|----|----|----|----| -[![Docker Cloud Build Status](https://img.shields.io/docker/cloud/build/usdotfhwastolcandidate/scheduling_service?label=tsc%20service&logoColor=%232496ED)](https://hub.docker.com/repository/docker/usdotfhwastolcandidate/scheduling_service) | [![Docker Cloud Build Status](https://img.shields.io/docker/cloud/build/usdotfhwastolcandidate/message_services?label=message%20services)](https://hub.docker.com/repository/docker/usdotfhwastolcandidate/message_services) | [ ![Docker Cloud Build Status](https://img.shields.io/docker/cloud/build/usdotfhwastolcandidate/intersection_model?label=intersection%20model)](https://hub.docker.com/repository/docker/usdotfhwastolcandidate/intersection_model) | [![Docker Cloud Build Status](https://img.shields.io/docker/cloud/build/usdotfhwastolcandidate/signal_opt_service?label=signal%20opt%20service)](https://hub.docker.com/repository/docker/usdotfhwastolcandidate/signal_opt_service) | [![Docker Cloud Build Status](https://img.shields.io/docker/cloud/build/usdotfhwastolcandidate/tsc_service?label=tsc%20service&logoColor=%232496ED)](https://hub.docker.com/repository/docker/usdotfhwastolcandidate/tsc_service) | +| Scheduling Service | Message Services | Intersection Model | Signal Opt Service | Tsc Service | Sensor Data Sharing Service +|----|----|----|----|----|----| +[![Docker Cloud Build Status](https://img.shields.io/docker/cloud/build/usdotfhwastolcandidate/scheduling_service?label=tsc%20service&logoColor=%232496ED)](https://hub.docker.com/repository/docker/usdotfhwastolcandidate/scheduling_service) | [![Docker Cloud Build Status](https://img.shields.io/docker/cloud/build/usdotfhwastolcandidate/message_services?label=message%20services)](https://hub.docker.com/repository/docker/usdotfhwastolcandidate/message_services) | [ ![Docker Cloud Build Status](https://img.shields.io/docker/cloud/build/usdotfhwastolcandidate/intersection_model?label=intersection%20model)](https://hub.docker.com/repository/docker/usdotfhwastolcandidate/intersection_model) | [![Docker Cloud Build Status](https://img.shields.io/docker/cloud/build/usdotfhwastolcandidate/signal_opt_service?label=signal%20opt%20service)](https://hub.docker.com/repository/docker/usdotfhwastolcandidate/signal_opt_service) | [![Docker Cloud Build Status](https://img.shields.io/docker/cloud/build/usdotfhwastolcandidate/tsc_service?label=tsc%20service&logoColor=%232496ED)](https://hub.docker.com/repository/docker/usdotfhwastolcandidate/tsc_service) | ![Docker Cloud Build Status](https://img.shields.io/docker/cloud/build/usdotfhwastolcandidate/sensor_data_sharing_service?label=sensor_data_sharing_%20service&logoColor=%232496ED)| # DockerHub Develop Builds | Scheduling Service | Message Services | Intersection Model | Signal Opt Service | Tsc Service | Sensor Data Sharing Service |-----|-----|-----|-----|-----|-----| From 02d6732e34c9baef5131b6cf3c0109e1fb6a5aad Mon Sep 17 00:00:00 2001 From: paulbourelly999 <77466294+paulbourelly999@users.noreply.github.com> Date: Mon, 8 Apr 2024 13:37:03 -0400 Subject: [PATCH 77/80] Update to release target versions (#412) * Update to release target versions * Update sensor_data_sharing_service base image * Sensor Data Sharing Service changes --- docker-compose.yml | 16 ++++++++-------- sensor_data_sharing_service/Dockerfile | 2 +- streets_service_base/Dockerfile | 2 +- streets_service_base_lanelet_aware/Dockerfile | 2 +- 4 files changed, 11 insertions(+), 11 deletions(-) diff --git a/docker-compose.yml b/docker-compose.yml index 690562eda..9e8f44878 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -82,7 +82,7 @@ services: - ./mysql/localhost.sql:/docker-entrypoint-initdb.d/localhost.sql - mysql-datavolume:/var/lib/mysql php: - image: usdotfhwaops/php:lavida + image: usdotfhwaops/php:7.6.0 container_name: php network_mode: host depends_on: @@ -91,7 +91,7 @@ services: tty: true v2xhub: - image: usdotfhwaops/v2xhubamd:lavida + image: usdotfhwaops/v2xhubamd:7.6.0 container_name: v2xhub network_mode: host restart: always @@ -117,7 +117,7 @@ services: - /etc/localtime:/etc/localtime:ro - /etc/timezone:/etc/timezone:ro scheduling_service: - image: usdotfhwastolcandidate/scheduling_service:lavida + image: usdotfhwastol/scheduling_service:carma-system-4.5.0 command: sh -c "/wait && /home/carma-streets/scheduling_service/build/scheduling_service" build: context: . @@ -145,7 +145,7 @@ services: - /etc/timezone:/etc/timezone:ro message_services: - image: usdotfhwastolcandidate/message_services:lavida + image: usdotfhwastol/message_services:carma-system-4.5.0 build: context: . dockerfile: message_services/Dockerfile @@ -164,7 +164,7 @@ services: - /etc/localtime:/etc/localtime:ro - /etc/timezone:/etc/timezone:ro intersection_model: - image: usdotfhwastolcandidate/intersection_model:lavida + image: usdotfhwastol/intersection_model:carma-system-4.5.0 build: context: . dockerfile: intersection_model/Dockerfile @@ -183,7 +183,7 @@ services: - /etc/localtime:/etc/localtime:ro - /etc/timezone:/etc/timezone:ro signal_opt_service: - image: usdotfhwastolcandidate/signal_opt_service:lavida + image: usdotfhwastol/signal_opt_service:carma-system-4.5.0 command: sh -c "/wait && /home/carma-streets/signal_opt_service/build/signal_opt_service" build: context: . @@ -214,7 +214,7 @@ services: - /etc/localtime:/etc/localtime:ro - /etc/timezone:/etc/timezone:ro tsc_service: - image: usdotfhwastolcandidate/tsc_service:lavida + image: usdotfhwastol/tsc_service:carma-system-4.5.0 command: sh -c "/wait && /home/carma-streets/tsc_client_service/build/traffic_signal_controller_service" build: context: . @@ -243,7 +243,7 @@ services: - /etc/localtime:/etc/localtime:ro - /etc/timezone:/etc/timezone:ro sensor_data_sharing_service: - image: usdotfhwastolcandidate/sensor_data_sharing_service:lavida + image: usdotfhwastol/sensor_data_sharing_service:carma-system-4.5.0 build: context: . dockerfile: sensor_data_sharing_service/Dockerfile diff --git a/sensor_data_sharing_service/Dockerfile b/sensor_data_sharing_service/Dockerfile index d86a30858..d09192a22 100644 --- a/sensor_data_sharing_service/Dockerfile +++ b/sensor_data_sharing_service/Dockerfile @@ -1,4 +1,4 @@ -FROM usdotfhwastoldev/streets_service_base_lanelet_aware:bionic +FROM usdotfhwastol/streets_service_base_lanelet_aware:carma-system-4.5.0-bionic COPY ./sensor_data_sharing_service/ /home/carma-streets/sensor_data_sharing_service COPY ./streets_utils/ /home/carma-streets/streets_utils COPY ./kafka_clients/ /home/carma-streets/kafka_clients diff --git a/streets_service_base/Dockerfile b/streets_service_base/Dockerfile index a2a4a5dc9..5741fbd74 100644 --- a/streets_service_base/Dockerfile +++ b/streets_service_base/Dockerfile @@ -1,4 +1,4 @@ ARG UBUNTU_CODENAME -FROM ghcr.io/usdot-fhwa-stol/carma-builds-x64:${UBUNTU_CODENAME} +FROM ghcr.io/usdot-fhwa-stol/carma-builds-x64:carma-system-4.5.0-${UBUNTU_CODENAME} COPY ./build_scripts /opt/carma-streets/build_scripts RUN /opt/carma-streets/build_scripts/install_streets_service_base_dependencies.sh diff --git a/streets_service_base_lanelet_aware/Dockerfile b/streets_service_base_lanelet_aware/Dockerfile index 1eba3fb0d..6e4ba143d 100644 --- a/streets_service_base_lanelet_aware/Dockerfile +++ b/streets_service_base_lanelet_aware/Dockerfile @@ -1,3 +1,3 @@ -FROM usdotfhwastoldev/streets_service_base:bionic +FROM usdotfhwastoldev/streets_service_base:carma-system-4.5.0-bionic ENV LANELET2_MAP="/home/carma-streets/MAP/Intersection.osm" RUN /opt/carma-streets/build_scripts/install_lanelet2_dependencies.sh From 6fa4c439a4139c71b8fcb8c5bc591f4a8a6abb47 Mon Sep 17 00:00:00 2001 From: paulbourelly999 <77466294+paulbourelly999@users.noreply.github.com> Date: Mon, 8 Apr 2024 16:31:36 -0400 Subject: [PATCH 78/80] Update organization for streets_service_base_lanelet_aware image (#415) --- streets_service_base_lanelet_aware/Dockerfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/streets_service_base_lanelet_aware/Dockerfile b/streets_service_base_lanelet_aware/Dockerfile index 6e4ba143d..02780d06f 100644 --- a/streets_service_base_lanelet_aware/Dockerfile +++ b/streets_service_base_lanelet_aware/Dockerfile @@ -1,3 +1,3 @@ -FROM usdotfhwastoldev/streets_service_base:carma-system-4.5.0-bionic +FROM usdotfhwastol/streets_service_base:carma-system-4.5.0-bionic ENV LANELET2_MAP="/home/carma-streets/MAP/Intersection.osm" RUN /opt/carma-streets/build_scripts/install_lanelet2_dependencies.sh From 0344b4af43de7d173faf01077352a1164dcbccab Mon Sep 17 00:00:00 2001 From: paulbourelly999 <77466294+paulbourelly999@users.noreply.github.com> Date: Tue, 9 Apr 2024 16:12:34 -0400 Subject: [PATCH 79/80] Fix ci master (#416) * Fix CI for master * Testing * Undo testing changes --- .github/workflows/develop_master.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/develop_master.yml b/.github/workflows/develop_master.yml index 240292443..197f7ad67 100644 --- a/.github/workflows/develop_master.yml +++ b/.github/workflows/develop_master.yml @@ -28,14 +28,14 @@ jobs: push: true build-args: | UBUNTU_CODENAME=${{ matrix.ubuntu-codename }} - tags: usdotfhwastoldev/streets_service_base:${{ matrix.ubuntu-codename }} + tags: usdotfhwastol/streets_service_base:${{ matrix.ubuntu-codename }} file: ./streets_service_base/Dockerfile - name: Docker Build Streets Service Base Lanelet Aware if: ${{ matrix.build_lanelet_aware }} uses: docker/build-push-action@v3 with: push: true - tags: usdotfhwastoldev/streets_service_base_lanelet_aware:${{ matrix.ubuntu-codename }} + tags: usdotfhwastol/streets_service_base_lanelet_aware:${{ matrix.ubuntu-codename }} file: ./streets_service_base_lanelet_aware/Dockerfile build: needs: docker-build From df49749990edd7a86f630413dd2dbe1cb8c42542 Mon Sep 17 00:00:00 2001 From: paulbourelly999 <77466294+paulbourelly999@users.noreply.github.com> Date: Wed, 10 Apr 2024 11:27:32 -0400 Subject: [PATCH 80/80] Added workflows for release and master builds for base imags (#417) * Added workflows for release and master builds for base imags * Update release workflow name --- .../{develop_master.yml => develop.yml} | 4 +- .github/workflows/master.yml | 134 +++++++++++++++++ .github/workflows/release.yml | 135 ++++++++++++++++++ 3 files changed, 271 insertions(+), 2 deletions(-) rename .github/workflows/{develop_master.yml => develop.yml} (99%) create mode 100644 .github/workflows/master.yml create mode 100644 .github/workflows/release.yml diff --git a/.github/workflows/develop_master.yml b/.github/workflows/develop.yml similarity index 99% rename from .github/workflows/develop_master.yml rename to .github/workflows/develop.yml index 197f7ad67..270f7a9c1 100644 --- a/.github/workflows/develop_master.yml +++ b/.github/workflows/develop.yml @@ -1,7 +1,7 @@ -name: develop_master +name: develop on: push: - branches: [develop, master] + branches: [develop] jobs: docker-build: strategy: diff --git a/.github/workflows/master.yml b/.github/workflows/master.yml new file mode 100644 index 000000000..2630f267a --- /dev/null +++ b/.github/workflows/master.yml @@ -0,0 +1,134 @@ +name: master +on: + push: + branches: [master] +jobs: + docker-build: + strategy: + matrix: + include: + - ubuntu-codename: bionic + build_lanelet_aware: true + # Currently install_lanelet2_dependencies.sh script only works for bionic. Disabled lanelet_aware build on newer distributions pending script updates. + - ubuntu-codename: focal + build_lanelet_aware: false + # Currently install_lanelet2_dependencies.sh script only works for bionic. Disabled lanelet_aware build on newer distributions pending script updates. + - ubuntu-codename: jammy + build_lanelet_aware: false + runs-on: ubuntu-latest + steps: + - name: Log in to the Container registry + uses: docker/login-action@v2 + with: + username: ${{ secrets.DOCKERHUB_USERNAME }} + password: ${{ secrets.DOCKERHUB_TOKEN }} + - name: Docker Build Streets Service Base + uses: docker/build-push-action@v3 + with: + push: true + build-args: | + UBUNTU_CODENAME=${{ matrix.ubuntu-codename }} + tags: usdotfhwastol/streets_service_base:${{ matrix.ubuntu-codename }} + file: ./streets_service_base/Dockerfile + - name: Docker Build Streets Service Base Lanelet Aware + if: ${{ matrix.build_lanelet_aware }} + uses: docker/build-push-action@v3 + with: + push: true + tags: usdotfhwastol/streets_service_base_lanelet_aware:${{ matrix.ubuntu-codename }} + file: ./streets_service_base_lanelet_aware/Dockerfile + build: + needs: docker-build + defaults: + run: + shell: bash + runs-on: ubuntu-latest-8-cores + container: + image: usdotfhwastol/streets_service_base_lanelet_aware:bionic + env: + DEBIAN_FRONTEND: noninteractive + SONAR_SCANNER_VERSION: "5.0.1.3006" + TERM: xterm + options: "--user root" + steps: + # Bionic's git version is not sufficient for actions/checkout 0 fetch-depth, + # remove this step after rebasing carma-streets to newer Ubuntu release + - name: Install newer git for checkout + run: | + apt-get update + apt-get install -y software-properties-common + add-apt-repository -u ppa:git-core/ppa + apt-get install -y git + - name: Checkout ${{ github.event.repository.name }} + uses: actions/checkout@v3.3.0 + with: + path: ${{ github.event.repository.name }} + fetch-depth: 0 + - name: Move source code + run: mv $GITHUB_WORKSPACE/${{ github.event.repository.name }} /home/carma-streets + - name: Install dependencies + run: | + cd /home/carma-streets/build_scripts + ./install_test_dependencies.sh + mkdir -p /home/carma-streets/ext + ./install_rest_server_dependencies.sh + - name: Install net-snmp + run: | + cd /home/carma-streets/ext/ + apt-get install -y libperl-dev curl + curl -k -L -O http://sourceforge.net/projects/net-snmp/files/net-snmp/5.9.1/net-snmp-5.9.1.tar.gz + tar -xvzf /home/carma-streets/ext/net-snmp-5.9.1.tar.gz + cd net-snmp-5.9.1/ + ./configure --with-default-snmp-version="1" --with-sys-contact="@@no.where" --with-sys-location="Unknown" --with-logfile="/var/log/snmpd.log" --with-persistent-directory="/var/net-snmp" + make -j + make install + - name: Set up JDK 17 + uses: actions/setup-java@v3 # The setup-java action provides the functionality for GitHub Actions runners for Downloading and setting up a requested version of Java + with: + java-version: 17 + distribution: "temurin" + - name: Install Sonar + run: | + SONAR_DIR=/opt/sonarqube + mkdir $SONAR_DIR + curl -o $SONAR_DIR/sonar-scanner.zip https://binaries.sonarsource.com/Distribution/sonar-scanner-cli/sonar-scanner-cli-${SONAR_SCANNER_VERSION}-linux.zip + curl -o $SONAR_DIR/build-wrapper.zip https://sonarcloud.io/static/cpp/build-wrapper-linux-x86.zip + curl -sL https://deb.nodesource.com/setup_16.x | bash - + apt-get install -y nodejs unzip + # Set the JAVA_HOME to a compatible version of Java, e.g., Java 17 + export JAVA_HOME=$GITHUB_WORKSPACE/java-17 + cd $SONAR_DIR + for ZIP in *.zip; do + unzip "$ZIP" -d . + rm "$ZIP" + done + mv $(ls $SONAR_DIR | grep "sonar-scanner-") $SONAR_DIR/sonar-scanner/ + mv $(ls $SONAR_DIR | grep "build-wrapper-") $SONAR_DIR/build-wrapper/ + echo $SONAR_DIR/sonar-scanner/bin >> $GITHUB_PATH + echo $SONAR_DIR/build-wrapper >> $GITHUB_PATH + env: + JAVA_HOME: $GITHUB_WORKSPACE/java-17 + + - name: Check Java Version + run: | + java -version + echo $JAVA_HOME + - name: Build + run: | + cd /home/carma-streets/ + build-wrapper-linux-x86-64 --out-dir /home/carma-streets/bw-output ./build.sh + - name: Tests + run: | + cd /home/carma-streets/ + ldconfig + ./coverage.sh + - name: Archive test results + uses: actions/upload-artifact@v3 + with: + name: Test Results + path: /home/carma-streets/test_results + - name: Run SonarScanner + uses: usdot-fhwa-stol/actions/sonar-scanner@main + with: + sonar-token: ${{ secrets.SONAR_TOKEN }} + working-dir: /home/carma-streets diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml new file mode 100644 index 000000000..4e26c995e --- /dev/null +++ b/.github/workflows/release.yml @@ -0,0 +1,135 @@ +name: release +on: + push: + tags: + - "carma-system-*" +jobs: + docker-build: + strategy: + matrix: + include: + - ubuntu-codename: bionic + build_lanelet_aware: true + # Currently install_lanelet2_dependencies.sh script only works for bionic. Disabled lanelet_aware build on newer distributions pending script updates. + - ubuntu-codename: focal + build_lanelet_aware: false + # Currently install_lanelet2_dependencies.sh script only works for bionic. Disabled lanelet_aware build on newer distributions pending script updates. + - ubuntu-codename: jammy + build_lanelet_aware: false + runs-on: ubuntu-latest + steps: + - name: Log in to the Container registry + uses: docker/login-action@v2 + with: + username: ${{ secrets.DOCKERHUB_USERNAME }} + password: ${{ secrets.DOCKERHUB_TOKEN }} + - name: Docker Build Streets Service Base + uses: docker/build-push-action@v3 + with: + push: true + build-args: | + UBUNTU_CODENAME=${{ matrix.ubuntu-codename }} + tags: usdotfhwastol/streets_service_base:${{ github.ref_name }}-${{ matrix.ubuntu-codename }} + file: ./streets_service_base/Dockerfile + - name: Docker Build Streets Service Base Lanelet Aware + if: ${{ matrix.build_lanelet_aware }} + uses: docker/build-push-action@v3 + with: + push: true + tags: usdotfhwastol/streets_service_base_lanelet_aware:${{ github.ref_name }}-${{ matrix.ubuntu-codename }} + file: ./streets_service_base_lanelet_aware/Dockerfile + build: + needs: docker-build + defaults: + run: + shell: bash + runs-on: ubuntu-latest-8-cores + container: + image: usdotfhwastol/streets_service_base_lanelet_aware:${{ github.ref_name }}-bionic + env: + DEBIAN_FRONTEND: noninteractive + SONAR_SCANNER_VERSION: "5.0.1.3006" + TERM: xterm + options: "--user root" + steps: + # Bionic's git version is not sufficient for actions/checkout 0 fetch-depth, + # remove this step after rebasing carma-streets to newer Ubuntu release + - name: Install newer git for checkout + run: | + apt-get update + apt-get install -y software-properties-common + add-apt-repository -u ppa:git-core/ppa + apt-get install -y git + - name: Checkout ${{ github.event.repository.name }} + uses: actions/checkout@v3.3.0 + with: + path: ${{ github.event.repository.name }} + fetch-depth: 0 + - name: Move source code + run: mv $GITHUB_WORKSPACE/${{ github.event.repository.name }} /home/carma-streets + - name: Install dependencies + run: | + cd /home/carma-streets/build_scripts + ./install_test_dependencies.sh + mkdir -p /home/carma-streets/ext + ./install_rest_server_dependencies.sh + - name: Install net-snmp + run: | + cd /home/carma-streets/ext/ + apt-get install -y libperl-dev curl + curl -k -L -O http://sourceforge.net/projects/net-snmp/files/net-snmp/5.9.1/net-snmp-5.9.1.tar.gz + tar -xvzf /home/carma-streets/ext/net-snmp-5.9.1.tar.gz + cd net-snmp-5.9.1/ + ./configure --with-default-snmp-version="1" --with-sys-contact="@@no.where" --with-sys-location="Unknown" --with-logfile="/var/log/snmpd.log" --with-persistent-directory="/var/net-snmp" + make -j + make install + - name: Set up JDK 17 + uses: actions/setup-java@v3 # The setup-java action provides the functionality for GitHub Actions runners for Downloading and setting up a requested version of Java + with: + java-version: 17 + distribution: "temurin" + - name: Install Sonar + run: | + SONAR_DIR=/opt/sonarqube + mkdir $SONAR_DIR + curl -o $SONAR_DIR/sonar-scanner.zip https://binaries.sonarsource.com/Distribution/sonar-scanner-cli/sonar-scanner-cli-${SONAR_SCANNER_VERSION}-linux.zip + curl -o $SONAR_DIR/build-wrapper.zip https://sonarcloud.io/static/cpp/build-wrapper-linux-x86.zip + curl -sL https://deb.nodesource.com/setup_16.x | bash - + apt-get install -y nodejs unzip + # Set the JAVA_HOME to a compatible version of Java, e.g., Java 17 + export JAVA_HOME=$GITHUB_WORKSPACE/java-17 + cd $SONAR_DIR + for ZIP in *.zip; do + unzip "$ZIP" -d . + rm "$ZIP" + done + mv $(ls $SONAR_DIR | grep "sonar-scanner-") $SONAR_DIR/sonar-scanner/ + mv $(ls $SONAR_DIR | grep "build-wrapper-") $SONAR_DIR/build-wrapper/ + echo $SONAR_DIR/sonar-scanner/bin >> $GITHUB_PATH + echo $SONAR_DIR/build-wrapper >> $GITHUB_PATH + env: + JAVA_HOME: $GITHUB_WORKSPACE/java-17 + + - name: Check Java Version + run: | + java -version + echo $JAVA_HOME + - name: Build + run: | + cd /home/carma-streets/ + build-wrapper-linux-x86-64 --out-dir /home/carma-streets/bw-output ./build.sh + - name: Tests + run: | + cd /home/carma-streets/ + ldconfig + ./coverage.sh + - name: Archive test results + uses: actions/upload-artifact@v3 + with: + name: Test Results + path: /home/carma-streets/test_results + - name: Run SonarScanner + uses: usdot-fhwa-stol/actions/sonar-scanner@main + with: + sonar-token: ${{ secrets.SONAR_TOKEN }} + working-dir: /home/carma-streets