diff --git a/doc/architecture/architecture_overview.adoc b/doc/architecture/architecture_overview.adoc index 7a9e98828..31f28c154 100644 --- a/doc/architecture/architecture_overview.adoc +++ b/doc/architecture/architecture_overview.adoc @@ -8,7 +8,7 @@ OSI contains an object-based environment description that uses the message forma Google developed and maintains the Protocol Buffer library. OSI defines top-level messages that are used to exchange data between separate models. Top-level messages define the `GroundTruth` interface, the `SensorData` interface, and – since OSI version 3.0.0 – the interfaces `SensorView` and `SensorViewConfiguration`. -Further top-level messages that were added in later versions of OSI are `TrafficCommand`, `TrafficUpdate`, `MotionRequest`, and `StreamingUpdate`. +Further top-level messages that were added in later versions of OSI are `HostVehicleData`, `TrafficCommand`, `TrafficCommandUpdate`, `TrafficUpdate`, `MotionRequest`, and `StreamingUpdate`. The following figure shows the interfaces and models involved in modeling a sensor. diff --git a/doc/architecture/trace_file_naming.adoc b/doc/architecture/trace_file_naming.adoc index 2c4bf46da..1c73ed90c 100644 --- a/doc/architecture/trace_file_naming.adoc +++ b/doc/architecture/trace_file_naming.adoc @@ -14,23 +14,35 @@ The names of OSI trace files should have the following format: **Types** -`sd`:: -Trace file contains `SensorData` messages. - `sv`:: Trace file contains `SensorView` messages. +`svc`:: +Trace file contains `SensorViewConfiguration` messages. + `gt`:: Trace file contains `GroundTruth` messages. -`tu`:: -Trace file contains `TrafficUpdate` messages. +`hvd`:: +Trace file contains `HostVehicleData` messages. + +`sd`:: +Trace file contains `SensorData` messages. `tc`:: Trace file contains `TrafficCommand` messages. -`hvd`:: -Trace file contains `HostVehicleData` messages. +`tcu`:: +Trace file contains `TrafficCommandUpdate` messages. + +`tu`:: +Trace file contains `TrafficUpdate` messages. + +`mr`:: +Trace file contains `MotionRequest` messages. + +`su`:: +Trace file contains `StreamingUpdate` messages. **Example** diff --git a/osi3trace/osi2read.py b/osi3trace/osi2read.py index c6bee861e..a75bb9a76 100644 --- a/osi3trace/osi2read.py +++ b/osi3trace/osi2read.py @@ -28,7 +28,7 @@ def command_line_arguments(): "--type", "-t", help="Name of the type used to serialize data.", - choices=["SensorView", "GroundTruth", "SensorData"], + choices=OSITrace.message_types(), default="SensorView", type=str, required=False, diff --git a/osi3trace/osi_trace.py b/osi3trace/osi_trace.py index 2b54e6d44..3c0577023 100644 --- a/osi3trace/osi_trace.py +++ b/osi3trace/osi_trace.py @@ -6,14 +6,28 @@ import struct from osi3.osi_sensorview_pb2 import SensorView +from osi3.osi_sensorviewconfiguration_pb2 import SensorViewConfiguration from osi3.osi_groundtruth_pb2 import GroundTruth +from osi3.osi_hostvehicledata_pb2 import HostVehicleData from osi3.osi_sensordata_pb2 import SensorData +from osi3.osi_trafficcommand_pb2 import TrafficCommand +from osi3.osi_trafficcommandupdate_pb2 import TrafficCommandUpdate +from osi3.osi_trafficupdate_pb2 import TrafficUpdate +from osi3.osi_motionrequest_pb2 import MotionRequest +from osi3.osi_streamingupdate_pb2 import StreamingUpdate MESSAGES_TYPE = { "SensorView": SensorView, + "SensorViewConfiguration": SensorViewConfiguration, "GroundTruth": GroundTruth, + "HostVehicleData": HostVehicleData, "SensorData": SensorData, + "TrafficCommand": TrafficCommand, + "TrafficCommandUpdate": TrafficCommandUpdate, + "TrafficUpdate": TrafficUpdate, + "MotionRequest": MotionRequest, + "StreamingUpdate": StreamingUpdate, } @@ -25,6 +39,11 @@ def map_message_type(type_name): """Map the type name to the protobuf message type.""" return MESSAGES_TYPE[type_name] + @staticmethod + def message_types(): + """Message types that OSITrace supports.""" + return list(MESSAGES_TYPE.keys()) + def __init__(self, path=None, type_name="SensorView", cache_messages=False): self.type = self.map_message_type(type_name) self.file = None diff --git a/tests/test_osi_trace.py b/tests/test_osi_trace.py index 428cecbb5..9d74f5a9d 100644 --- a/tests/test_osi_trace.py +++ b/tests/test_osi_trace.py @@ -4,28 +4,194 @@ from osi3trace.osi_trace import OSITrace from osi3.osi_sensorview_pb2 import SensorView +from osi3.osi_sensorviewconfiguration_pb2 import SensorViewConfiguration +from osi3.osi_groundtruth_pb2 import GroundTruth +from osi3.osi_hostvehicledata_pb2 import HostVehicleData +from osi3.osi_sensordata_pb2 import SensorData +from osi3.osi_trafficcommand_pb2 import TrafficCommand +from osi3.osi_trafficcommandupdate_pb2 import TrafficCommandUpdate +from osi3.osi_trafficupdate_pb2 import TrafficUpdate +from osi3.osi_motionrequest_pb2 import MotionRequest +from osi3.osi_streamingupdate_pb2 import StreamingUpdate + import struct class TestOSITrace(unittest.TestCase): - def test_osi_trace(self): + def test_osi_trace_sv(self): with tempfile.TemporaryDirectory() as tmpdirname: - path_output = os.path.join(tmpdirname, "output.txth") - path_input = os.path.join(tmpdirname, "input.osi") - create_sample(path_input) + path_output = os.path.join(tmpdirname, "output_sv.txth") + path_input = os.path.join(tmpdirname, "input_sv.osi") + create_sample_sv(path_input) trace = OSITrace(path_input) with open(path_output, "wt") as f: for message in trace: + self.assertIsInstance(message, SensorView) + f.write(str(message)) + + self.assertEqual(len(trace.retrieve_offsets()), 10) + trace.close() + + self.assertTrue(os.path.exists(path_output)) + + def test_osi_trace_svc(self): + with tempfile.TemporaryDirectory() as tmpdirname: + path_output = os.path.join(tmpdirname, "output_svc.txth") + path_input = os.path.join(tmpdirname, "input_svc.osi") + create_sample_svc(path_input) + + trace = OSITrace(path_input, "SensorViewConfiguration") + with open(path_output, "wt") as f: + for message in trace: + self.assertIsInstance(message, SensorViewConfiguration) + f.write(str(message)) + + self.assertEqual(len(trace.retrieve_offsets()), 1) + trace.close() + + self.assertTrue(os.path.exists(path_output)) + + def test_osi_trace_gt(self): + with tempfile.TemporaryDirectory() as tmpdirname: + path_output = os.path.join(tmpdirname, "output_gt.txth") + path_input = os.path.join(tmpdirname, "input_gt.osi") + create_sample_gt(path_input) + + trace = OSITrace(path_input, "GroundTruth") + with open(path_output, "wt") as f: + for message in trace: + self.assertIsInstance(message, GroundTruth) + f.write(str(message)) + + self.assertEqual(len(trace.retrieve_offsets()), 10) + trace.close() + + self.assertTrue(os.path.exists(path_output)) + + def test_osi_trace_hvd(self): + with tempfile.TemporaryDirectory() as tmpdirname: + path_output = os.path.join(tmpdirname, "output_hvd.txth") + path_input = os.path.join(tmpdirname, "input_hvd.osi") + create_sample_hvd(path_input) + + trace = OSITrace(path_input, "HostVehicleData") + with open(path_output, "wt") as f: + for message in trace: + self.assertIsInstance(message, HostVehicleData) + f.write(str(message)) + + self.assertEqual(len(trace.retrieve_offsets()), 10) + trace.close() + + self.assertTrue(os.path.exists(path_output)) + + def test_osi_trace_sd(self): + with tempfile.TemporaryDirectory() as tmpdirname: + path_output = os.path.join(tmpdirname, "output_sd.txth") + path_input = os.path.join(tmpdirname, "input_sd.osi") + create_sample_sd(path_input) + + trace = OSITrace(path_input, "SensorData") + with open(path_output, "wt") as f: + for message in trace: + self.assertIsInstance(message, SensorData) + f.write(str(message)) + + self.assertEqual(len(trace.retrieve_offsets()), 10) + trace.close() + + self.assertTrue(os.path.exists(path_output)) + + def test_osi_trace_tc(self): + with tempfile.TemporaryDirectory() as tmpdirname: + path_output = os.path.join(tmpdirname, "output_tc.txth") + path_input = os.path.join(tmpdirname, "input_tc.osi") + create_sample_tc(path_input) + + trace = OSITrace(path_input, "TrafficCommand") + with open(path_output, "wt") as f: + for message in trace: + self.assertIsInstance(message, TrafficCommand) + f.write(str(message)) + + self.assertEqual(len(trace.retrieve_offsets()), 10) + trace.close() + + self.assertTrue(os.path.exists(path_output)) + + def test_osi_trace_tcu(self): + with tempfile.TemporaryDirectory() as tmpdirname: + path_output = os.path.join(tmpdirname, "output_tcu.txth") + path_input = os.path.join(tmpdirname, "input_tcu.osi") + create_sample_tcu(path_input) + + trace = OSITrace(path_input, "TrafficCommandUpdate") + with open(path_output, "wt") as f: + for message in trace: + self.assertIsInstance(message, TrafficCommandUpdate) + f.write(str(message)) + + self.assertEqual(len(trace.retrieve_offsets()), 10) + trace.close() + + self.assertTrue(os.path.exists(path_output)) + + def test_osi_trace_tu(self): + with tempfile.TemporaryDirectory() as tmpdirname: + path_output = os.path.join(tmpdirname, "output_tu.txth") + path_input = os.path.join(tmpdirname, "input_tu.osi") + create_sample_tu(path_input) + + trace = OSITrace(path_input, "TrafficUpdate") + with open(path_output, "wt") as f: + for message in trace: + self.assertIsInstance(message, TrafficUpdate) + f.write(str(message)) + + self.assertEqual(len(trace.retrieve_offsets()), 10) + trace.close() + + self.assertTrue(os.path.exists(path_output)) + + def test_osi_trace_mr(self): + with tempfile.TemporaryDirectory() as tmpdirname: + path_output = os.path.join(tmpdirname, "output_mr.txth") + path_input = os.path.join(tmpdirname, "input_mr.osi") + create_sample_mr(path_input) + + trace = OSITrace(path_input, "MotionRequest") + with open(path_output, "wt") as f: + for message in trace: + self.assertIsInstance(message, MotionRequest) f.write(str(message)) + + self.assertEqual(len(trace.retrieve_offsets()), 10) + trace.close() + + self.assertTrue(os.path.exists(path_output)) + + def test_osi_trace_su(self): + with tempfile.TemporaryDirectory() as tmpdirname: + path_output = os.path.join(tmpdirname, "output_su.txth") + path_input = os.path.join(tmpdirname, "input_su.osi") + create_sample_su(path_input) + + trace = OSITrace(path_input, "StreamingUpdate") + with open(path_output, "wt") as f: + for message in trace: + self.assertIsInstance(message, StreamingUpdate) + f.write(str(message)) + + self.assertEqual(len(trace.retrieve_offsets()), 10) trace.close() self.assertTrue(os.path.exists(path_output)) def test_osi_trace_offsets_robustness(self): with tempfile.TemporaryDirectory() as tmpdirname: - path_input = os.path.join(tmpdirname, "input.osi") - create_sample(path_input) + path_input = os.path.join(tmpdirname, "input_robust.osi") + create_sample_sv(path_input) trace = OSITrace(path_input) # Test whether the function can handle be run multiple times safely @@ -37,10 +203,19 @@ def test_osi_trace_offsets_robustness(self): self.assertEqual(offsets, offsets2) -def create_sample(path): +def create_sample_sv(path): f = open(path, "ab") sensorview = SensorView() + sensorview.version.version_major = 3 + sensorview.version.version_minor = 0 + sensorview.version.version_patch = 0 + + sensorview.timestamp.seconds = 0 + sensorview.timestamp.nanos = 0 + + sensorview.sensor_id.value = 42 + sv_ground_truth = sensorview.global_ground_truth sv_ground_truth.version.version_major = 3 sv_ground_truth.version.version_minor = 0 @@ -55,6 +230,9 @@ def create_sample(path): # Generate 10 OSI messages for 9 seconds for i in range(10): # Increment the time + sensorview.timestamp.seconds += 1 + sensorview.timestamp.nanos += 100000 + sv_ground_truth.timestamp.seconds += 1 sv_ground_truth.timestamp.nanos += 100000 @@ -77,3 +255,335 @@ def create_sample(path): f.write(struct.pack("