From 5328f89f9f7b1d52834e84c78bed1853fb244425 Mon Sep 17 00:00:00 2001 From: Peyton Johnson <35665039+willjohnsonk@users.noreply.github.com> Date: Mon, 11 Sep 2023 16:24:43 -0400 Subject: [PATCH] Added conversion between JSON and ASN.1 C-struct for SDSM with Kafka updates (#562) # PR Details ## Description Added functionality to convert SDSMs from inbound json strings to outbound ASN.1 styled C-structs to facilitate communication over the CARMA Streets plugin in both directions. ## Related Issue CDAR-308 ## Motivation and Context ## How Has This Been Tested? Tested locally in VSCode with new unit tests for the conversion between jsons and the ASN.1 C-struct messages. ## Types of changes - [X] Defect fix (non-breaking change that fixes an issue) - [X] New feature (non-breaking change that adds functionality) - [ ] Breaking change (fix or feature that cause existing functionality to change) ## Checklist: - [ ] I have added any new packages to the sonar-scanner.properties file - [ ] My change requires a change to the documentation. - [ ] I have updated the documentation accordingly. - [X] I have read the **CONTRIBUTING** document. [V2XHUB Contributing Guide](https://github.com/usdot-fhwa-OPS/V2X-Hub/blob/develop/Contributing.md) - [X] I have added tests to cover my changes. - [X] All new and existing tests passed. --- src/v2i-hub/CARMAStreetsPlugin/CMakeLists.txt | 2 +- src/v2i-hub/CARMAStreetsPlugin/manifest.json | 10 + .../src/CARMAStreetsPlugin.cpp | 74 +++- .../src/CARMAStreetsPlugin.h | 25 ++ .../src/J3224ToSDSMJsonConverter.cpp | 18 +- .../src/JsonToJ3224SDSMConverter.cpp | 368 ++++++++++++++++++ .../src/JsonToJ3224SDSMConverter.h | 46 +++ .../test/test_J3224ToSDSMJsonConverter.cpp | 18 +- .../test/test_JsonToJ3224SDSMConverter.cpp | 188 +++++++++ 9 files changed, 728 insertions(+), 21 deletions(-) create mode 100644 src/v2i-hub/CARMAStreetsPlugin/src/JsonToJ3224SDSMConverter.cpp create mode 100644 src/v2i-hub/CARMAStreetsPlugin/src/JsonToJ3224SDSMConverter.h create mode 100644 src/v2i-hub/CARMAStreetsPlugin/test/test_JsonToJ3224SDSMConverter.cpp diff --git a/src/v2i-hub/CARMAStreetsPlugin/CMakeLists.txt b/src/v2i-hub/CARMAStreetsPlugin/CMakeLists.txt index f8c11c896..08981e25d 100644 --- a/src/v2i-hub/CARMAStreetsPlugin/CMakeLists.txt +++ b/src/v2i-hub/CARMAStreetsPlugin/CMakeLists.txt @@ -9,7 +9,7 @@ TARGET_LINK_LIBRARIES (${PROJECT_NAME} tmxutils rdkafka++ jsoncpp) ############# enable_testing() include_directories(${PROJECT_SOURCE_DIR}/src) -add_library(${PROJECT_NAME}_lib src/J2735MapToJsonConverter.cpp src/JsonToJ2735SSMConverter.cpp src/JsonToJ2735SpatConverter.cpp src/J2735ToSRMJsonConverter.cpp src/J3224ToSDSMJsonConverter.cpp) +add_library(${PROJECT_NAME}_lib src/J2735MapToJsonConverter.cpp src/JsonToJ2735SSMConverter.cpp src/JsonToJ2735SpatConverter.cpp src/J2735ToSRMJsonConverter.cpp src/J3224ToSDSMJsonConverter.cpp src/JsonToJ3224SDSMConverter.cpp) target_link_libraries(${PROJECT_NAME}_lib PUBLIC ${TMXAPI_LIBRARIES} ${ASN_J2735_LIBRARIES} ${MYSQL_LIBRARIES} diff --git a/src/v2i-hub/CARMAStreetsPlugin/manifest.json b/src/v2i-hub/CARMAStreetsPlugin/manifest.json index 22fb4a912..acd398414 100644 --- a/src/v2i-hub/CARMAStreetsPlugin/manifest.json +++ b/src/v2i-hub/CARMAStreetsPlugin/manifest.json @@ -116,6 +116,16 @@ "key": "SimSensorDetectedObjTopic", "default": "v2xhub_sim_sensor_detected_object", "description": "Apache Kafka topic plugin will transmit simulated sensor detected object to." + }, + { + "key": "SdsmSubscribeTopic", + "default": "v2xhub_sdsm_sub", + "description": "Apache Kafka topic plugin that will subscribe to SDSM streams." + }, + { + "key": "SdsmTransmitTopic", + "default": "v2xhub_sdsm_tra", + "description": "Apache Kafka topic plugin that will transmit SDSMs." } ] diff --git a/src/v2i-hub/CARMAStreetsPlugin/src/CARMAStreetsPlugin.cpp b/src/v2i-hub/CARMAStreetsPlugin/src/CARMAStreetsPlugin.cpp index 85dd69709..23a21d6bf 100755 --- a/src/v2i-hub/CARMAStreetsPlugin/src/CARMAStreetsPlugin.cpp +++ b/src/v2i-hub/CARMAStreetsPlugin/src/CARMAStreetsPlugin.cpp @@ -49,6 +49,8 @@ void CARMAStreetsPlugin::UpdateConfigSettings() { GetConfigValue("MapTopic", _transmitMAPTopic); GetConfigValue("SRMTopic", _transmitSRMTopic); GetConfigValue("SimSensorDetectedObjTopic", _transmitSimSensorDetectedObjTopic); + GetConfigValue("SdsmSubscribeTopic", _subscribeToSdsmTopic); + GetConfigValue("SdsmTransmitTopic", _transmitSDSMTopic); // Populate strategies config string config; GetConfigValue("MobilityOperationStrategies", config); @@ -81,12 +83,13 @@ void CARMAStreetsPlugin::InitKafkaConsumerProducers() _spat_kafka_consumer_ptr = client.create_consumer(kafkaConnectString, _subscribeToSpatTopic,this->_name); _scheduing_plan_kafka_consumer_ptr = client.create_consumer(kafkaConnectString, _subscribeToSchedulingPlanTopic, this->_name); _ssm_kafka_consumer_ptr = client.create_consumer(kafkaConnectString, _subscribeToSsmTopic,this->_name); - if(!_scheduing_plan_kafka_consumer_ptr || !_spat_kafka_consumer_ptr || !_ssm_kafka_consumer_ptr) + _sdsm_kafka_consumer_ptr = client.create_consumer(kafkaConnectString, _subscribeToSdsmTopic,this->_name); + if(!_scheduing_plan_kafka_consumer_ptr || !_spat_kafka_consumer_ptr || !_ssm_kafka_consumer_ptr || !_sdsm_kafka_consumer_ptr) { throw TmxException("Failed to create Kafka consumers."); } PLOG(logDEBUG) <<"Kafka consumers created"; - if(!_spat_kafka_consumer_ptr->init() || !_scheduing_plan_kafka_consumer_ptr->init() || !_ssm_kafka_consumer_ptr->init()) + if(!_spat_kafka_consumer_ptr->init() || !_scheduing_plan_kafka_consumer_ptr->init() || !_ssm_kafka_consumer_ptr->init() || !_sdsm_kafka_consumer_ptr->init()) { throw TmxException("Kafka consumers init() failed!"); } @@ -94,6 +97,7 @@ void CARMAStreetsPlugin::InitKafkaConsumerProducers() boost::thread thread_schpl(&CARMAStreetsPlugin::SubscribeSchedulingPlanKafkaTopic, this); boost::thread thread_spat(&CARMAStreetsPlugin::SubscribeSpatKafkaTopic, this); boost::thread thread_ssm(&CARMAStreetsPlugin::SubscribeSSMKafkaTopic, this); + boost::thread thread_sdsm(&CARMAStreetsPlugin::SubscribeSDSMKafkaTopic, this); } @@ -458,6 +462,19 @@ void CARMAStreetsPlugin::HandleMapMessage(MapDataMessage &msg, routeable_message produce_kafka_msg(message, _transmitMAPTopic); } +void CARMAStreetsPlugin::HandleSDSMMessage(SdsmMessage &msg, routeable_message &routeableMsg) +{ + std::shared_ptr sdsmMsgPtr = msg.get_j2735_data(); + PLOG(logDEBUG) << "Detected object count: " << sdsmMsgPtr->objects.list.count << std::endl; + Json::Value sdsmJson; + Json::StreamWriterBuilder builder; + J3224ToSDSMJsonConverter jsonConverter; + jsonConverter.convertJ3224ToSDSMJSON(sdsmMsgPtr, sdsmJson); + PLOG(logDEBUG) << "sdsmJson: " << sdsmJson << std::endl; + const std::string message = Json::writeString(builder, sdsmJson); + produce_kafka_msg(message, _transmitSDSMTopic); +} + void CARMAStreetsPlugin::produce_kafka_msg(const string& message, const string& topic_name) const { _kafka_producer_ptr->send(message, topic_name); @@ -638,6 +655,59 @@ void CARMAStreetsPlugin::SubscribeSSMKafkaTopic(){ } +void CARMAStreetsPlugin::SubscribeSDSMKafkaTopic(){ + // TODO: Update methods to represent consuming a single message from Kafka topic + if(_subscribeToSdsmTopic.length() > 0) + { + PLOG(logDEBUG) << "SubscribeSDSMKafkaTopics:" <<_subscribeToSdsmTopic << std::endl; + _sdsm_kafka_consumer_ptr->subscribe(); + //Initialize Json to J3224 SDSM convertor + JsonToJ3224SDSMConverter sdsm_convertor; + while (_sdsm_kafka_consumer_ptr->is_running()) + { + std::string payload_str = _sdsm_kafka_consumer_ptr->consume(500); + if(payload_str.length() > 0) + { + PLOG(logDEBUG) << "consumed message payload: " << payload_str <(Key_SDSMMessageSkipped, ++_sdsmMessageSkipped); + continue; + } + //Convert the SDSM JSON string into J3224 SDSM message and encode it. + auto sdsm_ptr = std::make_shared(); + sdsm_convertor.convertJsonToSDSM(sdsmDoc, sdsm_ptr); + tmx::messages::SdsmEncodedMessage sdsmEncodedMsg; + try + { + sdsm_convertor.encodeSDSM(sdsm_ptr, sdsmEncodedMsg); + } + catch (TmxException &ex) + { + // Skip messages that fail to encode. + PLOG(logERROR) << "Failed to encoded SDSM message : \n" << payload_str << std::endl << "Exception encountered: " + << ex.what() << std::endl; + ASN_STRUCT_FREE_CONTENTS_ONLY(asn_DEF_SensorDataSharingMessage, sdsm_ptr.get()); // may be unnecessary + SetStatus(Key_SDSMMessageSkipped, ++_sdsmMessageSkipped); + continue; + } + + ASN_STRUCT_FREE_CONTENTS_ONLY(asn_DEF_SensorDataSharingMessage, sdsm_ptr.get()); // same as above + PLOG(logDEBUG) << "sdsmEncodedMsg: " << sdsmEncodedMsg; + + //Broadcast the encoded SDSM message + sdsmEncodedMsg.set_flags(IvpMsgFlags_RouteDSRC); + sdsmEncodedMsg.addDsrcMetadata(0x8002); + BroadcastMessage(static_cast(sdsmEncodedMsg)); + } + } + } + +} + void CARMAStreetsPlugin::HandleSimulatedSensorDetectedMessage(simulation::SensorDetectedObject &msg, routeable_message &routeableMsg) { PLOG(logDEBUG) << "Produce sensor detected message in JSON format: " << msg.to_string() < #include "JsonToJ2735SSMConverter.h" #include +#include "JsonToJ3224SDSMConverter.h" +#include "J3224ToSDSMJsonConverter.h" #include "PluginClientClockAware.h" @@ -67,6 +69,12 @@ class CARMAStreetsPlugin: public PluginClientClockAware { * @param routeableMsg */ void HandleMapMessage(MapDataMessage &msg, routeable_message &routeableMsg); + /** + * @brief Subscribes to incoming ASN.1, C-Struct formatted SDSMs generated from broadcasting RSUs. These SDSM C-Structs are then converted to JSON to be forwarded to CARMA Streets/Kafka by the handler. + * @param msg The J3224 SDSM received from the internal + * @param routeableMsg + */ + void HandleSDSMMessage(SdsmMessage &msg, routeable_message &routeableMsg); /** * @brief Subscribe to SRM message received from RSU and publish the message to a Kafka topic */ @@ -83,6 +91,10 @@ class CARMAStreetsPlugin: public PluginClientClockAware { * @brief Subcribe to SSM Kafka topic created by carma-streets */ void SubscribeSSMKafkaTopic(); + /** + * @brief Subcribe to SDSM Kafka topic created by carma-streets + */ + void SubscribeSDSMKafkaTopic(); bool getEncodedtsm3(tsm3EncodedMessage *tsm3EncodedMsg, Json::Value metadata, Json::Value payload_json); /** @@ -105,6 +117,7 @@ class CARMAStreetsPlugin: public PluginClientClockAware { std::string _subscribeToSchedulingPlanConsumerGroupId; std::string _subscribeToSpatTopic; std::string _subscribeToSsmTopic; + std::string _subscribeToSdsmTopic; std::string _subscribeToSpatConsumerGroupId; std::string _subscribeToSSMConsumerGroupId; std::string _transmitMobilityPathTopic; @@ -112,12 +125,14 @@ class CARMAStreetsPlugin: public PluginClientClockAware { std::string _transmitMAPTopic; std::string _transmitSRMTopic; std::string _transmitSimSensorDetectedObjTopic; + std::string _transmitSDSMTopic; std::string _kafkaBrokerIp; std::string _kafkaBrokerPort; std::shared_ptr _kafka_producer_ptr; std::shared_ptr _spat_kafka_consumer_ptr; std::shared_ptr _scheduing_plan_kafka_consumer_ptr; std::shared_ptr _ssm_kafka_consumer_ptr; + std::shared_ptr _sdsm_kafka_consumer_ptr; std::vector _strategies; tmx::messages::tsm3Message *_tsm3Message{NULL}; std::mutex data_lock; @@ -192,6 +207,16 @@ class CARMAStreetsPlugin: public PluginClientClockAware { */ uint _ssmMessageSkipped = 0; + /** + * @brief Status label for SDSM messages skipped due to errors. + */ + const char* Key_SDSMMessageSkipped = "SDSM messages skipped due to errors."; + + /** + * @brief Count for SDSM messages skipped due to errors. + */ + uint _sdsmMessageSkipped = 0; + /** * @brief Intersection Id for intersection */ diff --git a/src/v2i-hub/CARMAStreetsPlugin/src/J3224ToSDSMJsonConverter.cpp b/src/v2i-hub/CARMAStreetsPlugin/src/J3224ToSDSMJsonConverter.cpp index 33fd68e2c..6e03bce7e 100644 --- a/src/v2i-hub/CARMAStreetsPlugin/src/J3224ToSDSMJsonConverter.cpp +++ b/src/v2i-hub/CARMAStreetsPlugin/src/J3224ToSDSMJsonConverter.cpp @@ -35,7 +35,7 @@ namespace CARMAStreetsPlugin Json::Value refPos; refPos["lat"] = sdsmMsgPtr->refPos.lat; - refPos["Long"] = sdsmMsgPtr->refPos.Long; + refPos["long"] = sdsmMsgPtr->refPos.Long; refPos["elevation"] = *sdsmMsgPtr->refPos.elevation; SDSMDataJson["ref_pos"] = refPos; @@ -61,8 +61,8 @@ namespace CARMAStreetsPlugin auto det_object = det_obj_list.list.array[i]; // Detected object common data - detectedObjectJson["detected_object_data"]["detected_object_common_data"]["object_type"] = det_object->detObjCommon.objType; - detectedObjectJson["detected_object_data"]["detected_object_common_data"]["object_type_conf"] = det_object->detObjCommon.objTypeCfd; + detectedObjectJson["detected_object_data"]["detected_object_common_data"]["obj_type"] = det_object->detObjCommon.objType; + detectedObjectJson["detected_object_data"]["detected_object_common_data"]["obj_type_cfd"] = det_object->detObjCommon.objTypeCfd; detectedObjectJson["detected_object_data"]["detected_object_common_data"]["object_id"] = det_object->detObjCommon.objectID; detectedObjectJson["detected_object_data"]["detected_object_common_data"]["measurement_time"] = det_object->detObjCommon.measurementTime; detectedObjectJson["detected_object_data"]["detected_object_common_data"]["time_confidence"] = det_object->detObjCommon.timeConfidence; @@ -156,13 +156,13 @@ namespace CARMAStreetsPlugin break; case DetectedObjectOptionalData_PR_detObst: - optionalDataJson["detected_obst_data"]["obst_size"]["width"] = det_object->detObjOptData->choice.detObst.obstSize.width; - optionalDataJson["detected_obst_data"]["obst_size"]["length"] = det_object->detObjOptData->choice.detObst.obstSize.length; - optionalDataJson["detected_obst_data"]["obst_size"]["height"] = *det_object->detObjOptData->choice.detObst.obstSize.height; // optional + optionalDataJson["detected_obstacle_data"]["obst_size"]["width"] = det_object->detObjOptData->choice.detObst.obstSize.width; + optionalDataJson["detected_obstacle_data"]["obst_size"]["length"] = det_object->detObjOptData->choice.detObst.obstSize.length; + optionalDataJson["detected_obstacle_data"]["obst_size"]["height"] = *det_object->detObjOptData->choice.detObst.obstSize.height; // optional - optionalDataJson["detected_obst_data"]["obst_size"]["width_confidence"] = det_object->detObjOptData->choice.detObst.obstSizeConfidence.widthConfidence; - optionalDataJson["detected_obst_data"]["obst_size"]["length_confidence"] = det_object->detObjOptData->choice.detObst.obstSizeConfidence.lengthConfidence; - optionalDataJson["detected_obst_data"]["obst_size"]["height_confidence"] = *det_object->detObjOptData->choice.detObst.obstSizeConfidence.heightConfidence; + optionalDataJson["detected_obstacle_data"]["obst_size_confidence"]["width_confidence"] = det_object->detObjOptData->choice.detObst.obstSizeConfidence.widthConfidence; + optionalDataJson["detected_obstacle_data"]["obst_size_confidence"]["length_confidence"] = det_object->detObjOptData->choice.detObst.obstSizeConfidence.lengthConfidence; + optionalDataJson["detected_obstacle_data"]["obst_size_confidence"]["height_confidence"] = *det_object->detObjOptData->choice.detObst.obstSizeConfidence.heightConfidence; break; } detectedObjectJson["detected_object_data"]["detected_object_optional_data"] = optionalDataJson; diff --git a/src/v2i-hub/CARMAStreetsPlugin/src/JsonToJ3224SDSMConverter.cpp b/src/v2i-hub/CARMAStreetsPlugin/src/JsonToJ3224SDSMConverter.cpp new file mode 100644 index 000000000..55e4cd8bf --- /dev/null +++ b/src/v2i-hub/CARMAStreetsPlugin/src/JsonToJ3224SDSMConverter.cpp @@ -0,0 +1,368 @@ +#include "JsonToJ3224SDSMConverter.h" + +namespace CARMAStreetsPlugin +{ + // TODO: Move these template functions to a more central location such as in a utility file + // Template to use when created shared pointer objects for optional data + template + T *create_store_shared(std::vector> &shared_pointers) + { + auto obj_shared = std::make_shared(); + shared_pointers.push_back(obj_shared); + return obj_shared.get(); + } + + // Template for shared pointers with array elements + template + T *create_store_shared_array(std::vector> &shared_pointers, int size) + { + std::shared_ptr array_shared(new T[size]{0}); + shared_pointers.push_back(array_shared); + return array_shared.get(); + } + + // TODO: Consolidate parseJsonString into a more central location since it is being used verbatim in other converters + bool JsonToJ3224SDSMConverter::parseJsonString(const std::string &consumedMsg, Json::Value &sdsmJsonOut) const + { + const auto jsonLen = static_cast(consumedMsg.length()); + Json::CharReaderBuilder builder; + JSONCPP_STRING err; + const std::unique_ptr reader(builder.newCharReader()); + bool parseResult = reader->parse(consumedMsg.c_str(), consumedMsg.c_str() + jsonLen, &sdsmJsonOut, &err); + if (!parseResult) + { + PLOG(logERROR) << "Parse error: " << err << std::endl; + } + return parseResult; + } + + + void JsonToJ3224SDSMConverter::convertJsonToSDSM(const Json::Value &sdsm_json, std::shared_ptr sdsm) const + { + std::vector> shared_ptrs; + + sdsm->msgCnt = sdsm_json["msg_cnt"].asInt64(); + + // TODO: confirm input sourceID from JSON to C struct constructs octet appropriately + // sourceID + TemporaryID_t tempID; + + std::string id_data = sdsm_json["source_id"].asString(); + std::vector id_vector(id_data.begin(), id_data.end()); + uint8_t *id_ptr = &id_vector[0]; + tempID.buf = id_ptr; + tempID.size = sizeof(id_ptr); + sdsm->sourceID = tempID; + + sdsm->equipmentType = sdsm_json["equipment_type"].asInt64(); + + + // sDSMTimeStamp + auto sdsm_time_stamp_ptr = CARMAStreetsPlugin::create_store_shared(shared_ptrs); + + auto year_ptr = CARMAStreetsPlugin::create_store_shared(shared_ptrs); + *year_ptr = sdsm_json["sdsm_time_stamp"]["year"].asInt64(); + sdsm_time_stamp_ptr->year = year_ptr; + + auto month_ptr = CARMAStreetsPlugin::create_store_shared(shared_ptrs); + *month_ptr = sdsm_json["sdsm_time_stamp"]["month"].asInt64(); + sdsm_time_stamp_ptr->month = month_ptr; + + auto day_ptr = CARMAStreetsPlugin::create_store_shared(shared_ptrs); + *day_ptr = sdsm_json["sdsm_time_stamp"]["day"].asInt64(); + sdsm_time_stamp_ptr->day = day_ptr; + + auto hour_ptr = CARMAStreetsPlugin::create_store_shared(shared_ptrs); + *hour_ptr = sdsm_json["sdsm_time_stamp"]["hour"].asInt64(); + sdsm_time_stamp_ptr->hour = hour_ptr; + + auto minute_ptr = CARMAStreetsPlugin::create_store_shared(shared_ptrs); + *minute_ptr = sdsm_json["sdsm_time_stamp"]["minute"].asInt64(); + sdsm_time_stamp_ptr->minute = minute_ptr; + + auto second_ptr = CARMAStreetsPlugin::create_store_shared(shared_ptrs); + *second_ptr = sdsm_json["sdsm_time_stamp"]["second"].asInt64(); + sdsm_time_stamp_ptr->second = second_ptr; + + auto offset_ptr = CARMAStreetsPlugin::create_store_shared(shared_ptrs); + *offset_ptr = sdsm_json["sdsm_time_stamp"]["offset"].asInt64(); + sdsm_time_stamp_ptr->offset = offset_ptr; + + sdsm->sDSMTimeStamp = *sdsm_time_stamp_ptr; + + // refPos + auto ref_pos_ptr = CARMAStreetsPlugin::create_store_shared(shared_ptrs); + ref_pos_ptr->lat = sdsm_json["ref_pos"]["lat"].asInt64(); + ref_pos_ptr->Long = sdsm_json["ref_pos"]["long"].asInt64(); + auto elevation_ptr = CARMAStreetsPlugin::create_store_shared(shared_ptrs); + *elevation_ptr = sdsm_json["ref_pos"]["elevation"].asInt64(); + ref_pos_ptr->elevation = elevation_ptr; + + sdsm->refPos = *ref_pos_ptr; + + // refPosXYConf + PositionalAccuracy_t ref_pos_xy_conf; + ref_pos_xy_conf.semiMajor = sdsm_json["ref_pos_xy_conf"]["semi_major"].asInt64(); + ref_pos_xy_conf.semiMinor = sdsm_json["ref_pos_xy_conf"]["semi_minor"].asInt64(); + ref_pos_xy_conf.orientation = sdsm_json["ref_pos_xy_conf"]["orientation"].asInt64(); + + sdsm->refPosXYConf = ref_pos_xy_conf; + + // refPosElConf + auto ref_pos_el_conf_ptr = CARMAStreetsPlugin::create_store_shared(shared_ptrs); + *ref_pos_el_conf_ptr = sdsm_json["ref_pos_el_conf"].asInt64(); + sdsm->refPosElConf = ref_pos_el_conf_ptr; + + // Creat initial pointers for detected object list and contained objects + auto object_list = CARMAStreetsPlugin::create_store_shared(shared_ptrs); + auto object_data = CARMAStreetsPlugin::create_store_shared(shared_ptrs); + + + if(sdsm_json.isMember("objects") && sdsm_json["objects"].isArray()){ + Json::Value objectsJsonArr = sdsm_json["objects"]; + for(auto itr = objectsJsonArr.begin(); itr != objectsJsonArr.end(); itr++){ + + auto common_data = CARMAStreetsPlugin::create_store_shared(shared_ptrs); + + // Propegate common data + common_data->objType = (*itr)["detected_object_data"]["detected_object_common_data"]["obj_type"].asInt64();; + common_data->objTypeCfd = (*itr)["detected_object_data"]["detected_object_common_data"]["obj_type_cfd"].asInt64(); + common_data->objectID = (*itr)["detected_object_data"]["detected_object_common_data"]["object_id"].asInt64(); + common_data->measurementTime = (*itr)["detected_object_data"]["detected_object_common_data"]["measurement_time"].asInt64(); + common_data->timeConfidence = (*itr)["detected_object_data"]["detected_object_common_data"]["time_confidence"].asInt64(); + + // pos (posOffsetXYZ) + common_data->pos.offsetX = (*itr)["detected_object_data"]["detected_object_common_data"]["pos"]["offset_x"].asInt64(); + common_data->pos.offsetY = (*itr)["detected_object_data"]["detected_object_common_data"]["pos"]["offset_y"].asInt64(); + auto offset_z_ptr = CARMAStreetsPlugin::create_store_shared(shared_ptrs); + *offset_z_ptr = (*itr)["detected_object_data"]["detected_object_common_data"]["pos"]["offset_z"].asInt64(); + common_data->pos.offsetZ = offset_z_ptr; + + // posConfidence + common_data->posConfidence.pos = (*itr)["detected_object_data"]["detected_object_common_data"]["pos_confidence"]["pos"].asInt64(); + common_data->posConfidence.elevation = (*itr)["detected_object_data"]["detected_object_common_data"]["pos_confidence"]["elevation"].asInt64(); + + // speed/speedConfidence + common_data->speed = (*itr)["detected_object_data"]["detected_object_common_data"]["speed"].asInt64(); + common_data->speedConfidence = (*itr)["detected_object_data"]["detected_object_common_data"]["speed_confidence"].asInt64(); + + // speedZ/speedConfidenceZ + auto speed_z_ptr = CARMAStreetsPlugin::create_store_shared(shared_ptrs); + *speed_z_ptr = (*itr)["detected_object_data"]["detected_object_common_data"]["speed_z"].asInt64(); + common_data->speedZ = speed_z_ptr; + auto speed_conf_z_ptr = CARMAStreetsPlugin::create_store_shared(shared_ptrs); + *speed_conf_z_ptr = (*itr)["detected_object_data"]["detected_object_common_data"]["speed_confidence_z"].asInt64(); + common_data->speedConfidenceZ = speed_conf_z_ptr; + + // heading/headingConf + common_data->heading = (*itr)["detected_object_data"]["detected_object_common_data"]["heading"].asInt64(); + common_data->headingConf = (*itr)["detected_object_data"]["detected_object_common_data"]["heading_conf"].asInt64(); + + // accel4way + auto accel_4_way_ptr = CARMAStreetsPlugin::create_store_shared(shared_ptrs); + accel_4_way_ptr->Long = (*itr)["detected_object_data"]["detected_object_common_data"]["accel_4_way"]["long"].asInt64(); + accel_4_way_ptr->lat = (*itr)["detected_object_data"]["detected_object_common_data"]["accel_4_way"]["lat"].asInt64(); + accel_4_way_ptr->vert = (*itr)["detected_object_data"]["detected_object_common_data"]["accel_4_way"]["vert"].asInt64(); + accel_4_way_ptr->yaw = (*itr)["detected_object_data"]["detected_object_common_data"]["accel_4_way"]["yaw"].asInt64(); + common_data->accel4way = accel_4_way_ptr; + + // accCfd(X/Y/Z/Yaw) + auto acc_cfd_x_ptr = CARMAStreetsPlugin::create_store_shared(shared_ptrs); + *acc_cfd_x_ptr = (*itr)["detected_object_data"]["detected_object_common_data"]["acc_cfd_x"].asInt64(); + common_data->accCfdX = acc_cfd_x_ptr; + + auto acc_cfd_y_ptr = CARMAStreetsPlugin::create_store_shared(shared_ptrs); + *acc_cfd_y_ptr = (*itr)["detected_object_data"]["detected_object_common_data"]["acc_cfd_y"].asInt64(); + common_data->accCfdY = acc_cfd_y_ptr; + + auto acc_cfd_z_ptr = CARMAStreetsPlugin::create_store_shared(shared_ptrs); + *acc_cfd_z_ptr = (*itr)["detected_object_data"]["detected_object_common_data"]["acc_cfd_z"].asInt64(); + common_data->accCfdZ = acc_cfd_z_ptr; + + auto acc_cfd_yaw_ptr = CARMAStreetsPlugin::create_store_shared(shared_ptrs); + *acc_cfd_yaw_ptr = (*itr)["detected_object_data"]["detected_object_common_data"]["acc_cfd_yaw"].asInt64(); + common_data->accCfdYaw = acc_cfd_yaw_ptr; + + + // Add common data to object data + object_data->detObjCommon = *common_data; + + + + // Propegate optional data fields + + // detectedObjectOptionalData + auto optional_data_ptr = CARMAStreetsPlugin::create_store_shared(shared_ptrs); + + // determine which optional data choice is being used if any + // detVeh + if((*itr)["detected_object_data"]["detected_object_optional_data"].isMember("detected_vehicle_data")){ + + // set presence val to veh + optional_data_ptr->present = DetectedObjectOptionalData_PR_detVeh; + + // TODO: find a better way to convert/test lights val without calloc + // // lights + // auto lights_ptr = CARMAStreetsPlugin::create_store_shared(shared_ptrs); + // auto lights = static_cast((*itr)["detected_object_data"]["detected_object_optional_data"]["detected_vehicle_data"]["lights"].asInt()); + // lights_ptr->buf = (uint8_t *)calloc(2, sizeof(uint8_t)); // TODO: find calloc alternative if possible, causes a memory leak + // lights_ptr->size = 2 * sizeof(uint8_t); + // lights_ptr->bits_unused = 0; + // lights_ptr->buf[1] = static_cast(lights); + // lights_ptr->buf[0] = (lights >> 8); + + // optional_data_ptr->choice.detVeh.lights = lights_ptr; + + + // vehAttitude + auto attitude_ptr = CARMAStreetsPlugin::create_store_shared(shared_ptrs); + attitude_ptr->pitch = (*itr)["detected_object_data"]["detected_object_optional_data"]["detected_vehicle_data"]["veh_attitude"]["pitch"].asInt64(); + attitude_ptr->roll = (*itr)["detected_object_data"]["detected_object_optional_data"]["detected_vehicle_data"]["veh_attitude"]["roll"].asInt64(); + attitude_ptr->yaw = (*itr)["detected_object_data"]["detected_object_optional_data"]["detected_vehicle_data"]["veh_attitude"]["yaw"].asInt64(); + optional_data_ptr->choice.detVeh.vehAttitude = attitude_ptr; + + // vehAttitudeConfidence + auto attitude_conf_ptr = CARMAStreetsPlugin::create_store_shared(shared_ptrs); + attitude_conf_ptr->pitchConfidence = (*itr)["detected_object_data"]["detected_object_optional_data"]["detected_vehicle_data"]["veh_attitude_confidence"]["pitch_confidence"].asInt64(); + attitude_conf_ptr->rollConfidence = (*itr)["detected_object_data"]["detected_object_optional_data"]["detected_vehicle_data"]["veh_attitude_confidence"]["roll_confidence"].asInt64(); + attitude_conf_ptr->yawConfidence = (*itr)["detected_object_data"]["detected_object_optional_data"]["detected_vehicle_data"]["veh_attitude_confidence"]["yaw_confidence"].asInt64(); + optional_data_ptr->choice.detVeh.vehAttitudeConfidence = attitude_conf_ptr; + + // vehAngVel + auto ang_vel_ptr = CARMAStreetsPlugin::create_store_shared(shared_ptrs); + ang_vel_ptr->pitchRate = (*itr)["detected_object_data"]["detected_object_optional_data"]["detected_vehicle_data"]["veh_ang_vel"]["pitch_rate"].asInt64(); + ang_vel_ptr->rollRate = (*itr)["detected_object_data"]["detected_object_optional_data"]["detected_vehicle_data"]["veh_ang_vel"]["roll_rate"].asInt64(); + optional_data_ptr->choice.detVeh.vehAngVel = ang_vel_ptr; + + // vehAngVelConfidence + auto ang_vel_conf_ptr = CARMAStreetsPlugin::create_store_shared(shared_ptrs); + auto pitch_conf_ptr = CARMAStreetsPlugin::create_store_shared(shared_ptrs); + *pitch_conf_ptr = (*itr)["detected_object_data"]["detected_object_optional_data"]["detected_vehicle_data"]["veh_ang_vel_confidence"]["pitch_rate_confidence"].asInt64(); + ang_vel_conf_ptr->pitchRateConfidence = pitch_conf_ptr; + auto roll_conf_ptr = CARMAStreetsPlugin::create_store_shared(shared_ptrs); + *roll_conf_ptr = (*itr)["detected_object_data"]["detected_object_optional_data"]["detected_vehicle_data"]["veh_ang_vel_confidence"]["roll_rate_confidence"].asInt64(); + ang_vel_conf_ptr->rollRateConfidence = roll_conf_ptr; + optional_data_ptr->choice.detVeh.vehAngVelConfidence = ang_vel_conf_ptr; + + // size (VehicleSize) + auto size_ptr = CARMAStreetsPlugin::create_store_shared(shared_ptrs); + size_ptr->width = (*itr)["detected_object_data"]["detected_object_optional_data"]["detected_vehicle_data"]["size"]["width"].asInt64(); + size_ptr->length = (*itr)["detected_object_data"]["detected_object_optional_data"]["detected_vehicle_data"]["size"]["length"].asInt64(); + optional_data_ptr->choice.detVeh.size = size_ptr; + + // height + auto height_ptr = CARMAStreetsPlugin::create_store_shared(shared_ptrs); + *height_ptr = (*itr)["detected_object_data"]["detected_object_optional_data"]["detected_vehicle_data"]["height"].asInt64(); + optional_data_ptr->choice.detVeh.height = height_ptr; + + // vehcleSizeConfidence + auto size_conf_ptr = CARMAStreetsPlugin::create_store_shared(shared_ptrs); + size_conf_ptr->vehicleWidthConfidence = (*itr)["detected_object_data"]["detected_object_optional_data"]["detected_vehicle_data"]["vehicle_size_confidence"]["vehicle_width_confidence"].asInt64(); + size_conf_ptr->vehicleLengthConfidence = (*itr)["detected_object_data"]["detected_object_optional_data"]["detected_vehicle_data"]["vehicle_size_confidence"]["vehicle_length_confidence"].asInt64(); + auto height_conf_ptr = CARMAStreetsPlugin::create_store_shared(shared_ptrs); + *height_conf_ptr = (*itr)["detected_object_data"]["detected_object_optional_data"]["detected_vehicle_data"]["vehicle_size_confidence"]["vehicle_height_confidence"].asInt64(); + size_conf_ptr->vehicleHeightConfidence = height_conf_ptr; + optional_data_ptr->choice.detVeh.vehicleSizeConfidence = size_conf_ptr; + + // vehicleClass + auto veh_class_ptr = CARMAStreetsPlugin::create_store_shared(shared_ptrs); + *veh_class_ptr = (*itr)["detected_object_data"]["detected_object_optional_data"]["detected_vehicle_data"]["vehicle_class"].asInt64(); + optional_data_ptr->choice.detVeh.vehicleClass = veh_class_ptr; + + // vehClassConf + auto veh_class_conf_ptr = CARMAStreetsPlugin::create_store_shared(shared_ptrs); + *veh_class_conf_ptr = (*itr)["detected_object_data"]["detected_object_optional_data"]["detected_vehicle_data"]["vehicle_class_conf"].asInt64(); + optional_data_ptr->choice.detVeh.classConf = veh_class_conf_ptr; + } + + // detVRU + else if((*itr)["detected_object_data"]["detected_object_optional_data"].isMember("detected_vru_data")){ + + optional_data_ptr->present = DetectedObjectOptionalData_PR_detVRU; + + // basicType + auto basic_type_ptr = CARMAStreetsPlugin::create_store_shared(shared_ptrs); + *basic_type_ptr = (*itr)["detected_object_data"]["detected_object_optional_data"]["detected_vru_data"]["basic_type"].asInt64(); + optional_data_ptr->choice.detVRU.basicType = basic_type_ptr; + + // propulsion choice struct (check to see if propulsion exists) + auto propulsion_ptr = CARMAStreetsPlugin::create_store_shared(shared_ptrs); + + if((*itr)["detected_object_data"]["detected_object_optional_data"]["detected_vru_data"]["propulsion"].isMember("human")){ + propulsion_ptr->present = PropelledInformation_PR_human; + propulsion_ptr->choice.human = (*itr)["detected_object_data"]["detected_object_optional_data"]["detected_vru_data"]["propulsion"]["human"].asInt64(); + } + else if((*itr)["detected_object_data"]["detected_object_optional_data"]["detected_vru_data"]["propulsion"].isMember("animal")){ + propulsion_ptr->present = PropelledInformation_PR_animal; + propulsion_ptr->choice.animal = (*itr)["detected_object_data"]["detected_object_optional_data"]["detected_vru_data"]["propulsion"]["animal"].asInt64(); + } + else if((*itr)["detected_object_data"]["detected_object_optional_data"]["detected_vru_data"]["propulsion"].isMember("motor")){ + propulsion_ptr->present = PropelledInformation_PR_motor; + propulsion_ptr->choice.motor = (*itr)["detected_object_data"]["detected_object_optional_data"]["detected_vru_data"]["propulsion"]["motor"].asInt64(); + } + else{ + propulsion_ptr->present = PropelledInformation_PR_NOTHING; + std::cout << "Value was nothing" << std::endl; + } + optional_data_ptr->choice.detVRU.propulsion = propulsion_ptr; + + // attachement + auto attachment_ptr = CARMAStreetsPlugin::create_store_shared(shared_ptrs); + *attachment_ptr = (*itr)["detected_object_data"]["detected_object_optional_data"]["detected_vru_data"]["attachment"].asInt64(); + optional_data_ptr->choice.detVRU.attachment = attachment_ptr; + + // radius + auto radius_ptr = CARMAStreetsPlugin::create_store_shared(shared_ptrs); + *radius_ptr = (*itr)["detected_object_data"]["detected_object_optional_data"]["detected_vru_data"]["radius"].asInt64(); + optional_data_ptr->choice.detVRU.radius = radius_ptr; + } + + + // detObst + else if((*itr)["detected_object_data"]["detected_object_optional_data"].isMember("detected_obstacle_data")){ + optional_data_ptr->present = DetectedObjectOptionalData_PR_detObst; + + ObstacleSize obst_size; + obst_size.width = (*itr)["detected_object_data"]["detected_object_optional_data"]["detected_obstacle_data"]["obst_size"]["width"].asInt64(); + obst_size.length = (*itr)["detected_object_data"]["detected_object_optional_data"]["detected_obstacle_data"]["obst_size"]["length"].asInt64(); + auto obst_height_ptr = CARMAStreetsPlugin::create_store_shared(shared_ptrs); + *obst_height_ptr = (*itr)["detected_object_data"]["detected_object_optional_data"]["detected_obstacle_data"]["obst_size"]["height"].asInt64(); + obst_size.height = obst_height_ptr; + optional_data_ptr->choice.detObst.obstSize = obst_size; + + ObstacleSizeConfidence obst_size_conf; + obst_size_conf.widthConfidence = (*itr)["detected_object_data"]["detected_object_optional_data"]["detected_obstacle_data"]["obst_size_confidence"]["width_confidence"].asInt64(); + obst_size_conf.lengthConfidence = (*itr)["detected_object_data"]["detected_object_optional_data"]["detected_obstacle_data"]["obst_size_confidence"]["length_confidence"].asInt64(); + auto obst_height_conf_ptr = CARMAStreetsPlugin::create_store_shared(shared_ptrs); + *obst_height_conf_ptr = (*itr)["detected_object_data"]["detected_object_optional_data"]["detected_obstacle_data"]["obst_size_confidence"]["height_confidence"].asInt64(); + obst_size_conf.heightConfidence = obst_height_conf_ptr; + optional_data_ptr->choice.detObst.obstSizeConfidence = obst_size_conf; + } + + + // Add optional data to object data + object_data->detObjOptData = optional_data_ptr; + + + } + } + + // Append the object to the detected objects list + asn_sequence_add(&object_list->list.array, object_data); + + // Set the data to the ASN.1 C struct + sdsm->objects = *object_list; + + + } + + + void JsonToJ3224SDSMConverter::encodeSDSM(const std::shared_ptr &sdsmPtr, tmx::messages::SdsmEncodedMessage &encodedSDSM) const + { + tmx::messages::MessageFrameMessage frame(sdsmPtr); + encodedSDSM.set_data(tmx::messages::TmxJ2735EncodedMessage::encode_j2735_message>(frame)); + free(frame.get_j2735_data().get()); + } + + +} \ No newline at end of file diff --git a/src/v2i-hub/CARMAStreetsPlugin/src/JsonToJ3224SDSMConverter.h b/src/v2i-hub/CARMAStreetsPlugin/src/JsonToJ3224SDSMConverter.h new file mode 100644 index 000000000..ea13123f2 --- /dev/null +++ b/src/v2i-hub/CARMAStreetsPlugin/src/JsonToJ3224SDSMConverter.h @@ -0,0 +1,46 @@ +#include "jsoncpp/json/json.h" +#include +#include +#include +#include +#include +#include +#include + +using namespace tmx::utils; + +namespace CARMAStreetsPlugin +{ + class JsonToJ3224SDSMConverter + { + + public: + + JsonToJ3224SDSMConverter() = default; + + /*** + * @brief Parse Json string into Json object + * @param jsonstring that is consumed from a kafka topic + * @param JsonObject that is populated by the input json string + * @return boolean. True if parse successfully, false if parse with errors + */ + bool parseJsonString(const std::string &consumed_msg, Json::Value &sdsm_json_output) const; + + /** + * @brief Convert the Json value with sdsm information info tmx SDSM object. + * @param json Incoming Json value with sdsm information that is consumed from a Kafka topic. + * @param sdsm Outgoing J3224 sdsm object that is populated by the json value. + */ + void convertJsonToSDSM(const Json::Value &sdsm_json, std::shared_ptr sdsm) const; + + /*** + * @brief Encode J3224 SDSM + * @param Pointer to J3224 SDSM object + * @param Encoded J3224 SDSM + */ + void encodeSDSM(const std::shared_ptr &sdsmPtr, tmx::messages::SdsmEncodedMessage &encodedSDSM) const; + + ~JsonToJ3224SDSMConverter() = default; + }; + +} \ No newline at end of file diff --git a/src/v2i-hub/CARMAStreetsPlugin/test/test_J3224ToSDSMJsonConverter.cpp b/src/v2i-hub/CARMAStreetsPlugin/test/test_J3224ToSDSMJsonConverter.cpp index 085e3ba24..132cd2e20 100644 --- a/src/v2i-hub/CARMAStreetsPlugin/test/test_J3224ToSDSMJsonConverter.cpp +++ b/src/v2i-hub/CARMAStreetsPlugin/test/test_J3224ToSDSMJsonConverter.cpp @@ -357,7 +357,7 @@ namespace unit_test ASSERT_EQ(10000, sdsmJson["sdsm_time_stamp"]["second"].asInt()); ASSERT_EQ(2000, sdsmJson["sdsm_time_stamp"]["offset"].asInt()); ASSERT_EQ(400000000, sdsmJson["ref_pos"]["lat"].asInt()); - ASSERT_EQ(800000000, sdsmJson["ref_pos"]["Long"].asInt()); + ASSERT_EQ(800000000, sdsmJson["ref_pos"]["long"].asInt()); ASSERT_EQ(30, sdsmJson["ref_pos"]["elevation"].asInt()); ASSERT_EQ(250, sdsmJson["ref_pos_xy_conf"]["semi_major"].asInt()); ASSERT_EQ(200, sdsmJson["ref_pos_xy_conf"]["semi_minor"].asInt()); @@ -365,8 +365,8 @@ namespace unit_test ASSERT_EQ(13, sdsmJson["ref_pos_el_conf"].asInt()); // Object 1 detected object common data - ASSERT_EQ(1, sdsmJson["objects"][0]["detected_object_data"]["detected_object_common_data"]["object_type"].asInt()); - ASSERT_EQ(65, sdsmJson["objects"][0]["detected_object_data"]["detected_object_common_data"]["object_type_conf"].asInt()); + ASSERT_EQ(1, sdsmJson["objects"][0]["detected_object_data"]["detected_object_common_data"]["obj_type"].asInt()); + ASSERT_EQ(65, sdsmJson["objects"][0]["detected_object_data"]["detected_object_common_data"]["obj_type_cfd"].asInt()); ASSERT_EQ(1200, sdsmJson["objects"][0]["detected_object_data"]["detected_object_common_data"]["object_id"].asInt()); ASSERT_EQ(500, sdsmJson["objects"][0]["detected_object_data"]["detected_object_common_data"]["measurement_time"].asInt()); ASSERT_EQ(8, sdsmJson["objects"][0]["detected_object_data"]["detected_object_common_data"]["time_confidence"].asInt()); @@ -422,12 +422,12 @@ namespace unit_test // Only test the ID and optional obstacle data for the 3rd object ASSERT_EQ(1000, sdsmJson["objects"][2]["detected_object_data"]["detected_object_common_data"]["object_id"].asInt()); - ASSERT_EQ(400, sdsmJson["objects"][2]["detected_object_data"]["detected_object_optional_data"]["detected_obst_data"]["obst_size"]["width"].asInt()); - ASSERT_EQ(300, sdsmJson["objects"][2]["detected_object_data"]["detected_object_optional_data"]["detected_obst_data"]["obst_size"]["length"].asInt()); - ASSERT_EQ(100, sdsmJson["objects"][2]["detected_object_data"]["detected_object_optional_data"]["detected_obst_data"]["obst_size"]["height"].asInt()); - ASSERT_EQ(4, sdsmJson["objects"][2]["detected_object_data"]["detected_object_optional_data"]["detected_obst_data"]["obst_size"]["width_confidence"].asInt()); - ASSERT_EQ(5, sdsmJson["objects"][2]["detected_object_data"]["detected_object_optional_data"]["detected_obst_data"]["obst_size"]["length_confidence"].asInt()); - ASSERT_EQ(6, sdsmJson["objects"][2]["detected_object_data"]["detected_object_optional_data"]["detected_obst_data"]["obst_size"]["height_confidence"].asInt()); + ASSERT_EQ(400, sdsmJson["objects"][2]["detected_object_data"]["detected_object_optional_data"]["detected_obstacle_data"]["obst_size"]["width"].asInt()); + ASSERT_EQ(300, sdsmJson["objects"][2]["detected_object_data"]["detected_object_optional_data"]["detected_obstacle_data"]["obst_size"]["length"].asInt()); + ASSERT_EQ(100, sdsmJson["objects"][2]["detected_object_data"]["detected_object_optional_data"]["detected_obstacle_data"]["obst_size"]["height"].asInt()); + ASSERT_EQ(4, sdsmJson["objects"][2]["detected_object_data"]["detected_object_optional_data"]["detected_obstacle_data"]["obst_size_confidence"]["width_confidence"].asInt()); + ASSERT_EQ(5, sdsmJson["objects"][2]["detected_object_data"]["detected_object_optional_data"]["detected_obstacle_data"]["obst_size_confidence"]["length_confidence"].asInt()); + ASSERT_EQ(6, sdsmJson["objects"][2]["detected_object_data"]["detected_object_optional_data"]["detected_obstacle_data"]["obst_size_confidence"]["height_confidence"].asInt()); } diff --git a/src/v2i-hub/CARMAStreetsPlugin/test/test_JsonToJ3224SDSMConverter.cpp b/src/v2i-hub/CARMAStreetsPlugin/test/test_JsonToJ3224SDSMConverter.cpp new file mode 100644 index 000000000..5d38d4716 --- /dev/null +++ b/src/v2i-hub/CARMAStreetsPlugin/test/test_JsonToJ3224SDSMConverter.cpp @@ -0,0 +1,188 @@ +#include +#include +#include +#include +#include +#include "jsoncpp/json/json.h" +#include "JsonToJ3224SDSMConverter.h" + +namespace CARMAStreetsPlugin +{ + class test_JsonToJ3224SDSMConverter : public ::testing::Test + { + }; + + // Test for SDSM string json parsing + TEST_F(test_JsonToJ3224SDSMConverter, parseJsonString) + { + // TODO: consider adding functionality for converting to input strings from existing json files + JsonToJ3224SDSMConverter converter; + std::string valid_sdsm_json = "{\"equipment_type\":1,\"msg_cnt\":1,\"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\":\"01020304\",\"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}}}]}"; + Json::Value root; + bool result = converter.parseJsonString(valid_sdsm_json, root); + ASSERT_TRUE(result); + std::string invalid_json = "invalid"; + result = converter.parseJsonString(invalid_json, root); + ASSERT_FALSE(result); + + } + + // Test for SDSM header data + TEST_F(test_JsonToJ3224SDSMConverter, convertJsonToSDSM_header) + { + JsonToJ3224SDSMConverter converter; + std::string valid_json_str = "{\"equipment_type\":1,\"msg_cnt\":1,\"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\":\"00001234\",\"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}}}]}"; + Json::Value root; + bool result = converter.parseJsonString(valid_json_str, root); + ASSERT_TRUE(result); + auto sdsmPtr = std::make_shared(); + converter.convertJsonToSDSM(root, sdsmPtr); + ASSERT_EQ(1, sdsmPtr->msgCnt); + + // TODO: find a way to generate test octet strings for unit test comparisons + size_t test_size = 8; + ASSERT_EQ(test_size, sdsmPtr->sourceID.size); // this only compares size since size is an int + + ASSERT_EQ(1, sdsmPtr->equipmentType); + ASSERT_EQ(2007, *sdsmPtr->sDSMTimeStamp.year); + ASSERT_EQ(7, *sdsmPtr->sDSMTimeStamp.month); + ASSERT_EQ(4, *sdsmPtr->sDSMTimeStamp.day); + ASSERT_EQ(19, *sdsmPtr->sDSMTimeStamp.hour); + ASSERT_EQ(15, *sdsmPtr->sDSMTimeStamp.minute); + ASSERT_EQ(5000, *sdsmPtr->sDSMTimeStamp.second); + ASSERT_EQ(400, *sdsmPtr->sDSMTimeStamp.offset); + + ASSERT_EQ(400000000, sdsmPtr->refPos.lat); + ASSERT_EQ(600000000, sdsmPtr->refPos.Long); + ASSERT_EQ(30, *sdsmPtr->refPos.elevation); + + ASSERT_EQ(235, sdsmPtr->refPosXYConf.semiMajor); + ASSERT_EQ(200, sdsmPtr->refPosXYConf.semiMinor); + ASSERT_EQ(25000, sdsmPtr->refPosXYConf.orientation); + + ASSERT_EQ(10, *sdsmPtr->refPosElConf); + + } + + // // Test for SDSM common data + TEST_F(test_JsonToJ3224SDSMConverter, convertJsonToSDSM_common) + { + JsonToJ3224SDSMConverter converter; + std::string valid_json_str = "{\"equipment_type\":1,\"msg_cnt\":1,\"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\":\"01020304\",\"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}}}]}"; + Json::Value root; + bool result = converter.parseJsonString(valid_json_str, root); + ASSERT_TRUE(result); + auto sdsmPtr = std::make_shared(); + converter.convertJsonToSDSM(root, sdsmPtr); + + ASSERT_EQ(1, sdsmPtr->objects.list.array[0]->detObjCommon.objType); + ASSERT_EQ(65, sdsmPtr->objects.list.array[0]->detObjCommon.objTypeCfd); + ASSERT_EQ(12200, sdsmPtr->objects.list.array[0]->detObjCommon.objectID); + ASSERT_EQ(-1100, sdsmPtr->objects.list.array[0]->detObjCommon.measurementTime); + ASSERT_EQ(2, sdsmPtr->objects.list.array[0]->detObjCommon.timeConfidence); + + ASSERT_EQ(4000, sdsmPtr->objects.list.array[0]->detObjCommon.pos.offsetX); + ASSERT_EQ(-720, sdsmPtr->objects.list.array[0]->detObjCommon.pos.offsetY); + ASSERT_EQ(20, *sdsmPtr->objects.list.array[0]->detObjCommon.pos.offsetZ); + + ASSERT_EQ(2, sdsmPtr->objects.list.array[0]->detObjCommon.posConfidence.pos); + ASSERT_EQ(5, sdsmPtr->objects.list.array[0]->detObjCommon.posConfidence.elevation); + + ASSERT_EQ(2100, sdsmPtr->objects.list.array[0]->detObjCommon.speed); + ASSERT_EQ(3, sdsmPtr->objects.list.array[0]->detObjCommon.speedConfidence); + ASSERT_EQ(1000, *sdsmPtr->objects.list.array[0]->detObjCommon.speedZ); + ASSERT_EQ(4, *sdsmPtr->objects.list.array[0]->detObjCommon.speedConfidenceZ); + + ASSERT_EQ(16000, sdsmPtr->objects.list.array[0]->detObjCommon.heading); + ASSERT_EQ(4, sdsmPtr->objects.list.array[0]->detObjCommon.headingConf); + + ASSERT_EQ(200, sdsmPtr->objects.list.array[0]->detObjCommon.accel4way->Long); + ASSERT_EQ(-500, sdsmPtr->objects.list.array[0]->detObjCommon.accel4way->lat); + ASSERT_EQ(1, sdsmPtr->objects.list.array[0]->detObjCommon.accel4way->vert); + ASSERT_EQ(400, sdsmPtr->objects.list.array[0]->detObjCommon.accel4way->yaw); + + } + + // Test for SDSM optional data - vehicle data + TEST_F(test_JsonToJ3224SDSMConverter, convertJsonToSDSM_veh) + { + JsonToJ3224SDSMConverter converter; + std::string valid_json_str = "{\"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\":\"01020304\"}"; + Json::Value root; + bool result = converter.parseJsonString(valid_json_str, root); + ASSERT_TRUE(result); + auto sdsmPtr = std::make_shared(); + converter.convertJsonToSDSM(root, sdsmPtr); + + // TODO: Find a better way to test light data following an updated implementation + // // Similar to sourceID, need a better way to compare retrieved ASN.1 values (in this case bit strings) to verify conversion + // size_t test_size = 2; + // ASSERT_EQ(test_size, sdsmPtr->objects.list.array[0]->detObjOptData->choice.detVeh.lights->size); + + ASSERT_EQ(2400, sdsmPtr->objects.list.array[0]->detObjOptData->choice.detVeh.vehAttitude->pitch); + ASSERT_EQ(-12000, sdsmPtr->objects.list.array[0]->detObjOptData->choice.detVeh.vehAttitude->roll); + ASSERT_EQ(400, sdsmPtr->objects.list.array[0]->detObjOptData->choice.detVeh.vehAttitude->yaw); + + ASSERT_EQ(2, sdsmPtr->objects.list.array[0]->detObjOptData->choice.detVeh.vehAttitudeConfidence->pitchConfidence); + ASSERT_EQ(3, sdsmPtr->objects.list.array[0]->detObjOptData->choice.detVeh.vehAttitudeConfidence->rollConfidence); + ASSERT_EQ(4, sdsmPtr->objects.list.array[0]->detObjOptData->choice.detVeh.vehAttitudeConfidence->yawConfidence); + + ASSERT_EQ(600, sdsmPtr->objects.list.array[0]->detObjOptData->choice.detVeh.vehAngVel->pitchRate); + ASSERT_EQ(-800, sdsmPtr->objects.list.array[0]->detObjOptData->choice.detVeh.vehAngVel->rollRate); + + ASSERT_EQ(3, *sdsmPtr->objects.list.array[0]->detObjOptData->choice.detVeh.vehAngVelConfidence->pitchRateConfidence); + ASSERT_EQ(4, *sdsmPtr->objects.list.array[0]->detObjOptData->choice.detVeh.vehAngVelConfidence->rollRateConfidence); + + ASSERT_EQ(300, sdsmPtr->objects.list.array[0]->detObjOptData->choice.detVeh.size->width); + ASSERT_EQ(700, sdsmPtr->objects.list.array[0]->detObjOptData->choice.detVeh.size->length); + ASSERT_EQ(70, *sdsmPtr->objects.list.array[0]->detObjOptData->choice.detVeh.height); + + ASSERT_EQ(7, sdsmPtr->objects.list.array[0]->detObjOptData->choice.detVeh.vehicleSizeConfidence->vehicleWidthConfidence); + ASSERT_EQ(6, sdsmPtr->objects.list.array[0]->detObjOptData->choice.detVeh.vehicleSizeConfidence->vehicleLengthConfidence); + ASSERT_EQ(5, *sdsmPtr->objects.list.array[0]->detObjOptData->choice.detVeh.vehicleSizeConfidence->vehicleHeightConfidence); + + ASSERT_EQ(11, *sdsmPtr->objects.list.array[0]->detObjOptData->choice.detVeh.vehicleClass); + ASSERT_EQ(75, *sdsmPtr->objects.list.array[0]->detObjOptData->choice.detVeh.classConf); + + } + + // Test for SDSM optional data - VRU data + TEST_F(test_JsonToJ3224SDSMConverter, convertJsonToSDSM_vru) + { + JsonToJ3224SDSMConverter converter; + std::string valid_json_str = "{\"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\":\"01020304\"}"; + Json::Value root; + bool result = converter.parseJsonString(valid_json_str, root); + ASSERT_TRUE(result); + auto sdsmPtr = std::make_shared(); + converter.convertJsonToSDSM(root, sdsmPtr); + + ASSERT_EQ(1, *sdsmPtr->objects.list.array[0]->detObjOptData->choice.detVRU.basicType); + ASSERT_EQ(2, sdsmPtr->objects.list.array[0]->detObjOptData->choice.detVRU.propulsion->choice.human); + ASSERT_EQ(3, *sdsmPtr->objects.list.array[0]->detObjOptData->choice.detVRU.attachment); + ASSERT_EQ(30, *sdsmPtr->objects.list.array[0]->detObjOptData->choice.detVRU.radius); + + } + + // Test for SDSM optional data - obstacle data + TEST_F(test_JsonToJ3224SDSMConverter, convertJsonToSDSM_obst) + { + JsonToJ3224SDSMConverter converter; + std::string valid_json_str = "{\"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\":\"01020304\"}"; + Json::Value root; + bool result = converter.parseJsonString(valid_json_str, root); + ASSERT_TRUE(result); + auto sdsmPtr = std::make_shared(); + converter.convertJsonToSDSM(root, sdsmPtr); + + ASSERT_EQ(400, sdsmPtr->objects.list.array[0]->detObjOptData->choice.detObst.obstSize.width); + ASSERT_EQ(300, sdsmPtr->objects.list.array[0]->detObjOptData->choice.detObst.obstSize.length); + ASSERT_EQ(100, *sdsmPtr->objects.list.array[0]->detObjOptData->choice.detObst.obstSize.height); + + ASSERT_EQ(6, sdsmPtr->objects.list.array[0]->detObjOptData->choice.detObst.obstSizeConfidence.widthConfidence); + ASSERT_EQ(7, sdsmPtr->objects.list.array[0]->detObjOptData->choice.detObst.obstSizeConfidence.lengthConfidence); + ASSERT_EQ(8, *sdsmPtr->objects.list.array[0]->detObjOptData->choice.detObst.obstSizeConfidence.heightConfidence); + + } + +} \ No newline at end of file