diff --git a/java/iot3/mobility/src/main/java/com/orange/iot3mobility/messages/cam/v240/codec/CamReader240.java b/java/iot3/mobility/src/main/java/com/orange/iot3mobility/messages/cam/v240/codec/CamReader240.java index bc8ae545..db13fe69 100644 --- a/java/iot3/mobility/src/main/java/com/orange/iot3mobility/messages/cam/v240/codec/CamReader240.java +++ b/java/iot3/mobility/src/main/java/com/orange/iot3mobility/messages/cam/v240/codec/CamReader240.java @@ -356,8 +356,7 @@ private RsuContainerHighFrequency readRsuHF(JsonParser parser) throws IOExceptio parser.skipChildren(); } } - return new RsuContainerHighFrequency( - requireField(zones, "rsu_container_high_frequency.protected_communication_zones_rsu")); + return new RsuContainerHighFrequency(zones); } private List readProtectedZones(JsonParser parser) throws IOException { diff --git a/java/iot3/mobility/src/main/java/com/orange/iot3mobility/messages/cam/v240/codec/CamWriter240.java b/java/iot3/mobility/src/main/java/com/orange/iot3mobility/messages/cam/v240/codec/CamWriter240.java index 183d8efa..4d7d89fd 100644 --- a/java/iot3/mobility/src/main/java/com/orange/iot3mobility/messages/cam/v240/codec/CamWriter240.java +++ b/java/iot3/mobility/src/main/java/com/orange/iot3mobility/messages/cam/v240/codec/CamWriter240.java @@ -189,24 +189,26 @@ private void writeBasicVehicleHF(JsonGenerator gen, BasicVehicleContainerHighFre private void writeRsuHF(JsonGenerator gen, RsuContainerHighFrequency container) throws IOException { gen.writeStartObject(); - gen.writeArrayFieldStart("protected_communication_zones_rsu"); - for (ProtectedCommunicationZone zone : container.protectedCommunicationZonesRsu()) { - gen.writeStartObject(); - gen.writeNumberField("protected_zone_type", zone.protectedZoneType()); - if (zone.expiryTime() != null) { - gen.writeNumberField("expiry_time", zone.expiryTime()); - } - gen.writeNumberField("protected_zone_latitude", zone.protectedZoneLatitude()); - gen.writeNumberField("protected_zone_longitude", zone.protectedZoneLongitude()); - if (zone.protectedZoneRadius() != null) { - gen.writeNumberField("protected_zone_radius", zone.protectedZoneRadius()); - } - if (zone.protectedZoneId() != null) { - gen.writeNumberField("protected_zone_id", zone.protectedZoneId()); + if (container.protectedCommunicationZonesRsu() != null) { + gen.writeArrayFieldStart("protected_communication_zones_rsu"); + for (ProtectedCommunicationZone zone : container.protectedCommunicationZonesRsu()) { + gen.writeStartObject(); + gen.writeNumberField("protected_zone_type", zone.protectedZoneType()); + if (zone.expiryTime() != null) { + gen.writeNumberField("expiry_time", zone.expiryTime()); + } + gen.writeNumberField("protected_zone_latitude", zone.protectedZoneLatitude()); + gen.writeNumberField("protected_zone_longitude", zone.protectedZoneLongitude()); + if (zone.protectedZoneRadius() != null) { + gen.writeNumberField("protected_zone_radius", zone.protectedZoneRadius()); + } + if (zone.protectedZoneId() != null) { + gen.writeNumberField("protected_zone_id", zone.protectedZoneId()); + } + gen.writeEndObject(); } - gen.writeEndObject(); + gen.writeEndArray(); } - gen.writeEndArray(); gen.writeEndObject(); } diff --git a/java/iot3/mobility/src/main/java/com/orange/iot3mobility/messages/cam/v240/model/highfrequencycontainer/RsuContainerHighFrequency.java b/java/iot3/mobility/src/main/java/com/orange/iot3mobility/messages/cam/v240/model/highfrequencycontainer/RsuContainerHighFrequency.java index 6f7c6116..7e7d1a57 100644 --- a/java/iot3/mobility/src/main/java/com/orange/iot3mobility/messages/cam/v240/model/highfrequencycontainer/RsuContainerHighFrequency.java +++ b/java/iot3/mobility/src/main/java/com/orange/iot3mobility/messages/cam/v240/model/highfrequencycontainer/RsuContainerHighFrequency.java @@ -8,16 +8,19 @@ package com.orange.iot3mobility.messages.cam.v240.model.highfrequencycontainer; import java.util.List; -import java.util.Objects; /** * RsuContainerHighFrequency v2.4.0 * - * @param protectedCommunicationZonesRsu List of RSU {@link ProtectedCommunicationZone} + * @param protectedCommunicationZonesRsu Optional list of RSU {@link ProtectedCommunicationZone}. + * May be {@code null} — the field is optional per the schema. + * When present, must contain between 1 and 16 entries. */ public record RsuContainerHighFrequency( List protectedCommunicationZonesRsu) implements HighFrequencyContainer { public RsuContainerHighFrequency { - protectedCommunicationZonesRsu = List.copyOf(Objects.requireNonNull(protectedCommunicationZonesRsu)); + protectedCommunicationZonesRsu = protectedCommunicationZonesRsu != null + ? List.copyOf(protectedCommunicationZonesRsu) + : null; } } diff --git a/java/iot3/mobility/src/main/java/com/orange/iot3mobility/messages/cam/v240/validation/CamValidator240.java b/java/iot3/mobility/src/main/java/com/orange/iot3mobility/messages/cam/v240/validation/CamValidator240.java index 706b2279..11b59242 100644 --- a/java/iot3/mobility/src/main/java/com/orange/iot3mobility/messages/cam/v240/validation/CamValidator240.java +++ b/java/iot3/mobility/src/main/java/com/orange/iot3mobility/messages/cam/v240/validation/CamValidator240.java @@ -151,9 +151,10 @@ private static void validateBasicVehicleHF(BasicVehicleContainerHighFrequency co } private static void validateRsuHF(RsuContainerHighFrequency container) { - List zones = - requireNonNull("rsu_container_high_frequency.protected_communication_zones_rsu", - container.protectedCommunicationZonesRsu()); + List zones = container.protectedCommunicationZonesRsu(); + if (zones == null) { + return; + } int size = zones.size(); if (size < 1 || size > 16) { throw new CamValidationException("protected_communication_zones_rsu size out of range [1,16]: " + size); diff --git a/java/iot3/mobility/src/test/java/com/orange/iot3mobility/messages/cam/CamCodecTest.java b/java/iot3/mobility/src/test/java/com/orange/iot3mobility/messages/cam/CamCodecTest.java index d968afe6..5590d517 100644 --- a/java/iot3/mobility/src/test/java/com/orange/iot3mobility/messages/cam/CamCodecTest.java +++ b/java/iot3/mobility/src/test/java/com/orange/iot3mobility/messages/cam/CamCodecTest.java @@ -25,6 +25,7 @@ import com.orange.iot3mobility.messages.cam.v240.model.highfrequencycontainer.BasicVehicleContainerHighFrequency; import com.orange.iot3mobility.messages.cam.v240.model.highfrequencycontainer.Curvature; import com.orange.iot3mobility.messages.cam.v240.model.highfrequencycontainer.Heading; +import com.orange.iot3mobility.messages.cam.v240.model.highfrequencycontainer.RsuContainerHighFrequency; import com.orange.iot3mobility.messages.cam.v240.model.highfrequencycontainer.Speed; import com.orange.iot3mobility.messages.cam.v240.model.highfrequencycontainer.VehicleLength; import com.orange.iot3mobility.messages.cam.v240.model.highfrequencycontainer.YawRate; @@ -83,6 +84,24 @@ void writeRead240WithLinkedStationIdRoundTrip() throws Exception { assertEquals(4294967295L, parsed.linkedStationId()); } + @Test + void writeRead240RsuWithoutZonesRoundTrip() throws Exception { + CamEnvelope240 envelope = validEnvelope240Rsu(); + CamCodec codec = new CamCodec(new JsonFactory()); + + ByteArrayOutputStream out = new ByteArrayOutputStream(); + codec.write(CamVersion.V2_4_0, envelope, out); + + CamCodec.CamFrame frame = codec.read(new ByteArrayInputStream(out.toByteArray())); + assertEquals(CamVersion.V2_4_0, frame.version()); + assertTrue(frame.envelope() instanceof CamEnvelope240); + CamEnvelope240 parsed = (CamEnvelope240) frame.envelope(); + assertEquals(envelope.sourceUuid(), parsed.sourceUuid()); + CamStructuredData parsedMsg = (CamStructuredData) parsed.message(); + assertTrue(parsedMsg.highFrequencyContainer() instanceof RsuContainerHighFrequency); + assertNull(((RsuContainerHighFrequency) parsedMsg.highFrequencyContainer()).protectedCommunicationZonesRsu()); + } + @Test void readRejectsMissingVersion() { CamCodec codec = new CamCodec(new JsonFactory()); @@ -154,6 +173,37 @@ private static CamEnvelope240 validEnvelope240() { .build(); } + private static CamEnvelope240 validEnvelope240Rsu() { + com.orange.iot3mobility.messages.cam.v240.model.basiccontainer.ReferencePosition reference = + com.orange.iot3mobility.messages.cam.v240.model.basiccontainer.ReferencePosition.builder() + .latitudeLongitude(0, 0) + .positionConfidenceEllipse(new PositionConfidenceEllipse(0, 0, 0)) + .altitude(new Altitude(0, 0)) + .build(); + + BasicContainer basic = BasicContainer.builder() + .stationType(15) + .referencePosition(reference) + .build(); + + RsuContainerHighFrequency rsu = new RsuContainerHighFrequency(null); + + CamStructuredData message = CamStructuredData.builder() + .protocolVersion(1) + .stationId(99) + .generationDeltaTime(1) + .basicContainer(basic) + .highFrequencyContainer(rsu) + .build(); + + return CamEnvelope240.builder() + .messageFormat("json/raw") + .sourceUuid("com_rsu_99") + .timestamp(1514764800000L) + .message(message) + .build(); + } + private static CamEnvelope240 validEnvelope240WithLinkedStationId() { com.orange.iot3mobility.messages.cam.v240.model.basiccontainer.ReferencePosition reference = com.orange.iot3mobility.messages.cam.v240.model.basiccontainer.ReferencePosition.builder() diff --git a/java/iot3/mobility/src/test/java/com/orange/iot3mobility/messages/cam/v240/validation/CamValidator240Test.java b/java/iot3/mobility/src/test/java/com/orange/iot3mobility/messages/cam/v240/validation/CamValidator240Test.java index 665d84de..031010f5 100644 --- a/java/iot3/mobility/src/test/java/com/orange/iot3mobility/messages/cam/v240/validation/CamValidator240Test.java +++ b/java/iot3/mobility/src/test/java/com/orange/iot3mobility/messages/cam/v240/validation/CamValidator240Test.java @@ -83,6 +83,28 @@ void validateEnvelopeRejectsAsn1InvalidBase64() { assertThrows(CamValidationException.class, () -> CamValidator240.validateEnvelope(envelope)); } + @Test + void validateEnvelopeAcceptsRsuWithoutProtectedZones() { + RsuContainerHighFrequency rsu = new RsuContainerHighFrequency(null); + + CamStructuredData message = CamStructuredData.builder() + .protocolVersion(1) + .stationId(42) + .generationDeltaTime(1) + .basicContainer(validBasicContainer()) + .highFrequencyContainer(rsu) + .build(); + + CamEnvelope240 envelope = CamEnvelope240.builder() + .messageFormat("json/raw") + .sourceUuid("com_rsu_42") + .timestamp(1514764800000L) + .message(message) + .build(); + + CamValidator240.validateEnvelope(envelope); + } + @Test void validateEnvelopeRejectsEmptyRsuZones() { ReferencePosition reference = ReferencePosition.builder()