From ae052a8efe6480b8ab5766202273710a95376e91 Mon Sep 17 00:00:00 2001 From: jacob6838 Date: Tue, 5 Mar 2024 08:55:52 -0700 Subject: [PATCH 001/189] Adding conflictvisualizer map page to cvmanager --- webapp/package-lock.json | 73 +- webapp/package.json | 5 +- webapp/src/apis/api-helper-cviz.ts | 104 + webapp/src/apis/assessments-api.ts | 57 + webapp/src/apis/configuration-param-api.ts | 234 ++ webapp/src/apis/events-api.ts | 83 + webapp/src/apis/graphs-api.ts | 132 + webapp/src/apis/keycloak-api.ts | 198 ++ webapp/src/apis/mm-api.ts | 107 + webapp/src/apis/notification-api.ts | 103 + webapp/src/apis/reports-api.ts | 90 + webapp/src/apis/rsu-api.ts | 2 +- webapp/src/apis/user-management-api.ts | 131 + webapp/src/components/SsmSrmItem.tsx | 2 +- webapp/src/components/map/control-panel.tsx | 582 ++++ webapp/src/components/map/custom-table.tsx | 44 + .../src/components/map/expandable-table.tsx | 75 + webapp/src/components/map/map-component.tsx | 383 +++ .../components/map/map-layer-style-slice.ts | 282 ++ webapp/src/components/map/map-legend.tsx | 229 ++ webapp/src/components/map/map-slice.tsx | 1292 +++++++++ webapp/src/components/map/popup.tsx | 93 + .../src/components/map/processed_map_v4.json | 2459 +++++++++++++++++ webapp/src/components/map/side-panel.tsx | 245 ++ webapp/src/components/map/utilities/colors.ts | 24 + .../map/utilities/file-utilities.ts | 28 + .../src/components/map/utilities/map-utils.ts | 25 + .../components/map/utilities/message-utils.ts | 199 ++ .../features/adminAddRsu/adminAddRsuSlice.tsx | 2 +- .../features/adminEditRsu/AdminEditRsu.tsx | 2 +- .../adminOrganizationTabSlice.tsx | 2 +- .../adminOrganizationTabRsuSlice.tsx | 2 +- .../features/adminRsuTab/adminRsuTabSlice.tsx | 2 +- webapp/src/features/menu/DisplayCounts.tsx | 4 +- webapp/src/features/menu/menuSlice.tsx | 2 +- webapp/src/generalSlices/rsuSlice.ts | 6 +- webapp/src/generalSlices/wzdxSlice.ts | 2 +- webapp/src/icons/bell.tsx | 8 + webapp/src/icons/chart-bar.tsx | 8 + webapp/src/icons/chevron-down.tsx | 12 + webapp/src/icons/chevron-right.tsx | 12 + webapp/src/icons/clock.tsx | 12 + webapp/src/icons/cog.tsx | 12 + webapp/src/icons/download.tsx | 12 + webapp/src/icons/facebook.tsx | 8 + webapp/src/icons/google.tsx | 8 + webapp/src/icons/lock.tsx | 12 + webapp/src/{images => icons}/logo.png | Bin webapp/src/icons/menu.tsx | 12 + webapp/src/icons/pencil-alt.tsx | 13 + webapp/src/icons/plus.tsx | 10 + webapp/src/icons/refresh.tsx | 12 + .../src/{images => icons}/rsu_configure.PNG | Bin webapp/src/{images => icons}/rsu_count.PNG | Bin webapp/src/{images => icons}/rsu_heatmap.PNG | Bin webapp/src/{images => icons}/rsu_menu.PNG | Bin webapp/src/{images => icons}/rsu_popup.PNG | Bin webapp/src/{images => icons}/rsu_status.PNG | Bin webapp/src/icons/search.tsx | 12 + webapp/src/icons/selector.tsx | 12 + webapp/src/icons/shopping-bag.tsx | 12 + webapp/src/icons/traffic-light-green.png | Bin 0 -> 15065 bytes webapp/src/icons/traffic-light-red.png | Bin 0 -> 15751 bytes webapp/src/icons/traffic-light-yellow.png | Bin 0 -> 15301 bytes webapp/src/icons/traffic-light.png | Bin 0 -> 17610 bytes webapp/src/icons/upload.tsx | 12 + webapp/src/icons/user-add.tsx | 8 + webapp/src/icons/user-circle.tsx | 12 + webapp/src/icons/user.tsx | 12 + webapp/src/icons/users.tsx | 8 + webapp/src/icons/x-circle.tsx | 12 + webapp/src/models/GraphData.d.ts | 32 + .../src/models/IntersectionReferenceData.d.ts | 7 + webapp/src/models/IntersectionSummary.d.ts | 4 + .../models/MessageMonitorNotification.d.ts | 12 + .../src/{types => models}/MessageTypes.d.ts | 0 webapp/src/models/NotificationType.d.ts | 12 + webapp/src/models/ParsedJwt.d.ts | 99 + webapp/src/models/ReduxStateType.d.ts | 11 + webapp/src/{types => models}/Rsu.d.ts | 0 webapp/src/{types => models}/Srm.d.ts | 0 webapp/src/models/User.d.ts | 25 + webapp/src/{types => models}/UserTypes.d.ts | 0 webapp/src/models/bsmCollection.d.ts | 37 + webapp/src/models/convert_java_to_ts.py | 117 + webapp/src/models/get_required_imports.py | 95 + webapp/src/models/imports/Coordinate.d.ts | 5 + .../src/models/imports/GeometryFactory.d.ts | 1 + .../imports/J2735/J2735AdvisorySpeedList.d.ts | 23 + .../imports/J2735/J2735AllowedManeuvers.d.ts | 14 + .../imports/J2735/J2735BsmCoreData.d.ts | 61 + .../imports/J2735/J2735GenericLane.d.ts | 186 ++ .../J2735/J2735IntersectionGeometry.d.ts | 13 + .../imports/J2735/J2735LaneSharing.d.ts | 12 + .../J2735/J2735MovementPhaseState.d.ts | 11 + webapp/src/models/imports/OdeBsmData.d.ts | 36 + webapp/src/models/imports/OdePosition3D.d.ts | 5 + webapp/src/models/imports/Point.d.ts | 1 + webapp/src/models/imports/Polygon.d.ts | 1 + webapp/src/models/imports/ProcessedMap.d.ts | 101 + webapp/src/models/imports/ProcessedSpat.d.ts | 60 + webapp/src/models/imports/README.md | 1 + .../AllowConcurrentPermissiveList.d.ts | 4 + .../AllowedConcurrentPermissive.d.ts | 10 + .../Intersection/Intersection.d.ts | 13 + .../Intersection/IntersectionLine.d.ts | 6 + .../Intersection/Lane.d.ts | 9 + .../Intersection/LaneConnection.d.ts | 8 + .../Intersection/LaneSegment.d.ts | 9 + .../Intersection/LineVehicleIntersection.d.ts | 5 + .../Intersection/VehiclePath.d.ts | 14 + .../jpo-conflictmonitor/LaneConnection.d.ts | 13 + .../jpo-conflictmonitor/MapIntersection.d.ts | 6 + .../NotificationSource.d.ts | 4 + .../RegulatorIntersectionId.d.ts | 5 + .../models/jpo-conflictmonitor/SpatMap.d.ts | 5 + .../jpo-conflictmonitor/VehicleEvent.d.ts | 7 + .../assessments/Assessment.d.ts | 8 + .../ConnectionOfTravelAggregator.d.ts | 6 + .../ConnectionOfTravelAssessment.d.ts | 5 + .../ConnectionOfTravelAssessmentGroup.d.ts | 7 + .../LaneDirectionOfTravelAggregator.d.ts | 8 + .../LaneDirectionOfTravelAssessment.d.ts | 5 + .../LaneDirectionOfTravelAssessmentGroup.d.ts | 14 + .../SignalStateEventAggregator.d.ts | 7 + .../SignalStateEventAssessment.d.ts | 5 + .../SignalStateEventAssessmentGroup.d.ts | 8 + .../assessments/StopLineStopAssessment.d.ts | 5 + .../StopLineStopAssessmentGroup.d.ts | 9 + .../bsm/BsmAggregator.d.ts | 4 + .../jpo-conflictmonitor/bsm/BsmEvent.d.ts | 11 + .../bsm/BsmEventIntersectionKey.d.ts | 5 + .../bsm/BsmIntersectionKey.d.ts | 6 + .../bsm/BsmTimestampExtractor.d.ts | 4 + .../jpo-conflictmonitor/config/Config.d.ts | 10 + .../config/ConfigCollection.d.ts | 1 + .../config/ConfigData.d.ts | 8 + .../config/ConfigDataClass.d.ts | 4 + .../jpo-conflictmonitor/config/ConfigMap.d.ts | 1 + .../config/DefaultBooleanConfig.d.ts | 1 + .../config/DefaultConfig.d.ts | 1 + .../config/DefaultConfigCollection.d.ts | 1 + .../config/DefaultDoubleConfig.d.ts | 1 + .../config/DefaultIntConfig.d.ts | 1 + .../config/DefaultLongConfig.d.ts | 1 + .../config/DefaultStringConfig.d.ts | 1 + .../config/IntersectionConfig.d.ts | 5 + .../config/IntersectionConfigCollection.d.ts | 1 + .../config/RsuConfigKey.d.ts | 4 + .../jpo-conflictmonitor/config/UnitsEnum.d.ts | 11 + .../config/UpdateType.d.ts | 1 + .../events/ConnectionOfTravelEvent.d.ts | 8 + .../jpo-conflictmonitor/events/Event.d.ts | 10 + .../IntersectionReferenceAlignmentEvent.d.ts | 7 + .../events/LaneDirectionOfTravelEvent.d.ts | 15 + .../events/ProcessingTimePeriod.d.ts | 5 + .../events/SignalGroupAlignmentEvent.d.ts | 7 + .../events/SignalStateConflictEvent.d.ts | 10 + .../events/SignalStateEvent.d.ts | 14 + .../events/SignalStateStopEvent.d.ts | 13 + .../events/StopLinePassageEvent.d.ts | 15 + .../events/StopLineStopEvent.d.ts | 19 + .../events/TimeChangeDetailsEvent.d.ts | 15 + .../ConnectionOfTravelTimestampExtractor.d.ts | 4 + ...neDirectionOfTravelTimestampExtractor.d.ts | 4 + .../SignalStateTimestampExtractor.d.ts | 4 + .../events/app_health/KafkaStreamsEvent.d.ts | 5 + .../KafkaStreamsStateChangeEvent.d.ts | 5 + .../KafkaStreamsUnhandledExceptionEvent.d.ts | 4 + .../broadcast_rate/BroadcastRateEvent.d.ts | 7 + .../broadcast_rate/MapBroadcastRateEvent.d.ts | 4 + .../SpatBroadcastRateEvent.d.ts | 4 + .../minimum_data/MapMinimumDataEvent.d.ts | 4 + .../events/minimum_data/MinimumDataEvent.d.ts | 6 + .../minimum_data/SpatMinimumDataEvent.d.ts | 4 + .../map/IntersectionRegion.d.ts | 5 + .../jpo-conflictmonitor/map/MapIndex.d.ts | 4 + .../map/MapTimeChangeDetail.d.ts | 4 + .../map/MapTimeChangeDetailAggregator.d.ts | 4 + .../map/MapTimeChangeDetailPair.d.ts | 4 + .../map/MapTimeChangeDetailState.d.ts | 4 + .../map/MapTimestampExtractor.d.ts | 4 + .../ConnectionOfTravelNotification.d.ts | 6 + ...sectionReferenceAlignmentNotification.d.ts | 4 + .../LaneDirectionOfTravelNotification.d.ts | 4 + .../notifications/Notification.d.ts | 15 + .../SignalGroupAlignmentNotification.d.ts | 4 + .../SignalStateConflictNotification.d.ts | 4 + .../TimeChangeDetailsNotification.d.ts | 4 + .../KafkaStreamsAnomalyNotification.d.ts | 5 + .../BroadcastRateNotification.d.ts | 4 + .../MapBroadcastRateNotification.d.ts | 4 + .../SpatBroadcastRateNotification.d.ts | 4 + .../spat/SpatAggregator.d.ts | 4 + .../spat/SpatTimeChangeDetail.d.ts | 8 + .../spat/SpatTimeChangeDetailAggregator.d.ts | 6 + .../spat/SpatTimeChangeDetailPair.d.ts | 5 + .../spat/SpatTimeChangeDetailState.d.ts | 7 + .../spat/SpatTimestampExtractor.d.ts | 4 + webapp/src/models/required_types.json | 19 + webapp/src/models/spatSignalGroups.d.ts | 39 + .../wzdx/WzdxWorkZoneFeed42.d.ts | 0 .../wzdx/work_zone_feed_schema_42.json | 0 webapp/src/pages/IntersectionMapView.tsx | 0 webapp/src/pages/Map.tsx | 2 +- webapp/src/store.tsx | 12 +- webapp/yarn.lock | 50 + 207 files changed, 9241 insertions(+), 21 deletions(-) create mode 100644 webapp/src/apis/api-helper-cviz.ts create mode 100644 webapp/src/apis/assessments-api.ts create mode 100644 webapp/src/apis/configuration-param-api.ts create mode 100644 webapp/src/apis/events-api.ts create mode 100644 webapp/src/apis/graphs-api.ts create mode 100644 webapp/src/apis/keycloak-api.ts create mode 100644 webapp/src/apis/mm-api.ts create mode 100644 webapp/src/apis/notification-api.ts create mode 100644 webapp/src/apis/reports-api.ts create mode 100644 webapp/src/apis/user-management-api.ts create mode 100644 webapp/src/components/map/control-panel.tsx create mode 100644 webapp/src/components/map/custom-table.tsx create mode 100644 webapp/src/components/map/expandable-table.tsx create mode 100644 webapp/src/components/map/map-component.tsx create mode 100644 webapp/src/components/map/map-layer-style-slice.ts create mode 100644 webapp/src/components/map/map-legend.tsx create mode 100644 webapp/src/components/map/map-slice.tsx create mode 100644 webapp/src/components/map/popup.tsx create mode 100644 webapp/src/components/map/processed_map_v4.json create mode 100644 webapp/src/components/map/side-panel.tsx create mode 100644 webapp/src/components/map/utilities/colors.ts create mode 100644 webapp/src/components/map/utilities/file-utilities.ts create mode 100644 webapp/src/components/map/utilities/map-utils.ts create mode 100644 webapp/src/components/map/utilities/message-utils.ts create mode 100644 webapp/src/icons/bell.tsx create mode 100644 webapp/src/icons/chart-bar.tsx create mode 100644 webapp/src/icons/chevron-down.tsx create mode 100644 webapp/src/icons/chevron-right.tsx create mode 100644 webapp/src/icons/clock.tsx create mode 100644 webapp/src/icons/cog.tsx create mode 100644 webapp/src/icons/download.tsx create mode 100644 webapp/src/icons/facebook.tsx create mode 100644 webapp/src/icons/google.tsx create mode 100644 webapp/src/icons/lock.tsx rename webapp/src/{images => icons}/logo.png (100%) create mode 100644 webapp/src/icons/menu.tsx create mode 100644 webapp/src/icons/pencil-alt.tsx create mode 100644 webapp/src/icons/plus.tsx create mode 100644 webapp/src/icons/refresh.tsx rename webapp/src/{images => icons}/rsu_configure.PNG (100%) rename webapp/src/{images => icons}/rsu_count.PNG (100%) rename webapp/src/{images => icons}/rsu_heatmap.PNG (100%) rename webapp/src/{images => icons}/rsu_menu.PNG (100%) rename webapp/src/{images => icons}/rsu_popup.PNG (100%) rename webapp/src/{images => icons}/rsu_status.PNG (100%) create mode 100644 webapp/src/icons/search.tsx create mode 100644 webapp/src/icons/selector.tsx create mode 100644 webapp/src/icons/shopping-bag.tsx create mode 100644 webapp/src/icons/traffic-light-green.png create mode 100644 webapp/src/icons/traffic-light-red.png create mode 100644 webapp/src/icons/traffic-light-yellow.png create mode 100644 webapp/src/icons/traffic-light.png create mode 100644 webapp/src/icons/upload.tsx create mode 100644 webapp/src/icons/user-add.tsx create mode 100644 webapp/src/icons/user-circle.tsx create mode 100644 webapp/src/icons/user.tsx create mode 100644 webapp/src/icons/users.tsx create mode 100644 webapp/src/icons/x-circle.tsx create mode 100644 webapp/src/models/GraphData.d.ts create mode 100644 webapp/src/models/IntersectionReferenceData.d.ts create mode 100644 webapp/src/models/IntersectionSummary.d.ts create mode 100644 webapp/src/models/MessageMonitorNotification.d.ts rename webapp/src/{types => models}/MessageTypes.d.ts (100%) create mode 100644 webapp/src/models/NotificationType.d.ts create mode 100644 webapp/src/models/ParsedJwt.d.ts create mode 100644 webapp/src/models/ReduxStateType.d.ts rename webapp/src/{types => models}/Rsu.d.ts (100%) rename webapp/src/{types => models}/Srm.d.ts (100%) create mode 100644 webapp/src/models/User.d.ts rename webapp/src/{types => models}/UserTypes.d.ts (100%) create mode 100644 webapp/src/models/bsmCollection.d.ts create mode 100644 webapp/src/models/convert_java_to_ts.py create mode 100644 webapp/src/models/get_required_imports.py create mode 100644 webapp/src/models/imports/Coordinate.d.ts create mode 100644 webapp/src/models/imports/GeometryFactory.d.ts create mode 100644 webapp/src/models/imports/J2735/J2735AdvisorySpeedList.d.ts create mode 100644 webapp/src/models/imports/J2735/J2735AllowedManeuvers.d.ts create mode 100644 webapp/src/models/imports/J2735/J2735BsmCoreData.d.ts create mode 100644 webapp/src/models/imports/J2735/J2735GenericLane.d.ts create mode 100644 webapp/src/models/imports/J2735/J2735IntersectionGeometry.d.ts create mode 100644 webapp/src/models/imports/J2735/J2735LaneSharing.d.ts create mode 100644 webapp/src/models/imports/J2735/J2735MovementPhaseState.d.ts create mode 100644 webapp/src/models/imports/OdeBsmData.d.ts create mode 100644 webapp/src/models/imports/OdePosition3D.d.ts create mode 100644 webapp/src/models/imports/Point.d.ts create mode 100644 webapp/src/models/imports/Polygon.d.ts create mode 100644 webapp/src/models/imports/ProcessedMap.d.ts create mode 100644 webapp/src/models/imports/ProcessedSpat.d.ts create mode 100644 webapp/src/models/imports/README.md create mode 100644 webapp/src/models/jpo-conflictmonitor/AllowConcurrentPermissiveList.d.ts create mode 100644 webapp/src/models/jpo-conflictmonitor/AllowedConcurrentPermissive.d.ts create mode 100644 webapp/src/models/jpo-conflictmonitor/Intersection/Intersection.d.ts create mode 100644 webapp/src/models/jpo-conflictmonitor/Intersection/IntersectionLine.d.ts create mode 100644 webapp/src/models/jpo-conflictmonitor/Intersection/Lane.d.ts create mode 100644 webapp/src/models/jpo-conflictmonitor/Intersection/LaneConnection.d.ts create mode 100644 webapp/src/models/jpo-conflictmonitor/Intersection/LaneSegment.d.ts create mode 100644 webapp/src/models/jpo-conflictmonitor/Intersection/LineVehicleIntersection.d.ts create mode 100644 webapp/src/models/jpo-conflictmonitor/Intersection/VehiclePath.d.ts create mode 100644 webapp/src/models/jpo-conflictmonitor/LaneConnection.d.ts create mode 100644 webapp/src/models/jpo-conflictmonitor/MapIntersection.d.ts create mode 100644 webapp/src/models/jpo-conflictmonitor/NotificationSource.d.ts create mode 100644 webapp/src/models/jpo-conflictmonitor/RegulatorIntersectionId.d.ts create mode 100644 webapp/src/models/jpo-conflictmonitor/SpatMap.d.ts create mode 100644 webapp/src/models/jpo-conflictmonitor/VehicleEvent.d.ts create mode 100644 webapp/src/models/jpo-conflictmonitor/assessments/Assessment.d.ts create mode 100644 webapp/src/models/jpo-conflictmonitor/assessments/ConnectionOfTravelAggregator.d.ts create mode 100644 webapp/src/models/jpo-conflictmonitor/assessments/ConnectionOfTravelAssessment.d.ts create mode 100644 webapp/src/models/jpo-conflictmonitor/assessments/ConnectionOfTravelAssessmentGroup.d.ts create mode 100644 webapp/src/models/jpo-conflictmonitor/assessments/LaneDirectionOfTravelAggregator.d.ts create mode 100644 webapp/src/models/jpo-conflictmonitor/assessments/LaneDirectionOfTravelAssessment.d.ts create mode 100644 webapp/src/models/jpo-conflictmonitor/assessments/LaneDirectionOfTravelAssessmentGroup.d.ts create mode 100644 webapp/src/models/jpo-conflictmonitor/assessments/SignalStateEventAggregator.d.ts create mode 100644 webapp/src/models/jpo-conflictmonitor/assessments/SignalStateEventAssessment.d.ts create mode 100644 webapp/src/models/jpo-conflictmonitor/assessments/SignalStateEventAssessmentGroup.d.ts create mode 100644 webapp/src/models/jpo-conflictmonitor/assessments/StopLineStopAssessment.d.ts create mode 100644 webapp/src/models/jpo-conflictmonitor/assessments/StopLineStopAssessmentGroup.d.ts create mode 100644 webapp/src/models/jpo-conflictmonitor/bsm/BsmAggregator.d.ts create mode 100644 webapp/src/models/jpo-conflictmonitor/bsm/BsmEvent.d.ts create mode 100644 webapp/src/models/jpo-conflictmonitor/bsm/BsmEventIntersectionKey.d.ts create mode 100644 webapp/src/models/jpo-conflictmonitor/bsm/BsmIntersectionKey.d.ts create mode 100644 webapp/src/models/jpo-conflictmonitor/bsm/BsmTimestampExtractor.d.ts create mode 100644 webapp/src/models/jpo-conflictmonitor/config/Config.d.ts create mode 100644 webapp/src/models/jpo-conflictmonitor/config/ConfigCollection.d.ts create mode 100644 webapp/src/models/jpo-conflictmonitor/config/ConfigData.d.ts create mode 100644 webapp/src/models/jpo-conflictmonitor/config/ConfigDataClass.d.ts create mode 100644 webapp/src/models/jpo-conflictmonitor/config/ConfigMap.d.ts create mode 100644 webapp/src/models/jpo-conflictmonitor/config/DefaultBooleanConfig.d.ts create mode 100644 webapp/src/models/jpo-conflictmonitor/config/DefaultConfig.d.ts create mode 100644 webapp/src/models/jpo-conflictmonitor/config/DefaultConfigCollection.d.ts create mode 100644 webapp/src/models/jpo-conflictmonitor/config/DefaultDoubleConfig.d.ts create mode 100644 webapp/src/models/jpo-conflictmonitor/config/DefaultIntConfig.d.ts create mode 100644 webapp/src/models/jpo-conflictmonitor/config/DefaultLongConfig.d.ts create mode 100644 webapp/src/models/jpo-conflictmonitor/config/DefaultStringConfig.d.ts create mode 100644 webapp/src/models/jpo-conflictmonitor/config/IntersectionConfig.d.ts create mode 100644 webapp/src/models/jpo-conflictmonitor/config/IntersectionConfigCollection.d.ts create mode 100644 webapp/src/models/jpo-conflictmonitor/config/RsuConfigKey.d.ts create mode 100644 webapp/src/models/jpo-conflictmonitor/config/UnitsEnum.d.ts create mode 100644 webapp/src/models/jpo-conflictmonitor/config/UpdateType.d.ts create mode 100644 webapp/src/models/jpo-conflictmonitor/events/ConnectionOfTravelEvent.d.ts create mode 100644 webapp/src/models/jpo-conflictmonitor/events/Event.d.ts create mode 100644 webapp/src/models/jpo-conflictmonitor/events/IntersectionReferenceAlignmentEvent.d.ts create mode 100644 webapp/src/models/jpo-conflictmonitor/events/LaneDirectionOfTravelEvent.d.ts create mode 100644 webapp/src/models/jpo-conflictmonitor/events/ProcessingTimePeriod.d.ts create mode 100644 webapp/src/models/jpo-conflictmonitor/events/SignalGroupAlignmentEvent.d.ts create mode 100644 webapp/src/models/jpo-conflictmonitor/events/SignalStateConflictEvent.d.ts create mode 100644 webapp/src/models/jpo-conflictmonitor/events/SignalStateEvent.d.ts create mode 100644 webapp/src/models/jpo-conflictmonitor/events/SignalStateStopEvent.d.ts create mode 100644 webapp/src/models/jpo-conflictmonitor/events/StopLinePassageEvent.d.ts create mode 100644 webapp/src/models/jpo-conflictmonitor/events/StopLineStopEvent.d.ts create mode 100644 webapp/src/models/jpo-conflictmonitor/events/TimeChangeDetailsEvent.d.ts create mode 100644 webapp/src/models/jpo-conflictmonitor/events/TimestampExtractors/ConnectionOfTravelTimestampExtractor.d.ts create mode 100644 webapp/src/models/jpo-conflictmonitor/events/TimestampExtractors/LaneDirectionOfTravelTimestampExtractor.d.ts create mode 100644 webapp/src/models/jpo-conflictmonitor/events/TimestampExtractors/SignalStateTimestampExtractor.d.ts create mode 100644 webapp/src/models/jpo-conflictmonitor/events/app_health/KafkaStreamsEvent.d.ts create mode 100644 webapp/src/models/jpo-conflictmonitor/events/app_health/KafkaStreamsStateChangeEvent.d.ts create mode 100644 webapp/src/models/jpo-conflictmonitor/events/app_health/KafkaStreamsUnhandledExceptionEvent.d.ts create mode 100644 webapp/src/models/jpo-conflictmonitor/events/broadcast_rate/BroadcastRateEvent.d.ts create mode 100644 webapp/src/models/jpo-conflictmonitor/events/broadcast_rate/MapBroadcastRateEvent.d.ts create mode 100644 webapp/src/models/jpo-conflictmonitor/events/broadcast_rate/SpatBroadcastRateEvent.d.ts create mode 100644 webapp/src/models/jpo-conflictmonitor/events/minimum_data/MapMinimumDataEvent.d.ts create mode 100644 webapp/src/models/jpo-conflictmonitor/events/minimum_data/MinimumDataEvent.d.ts create mode 100644 webapp/src/models/jpo-conflictmonitor/events/minimum_data/SpatMinimumDataEvent.d.ts create mode 100644 webapp/src/models/jpo-conflictmonitor/map/IntersectionRegion.d.ts create mode 100644 webapp/src/models/jpo-conflictmonitor/map/MapIndex.d.ts create mode 100644 webapp/src/models/jpo-conflictmonitor/map/MapTimeChangeDetail.d.ts create mode 100644 webapp/src/models/jpo-conflictmonitor/map/MapTimeChangeDetailAggregator.d.ts create mode 100644 webapp/src/models/jpo-conflictmonitor/map/MapTimeChangeDetailPair.d.ts create mode 100644 webapp/src/models/jpo-conflictmonitor/map/MapTimeChangeDetailState.d.ts create mode 100644 webapp/src/models/jpo-conflictmonitor/map/MapTimestampExtractor.d.ts create mode 100644 webapp/src/models/jpo-conflictmonitor/notifications/ConnectionOfTravelNotification.d.ts create mode 100644 webapp/src/models/jpo-conflictmonitor/notifications/IntersectionReferenceAlignmentNotification.d.ts create mode 100644 webapp/src/models/jpo-conflictmonitor/notifications/LaneDirectionOfTravelNotification.d.ts create mode 100644 webapp/src/models/jpo-conflictmonitor/notifications/Notification.d.ts create mode 100644 webapp/src/models/jpo-conflictmonitor/notifications/SignalGroupAlignmentNotification.d.ts create mode 100644 webapp/src/models/jpo-conflictmonitor/notifications/SignalStateConflictNotification.d.ts create mode 100644 webapp/src/models/jpo-conflictmonitor/notifications/TimeChangeDetailsNotification.d.ts create mode 100644 webapp/src/models/jpo-conflictmonitor/notifications/app_health/KafkaStreamsAnomalyNotification.d.ts create mode 100644 webapp/src/models/jpo-conflictmonitor/notifications/broadcast_rate/BroadcastRateNotification.d.ts create mode 100644 webapp/src/models/jpo-conflictmonitor/notifications/broadcast_rate/MapBroadcastRateNotification.d.ts create mode 100644 webapp/src/models/jpo-conflictmonitor/notifications/broadcast_rate/SpatBroadcastRateNotification.d.ts create mode 100644 webapp/src/models/jpo-conflictmonitor/spat/SpatAggregator.d.ts create mode 100644 webapp/src/models/jpo-conflictmonitor/spat/SpatTimeChangeDetail.d.ts create mode 100644 webapp/src/models/jpo-conflictmonitor/spat/SpatTimeChangeDetailAggregator.d.ts create mode 100644 webapp/src/models/jpo-conflictmonitor/spat/SpatTimeChangeDetailPair.d.ts create mode 100644 webapp/src/models/jpo-conflictmonitor/spat/SpatTimeChangeDetailState.d.ts create mode 100644 webapp/src/models/jpo-conflictmonitor/spat/SpatTimestampExtractor.d.ts create mode 100644 webapp/src/models/required_types.json create mode 100644 webapp/src/models/spatSignalGroups.d.ts rename webapp/src/{types => models}/wzdx/WzdxWorkZoneFeed42.d.ts (100%) rename webapp/src/{types => models}/wzdx/work_zone_feed_schema_42.json (100%) create mode 100644 webapp/src/pages/IntersectionMapView.tsx diff --git a/webapp/package-lock.json b/webapp/package-lock.json index ee8da4453..85a0ac8c7 100644 --- a/webapp/package-lock.json +++ b/webapp/package-lock.json @@ -1,12 +1,12 @@ { "name": "cv-manager", - "version": "0.1.0", + "version": "1.2.0-SNAPSHOT", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "cv-manager", - "version": "0.1.0", + "version": "1.2.0-SNAPSHOT", "dependencies": { "@emotion/react": "^11.10.5", "@emotion/styled": "^11.10.5", @@ -20,6 +20,7 @@ "@react-keycloak/web": "3.4.0", "@react-oauth/google": "^0.2.8", "@reduxjs/toolkit": "^1.9.1", + "@stomp/stompjs": "^7.0.0", "@syncfusion/ej2-react-calendars": "^19.4.56", "@testing-library/jest-dom": "^5.16.5", "@testing-library/react": "^12.1.2", @@ -29,8 +30,10 @@ "dayjs": "^1.11.7", "env-cmd": "^10.1.0", "fbjs": "^3.0.4", + "file-saver": "^2.0.5", "isomorphic-fetch": "^3.0.0", "jest-fetch-mock": "^3.0.3", + "jszip": "^3.10.1", "keycloak-js": "21.1.1", "luxon": "^2.3.2", "mapbox-gl": "^2.9.1", @@ -5878,6 +5881,11 @@ "@sinonjs/commons": "^1.7.0" } }, + "node_modules/@stomp/stompjs": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/@stomp/stompjs/-/stompjs-7.0.0.tgz", + "integrity": "sha512-fGdq4wPDnSV/KyOsjq4P+zLc8MFWC3lMmP5FBgLWKPJTYcuCbAIrnRGjB7q2jHZdYCOD5vxLuFoKIYLy5/u8Pw==" + }, "node_modules/@surma/rollup-plugin-off-main-thread": { "version": "2.2.3", "resolved": "https://registry.npmjs.org/@surma/rollup-plugin-off-main-thread/-/rollup-plugin-off-main-thread-2.2.3.tgz", @@ -11616,6 +11624,11 @@ "webpack": "^4.0.0 || ^5.0.0" } }, + "node_modules/file-saver": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/file-saver/-/file-saver-2.0.5.tgz", + "integrity": "sha512-P9bmyZ3h/PRG+Nzga+rbdI4OEpNDzAVyy74uVO9ATgzLK6VtAsYybF/+TOCvrc0MO793d6+42lLyZTw7/ArVzA==" + }, "node_modules/filelist": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/filelist/-/filelist-1.0.4.tgz", @@ -12848,6 +12861,11 @@ "node": ">= 4" } }, + "node_modules/immediate": { + "version": "3.0.6", + "resolved": "https://registry.npmjs.org/immediate/-/immediate-3.0.6.tgz", + "integrity": "sha512-XXOFtyqDjNDAQxVfYxuF7g9Il/IbWmmlQg2MYKOH8ExIT1qg6xc4zyS3HaEEATgs1btfzxq15ciUiY7gjSXRGQ==" + }, "node_modules/immer": { "version": "9.0.17", "resolved": "https://registry.npmjs.org/immer/-/immer-9.0.17.tgz", @@ -17952,6 +17970,44 @@ "node": ">=4.0" } }, + "node_modules/jszip": { + "version": "3.10.1", + "resolved": "https://registry.npmjs.org/jszip/-/jszip-3.10.1.tgz", + "integrity": "sha512-xXDvecyTpGLrqFrvkrUSoxxfJI5AH7U8zxxtVclpsUtMCq4JQ290LY8AW5c7Ggnr/Y/oK+bQMbqK2qmtk3pN4g==", + "dependencies": { + "lie": "~3.3.0", + "pako": "~1.0.2", + "readable-stream": "~2.3.6", + "setimmediate": "^1.0.5" + } + }, + "node_modules/jszip/node_modules/isarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", + "integrity": "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==" + }, + "node_modules/jszip/node_modules/readable-stream": { + "version": "2.3.8", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.8.tgz", + "integrity": "sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==", + "dependencies": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + } + }, + "node_modules/jszip/node_modules/string_decoder": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "dependencies": { + "safe-buffer": "~5.1.0" + } + }, "node_modules/kdbush": { "version": "4.0.2", "resolved": "https://registry.npmjs.org/kdbush/-/kdbush-4.0.2.tgz", @@ -18043,6 +18099,14 @@ "node": ">= 0.8.0" } }, + "node_modules/lie": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/lie/-/lie-3.3.0.tgz", + "integrity": "sha512-UaiMJzeWRlEujzAuw5LokY1L5ecNQYZKfmyZ9L7wDHb/p5etKaxXhohBcrw0EYby+G/NA52vRSN4N39dxHAIwQ==", + "dependencies": { + "immediate": "~3.0.5" + } + }, "node_modules/lilconfig": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/lilconfig/-/lilconfig-2.1.0.tgz", @@ -19145,6 +19209,11 @@ "node": ">=6" } }, + "node_modules/pako": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/pako/-/pako-1.0.11.tgz", + "integrity": "sha512-4hLB8Py4zZce5s4yd9XzopqwVv/yGNhV1Bl8NTmCq1763HeK2+EwVTv+leGeL13Dnh2wfbqowVPXCIO0z4taYw==" + }, "node_modules/param-case": { "version": "3.0.4", "resolved": "https://registry.npmjs.org/param-case/-/param-case-3.0.4.tgz", diff --git a/webapp/package.json b/webapp/package.json index e7b6c8ba5..1f096a8ca 100644 --- a/webapp/package.json +++ b/webapp/package.json @@ -15,6 +15,7 @@ "@react-keycloak/web": "3.4.0", "@react-oauth/google": "^0.2.8", "@reduxjs/toolkit": "^1.9.1", + "@stomp/stompjs": "^7.0.0", "@syncfusion/ej2-react-calendars": "^19.4.56", "@testing-library/jest-dom": "^5.16.5", "@testing-library/react": "^12.1.2", @@ -24,8 +25,10 @@ "dayjs": "^1.11.7", "env-cmd": "^10.1.0", "fbjs": "^3.0.4", + "file-saver": "^2.0.5", "isomorphic-fetch": "^3.0.0", "jest-fetch-mock": "^3.0.3", + "jszip": "^3.10.1", "keycloak-js": "21.1.1", "luxon": "^2.3.2", "mapbox-gl": "^2.9.1", @@ -101,4 +104,4 @@ "worker-loader!mapbox-gl/dist/mapbox-gl-csp-worker": "/mocks/workerLoader.js" } } -} \ No newline at end of file +} diff --git a/webapp/src/apis/api-helper-cviz.ts b/webapp/src/apis/api-helper-cviz.ts new file mode 100644 index 000000000..218d7dfe6 --- /dev/null +++ b/webapp/src/apis/api-helper-cviz.ts @@ -0,0 +1,104 @@ +import toast from 'react-hot-toast' + +class CvizApiHelper { + formatQueryParams(query_params?: Record): string { + if (!query_params || Object.keys(query_params).length === 0) return '' + return `?${new URLSearchParams(query_params).toString()}` + } + + async invokeApi({ + path, + basePath, + method = 'GET', + headers = {}, + queryParams, + body, + token, + timeout, + responseType = 'json', + booleanResponse = false, + toastOnFailure = true, + toastOnSuccess = false, + successMessage = 'Successfully completed request!', + failureMessage = 'Request failed to complete', + }: { + path: string + basePath?: string + method?: string + headers?: Record + queryParams?: Record + body?: Object + token?: string + timeout?: number + responseType?: string + booleanResponse?: boolean + toastOnFailure?: boolean + toastOnSuccess?: boolean + successMessage?: string + failureMessage?: string + }): Promise { + const url = (basePath ?? process.env.CVIZ_API_SERVER_URL!) + path + this.formatQueryParams(queryParams) + + const localHeaders: HeadersInit = { ...headers } + if (token) localHeaders['Authorization'] = `Bearer ${token}` + if (method === 'POST' && body && !('Content-Type' in localHeaders)) { + localHeaders['Content-Type'] = 'application/json' + } + + let controller: AbortController | undefined = undefined + let id: NodeJS.Timeout | undefined = undefined + if (timeout) { + controller = new AbortController() + id = setTimeout(() => controller?.abort(), timeout) + } + + const options: RequestInit = { + method: method, + headers: localHeaders, + body: body + ? localHeaders['Content-Type'] == 'application/x-www-form-urlencoded' + ? (body as string) + : JSON.stringify(body) + : undefined, + mode: 'cors', + signal: controller?.signal, + } + + console.debug('MAKING REQUEST TO ' + url + ' WITH OPTIONS', options) + + const resp = await fetch(url, options) + .then((response) => { + if (response.ok) { + if (toastOnSuccess) toast.success(successMessage) + if (booleanResponse) return true + } else { + console.error('Request failed with status code ' + response.status + ': ' + response.statusText) + if (response.status === 401) { + toast.error('Authentication failed, please sign in again') + // signIn(); + } else if (response.status === 403) { + toast.error('You are not authorized to perform this action.') + } else if (toastOnFailure) toast.error(failureMessage + ', with status code ' + response.status) + + if (booleanResponse) return false + return undefined + } + if (responseType === 'blob') { + return response.blob() + } else { + const resp = response.json() + resp.then((val) => console.debug('RESPONSE TO', url, val)) + return resp + } + }) + .catch((error: Error) => { + const errorMessage = failureMessage ?? 'Fetch request failed' + toast.error(errorMessage + '. Error: ' + error.message) + console.error(error.message) + }) + if (id) clearTimeout(id) + return resp + } +} + +export const authApiHelper = new CvizApiHelper() diff --git a/webapp/src/apis/assessments-api.ts b/webapp/src/apis/assessments-api.ts new file mode 100644 index 000000000..5c76919fd --- /dev/null +++ b/webapp/src/apis/assessments-api.ts @@ -0,0 +1,57 @@ +import { number } from 'prop-types' +import { authApiHelper } from './api-helper-cviz' + +class AssessmentsApi { + async getLatestAssessment( + token: string, + eventType: string, + intersectionId: number, + roadRegulatorId: number, + startTime?: Date, + endTime?: Date + ): Promise { + const queryParams: Record = {} + queryParams['intersection_id'] = intersectionId.toString() + queryParams['road_regulator_id'] = roadRegulatorId.toString() + queryParams['latest'] = 'true' + if (startTime) queryParams['start_time_utc_millis'] = startTime.getTime().toString() + if (endTime) queryParams['end_time_utc_millis'] = endTime.getTime().toString() + + var response = + (await authApiHelper.invokeApi({ + path: `/assessments/${eventType}`, + token: token, + queryParams, + failureMessage: `Failed to retrieve assessments of type ${eventType}`, + })) ?? [] + return response.pop() + } + + async getAssessments( + token: string, + eventType: string, + intersectionId: number, + roadRegulatorId: number, + startTime?: Date, + endTime?: Date + ): Promise { + const queryParams: Record = {} + // queryParams["road_regulator_id"] = road_regulator_id; + queryParams['intersection_id'] = intersectionId.toString() + queryParams['road_regulator_id'] = roadRegulatorId.toString() + queryParams['latest'] = 'false' + if (startTime) queryParams['start_time_utc_millis'] = startTime.getTime().toString() + if (endTime) queryParams['end_time_utc_millis'] = endTime.getTime().toString() + + return ( + (await authApiHelper.invokeApi({ + path: `/assessments/${eventType}`, + token: token, + queryParams, + failureMessage: `Failed to retrieve assessments of type ${eventType}`, + })) ?? [] + ) + } +} + +export default new AssessmentsApi() diff --git a/webapp/src/apis/configuration-param-api.ts b/webapp/src/apis/configuration-param-api.ts new file mode 100644 index 000000000..eb7ba7d1c --- /dev/null +++ b/webapp/src/apis/configuration-param-api.ts @@ -0,0 +1,234 @@ +import { authApiHelper } from './api-helper-cviz' + +// const CONFIGURATION_PARAMETERS = [ +// /config/intersection/signal_state/red_light_running_minimum_speed +// /config/intersection/signal_state/minimum_red_light_percentage_threshold +// /config/intersection/signal_state/minimum_number_of_events +// /config/intersection/signal_state/maximum_distance_from_stopbar +// /config/intersection/signal_state/look_back_period +// /config/intersection/signal_state/heading_tolerance +// /config/intersection/lane_direction_of_travel/minimum_speed_threshold +// /config/intersection/lane_direction_of_travel/minimum_number_of_points +// /config/intersection/lane_direction_of_travel/minimum_number_of_events +// /config/intersection/lane_direction_of_travel/look_back_period +// /config/intersection/lane_direction_of_travel/heading_tolerance +// /config/intersection/general/v2x_message_processing_frequency +// /config/intersection/general/spat_minimum_10_second_reception +// /config/intersection/general/spat_maximum_10_second_reception +// /config/intersection/general/message_storage_period +// /config/intersection/general/map_minimum_10_second_reception +// /config/intersection/general/map_maximum_10_second_reception +// /config/intersection/connection_of_travel/minimum_number_of_events +// /config/intersection/connection_of_travel/look_back_period +// ] + +class ConfigParamsApi { + async getGeneralParameters(token: string): Promise { + // return ConfigParamsGeneral; + try { + var response = await authApiHelper.invokeApi({ + path: '/config/default/all', + token: token, + failureMessage: 'Failed to retrieve general parameters', + }) + return response ?? ([] as Config[]) + } catch (exception_var) { + console.error(exception_var) + return [] + } + } + + async getIntersectionParameters( + token: string, + intersectionId: number, + roadRegulatorId: number + ): Promise { + // return configParamsIntersection; + try { + var response = await authApiHelper.invokeApi({ + path: '/config/intersection/unique', + token: token, + queryParams: { intersection_id: intersectionId.toString(), road_regulator_id: roadRegulatorId.toString() }, + failureMessage: 'Failed to retrieve unique intersection parameters', + }) + return response ?? ([] as IntersectionConfig[]) + } catch (exception_var) { + console.error(exception_var) + return [] + } + } + + async getAllParameters(token: string, intersectionId: number, roadRegulatorId: number): Promise { + try { + var response = await authApiHelper.invokeApi({ + path: '/config/intersection/unique', + token: token, + queryParams: { intersection_id: intersectionId.toString(), road_regulator_id: roadRegulatorId.toString() }, + failureMessage: 'Failed to retrieve unique intersection parameters', + }) + return response ?? ([] as IntersectionConfig[]) + } catch (exception_var) { + console.error(exception_var) + return [] + } + } + + async getParameterGeneral(token: string, key: string): Promise { + try { + var response = ( + await authApiHelper.invokeApi({ + path: `/config/default/all`, + token: token, + failureMessage: `Failed to Retrieve Configuration Parameter ${key}`, + }) + ) + .filter((c) => c.key === key) + .at(-1) + return response as Config + } catch (exception_var) { + console.error(exception_var) + return undefined + } + } + + async getParameterIntersection( + token: string, + key: string, + intersectionId: number, + roadRegulatorId: number + ): Promise { + try { + var response = ( + await authApiHelper.invokeApi({ + path: `/config/intersection/all`, + token: token, + queryParams: { intersection_id: intersectionId.toString(), road_regulator_id: roadRegulatorId.toString() }, + toastOnFailure: false, + // failureMessage: `Failed to Retrieve Configuration Parameter ${key}`, + }) + ) + .filter((c) => c.key === key && c.intersectionID !== null && c.intersectionID !== 0 && c.intersectionID !== -1) + .at(-1) + return response as IntersectionConfig + } catch (exception_var) { + console.error(exception_var) + return undefined + } + } + + async getParameter( + token: string, + key: string, + intersectionId: number, + roadRegulatorId: number + ): Promise { + // try to get intersection parameter first, if not found, get general parameter + var param: Config | undefined = undefined + if (intersectionId !== -1) { + var param: Config | undefined = await this.getParameterIntersection(token, key, intersectionId, roadRegulatorId) + } + if (param == undefined) { + param = await this.getParameterGeneral(token, key) + } + return param + } + + async updateDefaultParameter(token: string, name: string, param: Config): Promise { + try { + var response = await authApiHelper.invokeApi({ + path: '/config/default/', + token: token, + method: 'POST', + headers: { 'Content-Type': 'application/json' }, + body: param, + toastOnSuccess: true, + successMessage: `Successfully Update Configuration Parameter ${name}`, + failureMessage: `Failed to Update Configuration Parameter ${name}`, + }) + return response as Config + } catch (exception_var) { + console.error(exception_var) + return undefined + } + } + + async updateIntersectionParameter( + token: string, + name: string, + param: IntersectionConfig + ): Promise { + try { + var response = await authApiHelper.invokeApi({ + path: '/config/intersection/', + token: token, + method: 'POST', + headers: { 'Content-Type': 'application/json' }, + body: param, + toastOnSuccess: true, + successMessage: `Successfully Update Intersection Configuration Parameter ${name}`, + failureMessage: `Failed to Update Intersection Configuration Parameter ${name}`, + }) + return response as IntersectionConfig + } catch (exception_var) { + console.error(exception_var) + return undefined + } + } + + async createIntersectionParameter( + token: string, + name: string, + value: Config, + intersectionId: number, + roadRegulatorId: number + ): Promise { + const param: IntersectionConfig = { + intersectionID: intersectionId, + roadRegulatorID: roadRegulatorId, + rsuID: 'rsu_1', + ...value, + } + + try { + var response = await authApiHelper.invokeApi({ + path: '/config/intersection/create/' + name, + token: token, + method: 'POST', + headers: { 'Content-Type': 'application/json' }, + body: param, + toastOnSuccess: true, + successMessage: `Successfully Created Intersection Configuration Parameter ${name}`, + failureMessage: `Failed to Create Intersection Configuration Parameter ${name}`, + }) + return response as Config + } catch (exception_var) { + console.error(exception_var) + return undefined + } + } + + async removeOverriddenParameter( + token: string, + name: string, + config: IntersectionConfig + ): Promise { + try { + var response = await authApiHelper.invokeApi({ + path: '/config/intersection/', + token: token, + method: 'DELETE', + headers: { 'Content-Type': 'application/json' }, + body: config, + toastOnSuccess: true, + successMessage: `Successfully Removed Intersection Configuration Parameter ${name}`, + failureMessage: `Failed to Remove Intersection Configuration Parameter ${name}`, + }) + return response as Config + } catch (exception_var) { + console.error(exception_var) + return undefined + } + } +} + +export const configParamApi = new ConfigParamsApi() diff --git a/webapp/src/apis/events-api.ts b/webapp/src/apis/events-api.ts new file mode 100644 index 000000000..f9848c583 --- /dev/null +++ b/webapp/src/apis/events-api.ts @@ -0,0 +1,83 @@ +import { number } from 'prop-types' +import { authApiHelper } from './api-helper-cviz' + +interface Item { + label: string + value: string +} + +const EVENT_TYPES: Item[] = [ + { label: 'ConnectionOfTravelEvent', value: 'connection_of_travel' }, + { label: 'IntersectionReferenceAlignmentEvent', value: 'intersection_reference_alignment' }, + { label: 'LaneDirectionOfTravelEvent', value: 'lane_direction_of_travel' }, + // { label: "ProcessingTimePeriod", value: "processing_time_period" }, + { label: 'SignalGroupAlignmentEvent', value: 'signal_group_alignment' }, + { label: 'SignalStateConflictEvent', value: 'signal_state_conflict' }, + { label: 'SignalStateEvent', value: 'signal_state' }, + { label: 'SignalStateStopEvent', value: 'signal_state_stop' }, + { label: 'TimeChangeDetailsEvent', value: 'time_change_details' }, + { label: 'MapMinimumDataEvent', value: 'map_minimum_data' }, + { label: 'SpatMinimumDataEvent', value: 'spat_minimum_data' }, + { label: 'MapBroadcastRateEvent', value: 'map_broadcast_rate' }, + { label: 'SpatBroadcastRateEvent', value: 'spat_broadcast_rate' }, +] + +class EventsApi { + async getEvents( + token: string, + eventType: string, + intersectionId: number, + roadRegulatorId: number, + startTime: Date, + endTime: Date, + { latest = false }: { latest?: boolean } = {} + ): Promise { + const queryParams = { + intersection_id: intersectionId.toString(), + road_regulator_id: roadRegulatorId.toString(), + start_time_utc_millis: startTime.getTime().toString(), + end_time_utc_millis: endTime.getTime().toString(), + latest: latest.toString(), + } + // if (roadRegulatorId) queryParams["road_regulator_id"] = roadRegulatorId; + + const response = await authApiHelper.invokeApi({ + path: `/events/${eventType}`, + token: token, + queryParams: queryParams, + failureMessage: `Failed to retrieve events of type ${eventType}`, + }) + return response ?? ([] as MessageMonitor.Event[]) + } + + async getAllEvents( + token: string, + intersectionId: number, + roadRegulatorId: number, + startTime: Date, + endTime: Date + ): Promise { + const queryParams = { + intersection_id: intersectionId.toString(), + road_regulator_id: roadRegulatorId.toString(), + start_time_utc_millis: startTime.getTime().toString(), + end_time_utc_millis: endTime.getTime().toString(), + } + // if (roadRegulatorId) queryParams["road_regulator_id"] = roadRegulatorId; + + const events: MessageMonitor.Event[] = [] + for (const eventTypeObj of EVENT_TYPES) { + const response: MessageMonitor.Event[] = + (await authApiHelper.invokeApi({ + path: `/events/${eventTypeObj.value}`, + token: token, + queryParams: queryParams, + failureMessage: `Failed to retrieve events of type ${eventTypeObj.value}`, + })) ?? [] + events.push(...response) + } + return events + } +} + +export default new EventsApi() diff --git a/webapp/src/apis/graphs-api.ts b/webapp/src/apis/graphs-api.ts new file mode 100644 index 000000000..0140ad764 --- /dev/null +++ b/webapp/src/apis/graphs-api.ts @@ -0,0 +1,132 @@ +import { breadcrumbsClasses } from '@mui/material' +import { authApiHelper } from './api-helper-cviz' + +class GraphsApi { + createGraphDataVal = (id: number, event_type: string, count: number): GraphArrayDataType => { + const val = { + id, + ConnectionOfTravelEventCount: 0, + IntersectionReferenceAlignmentEventCount: 0, + LaneDirectionOfTravelEventCount: 0, + ProcessingTimePeriodCount: 0, + SignalGroupAlignmentEventCount: 0, + SignalStateConflictEventCount: 0, + SignalStateEventCount: 0, + SignalStateStopEventCount: 0, + TimeChangeDetailsEventCount: 0, + MapMinimumDataEventCount: 0, + SpatMinimumDataEventCount: 0, + MapBroadcastRateEventCount: 0, + SpatBroadcastRateEventCount: 0, + } + switch (event_type) { + case 'connection_of_travel': + val.ConnectionOfTravelEventCount = count + break + case 'intersection_reference_alignment': + val.IntersectionReferenceAlignmentEventCount = count + break + case 'lane_direction_of_travel': + val.LaneDirectionOfTravelEventCount = count + break + case 'processing_time_period': + val.ProcessingTimePeriodCount = count + break + case 'signal_group_alignment': + val.SignalGroupAlignmentEventCount = count + break + case 'signal_state_conflict': + val.SignalStateConflictEventCount = count + break + case 'signal_state': + val.SignalStateEventCount = count + break + case 'signal_state_stop': + val.SignalStateStopEventCount = count + break + case 'time_change_details': + val.TimeChangeDetailsEventCount = count + break + case 'map_minimum_data': + val.MapMinimumDataEventCount = count + break + case 'spat_minimum_data': + val.SpatMinimumDataEventCount = count + break + case 'map_broadcast_rate': + val.MapBroadcastRateEventCount = count + break + case 'spat_broadcast_rate': + val.SpatBroadcastRateEventCount = count + break + } + return val + } + + combineGraphDataVals = (val1: GraphArrayDataType, val2: GraphArrayDataType): GraphArrayDataType => ({ + id: val1.id, + ConnectionOfTravelEventCount: val1.ConnectionOfTravelEventCount + val2.ConnectionOfTravelEventCount, + IntersectionReferenceAlignmentEventCount: + val1.IntersectionReferenceAlignmentEventCount + val2.IntersectionReferenceAlignmentEventCount, + LaneDirectionOfTravelEventCount: val1.LaneDirectionOfTravelEventCount + val2.LaneDirectionOfTravelEventCount, + ProcessingTimePeriodCount: val1.ProcessingTimePeriodCount + val2.ProcessingTimePeriodCount, + SignalGroupAlignmentEventCount: val1.SignalGroupAlignmentEventCount + val2.SignalGroupAlignmentEventCount, + SignalStateConflictEventCount: val1.SignalStateConflictEventCount + val2.SignalStateConflictEventCount, + SignalStateEventCount: val1.SignalStateEventCount + val2.SignalStateEventCount, + SignalStateStopEventCount: val1.SignalStateStopEventCount + val2.SignalStateStopEventCount, + TimeChangeDetailsEventCount: val1.TimeChangeDetailsEventCount + val2.TimeChangeDetailsEventCount, + MapMinimumDataEventCount: val1.MapMinimumDataEventCount + val2.MapMinimumDataEventCount, + SpatMinimumDataEventCount: val1.SpatMinimumDataEventCount + val2.SpatMinimumDataEventCount, + MapBroadcastRateEventCount: val1.MapBroadcastRateEventCount + val2.MapBroadcastRateEventCount, + SpatBroadcastRateEventCount: val1.SpatBroadcastRateEventCount + val2.SpatBroadcastRateEventCount, + }) + + async getGraphData({ + token, + intersectionId, + roadRegulatorId, + event_types, + startTime, + endTime, + }: { + token: string + intersectionId: number + roadRegulatorId: number + event_types: string[] + startTime: Date + endTime: Date + }): Promise> { + const queryParams: Record = {} + queryParams['intersection_id'] = intersectionId.toString() + queryParams['road_regulator_id'] = roadRegulatorId.toString() + queryParams['start_time_utc_millis'] = startTime.getTime().toString() + queryParams['end_time_utc_millis'] = endTime.getTime().toString() + + const results: { [id: string]: GraphArrayDataType } = {} + + for (const event_type of event_types) { + try { + const graphData: Array<{ id: string; count: number }> = await authApiHelper.invokeApi({ + path: `/events/${event_type}/daily_counts`, + token: token, + queryParams, + failureMessage: 'Failed to generate graph data', + }) + graphData.forEach((data) => { + const val = this.createGraphDataVal(new Date(data.id).getTime(), event_type, data.count) + if (results[data.id] === undefined) { + results[data.id] = val + } else { + results[data.id] = this.combineGraphDataVals(results[data.id], val) + } + }) + } catch (e) { + console.error('Failed to generate graph data for event type ' + event_type, e) + } + } + + return Object.values(results) + } +} + +export default new GraphsApi() diff --git a/webapp/src/apis/keycloak-api.ts b/webapp/src/apis/keycloak-api.ts new file mode 100644 index 000000000..b3b694e95 --- /dev/null +++ b/webapp/src/apis/keycloak-api.ts @@ -0,0 +1,198 @@ +import { authApiHelper } from './api-helper-cviz' + +const KEYCLOAK_ADMIN_ENDPOINT = `${process.env.CVIZ_AUTH_SERVER_URL}/admin/realms/${process.env.CVIZ_KEYCLOAK_REALM}` +const KEYCLOAK_AUTH_ENDPOINT = `${process.env.CVIZ_AUTH_SERVER_URL}/realms/${process.env.CVIZ_KEYCLOAK_REALM}` + +class KeycloakApi { + getEmailPreferences(attributes: Record): EmailPreferences { + return { + receiveAnnouncements: attributes?.['receiveAnnouncements']?.[0] === 'true', + notificationFrequency: (attributes?.['notificationFrequency']?.[0] ?? 'NEVER') as EmailFrequency, + receiveCeaseBroadcastRecommendations: attributes?.['receiveCeaseBroadcastRecommendations']?.[0] === 'true', + receiveCriticalErrorMessages: attributes?.['receiveCriticalErrorMessages']?.[0] === 'true', + receiveNewUserRequests: attributes?.['receiveNewUserRequests']?.[0] === 'true', + } + } + + async getGroups({ token }: { token: string }): Promise { + return ( + (await authApiHelper.invokeApi({ + path: `/groups`, + basePath: KEYCLOAK_ADMIN_ENDPOINT, + token: token, + failureMessage: 'Failed to get Keycloak groups', + })) ?? [] + ).map((kRole: any) => { + return { + id: kRole.id, + name: kRole.name, + } + }) + } + + async getUserRoles({ token, id }: { token: string; id: string }): Promise { + return ( + (await authApiHelper.invokeApi({ + path: `/users/${id}/groups`, + basePath: KEYCLOAK_ADMIN_ENDPOINT, + token: token, + failureMessage: 'Failed to get user roles', + })) ?? [] + ) + .map((role: any) => role.name) + .filter((role: string) => ['ADMIN', 'USER'].includes(role)) + .pop() + } + + async getUsersList({ token }: { token: string }): Promise { + const users = + (await authApiHelper.invokeApi({ + path: `/users`, + basePath: KEYCLOAK_ADMIN_ENDPOINT, + token: token, + failureMessage: 'Failed to get users. If this failure repeats, please log out and log back in.', + })) ?? [] + return await Promise.all( + users.map(async (kUser: any) => { + const user: User = { + id: kUser.id, + email: kUser.username, + first_name: kUser.firstName, + last_name: kUser.lastName, + role: (await this.getUserRoles({ token, id: kUser.id })) ?? 'USER', + email_preference: this.getEmailPreferences(kUser.attributes), + } + return user + }) + ) + } + + async getUserInfo({ token, id }: { token: string; id: string }): Promise { + const kUser = await authApiHelper.invokeApi({ + path: `/users/${id}`, + basePath: KEYCLOAK_ADMIN_ENDPOINT, + token: token, + failureMessage: 'Failed to get user info', + }) + if (kUser) { + return { + id: kUser.id, + email: kUser.username, + first_name: kUser.firstName, + last_name: kUser.lastName, + role: (await this.getUserRoles({ token, id: kUser.id })) ?? 'USER', + email_preference: kUser.attributes?.EMAIL_FREQUENCY?.[0] ?? 'NEVER', + } + } else { + return undefined + } + } + + async removeUser({ token, id }: { token: string; id: string }): Promise { + return (await authApiHelper.invokeApi({ + path: `/users/${id}`, + basePath: KEYCLOAK_ADMIN_ENDPOINT, + method: 'DELETE', + token: token, + booleanResponse: true, + failureMessage: 'Failed to get user info', + })) as boolean + } + + async addUserToGroup({ token, id, role }: { token: string; id: string; role: UserRole }): Promise { + const groupId: string | undefined = (await this.getGroups({ token })).find((r) => r.name == role)?.id + return (await authApiHelper.invokeApi({ + path: `/users/${id}/groups/${groupId}`, + basePath: KEYCLOAK_ADMIN_ENDPOINT, + method: 'PUT', + token: token, + headers: { + 'Content-Type': 'application/json', + }, + booleanResponse: true, + failureMessage: `Failed to add user to ${role} group`, + })) as boolean + } + + async removeUserFromGroup({ token, id, role }: { token: string; id: string; role: UserRole }): Promise { + const groupId: string | undefined = (await this.getGroups({ token })).find((r) => r.name == role)?.id + return (await authApiHelper.invokeApi({ + path: `/users/${id}/groups/${groupId}`, + basePath: KEYCLOAK_ADMIN_ENDPOINT, + method: 'DELETE', + token: token, + headers: { + 'Content-Type': 'application/json', + }, + booleanResponse: true, + failureMessage: `Failed to remove user from ${role} group`, + })) as boolean + } + + async updateUserInfo({ + token, + id, + email, + first_name, + last_name, + }: { + token: string + id: string + email?: string + first_name?: string + last_name?: string + }): Promise { + const updatedParams = {} + if (email) { + updatedParams['username'] = email + updatedParams['email'] = email + updatedParams['emailVerified'] = false + } + if (first_name) { + updatedParams['firstName'] = first_name + } + if (last_name) { + updatedParams['lastName'] = last_name + } + + return (await authApiHelper.invokeApi({ + path: `/users/${id}`, + basePath: KEYCLOAK_ADMIN_ENDPOINT, + method: 'PUT', + token: token, + body: updatedParams, + headers: { + 'Content-Type': 'application/json', + }, + booleanResponse: true, + failureMessage: 'Failed to update user info', + })) as boolean + } + + async logout({ token, refresh_token }: { token?: string; refresh_token: string }): Promise { + return (await authApiHelper.invokeApi({ + path: `/protocol/openid-connect/logout`, + basePath: KEYCLOAK_AUTH_ENDPOINT, + method: 'POST', + token: token, + body: `client_id=conflictvisualizer-gui&client_secret=O2HDhrF14XJE9FhTTYX6iEdgBdSy9E6d&refresh_token=${refresh_token}`, + headers: { + 'Content-Type': 'application/x-www-form-urlencoded', + }, + booleanResponse: true, + failureMessage: 'Failed to logout user', + })) as boolean + } + + async validateToken({ token }: { token?: string }): Promise { + return (await authApiHelper.invokeApi({ + path: `/protocol/openid-connect/userinfo`, + basePath: KEYCLOAK_AUTH_ENDPOINT, + method: 'GET', + token: token, + booleanResponse: true, + })) as boolean + } +} + +export default new KeycloakApi() diff --git a/webapp/src/apis/mm-api.ts b/webapp/src/apis/mm-api.ts new file mode 100644 index 000000000..7ccfe717c --- /dev/null +++ b/webapp/src/apis/mm-api.ts @@ -0,0 +1,107 @@ +import { authApiHelper } from './api-helper-cviz' + +class MessageMonitorApi { + async getIntersections({ token }): Promise { + var response = await authApiHelper.invokeApi({ + path: '/intersection/list', + token: token, + failureMessage: 'Failed to retrieve intersection list', + }) + return response ?? [] + } + + async getSpatMessages({ + token, + intersectionId, + roadRegulatorId, + startTime, + endTime, + }: { + token: string + intersectionId: number + roadRegulatorId: number + startTime?: Date + endTime?: Date + }): Promise { + const queryParams: Record = {} + queryParams['intersection_id'] = intersectionId.toString() + queryParams['road_regulator_id'] = roadRegulatorId.toString() + if (startTime) queryParams['start_time_utc_millis'] = startTime.getTime().toString() + if (endTime) queryParams['end_time_utc_millis'] = endTime.getTime().toString() + + var response = await authApiHelper.invokeApi({ + path: '/spat/json', + token: token, + queryParams, + failureMessage: 'Failed to retrieve SPAT messages', + }) + return response ?? ([] as ProcessedSpat[]) + } + + async getMapMessages({ + token, + intersectionId, + roadRegulatorId, + startTime, + endTime, + latest, + }: { + token: string + intersectionId: number + roadRegulatorId: number + startTime?: Date + endTime?: Date + latest?: boolean + }): Promise { + const queryParams: Record = {} + queryParams['intersection_id'] = intersectionId.toString() + queryParams['road_regulator_id'] = roadRegulatorId.toString() + if (startTime) queryParams['start_time_utc_millis'] = startTime.getTime().toString() + if (endTime) queryParams['end_time_utc_millis'] = endTime.getTime().toString() + if (latest !== undefined) queryParams['latest'] = latest.toString() + + var response = await authApiHelper.invokeApi({ + path: '/map/json', + token: token, + queryParams, + failureMessage: 'Failed to retrieve MAP messages', + }) + return response ?? ([] as ProcessedMap[]) + } + + async getBsmMessages({ + token, + vehicleId, + startTime, + endTime, + long, + lat, + distance, + }: { + token: string + vehicleId?: string + startTime?: Date + endTime?: Date + long?: number + lat?: number + distance?: number + }): Promise { + const queryParams: Record = {} + if (vehicleId) queryParams['origin_ip'] = vehicleId + if (startTime) queryParams['start_time_utc_millis'] = startTime.getTime().toString() + if (endTime) queryParams['end_time_utc_millis'] = endTime.getTime().toString() + if (long) queryParams['longitude'] = long.toString() + if (lat) queryParams['latitude'] = lat.toString() + if (distance) queryParams['distance'] = distance.toString() + + var response = await authApiHelper.invokeApi({ + path: '/bsm/json', + token: token, + queryParams, + failureMessage: 'Failed to retrieve BSM messages', + }) + return response ?? ([] as OdeBsmData[]) + } +} + +export default new MessageMonitorApi() diff --git a/webapp/src/apis/notification-api.ts b/webapp/src/apis/notification-api.ts new file mode 100644 index 000000000..cf778a8c7 --- /dev/null +++ b/webapp/src/apis/notification-api.ts @@ -0,0 +1,103 @@ +import toast from 'react-hot-toast' +import { authApiHelper } from './api-helper-cviz' + +const NOTIFICATION_TYPES: string[] = [ + 'connection_of_travel', + 'intersection_reference_alignment', + 'lane_direction_of_travel', + 'signal_state_conflict_notification', + 'signal_group_alignment_notification', + 'map_broadcast_rate_notification', + 'spat_broadcast_rate_notification', +] + +class NotificationApi { + async getActiveNotifications({ + token, + intersectionId, + roadRegulatorId, + startTime, + endTime, + key, + }: { + token: string + intersectionId: number + roadRegulatorId: number + startTime?: Date + endTime?: Date + key?: string + }): Promise { + const queryParams: Record = {} + queryParams['intersection_id'] = intersectionId.toString() + queryParams['road_regulator_id'] = roadRegulatorId.toString() + if (startTime) queryParams['start_time_utc_millis'] = startTime.getTime().toString() + if (endTime) queryParams['end_time_utc_millis'] = endTime.getTime().toString() + if (key) queryParams['key'] = key + + const notifications = await authApiHelper.invokeApi({ + path: `/notifications/active`, + token: token, + queryParams, + failureMessage: 'Failed to retrieve active notifications', + }) + + return notifications ?? [] + } + + async dismissNotifications({ token, ids }: { token: string; ids: string[] }): Promise { + let success = true + for (const id of ids) { + success = + success && + (await authApiHelper.invokeApi({ + path: `/notifications/active`, + method: 'DELETE', + token: token, + body: id.toString(), + booleanResponse: true, + })) + } + if (success) { + toast.success(`Successfully Dismissed ${ids.length} Notifications`) + } else { + toast.error(`Failed to Dismiss some Notifications`) + } + return true + } + + async getAllNotifications({ + token, + intersectionId, + roadRegulatorId, + startTime, + endTime, + }: { + token: string + intersectionId: number + roadRegulatorId: number + startTime?: Date + endTime?: Date + }): Promise { + const queryParams: Record = {} + queryParams['intersection_id'] = intersectionId.toString() + queryParams['road_regulator_id'] = roadRegulatorId.toString() + if (startTime) queryParams['start_time_utc_millis'] = startTime.getTime().toString() + if (endTime) queryParams['end_time_utc_millis'] = endTime.getTime().toString() + + const notifications: MessageMonitor.Notification[] = [] + for (const notificationType of NOTIFICATION_TYPES) { + const resp: MessageMonitor.Notification[] = + (await authApiHelper.invokeApi({ + path: `/notifications/${notificationType}`, + token: token, + queryParams, + failureMessage: `Failed to retrieve notifications of type ${notificationType}`, + })) ?? [] + notifications.push(...resp) + } + + return notifications + } +} + +export default new NotificationApi() diff --git a/webapp/src/apis/reports-api.ts b/webapp/src/apis/reports-api.ts new file mode 100644 index 000000000..6f4396ca1 --- /dev/null +++ b/webapp/src/apis/reports-api.ts @@ -0,0 +1,90 @@ +import { authApiHelper } from './api-helper-cviz' + +export type ReportMetadata = { + reportName: string + intersectionID: number + roadRegulatorID: string + reportGeneratedAt: Date + reportStartTime: Date + reportStopTime: Date + reportContents: string[] +} + +class ReportsApi { + async generateReport({ + token, + intersectionId, + roadRegulatorId, + startTime, + endTime, + }: { + token: string + intersectionId: number + roadRegulatorId: number + startTime: Date + endTime: Date + }): Promise { + const queryParams: Record = {} + queryParams['intersection_id'] = intersectionId.toString() + queryParams['road_regulator_id'] = roadRegulatorId.toString() + if (startTime) queryParams['start_time_utc_millis'] = startTime.getTime().toString() + if (endTime) queryParams['end_time_utc_millis'] = endTime.getTime().toString() + + const pdfReport = await authApiHelper.invokeApi({ + path: `/reports/generate`, + token: token, + responseType: 'blob', + queryParams, + failureMessage: 'Failed to generate PDF report', + }) + + return pdfReport + } + + async listReports({ + token, + intersectionId, + roadRegulatorId, + startTime, + endTime, + }: { + token: string + intersectionId: number + roadRegulatorId: number + startTime: Date + endTime: Date + }): Promise { + const queryParams: Record = {} + queryParams['intersection_id'] = intersectionId.toString() + queryParams['road_regulator_id'] = roadRegulatorId.toString() + queryParams['start_time_utc_millis'] = startTime.getTime().toString() + queryParams['end_time_utc_millis'] = endTime.getTime().toString() + queryParams['latest'] = 'false' + + const pdfReport = await authApiHelper.invokeApi({ + path: `/reports/list`, + token: token, + queryParams, + failureMessage: 'Failed to list PDF reports', + }) + + return pdfReport + } + + async downloadReport({ token, reportName }: { token: string; reportName: string }): Promise { + const queryParams: Record = {} + queryParams['report_name'] = reportName + + const pdfReport = await authApiHelper.invokeApi({ + path: `/reports/download`, + token: token, + responseType: 'blob', + queryParams, + failureMessage: `Failed to download PDF report ${reportName}`, + }) + + return pdfReport + } +} + +export default new ReportsApi() diff --git a/webapp/src/apis/rsu-api.ts b/webapp/src/apis/rsu-api.ts index 81d7f28ab..373b8b1de 100644 --- a/webapp/src/apis/rsu-api.ts +++ b/webapp/src/apis/rsu-api.ts @@ -1,5 +1,5 @@ import EnvironmentVars from '../EnvironmentVars' -import { WZDxWorkZoneFeed } from '../types/wzdx/WzdxWorkZoneFeed42' +import { WZDxWorkZoneFeed } from '../models/wzdx/WzdxWorkZoneFeed42' import apiHelper from './api-helper' import { ApiMsgRespWithCodes, diff --git a/webapp/src/apis/user-management-api.ts b/webapp/src/apis/user-management-api.ts new file mode 100644 index 000000000..51e264311 --- /dev/null +++ b/webapp/src/apis/user-management-api.ts @@ -0,0 +1,131 @@ +import { User } from '../icons/user' +import { authApiHelper } from './api-helper-cviz' + +class UserManagementApi { + async createUserCreationRequest({ + email, + first_name, + last_name, + role, + }: { + email: string + first_name: string + last_name: string + role: UserRole + }): Promise { + return (await authApiHelper.invokeApi({ + path: `/users/create_user_creation_request`, + method: 'POST', + body: { + email: email, + firstName: first_name, + lastName: last_name, + role: role, + }, + booleanResponse: true, + toastOnSuccess: true, + successMessage: 'User created successfully', + failureMessage: 'Failed to create user creation request', + })) as boolean + } + + async getUserCreationRequests({ token }: { token: string }): Promise { + const creationRequests: User[] = ( + (await authApiHelper.invokeApi({ + path: `/users/find_user_creation_request`, + token: token, + method: 'GET', + failureMessage: 'Failed to get user creation requests', + })) ?? [] + ).map((user: any) => ({ + ...user, + first_name: user.firstName, + last_name: user.lastName, + role: user.role.toLowerCase(), + })) + + return creationRequests + } + + async createUser({ + token, + email, + first_name, + last_name, + role, + }: { + token: string + email: string + first_name: string + last_name: string + role: UserRole + }): Promise { + return (await authApiHelper.invokeApi({ + path: `/users/accept_user_creation_request`, + token: token, + method: 'POST', + body: { + email: email, + firstName: first_name, + lastName: last_name, + role: role, + id: '', + }, + booleanResponse: true, + toastOnSuccess: true, + successMessage: 'User created successfully', + failureMessage: 'Failed to create new user', + })) as boolean + } + + async removeUserCreationRequest({ token, email }: { token: string; email: string }): Promise { + return (await authApiHelper.invokeApi({ + path: `/users/delete_user_creation_request`, + token: token, + method: 'DELETE', + queryParams: { email }, + booleanResponse: true, + failureMessage: 'Failed to remove user creation request from database', + })) as boolean + } + + async updateUserEmailPreference({ + token, + email, + preferences, + }: { + token: string + email: string + preferences: EmailPreferences + }): Promise { + return (await authApiHelper.invokeApi({ + path: `/users/update_user_email_preference`, + token: token, + method: 'POST', + body: preferences, + booleanResponse: true, + toastOnSuccess: true, + successMessage: 'Email preferences updated successfully', + failureMessage: 'Failed to update email preferences', + })) as boolean + } + + async getUserEmailPreference({ token }: { token: string }): Promise { + return ( + (await authApiHelper.invokeApi({ + path: `/users/get_user_email_preference`, + method: 'POST', + token: token, + toastOnFailure: false, + })) ?? { + receiveAnnouncements: true, + notificationFrequency: 'ONCE_PER_DAY', + receiveCeaseBroadcastRecommendations: true, + receiveCriticalErrorMessages: true, + receiveNewUserRequests: false, + } + ) + } +} + +export default new UserManagementApi() diff --git a/webapp/src/components/SsmSrmItem.tsx b/webapp/src/components/SsmSrmItem.tsx index 327b735b9..ea86d2d20 100644 --- a/webapp/src/components/SsmSrmItem.tsx +++ b/webapp/src/components/SsmSrmItem.tsx @@ -1,5 +1,5 @@ import React, { useEffect } from 'react' -import { SelectedSrm } from '../types/Srm' +import { SelectedSrm } from '../models/Srm' import './css/SsmSrmItem.css' diff --git a/webapp/src/components/map/control-panel.tsx b/webapp/src/components/map/control-panel.tsx new file mode 100644 index 000000000..7adbd5343 --- /dev/null +++ b/webapp/src/components/map/control-panel.tsx @@ -0,0 +1,582 @@ +import React, { useState, useEffect, ChangeEvent } from 'react' +import Slider from '@mui/material/Slider' +import dayjs from 'dayjs' +import { LocalizationProvider } from '@mui/x-date-pickers/LocalizationProvider' +import { AdapterDayjs } from '@mui/x-date-pickers/AdapterDayjs' +import { DateTimePicker } from '@mui/x-date-pickers/DateTimePicker' +import { Box, Typography, TextField, Button, Checkbox, InputAdornment, Chip } from '@mui/material' +import MuiAccordion, { AccordionProps } from '@mui/material/Accordion' +import MuiAccordionSummary, { AccordionSummaryProps } from '@mui/material/AccordionSummary' +import MuiAccordionDetails from '@mui/material/AccordionDetails' +import ArrowForwardIosSharpIcon from '@mui/icons-material/ArrowForwardIosSharp' +import { styled, SxProps, Theme } from '@mui/material/styles' +import { format } from 'date-fns' +import JSZip from 'jszip' +import { AnyAction, ThunkDispatch } from '@reduxjs/toolkit' +import { RootState } from '../../store' +import { useDispatch, useSelector } from 'react-redux' +import { + downloadMapData, + handleImportedMapMessageData, + onTimeQueryChanged, + selectBsmTrailLength, + selectSliderTimeValue, + setBsmTrailLength, + setLaneLabelsVisible, + setShowPopupOnHover, + setSigGroupLabelsVisible, + setSliderValue, + toggleLiveDataActive, +} from './map-slice' +import { + selectAllInteractiveLayerIds, + selectBsmData, + selectConnectingLanes, + selectCurrentBsmData, + selectCurrentBsms, + selectCurrentMapData, + selectCurrentSignalGroups, + selectCurrentSpatData, + selectCursor, + selectFilteredSurroundingEvents, + selectFilteredSurroundingNotifications, + selectHoveredFeature, + selectImportedMessageData, + selectIntersectionId, + selectLaneLabelsVisible, + selectLayersVisible, + selectLiveDataActive, + selectLoadInitialDataTimeoutId, + selectLoadOnNull, + selectMapData, + selectMapSignalGroups, + selectMapSpatTimes, + selectQueryParams, + selectRawData, + selectRenderTimeInterval, + selectRoadRegulatorId, + selectSelectedFeature, + selectShowPopupOnHover, + selectSigGroupLabelsVisible, + selectSignalStateData, + selectSliderValue, + selectSourceData, + selectSourceDataType, + selectSpatSignalGroups, + selectSurroundingNotifications, + selectTimeWindowSeconds, + selectViewState, +} from './map-slice' +import { selectToken } from '../../generalSlices/userSlice' +import { selectSignalStateLayerStyle, setSignalLayerLayout } from './map-layer-style-slice' +import { getTimeRange } from './utilities/map-utils' + +const Accordion = styled((props: AccordionProps) => )( + ({ theme }) => ({ + //border: `1px solid ${theme.palette.divider}`, + // '&:not(:last-child)': { + // borderBottom: 0, + // }, + // '&:before': { + // display: 'none', + // }, + }) +) + +const AccordionSummary = styled((props: AccordionSummaryProps) => ( + } {...props} /> +))(({ theme }) => ({ + minHeight: 0, + paddingLeft: 10, + flexDirection: 'row-reverse', + '& .MuiAccordionSummary-expandIconWrapper.Mui-expanded': { + transform: 'rotate(90deg)', + }, + '& .MuiAccordionSummary-content': { + marginLeft: theme.spacing(1), + marginTop: 0, + marginBottom: 0, + }, +})) + +const AccordionDetails = styled(MuiAccordionDetails)(({ theme }) => ({})) + +interface ControlPanelProps { + sx: SxProps | undefined + timeQueryParams: { + startDate: Date + endDate: Date + eventDate: Date + timeWindowSeconds: number + } + onTimeQueryChanged: (eventTime?: Date, timeBefore?: number, timeAfter?: number, timeWindowSeconds?: number) => void + handleImportedMessageData: (messageData: any) => void + sliderValue: number + setSlider: (event: Event, value: number | number[], activeThumb: number) => void + max: number + sliderTimeValue: { + start: Date + end: Date + } + mapSpatTimes: { + mapTime: number + spatTime: number + } + downloadAllData: () => void + signalStateLayer: any + setSignalStateLayer: React.Dispatch> + laneLabelsVisible: boolean + setLaneLabelsVisible: React.Dispatch> + sigGroupLabelsVisible: boolean + setSigGroupLabelsVisible: React.Dispatch> + showPopupOnHover: boolean + setShowPopupOnHover: React.Dispatch> + liveDataActive: boolean + setLiveDataActive: React.Dispatch> + bsmTrailLength: number + setBsmTrailLength: React.Dispatch> +} + +function ControlPanel() { + const dispatch: ThunkDispatch = useDispatch() + + const signalStateLayerStyle = useSelector(selectSignalStateLayerStyle) + + const queryParams = useSelector(selectQueryParams) + const timeWindowSeconds = useSelector(selectTimeWindowSeconds) + const sliderValue = useSelector(selectSliderValue) + const mapSpatTimes = useSelector(selectMapSpatTimes) + const sigGroupLabelsVisible = useSelector(selectSigGroupLabelsVisible) + const laneLabelsVisible = useSelector(selectLaneLabelsVisible) + const showPopupOnHover = useSelector(selectShowPopupOnHover) + const liveDataActive = useSelector(selectLiveDataActive) + const sliderTimeValue = useSelector(selectSliderTimeValue) + const bsmTrailLength = useSelector(selectBsmTrailLength) + + const getQueryParams = ({ + startDate, + endDate, + eventDate, + timeWindowSeconds, + }: { + startDate: Date + endDate: Date + eventDate: Date + timeWindowSeconds: number + }) => { + return { + eventTime: eventDate, + timeBefore: Math.round((eventDate.getTime() - startDate.getTime()) / 1000), + timeAfter: Math.round((endDate.getTime() - eventDate.getTime()) / 1000), + timeWindowSeconds: timeWindowSeconds, + } + } + + const [shouldReRenderEventTime, setShouldReRenderEventTime] = useState(true) + const [shouldReRenderTimeBefore, setShouldReRenderTimeBefore] = useState(true) + const [shouldReRenderTimeAfter, setShouldReRenderTimeAfter] = useState(true) + const [shouldReRenderTimeWindowSeconds, setShouldReRenderTimeWindowSeconds] = useState(true) + const [shouldReRenderBsmTrail, setShouldReRenderBsmTrail] = useState(true) + const [bsmTrailLengthLocal, setBsmTrailLengthLocal] = useState(bsmTrailLength.toString()) + const [prevBsmTrailLength, setPrevBsmTrailLength] = useState(bsmTrailLength) + const [oldDateParams, setOldDateParams] = useState(getQueryParams({ ...queryParams, timeWindowSeconds })) + const [eventTime, setEventTime] = useState( + dayjs(getQueryParams({ ...queryParams, timeWindowSeconds }).eventTime.toString()) + ) + const [timeBefore, setTimeBefore] = useState( + getQueryParams({ ...queryParams, timeWindowSeconds }).timeBefore.toString() + ) + const [timeAfter, setTimeAfter] = useState( + getQueryParams({ ...queryParams, timeWindowSeconds }).timeAfter.toString() + ) + const [timeWindowSecondsLocal, setTimeWindowSeconds] = useState( + getQueryParams({ ...queryParams, timeWindowSeconds }).timeWindowSeconds.toString() + ) + + useEffect(() => { + const newDateParams = getQueryParams({ ...queryParams, timeWindowSeconds }) + if (newDateParams.eventTime.getTime() != oldDateParams.eventTime.getTime()) { + setShouldReRenderEventTime(false) + setEventTime(dayjs(newDateParams.eventTime)) + } + if (newDateParams.timeBefore != oldDateParams.timeBefore) { + setShouldReRenderTimeBefore(false) + setTimeBefore(newDateParams.timeBefore.toString()) + } + if (newDateParams.timeAfter != oldDateParams.timeAfter) { + setShouldReRenderTimeAfter(false) + setTimeAfter(newDateParams.timeAfter.toString()) + } + if (newDateParams.timeWindowSeconds != oldDateParams.timeWindowSeconds) { + setShouldReRenderTimeWindowSeconds(false) + setTimeWindowSeconds(newDateParams.timeWindowSeconds.toString()) + } + }, [{ ...queryParams, timeWindowSeconds }]) + + useEffect(() => { + if (bsmTrailLength != prevBsmTrailLength) { + setShouldReRenderTimeAfter(false) + setPrevBsmTrailLength(bsmTrailLength) + setBsmTrailLengthLocal(bsmTrailLength.toString()) + } + }, [bsmTrailLength]) + + useEffect(() => { + if (shouldReRenderEventTime && isFormValid()) { + setOldDateParams({ + eventTime: eventTime!.toDate(), + timeBefore: getNumber(timeBefore)!, + timeAfter: getNumber(timeAfter)!, + timeWindowSeconds: getNumber(timeWindowSecondsLocal)!, + }) + dispatch( + onTimeQueryChanged({ + eventTime: eventTime!.toDate(), + timeBefore: getNumber(timeBefore), + timeAfter: getNumber(timeAfter), + timeWindowSeconds: getNumber(timeWindowSecondsLocal), + }) + ) + } else { + setShouldReRenderEventTime(true) + } + }, [eventTime]) + + useEffect(() => { + if (shouldReRenderTimeBefore && isFormValid()) { + setOldDateParams({ + eventTime: eventTime!.toDate(), + timeBefore: getNumber(timeBefore)!, + timeAfter: getNumber(timeAfter)!, + timeWindowSeconds: getNumber(timeWindowSecondsLocal)!, + }) + dispatch( + onTimeQueryChanged({ + eventTime: eventTime!.toDate(), + timeBefore: getNumber(timeBefore), + timeAfter: getNumber(timeAfter), + timeWindowSeconds: getNumber(timeWindowSecondsLocal), + }) + ) + } else { + setShouldReRenderTimeBefore(true) + } + }, [timeBefore]) + + useEffect(() => { + if (shouldReRenderTimeAfter && isFormValid()) { + setOldDateParams({ + eventTime: eventTime!.toDate(), + timeBefore: getNumber(timeBefore)!, + timeAfter: getNumber(timeAfter)!, + timeWindowSeconds: getNumber(timeWindowSecondsLocal)!, + }) + dispatch( + onTimeQueryChanged({ + eventTime: eventTime!.toDate(), + timeBefore: getNumber(timeBefore), + timeAfter: getNumber(timeAfter), + timeWindowSeconds: getNumber(timeWindowSecondsLocal), + }) + ) + } else { + setShouldReRenderTimeAfter(true) + } + }, [timeAfter]) + + useEffect(() => { + if (shouldReRenderTimeWindowSeconds && isFormValid()) { + setOldDateParams({ + eventTime: eventTime!.toDate(), + timeBefore: getNumber(timeBefore)!, + timeAfter: getNumber(timeAfter)!, + timeWindowSeconds: getNumber(timeWindowSecondsLocal)!, + }) + dispatch( + onTimeQueryChanged({ + eventTime: eventTime!.toDate(), + timeBefore: getNumber(timeBefore), + timeAfter: getNumber(timeAfter), + timeWindowSeconds: getNumber(timeWindowSecondsLocal), + }) + ) + } else { + setShouldReRenderTimeWindowSeconds(true) + } + }, [timeWindowSeconds]) + + useEffect(() => { + if (shouldReRenderBsmTrail && getNumber(bsmTrailLengthLocal) != null) { + dispatch(setBsmTrailLength(getNumber(bsmTrailLengthLocal)!)) + } else { + setShouldReRenderBsmTrail(true) + } + }, [bsmTrailLengthLocal]) + + const isFormValid = () => { + try { + const d = eventTime?.toDate().getTime()! + return ( + !isNaN(d) && + getNumber(timeBefore) != null && + getNumber(timeAfter) != null && + getNumber(timeWindowSecondsLocal) != null + ) + } catch (e) { + return false + } + } + + const getNumber = (value: string | undefined): number | undefined => { + if (value == null) return undefined + const num = parseInt(value) + if (isNaN(num)) { + return undefined + } + return num + } + + const openMessageData = (files: FileList | null) => { + if (files == null) return + const file = files[0] + var jsZip = new JSZip() + const messageData: { + mapData: ProcessedMap[] + bsmData: OdeBsmData[] + spatData: ProcessedSpat[] + notificationData: any + } = { + mapData: [], + bsmData: [], + spatData: [], + notificationData: undefined, + } + jsZip.loadAsync(file).then(async (zip) => { + const zipObjects: { relativePath: string; zipEntry: JSZip.JSZipObject }[] = [] + zip.forEach((relativePath, zipEntry) => zipObjects.push({ relativePath, zipEntry })) + for (let i = 0; i < zipObjects.length; i++) { + const { relativePath, zipEntry } = zipObjects[i] + if (relativePath.endsWith('_MAP_data.json')) { + const data = await zipEntry.async('string') + messageData.mapData = JSON.parse(data) + } else if (relativePath.endsWith('_BSM_data.json')) { + const data = await zipEntry.async('string') + messageData.bsmData = JSON.parse(data) + } else if (relativePath.endsWith('_Notification_data.json')) { + const data = await zipEntry.async('string') + messageData.notificationData = JSON.parse(data) + } else if (relativePath.endsWith('_SPAT_data.json')) { + const data = await zipEntry.async('string') + messageData.spatData = JSON.parse(data) + } + } + dispatch(handleImportedMapMessageData(messageData)) + }) + } + + return ( +
+ + + + Time Query + {liveDataActive && } + + + + + + { + setTimeBefore(e.target.value) + }} + InputProps={{ + endAdornment: seconds, + }} + value={timeBefore} + /> + + { + setEventTime(e) + //?.toDate()! + }} + renderInput={(params) => } + /> + + { + setTimeAfter(e.target.value) + }} + InputProps={{ + endAdornment: seconds, + }} + value={timeAfter} + /> + { + setTimeWindowSeconds(e.target.value) + }} + InputProps={{ + endAdornment: seconds, + }} + value={timeWindowSeconds} + /> + + { + dispatch(toggleLiveDataActive()) + }} + color={liveDataActive ? 'success' : 'default'} + variant={liveDataActive ? undefined : 'outlined'} + /> + + + + + + + Message Times & Download + + +
+

+ Visualization Time: {format(sliderTimeValue.start, 'MM/dd/yyyy HH:mm:ss')} -{' '} + {format(sliderTimeValue.end, 'MM/dd/yyyy HH:mm:ss')} +

+

+ MAP Message Time:{' '} + {mapSpatTimes.mapTime == 0 ? 'No Data' : format(mapSpatTimes.mapTime * 1000, 'MM/dd/yyyy HH:mm:ss')} +

+ +

+ SPAT Message Time:{' '} + {mapSpatTimes.spatTime == 0 ? 'No Data' : format(mapSpatTimes.spatTime * 1000, 'MM/dd/yyyy HH:mm:ss')} +

+ +

+ Upload Message Data:{' '} + +

+
+
+
+ + + + Visual Settings + + +
+
+

Rotate Signal Head Icons With Map

+ + dispatch( + setSignalLayerLayout({ + ...signalStateLayerStyle.layout, + 'icon-rotation-alignment': event.target.checked ? 'map' : 'viewport', + 'icon-rotate': event.target.checked ? ['get', 'orientation'] : 0, + }) + ) + } + /> +
+
+

Show Lane IDs

+ dispatch(setLaneLabelsVisible(event.target.checked))} + /> +
+
+

Show Signal Group IDs

+ dispatch(setSigGroupLabelsVisible(event.target.checked))} + /> +
+
+

Show Popup on Hover

+ dispatch(setShowPopupOnHover(event.target.checked))} + /> +
+
+ { + setBsmTrailLengthLocal(e.target.value) + }} + value={bsmTrailLengthLocal} + /> +
+
+
+
+ + dispatch(setSliderValue(value))} + min={0} + max={getTimeRange(queryParams.startDate, queryParams.endDate)} + valueLabelDisplay="auto" + disableSwap + /> +
+ ) +} + +export default ControlPanel diff --git a/webapp/src/components/map/custom-table.tsx b/webapp/src/components/map/custom-table.tsx new file mode 100644 index 000000000..81c9480c2 --- /dev/null +++ b/webapp/src/components/map/custom-table.tsx @@ -0,0 +1,44 @@ +import React from 'react' +import { + Paper, + Box, + IconButton, + Typography, + TableContainer, + Table, + TableHead, + TableRow, + TableBody, + TableCell, +} from '@mui/material' + +export const CustomTable = (props) => { + const { headers, data }: { headers: string[]; data: string[][] } = props + let rowKey = 0 + let cellKey = 0 + + return ( + + + + + {headers.map((head) => ( + {head} + ))} + + + + {data.map((row) => ( + + {row.map((cell) => ( + + <>{cell} + + ))} + + ))} + +
+
+ ) +} diff --git a/webapp/src/components/map/expandable-table.tsx b/webapp/src/components/map/expandable-table.tsx new file mode 100644 index 000000000..804b10ce4 --- /dev/null +++ b/webapp/src/components/map/expandable-table.tsx @@ -0,0 +1,75 @@ +import { + Paper, + Box, + IconButton, + Typography, + TableContainer, + Table, + TableHead, + TableRow, + TableBody, + TableCell, + Collapse, +} from '@mui/material' +import React, { useState } from 'react' +import KeyboardArrowDownIcon from '@mui/icons-material/KeyboardArrowDown' +import KeyboardArrowUpIcon from '@mui/icons-material/KeyboardArrowUp' + +export const ExpandableTable = (props) => { + const { headers, data }: { headers: string[]; data: string[][]; details: string[] } = props + const [expandedRows, setExpandedRows] = useState([]) + + const handleExpandOne = (rowKey: number) => { + if (!expandedRows.includes(rowKey)) { + setExpandedRows((prevExpanded: number[]) => [...prevExpanded, rowKey]) + } else { + setExpandedRows((prevExpanded: number[]) => prevExpanded.filter((key: number) => key !== rowKey)) + } + } + + return ( + + + + + {headers.map((head) => ( + {head} + ))} + + + + {data.map((row, rowKey) => { + const isRowExpanded = [...expandedRows].indexOf(rowKey) !== -1 + return ( + <> + + + handleExpandOne(rowKey)}> + {isRowExpanded ? : } + + + {row.map((cell, cellKey) => ( + + <>{cell} + + ))} + + + + + + + {props.details[rowKey]} + + + + + + + ) + })} + +
+
+ ) +} diff --git a/webapp/src/components/map/map-component.tsx b/webapp/src/components/map/map-component.tsx new file mode 100644 index 000000000..ece12d992 --- /dev/null +++ b/webapp/src/components/map/map-component.tsx @@ -0,0 +1,383 @@ +import React, { useState, useEffect } from 'react' +import Map, { Source, Layer } from 'react-map-gl' + +import { Container, Col } from 'reactstrap' + +import { Paper, Box } from '@mui/material' + +import ControlPanel from './control-panel' +import { SidePanel } from './side-panel' +import { CustomPopup } from './popup' +import { useDispatch, useSelector } from 'react-redux' +import { selectToken } from '../../generalSlices/userSlice' +import { + selectBsmLayerStyle, + selectConnectingLanesLabelsLayerStyle, + selectConnectingLanesLayerStyle, + selectMapMessageLabelsLayerStyle, + selectMapMessageLayerStyle, + selectMarkerLayerStyle, + selectSignalStateLayerStyle, +} from './map-layer-style-slice' +import { + MAP_PROPS, + cleanUpLiveStreaming, + clearHoveredFeature, + clearSelectedFeature, + initializeLiveStreaming, + onMapClick, + onMapMouseEnter, + onMapMouseLeave, + onMapMouseMove, + pullInitialData, + selectAllInteractiveLayerIds, + selectBsmData, + selectConnectingLanes, + selectCurrentBsms, + selectCurrentSignalGroups, + selectCursor, + selectFilteredSurroundingEvents, + selectFilteredSurroundingNotifications, + selectHoveredFeature, + selectLaneLabelsVisible, + selectLiveDataActive, + selectLoadInitialDataTimeoutId, + selectMapData, + selectMapSignalGroups, + selectQueryParams, + selectRenderTimeInterval, + selectSelectedFeature, + selectShowPopupOnHover, + selectSigGroupLabelsVisible, + selectSignalStateData, + selectSliderValue, + selectSpatSignalGroups, + selectTimeWindowSeconds, + selectViewState, + setLoadInitialdataTimeoutId, + setMapProps, + setViewState, + updateQueryParams, + updateRenderTimeInterval, + updateRenderedMapState, +} from './map-slice' +import { addConnections, createMarkerForNotification } from './utilities/message-utils' +import { AnyAction, ThunkDispatch } from '@reduxjs/toolkit' +import { RootState } from '../../store' +import { MapLegend } from './map-legend' + +const generateQueryParams = ( + source: MessageMonitor.Notification | MessageMonitor.Event | Assessment | timestamp | undefined, + sourceDataType: 'notification' | 'event' | 'assessment' | 'timestamp' | undefined +) => { + const startOffset = 1000 * 60 * 1 + const endOffset = 1000 * 60 * 1 + + switch (sourceDataType) { + case 'notification': + const notification = source as MessageMonitor.Notification + return { + startDate: new Date(notification.notificationGeneratedAt - startOffset), + endDate: new Date(notification.notificationGeneratedAt + endOffset), + eventDate: new Date(notification.notificationGeneratedAt), + vehicleId: undefined, + } + case 'event': + const event = source as MessageMonitor.Event + return { + startDate: new Date(event.eventGeneratedAt - startOffset), + endDate: new Date(event.eventGeneratedAt + endOffset), + eventDate: new Date(event.eventGeneratedAt), + vehicleId: undefined, + } + case 'assessment': + const assessment = source as Assessment + return { + startDate: new Date(assessment.assessmentGeneratedAt - startOffset), + endDate: new Date(assessment.assessmentGeneratedAt + endOffset), + eventDate: new Date(assessment.assessmentGeneratedAt), + vehicleId: undefined, + } + case 'timestamp': + const ts = (source as timestamp).timestamp + return { + startDate: new Date(ts - startOffset), + endDate: new Date(ts + endOffset), + eventDate: new Date(ts), + vehicleId: undefined, + } + default: + return { + startDate: new Date(Date.now() - startOffset), + endDate: new Date(Date.now() + endOffset), + eventDate: new Date(Date.now()), + vehicleId: undefined, + } + } +} + +type timestamp = { + timestamp: number +} + +const MapTab = (props: MAP_PROPS) => { + const MAPBOX_API_TOKEN = process.env.MAPBOX_TOKEN! + const dispatch: ThunkDispatch = useDispatch() + + // userSlice + const authToken = useSelector(selectToken) + + const mapMessageLayerStyle = useSelector(selectMapMessageLayerStyle) + const mapMessageLabelsLayerStyle = useSelector(selectMapMessageLabelsLayerStyle) + const connectingLanesLayerStyle = useSelector(selectConnectingLanesLayerStyle) + const connectingLanesLabelsLayerStyle = useSelector(selectConnectingLanesLabelsLayerStyle) + const markerLayerStyle = useSelector(selectMarkerLayerStyle) + const bsmLayerStyle = useSelector(selectBsmLayerStyle) + const signalStateLayerStyle = useSelector(selectSignalStateLayerStyle) + + const allInteractiveLayerIds = useSelector(selectAllInteractiveLayerIds) + const queryParams = useSelector(selectQueryParams) + const mapData = useSelector(selectMapData) + const bsmData = useSelector(selectBsmData) + const mapSignalGroups = useSelector(selectMapSignalGroups) + const signalStateData = useSelector(selectSignalStateData) + const spatSignalGroups = useSelector(selectSpatSignalGroups) + const currentSignalGroups = useSelector(selectCurrentSignalGroups) + const currentBsms = useSelector(selectCurrentBsms) + const connectingLanes = useSelector(selectConnectingLanes) + const filteredSurroundingEvents = useSelector(selectFilteredSurroundingEvents) + const filteredSurroundingNotifications = useSelector(selectFilteredSurroundingNotifications) + const viewState = useSelector(selectViewState) + const timeWindowSeconds = useSelector(selectTimeWindowSeconds) + const sliderValue = useSelector(selectSliderValue) + const renderTimeInterval = useSelector(selectRenderTimeInterval) + const hoveredFeature = useSelector(selectHoveredFeature) + const selectedFeature = useSelector(selectSelectedFeature) + const sigGroupLabelsVisible = useSelector(selectSigGroupLabelsVisible) + const laneLabelsVisible = useSelector(selectLaneLabelsVisible) + const showPopupOnHover = useSelector(selectShowPopupOnHover) + const cursor = useSelector(selectCursor) + const loadInitialDataTimeoutId = useSelector(selectLoadInitialDataTimeoutId) + const liveDataActive = useSelector(selectLiveDataActive) + + const mapRef = React.useRef(null) + const [bsmTrailLength, setBsmTrailLength] = useState(5) + + useEffect(() => { + console.debug('SELECTED FEATURE', selectedFeature) + }, [selectedFeature]) + + useEffect(() => { + dispatch(setMapProps(props)) + }, [props]) + + useEffect(() => { + if (props.intersectionId != queryParams.intersectionId || props.roadRegulatorId != queryParams.roadRegulatorId) { + dispatch( + updateQueryParams({ + intersectionId: props.intersectionId, + roadRegulatorId: props.roadRegulatorId, + }) + ) + if (liveDataActive && authToken && props.roadRegulatorId && props.intersectionId) { + cleanUpLiveStreaming() + dispatch( + initializeLiveStreaming({ + token: authToken, + roadRegulatorId: props.roadRegulatorId, + intersectionId: props.intersectionId, + }) + ) + } + } + }, [props.intersectionId, props.roadRegulatorId]) + + useEffect(() => { + dispatch( + updateQueryParams({ + ...generateQueryParams(props.sourceData, props.sourceDataType), + intersectionId: props.intersectionId, + roadRegulatorId: props.roadRegulatorId, + resetTimeWindow: true, + }) + ) + }, [props.sourceData]) + + useEffect(() => { + if (liveDataActive) { + return + } + if (loadInitialDataTimeoutId) { + clearTimeout(loadInitialDataTimeoutId) + } + const timeoutId = setTimeout(() => dispatch(pullInitialData()), 500) + dispatch(setLoadInitialdataTimeoutId(timeoutId)) + }, [queryParams]) + + useEffect(() => { + if (!mapSignalGroups || !spatSignalGroups) { + console.debug('BSM Loading: No map or SPAT data', mapSignalGroups, spatSignalGroups) + return + } + + dispatch(updateRenderedMapState()) + }, [bsmData, mapSignalGroups, renderTimeInterval, spatSignalGroups]) + + useEffect(() => { + dispatch(updateRenderTimeInterval()) + }, [sliderValue, queryParams, timeWindowSeconds]) + + useEffect(() => { + if (liveDataActive) { + if (authToken && props.roadRegulatorId && props.intersectionId) { + dispatch( + initializeLiveStreaming({ + token: authToken, + roadRegulatorId: props.roadRegulatorId, + intersectionId: props.intersectionId, + }) + ) + if (bsmTrailLength > 15) setBsmTrailLength(5) + } else { + console.error( + 'Did not attempt to update notifications. Access token:', + authToken, + 'Intersection ID:', + props.intersectionId, + 'Road Regulator ID:', + props.roadRegulatorId + ) + } + } else { + if (bsmTrailLength < 15) setBsmTrailLength(20) + dispatch(cleanUpLiveStreaming()) + } + }, [liveDataActive]) + + return ( + + +
+ + + + + +
+
+ + + +
+ + {}} + mapStyle={process.env.MAPBOX_STYLE_URL!} + mapboxAccessToken={MAPBOX_API_TOKEN} + attributionControl={true} + customAttribution={['© CDOT']} + styleDiffing + style={{ width: '100%', height: '100%' }} + onMove={(evt) => dispatch(setViewState(evt.viewState))} + onClick={(e) => dispatch(onMapClick({ event: { point: e.point, lngLat: e.lngLat }, mapRef }))} + interactiveLayerIds={allInteractiveLayerIds} + cursor={cursor} + onMouseMove={(e) => dispatch(onMapMouseMove({ features: e.features, lngLat: e.lngLat }))} + onMouseEnter={(e) => dispatch(onMapMouseEnter({ features: e.features, lngLat: e.lngLat }))} + onMouseLeave={(e) => dispatch(onMapMouseLeave())} + > + + + + + + + + + + + + + + + + + + + + + + {selectedFeature && ( + dispatch(clearSelectedFeature())} /> + )} + {showPopupOnHover && hoveredFeature && !selectedFeature && ( + dispatch(clearHoveredFeature())} /> + )} + + + +
+ ) +} + +export default MapTab diff --git a/webapp/src/components/map/map-layer-style-slice.ts b/webapp/src/components/map/map-layer-style-slice.ts new file mode 100644 index 000000000..b627b92de --- /dev/null +++ b/webapp/src/components/map/map-layer-style-slice.ts @@ -0,0 +1,282 @@ +import { AnyAction, createAsyncThunk, createSlice, PayloadAction, ThunkDispatch } from '@reduxjs/toolkit' +import { CircleLayer, LayerProps, LineLayer, SymbolLayer } from 'react-map-gl' +import { CirclePaint, Layer } from 'mapbox-gl' +import { RootState } from '../../store' +import { createDraft } from 'immer' + +const mapMessageLayer: LineLayer = { + id: 'map-message', + type: 'line', + paint: { + 'line-width': 5, + 'line-color': ['case', ['==', ['get', 'ingressPath'], true], '#eb34e8', '#0004ff'], + }, +} + +const mapMessageLabelsLayer: SymbolLayer = { + id: 'map-message-labels', + type: 'symbol', + layout: { + 'text-field': ['concat', 'lane: ', ['to-string', ['get', 'laneId']]], + 'text-size': 20, + // "text-offset": [0, 1], + 'text-variable-anchor': ['top', 'left', 'right', 'bottom'], + 'text-allow-overlap': true, + 'icon-allow-overlap': true, + }, + paint: { + 'text-color': '#000000', + 'text-halo-color': '#ffffff', + 'text-halo-width': 5, + }, +} + +const connectingLanesLayer: LineLayer = { + id: 'connecting-lanes', + type: 'line', + paint: { + 'line-width': [ + 'match', + ['get', 'signalState'], + 'UNAVAILABLE', + 3, + 'DARK', + 3, + 'STOP_THEN_PROCEED', + 3, + 'STOP_AND_REMAIN', + 3, + 'PRE_MOVEMENT', + 5, + 'PERMISSIVE_MOVEMENT_ALLOWED', + 5, + 'PROTECTED_MOVEMENT_ALLOWED', + 5, + 'PERMISSIVE_CLEARANCE', + 5, + 'PROTECTED_CLEARANCE', + 5, + 'CAUTION_CONFLICTING_TRAFFIC', + 5, + 5, + ], + 'line-color': [ + 'match', + ['get', 'signalState'], + 'UNAVAILABLE', + '#797979', + 'DARK', + '#3a3a3a', + 'STOP_THEN_PROCEED', + '#c00000', + 'STOP_AND_REMAIN', + '#c00000', + 'PRE_MOVEMENT', + '#c00000', + 'PERMISSIVE_MOVEMENT_ALLOWED', + '#267700', + 'PROTECTED_MOVEMENT_ALLOWED', + '#267700', + 'PERMISSIVE_CLEARANCE', + '#e6b000', + 'PROTECTED_CLEARANCE', + '#e6b000', + 'CAUTION_CONFLICTING_TRAFFIC', + '#e6b000', + '#797979', + ], + 'line-dasharray': [ + 'match', + ['get', 'signalState'], + 'UNAVAILABLE', + ['literal', [2, 1]], + 'DARK', + ['literal', [2, 1]], + 'STOP_THEN_PROCEED', + ['literal', [2, 1]], + 'STOP_AND_REMAIN', + ['literal', [1]], + 'PRE_MOVEMENT', + ['literal', [2, 2]], + 'PERMISSIVE_MOVEMENT_ALLOWED', + ['literal', [2, 1]], + 'PROTECTED_MOVEMENT_ALLOWED', + ['literal', [1]], + 'PERMISSIVE_CLEARANCE', + ['literal', [2, 1]], + 'PROTECTED_CLEARANCE', + ['literal', [1]], + 'CAUTION_CONFLICTING_TRAFFIC', + ['literal', [1, 4]], + ['literal', [2, 1]], + ], + }, +} + +const connectingLanesLabelsLayer: SymbolLayer = { + id: 'connecting-lanes-labels', + type: 'symbol', + layout: { + 'text-field': ['concat', 'sig-group: ', ['to-string', ['get', 'signalGroupId']]], + 'text-size': 20, + 'text-offset': [0, 1], + 'text-variable-anchor': ['top', 'left', 'right', 'bottom'], + 'text-allow-overlap': true, + 'icon-allow-overlap': true, + 'icon-image': 'rounded', + 'icon-text-fit': 'both', + }, + paint: { + 'text-color': '#000000', + 'text-halo-color': '#ffffff', + 'text-halo-width': 5, + }, +} + +const markerLayer: LineLayer = { + id: 'invalid-lane-collection', + type: 'line', + paint: { + 'line-width': 20, + 'line-color': '#d40000', + // "line-dasharray": [2, 1], + }, +} + +const bsmLayerStyle: CircleLayer = { + id: 'bsm', + type: 'circle', + paint: { + 'circle-color': ['match', ['get', 'id'], 'temp-id', '#0004ff', '#0004ff'], + 'circle-radius': 8, + }, +} + +const signalStateLayer: SymbolLayer = { + id: 'signal-states', + type: 'symbol', + layout: { + 'icon-image': [ + 'match', + ['get', 'signalState'], + 'UNAVAILABLE', + 'traffic-light-icon-unknown', + 'DARK', + 'traffic-light-icon-unknown', + 'STOP_THEN_PROCEED', + 'traffic-light-icon-red-flashing', + 'STOP_AND_REMAIN', + 'traffic-light-icon-red-1', + 'PRE_MOVEMENT', + 'traffic-light-icon-yellow-red-1', + 'PERMISSIVE_MOVEMENT_ALLOWED', + 'traffic-light-icon-yellow-1', + 'PROTECTED_MOVEMENT_ALLOWED', + 'traffic-light-icon-green-1', + 'PERMISSIVE_CLEARANCE', + 'traffic-light-icon-yellow-1', + 'PROTECTED_CLEARANCE', + 'traffic-light-icon-yellow-1', + 'CAUTION_CONFLICTING_TRAFFIC', + 'traffic-light-icon-yellow-1', + 'traffic-light-icon-unknown', + ], + 'icon-rotate': ['get', 'orientation'], + 'icon-allow-overlap': true, + 'icon-rotation-alignment': 'map', + 'icon-size': ['interpolate', ['linear'], ['zoom'], 0, 0, 9, 0.01, 19, 0.15, 22, 0.4], + }, +} + +export type MAP_LEGEND_COLORS = { + bsmColors: { [key: string]: string } + laneColors: { [key: string]: string } + travelConnectionColors: { [key: string]: [string, number[]] } + signalHeadIcons: { [key: string]: string } +} + +const mapLegendColors: MAP_LEGEND_COLORS = { + bsmColors: { Other: '#0004ff' }, + laneColors: { + Ingress: '#eb34e8', + Egress: '#0004ff', + }, + travelConnectionColors: { + UNAVAILABLE: ['#797979', [2, 1]], + DARK: ['#3a3a3a', [2, 1]], + STOP_THEN_PROCEED: ['#c00000', [2, 1]], + STOP_AND_REMAIN: ['#c00000', [1]], + PRE_MOVEMENT: ['#c00000', [2, 2]], + PERMISSIVE_MOVEMENT_ALLOWED: ['#267700', [2, 1]], + PROTECTED_MOVEMENT_ALLOWED: ['#267700', [1]], + PERMISSIVE_CLEARANCE: ['#e6b000', [2, 1]], + PROTECTED_CLEARANCE: ['#e6b000', [1]], + CAUTION_CONFLICTING_TRAFFIC: ['#e6b000', [1, 4]], + }, + signalHeadIcons: { + UNAVAILABLE: '/icons/traffic-light-icon-unknown.svg', + DARK: '/icons/traffic-light-icon-unknown.svg', + STOP_THEN_PROCEED: '/icons/traffic-light-icon-red-flashing.svg', + STOP_AND_REMAIN: '/icons/traffic-light-icon-red-1.svg', + PRE_MOVEMENT: '/icons/traffic-light-icon-yellow-red-1.svg', + PERMISSIVE_MOVEMENT_ALLOWED: '/icons/traffic-light-icon-yellow-1.svg', + PROTECTED_MOVEMENT_ALLOWED: '/icons/traffic-light-icon-green-1.svg', + PERMISSIVE_CLEARANCE: '/icons/traffic-light-icon-yellow-1.svg', + PROTECTED_CLEARANCE: '/icons/traffic-light-icon-yellow-1.svg', + CAUTION_CONFLICTING_TRAFFIC: '/icons/traffic-light-icon-yellow-1.svg', + }, +} + +export const initialState = { + mapMessageLayerStyle: { ...mapMessageLayer, source: 'string' }, + mapMessageLabelsLayerStyle: { ...mapMessageLabelsLayer, source: 'string' }, + connectingLanesLayerStyle: { ...connectingLanesLayer, source: 'string' }, + connectingLanesLabelsLayerStyle: { ...connectingLanesLabelsLayer, source: 'string' }, + markerLayerStyle: { ...markerLayer, source: 'string' }, + bsmLayerStyle: { ...bsmLayerStyle, source: 'string' }, + signalStateLayerStyle: { ...signalStateLayer, source: 'string' }, + mapLegendColors: mapLegendColors, +} + +export const intersectionMapLayerStyleSlice = createSlice({ + name: 'intersectionMapLayerStyle', + initialState: { + loading: false, + value: initialState, + }, + reducers: { + setBsmLegendColors: (state, action: PayloadAction<{ [key: string]: string }>) => { + state.value.mapLegendColors = { ...state.value.mapLegendColors, bsmColors: action.payload } + }, + setBsmCircleColor: (state, action: PayloadAction) => { + state.value.bsmLayerStyle = { + ...state.value.bsmLayerStyle, + paint: { ...state.value.bsmLayerStyle.paint, 'circle-color': action.payload }, + } + }, + setSignalLayerLayout: (state, action: PayloadAction) => { + state.value.signalStateLayerStyle = { ...state.value.signalStateLayerStyle, layout: action.payload } + }, + }, + extraReducers: (builder) => { + builder + }, +}) + +export const selectMapMessageLayerStyle = (state: RootState) => + state.intersectionMapLayerStyle.value.mapMessageLayerStyle +export const selectMapMessageLabelsLayerStyle = (state: RootState) => + state.intersectionMapLayerStyle.value.mapMessageLabelsLayerStyle +export const selectConnectingLanesLayerStyle = (state: RootState) => + state.intersectionMapLayerStyle.value.connectingLanesLayerStyle +export const selectConnectingLanesLabelsLayerStyle = (state: RootState) => + state.intersectionMapLayerStyle.value.connectingLanesLabelsLayerStyle +export const selectMarkerLayerStyle = (state: RootState) => state.intersectionMapLayerStyle.value.markerLayerStyle +export const selectBsmLayerStyle = (state: RootState) => state.intersectionMapLayerStyle.value.bsmLayerStyle +export const selectSignalStateLayerStyle = (state: RootState) => + state.intersectionMapLayerStyle.value.signalStateLayerStyle +export const selectMapLegendColors = (state: RootState) => state.intersectionMapLayerStyle.value.mapLegendColors + +export const { setBsmLegendColors, setBsmCircleColor, setSignalLayerLayout } = intersectionMapLayerStyleSlice.actions + +export default intersectionMapLayerStyleSlice.reducer diff --git a/webapp/src/components/map/map-legend.tsx b/webapp/src/components/map/map-legend.tsx new file mode 100644 index 000000000..07a24b2b0 --- /dev/null +++ b/webapp/src/components/map/map-legend.tsx @@ -0,0 +1,229 @@ +import { + Paper, + Box, + IconButton, + Typography, + TableContainer, + Table, + TableHead, + TableRow, + TableBody, + TableCell, + Divider, +} from '@mui/material' +import React from 'react' +import MuiAccordion, { AccordionProps } from '@mui/material/Accordion' +import MuiAccordionSummary, { AccordionSummaryProps } from '@mui/material/AccordionSummary' +import MuiAccordionDetails from '@mui/material/AccordionDetails' +import ArrowForwardIosSharpIcon from '@mui/icons-material/ArrowForwardIosSharp' +import { styled } from '@mui/material/styles' +import trafficLightIconUnknown from '../../../public/icons/traffic-light-icon-unknown.svg' +import trafficLightIconRedFlashing from '../../../public/icons/traffic-light-icon-red-flashing.svg' +import trafficLightIconRed1 from '../../../public/icons/traffic-light-icon-red-1.svg' +import trafficLightIconYellowRed1 from '../../../public/icons/traffic-light-icon-yellow-red-1.svg' +import trafficLightIconYellow1 from '../../../public/icons/traffic-light-icon-yellow-1.svg' +import trafficLightIconGreen1 from '../../../public/icons/traffic-light-icon-green-1.svg' +import { selectMapLegendColors } from './map-layer-style-slice' +import { useSelector } from 'react-redux' + +// "DARK": "/icons/traffic-light-icon-unknown", +// "STOP_THEN_PROCEED": "/icons/traffic-light-icon-red-flashing", +// "STOP_AND_REMAIN": "/icons/traffic-light-icon-red-1", +// "PRE_MOVEMENT": "/icons/traffic-light-icon-yellow-red-1", +// "PERMISSIVE_MOVEMENT_ALLOWED": "/icons/traffic-light-icon-yellow-1", +// "PROTECTED_MOVEMENT_ALLOWED": "/icons/traffic-light-icon-green-1", +// "PERMISSIVE_CLEARANCE": "/icons/traffic-light-icon-yellow-1", +// "PROTECTED_CLEARANCE": "/icons/traffic-light-icon-yellow-1", +// "CAUTION_CONFLICTING_TRAFFIC": "/icons/traffic-light-icon-yellow-1", + +const signalHeadIconsObj = { + 'traffic-light-icon-unknown': trafficLightIconUnknown, + 'traffic-light-icon-red-flashing': trafficLightIconRedFlashing, + 'traffic-light-icon-red-1': trafficLightIconRed1, + 'traffic-light-icon-yellow-red-1': trafficLightIconYellowRed1, + 'traffic-light-icon-yellow-1': trafficLightIconYellow1, + 'traffic-light-icon-green-1': trafficLightIconGreen1, +} + +const Accordion = styled((props: AccordionProps) => )( + ({ theme }) => ({ + //border: `1px solid ${theme.palette.divider}`, + // '&:not(:last-child)': { + // borderBottom: 0, + // }, + // '&:before': { + // display: 'none', + // }, + }) +) + +const AccordionSummary = styled((props: AccordionSummaryProps) => ( + } {...props} /> +))(({ theme }) => ({ + minHeight: 0, + paddingLeft: 10, + flexDirection: 'row-reverse', + '& .MuiAccordionSummary-expandIconWrapper.Mui-expanded': { + transform: 'rotate(90deg)', + }, + '& .MuiAccordionSummary-content': { + marginLeft: theme.spacing(1), + marginTop: 0, + marginBottom: 0, + }, +})) + +const AccordionDetails = styled(MuiAccordionDetails)(({ theme }) => ({})) + +export const MapLegend = () => { + const mapLegendColors = useSelector(selectMapLegendColors) + + const { bsmColors, travelConnectionColors, laneColors, signalHeadIcons } = mapLegendColors + + const bsmColorsList: JSX.Element[] = [] + for (const [key, value] of Object.entries(bsmColors)) { + bsmColorsList.push( + <> +
+
{key}:
+
+
+

|

+ + ) + } + + const travelConnectionColorsList: JSX.Element[] = [] + for (const [key, value] of Object.entries(travelConnectionColors)) { + const heightFactor = 20 / 2 / value[1].reduce((partialSum, a) => partialSum + a, 0) // 20 is the height of the colored bar, 2 is the number of sets of colors + const heightColored = value[1][0] * heightFactor + const heightWhite = (value[1].length == 1 ? 0 : value[1][1]) * heightFactor + travelConnectionColorsList.push( + <> +
+
{key}:
+
+
+
+
+
+
+
+

|

+ + ) + } + + const laneColorsList: JSX.Element[] = [] + for (const [key, value] of Object.entries(laneColors)) { + laneColorsList.push( + <> +
+
{key}:
+
+
|
+
+ + ) + } + + const signalHeadIconsList: JSX.Element[] = [] + for (const [key, value] of Object.entries(signalHeadIcons)) { + signalHeadIconsList.push( + <> +
+
{key}:
+ +
|
+
+ + ) + } + + return ( + + + Legend + + + + + + Signal Heads + + +
{signalHeadIconsList}
+
+
+ + + + + + Lane Lines + + +
{laneColorsList}
+
+
+ + + + + + Lane Connections + + +
+ {travelConnectionColorsList} +
+
+
+ + + + BSM Colors + + +
{bsmColorsList}
+
+
+ + +
+
+
+ ) +} diff --git a/webapp/src/components/map/map-slice.tsx b/webapp/src/components/map/map-slice.tsx new file mode 100644 index 000000000..276da0218 --- /dev/null +++ b/webapp/src/components/map/map-slice.tsx @@ -0,0 +1,1292 @@ +import { AnyAction, createAsyncThunk, createSlice, PayloadAction, ThunkDispatch } from '@reduxjs/toolkit' +import { RootState } from '../../store' +import { selectToken } from '../../generalSlices/userSlice' +import { Client, CompatClient, IMessage, Stomp } from '@stomp/stompjs' +import MessageMonitorApi from '../../apis/mm-api' +import EventsApi from '../../apis/events-api' +import NotificationApi from '../../apis/notification-api' +import toast from 'react-hot-toast' +import { + generateSignalStateFeatureCollection, + parseBsmToGeojson, + parseMapSignalGroups, + parseSpatSignalGroups, +} from './utilities/message-utils' +import { generateColorDictionary, generateMapboxStyleExpression } from './utilities/colors' +import { setBsmCircleColor, setBsmLegendColors } from './map-layer-style-slice' +import { getTimeRange } from './utilities/map-utils' +import { ViewState } from 'react-map-gl' +import JSZip from 'jszip' +import FileSaver from 'file-saver' +import { features } from 'process' +import { selectMapDate, selectMapList, selectRsuData, selectRsuIpv4, selectRsuMapData, selectSelectedRsu, selectSelectedSrm, selectSrmSsmList, selectSsmDisplay } from '../../generalSlices/rsuSlice' + +export type MAP_LAYERS = + | 'mapMessage' + | 'mapMessageLabels' + | 'connectingLanes' + | 'connectingLanesLabels' + | 'invalidLaneCollection' + | 'bsm' + | 'signalStates' + +export type MAP_QUERY_PARAMS = { + startDate: Date + endDate: Date + eventDate: Date + vehicleId?: string + intersectionId?: number + roadRegulatorId?: number +} + +export type IMPORTED_MAP_MESSAGE_DATA = { + mapData: ProcessedMap[] + bsmData: OdeBsmData[] + spatData: ProcessedSpat[] + notificationData: any +} + +type timestamp = { + timestamp: number +} + +export type MAP_PROPS = { + sourceApi: 'conflictvisualizer' | 'cvmanager' + sourceData: MessageMonitor.Notification | MessageMonitor.Event | Assessment | timestamp | string | undefined + sourceDataType: 'notification' | 'event' | 'assessment' | 'timestamp' | 'rsu_ip' | undefined + intersectionId: number | undefined + roadRegulatorId: number | undefined + loadOnNull?: boolean +} + +type RAW_MESSAGE_DATA_EXPORT = { + map?: ProcessedMap[] + spat?: ProcessedSpat[] + bsm?: BsmFeatureCollection + notification?: MessageMonitor.Notification + event?: MessageMonitor.Event + assessment?: Assessment +} + +interface MinimalClient { + connect: (headers: unknown, connectCallback: () => void, errorCallback?: (error: string) => void) => void + subscribe: (destination: string, callback: (message: IMessage) => void) => void + disconnect: (disconnectCallback: () => void) => void +} + +const initialState = { + layersVisible: { + mapMessage: false, + mapMessageLabels: false, + connectingLanes: false, + connectingLanesLabels: false, + invalidLaneCollection: false, + bsm: false, + signalStates: false, + } as Record, + allInteractiveLayerIds: ['mapMessage', 'connectingLanes', 'signalStates', 'bsm'] as MAP_LAYERS[], + queryParams: { + startDate: new Date(Date.now() - 1000 * 60 * 1), + endDate: new Date(Date.now() + 1000 * 60 * 1), + eventDate: new Date(Date.now()), + vehicleId: undefined, + intersectionId: undefined, + roadRegulatorId: undefined, + } as MAP_QUERY_PARAMS, + sourceApi: undefined as MAP_PROPS['sourceApi'] | undefined, + sourceData: undefined as MAP_PROPS['sourceData'] | undefined, + sourceDataType: undefined as MAP_PROPS['sourceDataType'] | undefined, + intersectionId: undefined as MAP_PROPS['intersectionId'] | undefined, + roadRegulatorId: undefined as MAP_PROPS['roadRegulatorId'] | undefined, + loadOnNull: undefined as MAP_PROPS['loadOnNull'] | undefined, + mapData: undefined as ProcessedMap | undefined, + mapSignalGroups: undefined as SignalStateFeatureCollection | undefined, + signalStateData: undefined as SignalStateFeatureCollection | undefined, + spatSignalGroups: undefined as SpatSignalGroups | undefined, + currentSignalGroups: undefined as SpatSignalGroup[] | undefined, + currentBsms: { + type: 'FeatureCollection' as 'FeatureCollection', + features: [], + } as BsmUiFeatureCollection, + connectingLanes: undefined as ConnectingLanesFeatureCollection | undefined, + bsmData: { + type: 'FeatureCollection' as 'FeatureCollection', + features: [], + } as BsmFeatureCollection, + surroundingEvents: [] as MessageMonitor.Event[], + filteredSurroundingEvents: [] as MessageMonitor.Event[], + surroundingNotifications: [] as MessageMonitor.Notification[], + filteredSurroundingNotifications: [] as MessageMonitor.Notification[], + viewState: { + latitude: 39.587905, + longitude: -105.0907089, + zoom: 19, + }, + timeWindowSeconds: 60, + sliderValue: 0, + sliderTimeValue: { + start: new Date(), + end: new Date(), + }, + lastSliderUpdate: undefined as number | undefined, + renderTimeInterval: [0, 0], + hoveredFeature: undefined as any, + selectedFeature: undefined as any, + rawData: {} as RAW_MESSAGE_DATA_EXPORT, + mapSpatTimes: { mapTime: 0, spatTime: 0 }, + sigGroupLabelsVisible: false, + laneLabelsVisible: false, + showPopupOnHover: false, + importedMessageData: undefined as IMPORTED_MAP_MESSAGE_DATA | undefined, + cursor: 'default', + loadInitialDataTimeoutId: undefined as NodeJS.Timeout | undefined, + wsClient: undefined as MinimalClient | undefined, + liveDataActive: false, + currentMapData: [] as ProcessedMap[], + currentSpatData: [] as ProcessedSpat[], + currentBsmData: { + type: 'FeatureCollection', + features: [], + } as BsmFeatureCollection, + bsmTrailLength: 20, +} + +const getNewSliderTimeValue = (startDate: Date, sliderValue: number, timeWindowSeconds: number) => { + return { + start: new Date((startDate.getTime() / 1000 + sliderValue - timeWindowSeconds) * 1000), + end: new Date((startDate.getTime() / 1000 + sliderValue) * 1000), + } +} + +export const pullInitialData = createAsyncThunk( + 'intersectionMap/pullInitialData', + async (_, { getState, dispatch }) => { + const currentState = getState() as RootState + const authToken = selectToken(currentState)! + const importedMessageData = selectImportedMessageData(currentState) + const queryParams = selectQueryParams(currentState) + + console.debug('Pulling Initial Data') + let rawMap: ProcessedMap[] = [] + let rawSpat: ProcessedSpat[] = [] + let rawBsm: OdeBsmData[] = [] + if (!importedMessageData) { + // ######################### Retrieve MAP Data ######################### + const rawMapPromise = MessageMonitorApi.getMapMessages({ + token: authToken, + intersectionId: queryParams.intersectionId!, + roadRegulatorId: queryParams.roadRegulatorId!, + //startTime: new Date(queryParams.startDate.getTime() - 1000 * 60 * 60 * 1), + endTime: queryParams.endDate, + latest: true, + }) + toast.promise(rawMapPromise, { + loading: `Loading MAP Data`, + success: `Successfully got MAP Data`, + error: `Failed to get MAP data. Please see console`, + }) + rawMap = await rawMapPromise + + // ######################### Retrieve SPAT Data ######################### + const rawSpatPromise = MessageMonitorApi.getSpatMessages({ + token: authToken, + intersectionId: queryParams.intersectionId!, + roadRegulatorId: queryParams.roadRegulatorId!, + startTime: queryParams.startDate, + endTime: queryParams.endDate, + }) + toast.promise(rawSpatPromise, { + loading: `Loading SPAT Data`, + success: `Successfully got SPAT Data`, + error: `Failed to get SPAT data. Please see console`, + }) + rawSpat = (await rawSpatPromise).sort((a, b) => Number(a.utcTimeStamp) - Number(b.utcTimeStamp)) + + dispatch(getSurroundingEvents()) + dispatch(getSurroundingNotifications()) + } else { + rawMap = importedMessageData.mapData + rawSpat = importedMessageData.spatData.sort((a, b) => Number(a.utcTimeStamp) - Number(b.utcTimeStamp)) + rawBsm = importedMessageData.bsmData + } + if (!rawMap || rawMap.length == 0) { + console.info('NO MAP MESSAGES WITHIN TIME') + // return; + } + + const latestMapMessage: ProcessedMap = rawMap.at(-1)! + const mapCoordinates: OdePosition3D = latestMapMessage?.properties.refPoint + const mapSignalGroupsLocal = parseMapSignalGroups(latestMapMessage) + const spatSignalGroupsLocal = parseSpatSignalGroups(rawSpat) + + // ######################### BSMs ######################### + if (!importedMessageData) { + const rawBsmPromise = MessageMonitorApi.getBsmMessages({ + token: authToken, + vehicleId: queryParams.vehicleId, + startTime: queryParams.startDate, + endTime: queryParams.endDate, + long: mapCoordinates.longitude, + lat: mapCoordinates.latitude, + distance: 500, + }) + toast.promise(rawBsmPromise, { + loading: `Loading BSM Data`, + success: `Successfully got BSM Data`, + error: `Failed to get BSM data. Please see console`, + }) + rawBsm = await rawBsmPromise + } + let bsmGeojson = parseBsmToGeojson(rawBsm) + bsmGeojson = { + ...bsmGeojson, + features: [...bsmGeojson.features.sort((a, b) => b.properties.odeReceivedAt - a.properties.odeReceivedAt)], + } + dispatch(renderEntireMap({ currentMapData: rawMap, currentSpatData: rawSpat, currentBsmData: bsmGeojson })) + return { + mapData: latestMapMessage, + connectingLanes: latestMapMessage.connectingLanesFeatureCollection, + spatSignalGroups: spatSignalGroupsLocal, + mapSignalGroups: mapSignalGroupsLocal, + mapTime: latestMapMessage.properties.odeReceivedAt as unknown as number, + } + }, + { + condition: (_, { getState }) => + selectToken(getState() as RootState) != undefined && + selectQueryParams(getState() as RootState).intersectionId != undefined && + selectQueryParams(getState() as RootState).roadRegulatorId != undefined && + (selectSourceData(getState() as RootState) != undefined || selectLoadOnNull(getState() as RootState) == true), + } +) + +export const renderEntireMap = createAsyncThunk( + 'intersectionMap/renderEntireMap', + async ( + args: { currentMapData: ProcessedMap[]; currentSpatData: ProcessedSpat[]; currentBsmData: BsmFeatureCollection }, + { getState, dispatch } + ) => { + const { currentMapData, currentSpatData, currentBsmData } = args + const currentState = getState() as RootState + const sourceApi = selectSourceApi(currentState) + + if (sourceApi == 'conflictvisualizer') { + const queryParams = selectQueryParams(currentState) + const sourceData = selectSourceData(currentState) + const sourceDataType = selectSourceDataType(currentState) + + // ######################### MAP Data ######################### + const latestMapMessage: ProcessedMap = currentMapData.at(-1)! + + // ######################### SPAT Signal Groups ######################### + const mapSignalGroupsLocal = parseMapSignalGroups(latestMapMessage) + + const spatSignalGroupsLocal = parseSpatSignalGroups(currentSpatData) + + const uniqueIds = new Set(currentBsmData.features.map((bsm) => bsm.properties?.id)) + // generate equally spaced unique colors for each uniqueId + const colors = generateColorDictionary(uniqueIds) + dispatch(setBsmLegendColors(colors)) + // add color to each feature + const bsmLayerStyle = generateMapboxStyleExpression(colors) + dispatch(setBsmCircleColor(bsmLayerStyle)) + + // ######################### Message Data ######################### + const rawData = {} + rawData['map'] = currentMapData + rawData['spat'] = currentSpatData + rawData['bsm'] = currentBsmData + if (sourceDataType == 'notification') { + rawData['notification'] = sourceData as MessageMonitor.Notification + } else if (sourceDataType == 'event') { + rawData['event'] = sourceData as MessageMonitor.Event + } else if (sourceDataType == 'assessment') { + rawData['assessment'] = sourceData as Assessment + } else if (sourceDataType == 'rsu_ip') { + const rsuData = selectRsuData(currentState) + const mapList = selectMapList(currentState) + const rsu = rsuData.find((rsu) => rsu.properties.ipv4_address == sourceData) + if (rsu != null && mapList.includes(rsu.properties.ipv4_address)) { + } + } + return { + connectingLanes: latestMapMessage.connectingLanesFeatureCollection, + mapData: latestMapMessage, + mapTime: latestMapMessage.properties.odeReceivedAt as unknown as number, + mapSignalGroups: mapSignalGroupsLocal, + spatSignalGroups: spatSignalGroupsLocal, + bsmData: currentBsmData, + rawData: rawData, + sliderValue: Math.min( + getTimeRange(queryParams.startDate, queryParams.eventDate ?? new Date()), + getTimeRange(queryParams.startDate, queryParams.endDate) + ), + } + } + else { + const queryParams = selectQueryParams(currentState) + const sourceData = selectSourceData(currentState) + const sourceDataType = selectSourceDataType(currentState) + + // ######################### MAP Data ######################### + const latestMapMessage: ProcessedMap = currentMapData.at(-1)! + + // ######################### SPAT Signal Groups ######################### + const mapSignalGroupsLocal = parseMapSignalGroups(latestMapMessage) + + const spatSignalGroupsLocal = parseSpatSignalGroups(currentSpatData) + + const uniqueIds = new Set(currentBsmData.features.map((bsm) => bsm.properties?.id)) + // generate equally spaced unique colors for each uniqueId + const colors = generateColorDictionary(uniqueIds) + dispatch(setBsmLegendColors(colors)) + // add color to each feature + const bsmLayerStyle = generateMapboxStyleExpression(colors) + dispatch(setBsmCircleColor(bsmLayerStyle)) + + // ######################### Message Data ######################### + const rawData = {} + rawData['map'] = currentMapData + rawData['spat'] = currentSpatData + rawData['bsm'] = currentBsmData + if (sourceDataType == 'rsu_ip') { + const rsuData = selectRsuData(currentState) + const mapList = selectMapList(currentState) + const rsu = rsuData.find((rsu) => rsu.properties.ipv4_address == sourceData) + if (rsu != null && mapList.includes(rsu.properties.ipv4_address)) { + + + const rsuMapData = selectRsuMapData(currentState); + const selectedRsu = selectSelectedRsu(currentState); + const selectedSrm = selectSelectedSrm(currentState); + const mapDate = selectMapDate(currentState); + const ssmDisplay = selectSsmDisplay(currentState); + const rsuIpv4 = selectRsuIpv4(currentState); + const srmSsmList = selectSrmSsmList(currentState); + + } + } + return { + connectingLanes: latestMapMessage.connectingLanesFeatureCollection, + mapData: latestMapMessage, + mapTime: latestMapMessage.properties.odeReceivedAt as unknown as number, + mapSignalGroups: mapSignalGroupsLocal, + spatSignalGroups: spatSignalGroupsLocal, + bsmData: currentBsmData, + rawData: rawData, + sliderValue: Math.min( + getTimeRange(queryParams.startDate, queryParams.eventDate ?? new Date()), + getTimeRange(queryParams.startDate, queryParams.endDate) + ), + } + } + }, + { + condition: ( + args: { currentMapData: ProcessedMap[]; currentSpatData: ProcessedSpat[]; currentBsmData: BsmFeatureCollection }, + { getState } + ) => args.currentMapData.length != 0, + } +) + +export const renderIterative_Map = createAsyncThunk( + 'intersectionMap/renderIterative_Map', + async (newMapData: ProcessedMap[], { getState, dispatch }) => { + const currentState = getState() as RootState + const queryParams = selectQueryParams(currentState) + const currentMapData: ProcessedMap[] = selectCurrentMapData(currentState) + + const start = Date.now() + const OLDEST_DATA_TO_KEEP = queryParams.eventDate.getTime() - queryParams.startDate.getTime() // milliseconds + + const currTimestamp = Date.parse(newMapData.at(-1)!.properties.odeReceivedAt) / 1000 + let oldIndex = 0 + for (let i = 0; i < currentMapData.length; i++) { + if ((currentMapData[i].properties.odeReceivedAt as unknown as number) < currTimestamp - OLDEST_DATA_TO_KEEP) { + oldIndex = i + } else { + break + } + } + const currentMapDataLocal = currentMapData.slice(oldIndex, currentMapData.length).concat(newMapData) + + // ######################### MAP Data ######################### + const latestMapMessage: ProcessedMap = currentMapDataLocal.at(-1)! + + // ######################### SPAT Signal Groups ######################### + const mapSignalGroupsLocal = parseMapSignalGroups(latestMapMessage) + console.debug('MAP RENDER TIME:', Date.now() - start, 'ms') + dispatch(setRawData({ map: currentMapDataLocal })) + return { + currentMapData: currentMapDataLocal, + connectingLanes: latestMapMessage.connectingLanesFeatureCollection, + mapData: latestMapMessage, + mapTime: currTimestamp, + mapSignalGroups: mapSignalGroupsLocal, + } + }, + { + condition: (newMapData: ProcessedMap[], { getState }) => newMapData.length != 0, + } +) + +export const renderIterative_Spat = createAsyncThunk( + 'intersectionMap/renderIterative_Spat', + async (newSpatData: ProcessedSpat[], { getState, dispatch }) => { + const currentState = getState() as RootState + const queryParams = selectQueryParams(currentState) + const currentSpatSignalGroups: ProcessedSpat[] = selectCurrentSpatData(currentState) + const OLDEST_DATA_TO_KEEP = queryParams.eventDate.getTime() - queryParams.startDate.getTime() // milliseconds + // Inject and filter spat data + const currTimestamp = Date.parse(newSpatData.at(-1)!.utcTimeStamp) + let oldIndex = 0 + const currentSpatSignalGroupsArr = Object.keys(currentSpatSignalGroups).map((key) => ({ + key, + sigGroup: currentSpatSignalGroups[key], + })) + for (let i = 0; i < currentSpatSignalGroupsArr.length; i++) { + if (Number(currentSpatSignalGroupsArr[i].key) < currTimestamp - OLDEST_DATA_TO_KEEP) { + oldIndex = i + } else { + break + } + } + const newSpatSignalGroups = parseSpatSignalGroups(newSpatData) + const newSpatSignalGroupsArr = Object.keys(newSpatSignalGroups).map((key) => ({ + key, + sigGroup: newSpatSignalGroups[key], + })) + const filteredSpatSignalGroupsArr = currentSpatSignalGroupsArr + .slice(oldIndex, currentSpatSignalGroupsArr.length) + .concat(newSpatSignalGroupsArr) + const currentSpatSignalGroupsLocal = filteredSpatSignalGroupsArr.reduce((acc, curr) => { + acc[curr.key] = curr.sigGroup + return acc + }, {} as SpatSignalGroups) + + // Update current processed spat data + oldIndex = 0 + for (let i = 0; i < currentSpatSignalGroups.length; i++) { + if (Date.parse(currentSpatSignalGroups[i].utcTimeStamp) < currTimestamp - OLDEST_DATA_TO_KEEP) { + oldIndex = i + } else { + break + } + } + const currentProcessedSpatDataLocal = currentSpatSignalGroups + .slice(oldIndex, currentSpatSignalGroups.length) + .concat(newSpatData) + dispatch(setRawData({ spat: currentProcessedSpatDataLocal })) + + return { + currentProcessedSpatDataLocal, + currentSpatSignalGroupsLocal, + } + }, + { + condition: (newSpatData: ProcessedSpat[], { getState }) => newSpatData.length != 0, + } +) + +export const renderIterative_Bsm = createAsyncThunk( + 'intersectionMap/renderIterative_Bsm', + async (newBsmData: OdeBsmData[], { getState, dispatch }) => { + const currentState = getState() as RootState + const queryParams = selectQueryParams(currentState) + const currentBsmData: BsmFeatureCollection = selectCurrentBsmData(currentState) + + const OLDEST_DATA_TO_KEEP = queryParams.eventDate.getTime() - queryParams.startDate.getTime() // milliseconds + // Inject and filter spat data + const currTimestamp = new Date(newBsmData.at(-1)!.metadata.odeReceivedAt as string).getTime() / 1000 + let oldIndex = 0 + for (let i = 0; i < currentBsmData.features.length; i++) { + if (Number(currentBsmData.features[i].properties.odeReceivedAt) < currTimestamp - OLDEST_DATA_TO_KEEP) { + oldIndex = i + } else { + break + } + } + const newBsmGeojson = parseBsmToGeojson(newBsmData) + const currentBsmGeojson = { + ...currentBsmData, + features: currentBsmData.features.slice(oldIndex, currentBsmData.features.length).concat(newBsmGeojson.features), + } + + const uniqueIds = new Set(currentBsmGeojson.features.map((bsm) => bsm.properties?.id)) + // generate equally spaced unique colors for each uniqueId + const colors = generateColorDictionary(uniqueIds) + dispatch(setBsmLegendColors(colors)) + // add color to each feature + const bsmLayerStyle = generateMapboxStyleExpression(colors) + dispatch(setBsmCircleColor(bsmLayerStyle)) + dispatch(setRawData({ bsm: currentBsmGeojson })) + return currentBsmGeojson + }, + { + condition: (newBsmData: OdeBsmData[], { getState }) => newBsmData.length != 0, + } +) + +export const getSurroundingEvents = createAsyncThunk( + 'intersectionMap/getSurroundingEvents', + async (_, { getState }) => { + const currentState = getState() as RootState + const authToken = selectToken(currentState)! + const queryParams = selectQueryParams(currentState) + + const surroundingEventsPromise = EventsApi.getAllEvents( + authToken, + queryParams.intersectionId!, + queryParams.roadRegulatorId!, + queryParams.startDate, + queryParams.endDate + ) + toast.promise(surroundingEventsPromise, { + loading: `Loading Event Data`, + success: `Successfully got Event Data`, + error: `Failed to get Event data. Please see console`, + }) + return surroundingEventsPromise + }, + { + condition: (_, { getState }) => + selectToken(getState() as RootState) != undefined && + selectQueryParams(getState() as RootState).intersectionId != undefined && + selectQueryParams(getState() as RootState).roadRegulatorId != undefined, + } +) + +export const getSurroundingNotifications = createAsyncThunk( + 'intersectionMap/getSurroundingNotifications', + async (_, { getState }) => { + const currentState = getState() as RootState + const authToken = selectToken(currentState)! + const queryParams = selectQueryParams(currentState) + + const surroundingNotificationsPromise = NotificationApi.getAllNotifications({ + token: authToken, + intersectionId: queryParams.intersectionId!, + roadRegulatorId: queryParams.roadRegulatorId!, + startTime: queryParams.startDate, + endTime: queryParams.endDate, + }) + toast.promise(surroundingNotificationsPromise, { + loading: `Loading Notification Data`, + success: `Successfully got Notification Data`, + error: `Failed to get Notification data. Please see console`, + }) + return surroundingNotificationsPromise + }, + { + condition: (_, { getState }) => + selectToken(getState() as RootState) != undefined && + selectQueryParams(getState() as RootState).intersectionId != undefined && + selectQueryParams(getState() as RootState).roadRegulatorId != undefined, + } +) + +export const initializeLiveStreaming = createAsyncThunk( + 'intersectionMap/initializeLiveStreaming', + async (args: { token: string; roadRegulatorId: number; intersectionId: number }, { getState, dispatch }) => { + const { token, roadRegulatorId, intersectionId } = args + // Connect to WebSocket when component mounts + dispatch(onTimeQueryChanged({ eventTime: new Date(), timeBefore: 10, timeAfter: 0, timeWindowSeconds: 2 })) + + let protocols = ['v10.stomp', 'v11.stomp'] + protocols.push(token) + const url = `${process.env.CVIZ_API_WS_URL}/stomp` + console.debug('Connecting to STOMP endpoint: ' + url + ' with token: ' + token) + + // Stomp Client Documentation: https://stomp-js.github.io/stomp-websocket/codo/extra/docs-src/Usage.md.html + let client = Stomp.client(url, protocols) + client.debug = () => {} + + // Topics are in the format /live/{roadRegulatorID}/{intersectionID}/{spat,map,bsm} + let spatTopic = `/live/${roadRegulatorId}/${intersectionId}/spat` + let mapTopic = `/live/${roadRegulatorId}/${intersectionId}/map` + let bsmTopic = `/live/${roadRegulatorId}/${intersectionId}/bsm` // TODO: Filter by road regulator ID + let spatTime = Date.now() + let mapTime = Date.now() + let bsmTime = Date.now() + client.connect( + { + // "username": "test", + // "password": "test", + // Token: token, + }, + () => { + client.subscribe(spatTopic, function (mes: IMessage) { + const spatMessage: ProcessedSpat = JSON.parse(mes.body) + console.debug('Received SPaT message ' + (Date.now() - spatTime) + ' ms') + spatTime = Date.now() + dispatch(renderIterative_Spat([spatMessage])) + dispatch(maybeUpdateSliderValue()) + }) + + client.subscribe(mapTopic, function (mes: IMessage) { + const mapMessage: ProcessedMap = JSON.parse(mes.body) + console.debug('Received MAP message ' + (Date.now() - mapTime) + ' ms') + mapTime = Date.now() + dispatch(renderIterative_Map([mapMessage])) + dispatch(maybeUpdateSliderValue()) + }) + + client.subscribe(bsmTopic, function (mes: IMessage) { + const bsmData: OdeBsmData = JSON.parse(mes.body) + console.debug('Received BSM message ' + (Date.now() - bsmTime) + ' ms') + bsmTime = Date.now() + dispatch(renderIterative_Bsm([bsmData])) + dispatch(maybeUpdateSliderValue()) + }) + }, + (error) => { + console.error('ERROR connecting to live data Websockets', error) + } + ) + } +) + +export const updateRenderedMapState = createAsyncThunk( + 'intersectionMap/updateRenderedMapState', + async (_, { getState, dispatch }) => { + const currentState = getState() as RootState + const authToken = selectToken(currentState)! + const queryParams = selectQueryParams(currentState) + const spatSignalGroups = selectSpatSignalGroups(currentState) + const mapSignalGroups = selectMapSignalGroups(currentState) + const renderTimeInterval = selectRenderTimeInterval(currentState) + const bsmData = selectBsmData(currentState) + const bsmTrailLength = selectBsmTrailLength(currentState) + const surroundingEvents = selectSurroundingEvents(currentState) + const surroundingNotifications = selectSurroundingNotifications(currentState) + + // ASSUMPTION: mapSignalGroups && spatSignalGroups + + let currentSignalGroups: SpatSignalGroup[] | undefined + let signalStateData: SignalStateFeatureCollection | undefined + let spatTime: number | undefined + + // retrieve filtered SPATs + let closestSignalGroup: { spat: SpatSignalGroup[]; datetime: number } | null = null + for (const datetime in spatSignalGroups) { + const datetimeNum = Number(datetime) / 1000 // milliseconds to seconds + if (datetimeNum >= renderTimeInterval[0] && datetimeNum <= renderTimeInterval[1]) { + if ( + closestSignalGroup === null || + Math.abs(datetimeNum - renderTimeInterval[1]) < Math.abs(closestSignalGroup.datetime - renderTimeInterval[1]) + ) { + closestSignalGroup = { datetime: datetimeNum, spat: spatSignalGroups[datetime] } + } + } + } + if (closestSignalGroup !== null) { + currentSignalGroups = closestSignalGroup.spat + signalStateData = generateSignalStateFeatureCollection(mapSignalGroups!, closestSignalGroup.spat) + spatTime = closestSignalGroup.datetime + } + + // retrieve filtered BSMs + const filteredBsms: BsmFeature[] = bsmData?.features?.filter( + (feature) => + feature.properties?.odeReceivedAt >= renderTimeInterval[0] && + feature.properties?.odeReceivedAt <= renderTimeInterval[1] + ) + const sortedBsms = filteredBsms.sort((a, b) => b.properties.odeReceivedAt - a.properties.odeReceivedAt) + const lastBsms = sortedBsms.slice(0, bsmTrailLength) // Apply BSM trail length + const currentBsms = { ...bsmData, features: lastBsms } + + // Update BSM legend colors + const uniqueIds = new Set(filteredBsms.map((bsm) => bsm.properties?.id)) + const colors = generateColorDictionary(uniqueIds) + + dispatch(setBsmLegendColors(colors)) + const bsmLayerStyle = generateMapboxStyleExpression(colors) + dispatch(setBsmCircleColor(bsmLayerStyle)) + + const filteredEvents: MessageMonitor.Event[] = surroundingEvents.filter( + (event) => + event.eventGeneratedAt / 1000 >= renderTimeInterval[0] && event.eventGeneratedAt / 1000 <= renderTimeInterval[1] + ) + + const filteredNotifications: MessageMonitor.Notification[] = surroundingNotifications.filter( + (notification) => + notification.notificationGeneratedAt / 1000 >= renderTimeInterval[0] && + notification.notificationGeneratedAt / 1000 <= renderTimeInterval[1] + ) + + return { + currentSignalGroups: closestSignalGroup?.spat, + signalStateData: closestSignalGroup + ? generateSignalStateFeatureCollection(mapSignalGroups!, closestSignalGroup?.spat) + : undefined, + spatTime: closestSignalGroup?.datetime, + currentBsms, + filteredSurroundingEvents: filteredEvents, + filteredSurroundingNotifications: filteredNotifications, + } + }, + { + condition: (_, { getState }) => + selectToken(getState() as RootState) != undefined && + selectQueryParams(getState() as RootState).intersectionId != undefined && + selectQueryParams(getState() as RootState).roadRegulatorId != undefined, + } +) + +const compareQueryParams = (oldParams: MAP_QUERY_PARAMS, newParams: MAP_QUERY_PARAMS) => { + return ( + oldParams.startDate.getTime() != newParams.startDate.getTime() || + oldParams.endDate.getTime() != newParams.endDate.getTime() || + oldParams.eventDate.getTime() != newParams.eventDate.getTime() || + oldParams.vehicleId != newParams.vehicleId || + oldParams.intersectionId != newParams.intersectionId || + oldParams.roadRegulatorId != newParams.roadRegulatorId + ) +} + +const generateRenderTimeInterval = (startDate: Date, sliderValue: number, timeWindowSeconds: number) => { + const startTime = startDate.getTime() / 1000 + + const filteredStartTime = startTime + sliderValue - timeWindowSeconds + const filteredEndTime = startTime + sliderValue + + return [filteredStartTime, filteredEndTime] +} + +const _updateQueryParams = ({ + state, + startDate, + endDate, + eventDate, + vehicleId, + intersectionId, + roadRegulatorId, + resetTimeWindow, + updateSlider, +}: { + state: RootState['intersectionMap'] + startDate?: Date + endDate?: Date + eventDate?: Date + vehicleId?: string + intersectionId?: number + roadRegulatorId?: number + resetTimeWindow?: boolean + updateSlider?: boolean +}) => { + const newQueryParams = { + startDate: startDate ?? state.value.queryParams.startDate, + endDate: endDate ?? state.value.queryParams.endDate, + eventDate: eventDate ?? state.value.queryParams.eventDate, + vehicleId: vehicleId ?? state.value.queryParams.vehicleId, + intersectionId: intersectionId ?? state.value.queryParams.intersectionId, + roadRegulatorId: roadRegulatorId ?? state.value.queryParams.roadRegulatorId, + } + if (compareQueryParams(state.value.queryParams, newQueryParams)) { + state.value.queryParams = newQueryParams + state.value.sliderTimeValue = getNewSliderTimeValue( + state.value.queryParams.startDate, + state.value.sliderValue, + state.value.timeWindowSeconds + ) + if (resetTimeWindow) state.value.timeWindowSeconds = 60 + if (updateSlider) state.value.sliderValue = getTimeRange(newQueryParams.startDate, newQueryParams.endDate) + } +} + +const _downloadData = (rawData: RAW_MESSAGE_DATA_EXPORT, queryParams: MAP_QUERY_PARAMS) => { + var zip = new JSZip() + zip.file(`intersection_${queryParams.intersectionId}_MAP_data.json`, JSON.stringify(rawData.map)) + zip.file(`intersection_${queryParams.intersectionId}_SPAT_data.json`, JSON.stringify(rawData.spat)) + zip.file(`intersection_${queryParams.intersectionId}_BSM_data.json`, JSON.stringify(rawData.bsm)) + if (rawData.event) + zip.file(`intersection_${queryParams.intersectionId}_Event_data.json`, JSON.stringify(rawData.event)) + if (rawData.assessment) + zip.file(`intersection_${queryParams.intersectionId}_Assessment_data.json`, JSON.stringify(rawData.assessment)) + if (rawData.notification) + zip.file(`intersection_${queryParams.intersectionId}_Notification_data.json`, JSON.stringify(rawData.notification)) + + zip.generateAsync({ type: 'blob' }).then(function (content) { + FileSaver.saveAs(content, `intersection_${queryParams.intersectionId}_data.zip`) + }) +} + +export const downloadMapData = createAsyncThunk( + 'intersectionMap/downloadMapData', + async (_, { getState }) => { + const currentState = getState() as RootState + const rawData = selectRawData(currentState)! + const queryParams = selectQueryParams(currentState) + + return _downloadData(rawData, queryParams) + }, + { + condition: (_, { getState }) => + selectToken(getState() as RootState) != undefined && + selectQueryParams(getState() as RootState).intersectionId != undefined && + selectQueryParams(getState() as RootState).roadRegulatorId != undefined, + } +) + +export const intersectionMapSlice = createSlice({ + name: 'intersectionMap', + initialState: { + loading: false, + value: initialState, + }, + reducers: { + setSurroundingEvents: (state, action: PayloadAction) => { + state.value.surroundingEvents = action.payload + }, + maybeUpdateSliderValue: (state) => { + if ( + state.value.liveDataActive && + (!state.value.lastSliderUpdate || Date.now() - state.value.lastSliderUpdate > 1 * 1000) + ) { + _updateQueryParams({ + state, + startDate: new Date( + Date.now() - (state.value.queryParams.endDate.getTime() - state.value.queryParams.startDate.getTime()) + ), + endDate: new Date(Date.now()), + eventDate: new Date(Date.now()), + vehicleId: undefined, + intersectionId: state.value.queryParams.intersectionId, + roadRegulatorId: state.value.queryParams.roadRegulatorId, + updateSlider: true, + }) + } + }, + updateLiveSliderValue: (state) => { + const newQueryParams = { + startDate: new Date( + Date.now() - (state.value.queryParams.endDate.getTime() - state.value.queryParams.startDate.getTime()) + ), + endDate: new Date(Date.now()), + eventDate: new Date(Date.now()), + vehicleId: undefined, + intersectionId: state.value.queryParams.intersectionId, + roadRegulatorId: state.value.queryParams.roadRegulatorId, + } + state.value.queryParams = newQueryParams + state.value.sliderValue = getTimeRange(newQueryParams.startDate, newQueryParams.endDate) + state.value.renderTimeInterval = [ + newQueryParams.startDate.getTime() / 1000, + newQueryParams.endDate.getTime() / 1000, + ] + state.value.sliderTimeValue = getNewSliderTimeValue( + state.value.queryParams.startDate, + state.value.sliderValue, + state.value.timeWindowSeconds + ) + }, + setViewState: (state, action: PayloadAction) => { + state.value.viewState = action.payload + }, + handleImportedMapMessageData: ( + state, + action: PayloadAction<{ + mapData: ProcessedMap[] + bsmData: OdeBsmData[] + spatData: ProcessedSpat[] + notificationData: any + }> + ) => { + const { mapData, bsmData, spatData, notificationData } = action.payload + const sortedSpatData = spatData.sort((x, y) => { + if (x.utcTimeStamp < y.utcTimeStamp) { + return 1 + } + if (x.utcTimeStamp > y.utcTimeStamp) { + return -1 + } + return 0 + }) + const endTime = new Date(Date.parse(sortedSpatData[0].utcTimeStamp)) + const startTime = new Date(Date.parse(sortedSpatData[sortedSpatData.length - 1].utcTimeStamp)) + state.value.importedMessageData = { mapData, bsmData, spatData, notificationData } + state.value.queryParams = { + startDate: startTime, + endDate: endTime, + eventDate: startTime, + intersectionId: mapData[0].properties.intersectionId, + roadRegulatorId: -1, + } + state.value.sliderTimeValue = getNewSliderTimeValue( + state.value.queryParams.startDate, + state.value.sliderValue, + state.value.timeWindowSeconds + ) + state.value.timeWindowSeconds = 60 + }, + updateQueryParams: ( + state, + action: PayloadAction<{ + startDate?: Date + endDate?: Date + eventDate?: Date + vehicleId?: string + intersectionId?: number + roadRegulatorId?: number + resetTimeWindow?: boolean + updateSlider?: boolean + }> + ) => { + _updateQueryParams({ state, ...action.payload }) + }, + onTimeQueryChanged: ( + state, + action: PayloadAction<{ + eventTime?: Date + timeBefore?: number + timeAfter?: number + timeWindowSeconds?: number + }> + ) => { + let { eventTime, timeBefore, timeAfter, timeWindowSeconds } = action.payload + eventTime ??= new Date() + const updatedQueryParams = { + startDate: new Date(eventTime.getTime() - (timeBefore ?? 0) * 1000), + endDate: new Date(eventTime.getTime() + (timeAfter ?? 0) * 1000), + eventDate: eventTime, + intersectionId: state.value.queryParams.intersectionId, + roadRegulatorId: state.value.queryParams.roadRegulatorId, + } + if (compareQueryParams(state.value.queryParams, updatedQueryParams)) { + // Detected change in query params + state.value.queryParams = updatedQueryParams + state.value.sliderTimeValue = getNewSliderTimeValue( + state.value.queryParams.startDate, + state.value.sliderValue, + state.value.timeWindowSeconds + ) + } else { + // No change in query params + } + state.value.timeWindowSeconds = timeWindowSeconds ?? state.value.timeWindowSeconds + }, + setSliderValue: (state, action: PayloadAction) => { + state.value.sliderValue = action.payload as number + state.value.liveDataActive = false + }, + updateRenderTimeInterval: (state) => { + state.value.renderTimeInterval = generateRenderTimeInterval( + state.value.queryParams.startDate, + state.value.sliderValue, + state.value.timeWindowSeconds + ) + }, + onMapClick: ( + state, + action: PayloadAction<{ + event: { point: mapboxgl.Point; lngLat: mapboxgl.LngLat } + mapRef: React.MutableRefObject + }> + ) => { + const features = action.payload.mapRef.current.queryRenderedFeatures(action.payload.event.point, { + // layers: allInteractiveLayerIds, + }) + const feature = features?.[0] + if (feature && state.value.allInteractiveLayerIds.includes(feature.layer.id)) { + state.value.selectedFeature = { clickedLocation: action.payload.event.lngLat, feature } + } else { + state.value.selectedFeature = undefined + } + }, + onMapMouseMove: ( + state, + action: PayloadAction<{ features: mapboxgl.MapboxGeoJSONFeature[] | undefined; lngLat: mapboxgl.LngLat }> + ) => { + const feature = action.payload.features?.[0] + if (feature && state.value.allInteractiveLayerIds.includes(feature.layer.id as MAP_LAYERS)) { + state.value.hoveredFeature = { clickedLocation: action.payload.lngLat, feature } + } + }, + onMapMouseEnter: ( + state, + action: PayloadAction<{ features: mapboxgl.MapboxGeoJSONFeature[] | undefined; lngLat: mapboxgl.LngLat }> + ) => { + state.value.cursor = 'pointer' + const feature = action.payload.features?.[0] + if (feature && state.value.allInteractiveLayerIds.includes(feature.layer.id as MAP_LAYERS)) { + state.value.hoveredFeature = { clickedLocation: action.payload.lngLat, feature } + } else { + state.value.hoveredFeature = undefined + } + }, + onMapMouseLeave: (state) => { + state.value.cursor = '' + state.value.hoveredFeature = undefined + }, + cleanUpLiveStreaming: (state) => { + if (state.value.wsClient) { + state.value.wsClient.disconnect(() => { + console.debug('Disconnected from STOMP endpoint') + }) + state.value.timeWindowSeconds = 60 + } + state.value.wsClient = undefined + }, + setLoadInitialdataTimeoutId: (state, action: PayloadAction) => { + state.value.loadInitialDataTimeoutId = action.payload + }, + clearSelectedFeature: (state) => { + state.value.selectedFeature = undefined + }, + clearHoveredFeature: (state) => { + state.value.hoveredFeature = undefined + }, + setLaneLabelsVisible: (state, action: PayloadAction) => { + state.value.laneLabelsVisible = action.payload + }, + setSigGroupLabelsVisible: (state, action: PayloadAction) => { + state.value.sigGroupLabelsVisible = action.payload + }, + setShowPopupOnHover: (state, action: PayloadAction) => { + state.value.showPopupOnHover = action.payload + }, + toggleLiveDataActive: (state) => { + state.value.liveDataActive = !state.value.liveDataActive + }, + setBsmTrailLength: (state, action: PayloadAction) => { + state.value.bsmTrailLength = action.payload + }, + setTimeWindowSeconds: (state, action: PayloadAction) => { + state.value.bsmTrailLength = action.payload + }, + setRawData: (state, action: PayloadAction) => { + state.value.rawData.map = action.payload.map ?? state.value.rawData.map + state.value.rawData.spat = action.payload.spat ?? state.value.rawData.spat + state.value.rawData.bsm = action.payload.bsm ?? state.value.rawData.bsm + state.value.rawData.notification = action.payload.notification ?? state.value.rawData.notification + state.value.rawData.event = action.payload.event ?? state.value.rawData.event + state.value.rawData.assessment = action.payload.assessment ?? state.value.rawData.assessment + }, + setMapProps: (state, action: PayloadAction) => { + state.value.sourceData = action.payload.sourceData + state.value.sourceDataType = action.payload.sourceDataType + state.value.intersectionId = action.payload.intersectionId + state.value.roadRegulatorId = action.payload.roadRegulatorId + state.value.loadOnNull = action.payload.loadOnNull + }, + }, + extraReducers: (builder) => { + builder + .addCase(getSurroundingEvents.fulfilled, (state, action: PayloadAction) => { + state.value.surroundingEvents = action.payload + }) + .addCase(getSurroundingNotifications.fulfilled, (state, action: PayloadAction) => { + state.value.surroundingNotifications = action.payload + }) + .addCase( + pullInitialData.fulfilled, + ( + state, + action: PayloadAction< + | { + mapData: ProcessedMap + connectingLanes: ConnectingLanesFeatureCollection + spatSignalGroups: SpatSignalGroups + mapSignalGroups: SignalStateFeatureCollection + mapTime: number + } + | undefined + > + ) => { + if (!action.payload) return + state.value.mapData = action.payload.mapData + if (action.payload.mapData != null) + state.value.viewState = { + latitude: action.payload.mapData.properties.refPoint.latitude, + longitude: action.payload.mapData.properties.refPoint.longitude, + zoom: 19, + } + state.value.connectingLanes = action.payload.connectingLanes + state.value.spatSignalGroups = action.payload.spatSignalGroups + state.value.mapSignalGroups = action.payload.mapSignalGroups + state.value.mapSpatTimes = { ...state.value.mapSpatTimes, mapTime: action.payload.mapTime } + } + ) + .addCase( + renderEntireMap.fulfilled, + ( + state, + action: PayloadAction<{ + mapData: ProcessedMap + connectingLanes: ConnectingLanesFeatureCollection + spatSignalGroups: SpatSignalGroups + mapSignalGroups: SignalStateFeatureCollection + mapTime: number + bsmData: BsmFeatureCollection + rawData: any + sliderValue: number + }> + ) => { + state.value.mapData = action.payload.mapData + if (action.payload.mapData != null) + state.value.viewState = { + latitude: action.payload.mapData.properties.refPoint.latitude, + longitude: action.payload.mapData.properties.refPoint.longitude, + zoom: 19, + } + state.value.connectingLanes = action.payload.connectingLanes + state.value.spatSignalGroups = action.payload.spatSignalGroups + state.value.mapSignalGroups = action.payload.mapSignalGroups + state.value.mapSpatTimes = { ...state.value.mapSpatTimes, mapTime: action.payload.mapTime } + state.value.bsmData = action.payload.bsmData + state.value.rawData = action.payload.rawData + state.value.sliderValue = action.payload.sliderValue + state.value.sliderTimeValue = getNewSliderTimeValue( + state.value.queryParams.startDate, + state.value.sliderValue, + state.value.timeWindowSeconds + ) + } + ) + .addCase( + renderIterative_Map.fulfilled, + ( + state, + action: PayloadAction<{ + currentMapData: ProcessedMap[] + connectingLanes: ConnectingLanesFeatureCollection + mapData: ProcessedMap + mapTime: number + mapSignalGroups: SignalStateFeatureCollection + }> + ) => { + state.value.currentMapData = action.payload.currentMapData + const previousMapMessage: ProcessedMap | undefined = action.payload.currentMapData.at(-1) + if ( + state.value.mapData != null && + (state.value.mapData.properties.refPoint.latitude != previousMapMessage?.properties.refPoint.latitude || + state.value.mapData.properties.refPoint.longitude != previousMapMessage?.properties.refPoint.longitude) + ) + state.value.viewState = { + latitude: action.payload.mapData.properties.refPoint.latitude, + longitude: action.payload.mapData.properties.refPoint.longitude, + zoom: 19, + } + state.value.connectingLanes = action.payload.connectingLanes + state.value.mapData = action.payload.mapData + state.value.mapSignalGroups = action.payload.mapSignalGroups + state.value.mapSpatTimes = { ...state.value.mapSpatTimes, mapTime: action.payload.mapTime } + } + ) + .addCase( + renderIterative_Spat.fulfilled, + ( + state, + action: PayloadAction<{ + currentProcessedSpatDataLocal: ProcessedSpat[] + currentSpatSignalGroupsLocal: SpatSignalGroups + }> + ) => { + state.value.currentSpatData = action.payload.currentProcessedSpatDataLocal + state.value.spatSignalGroups = action.payload.currentSpatSignalGroupsLocal + } + ) + .addCase(renderIterative_Bsm.fulfilled, (state, action: PayloadAction) => { + state.value.currentBsmData = action.payload + }) + .addCase( + updateRenderedMapState.fulfilled, + ( + state, + action: PayloadAction<{ + currentSignalGroups: SpatSignalGroup[] | undefined + signalStateData: SignalStateFeatureCollection | undefined + spatTime: number | undefined + currentBsms: BsmFeatureCollection + filteredSurroundingEvents: MessageMonitor.Event[] + filteredSurroundingNotifications: MessageMonitor.Notification[] + }> + ) => { + state.value.currentSignalGroups = action.payload.currentSignalGroups ?? state.value.currentSignalGroups + state.value.signalStateData = action.payload.signalStateData ?? state.value.signalStateData + state.value.mapSpatTimes = { + ...state.value.mapSpatTimes, + spatTime: action.payload.spatTime ?? state.value.mapSpatTimes.spatTime, + } + state.value.currentBsms = action.payload.currentBsms + state.value.filteredSurroundingEvents = action.payload.filteredSurroundingEvents + state.value.filteredSurroundingNotifications = action.payload.filteredSurroundingNotifications + } + ) + }, +}) + +export const selectLoading = (state: RootState) => state.intersectionMap.loading + +export const selectLayersVisible = (state: RootState) => state.intersectionMap.value.layersVisible +export const selectAllInteractiveLayerIds = (state: RootState) => state.intersectionMap.value.allInteractiveLayerIds +export const selectQueryParams = (state: RootState) => state.intersectionMap.value.queryParams +export const selectSourceApi = (state: RootState) => state.intersectionMap.value.sourceApi +export const selectSourceData = (state: RootState) => state.intersectionMap.value.sourceData +export const selectSourceDataType = (state: RootState) => state.intersectionMap.value.sourceDataType +export const selectIntersectionId = (state: RootState) => state.intersectionMap.value.intersectionId +export const selectRoadRegulatorId = (state: RootState) => state.intersectionMap.value.roadRegulatorId +export const selectLoadOnNull = (state: RootState) => state.intersectionMap.value.loadOnNull +export const selectMapData = (state: RootState) => state.intersectionMap.value.mapData +export const selectBsmData = (state: RootState) => state.intersectionMap.value.bsmData +export const selectMapSignalGroups = (state: RootState) => state.intersectionMap.value.mapSignalGroups +export const selectSignalStateData = (state: RootState) => state.intersectionMap.value.signalStateData +export const selectSpatSignalGroups = (state: RootState) => state.intersectionMap.value.spatSignalGroups +export const selectCurrentSignalGroups = (state: RootState) => state.intersectionMap.value.currentSignalGroups +export const selectCurrentBsms = (state: RootState) => state.intersectionMap.value.currentBsms +export const selectConnectingLanes = (state: RootState) => state.intersectionMap.value.connectingLanes +export const selectSurroundingEvents = (state: RootState) => state.intersectionMap.value.surroundingEvents +export const selectFilteredSurroundingEvents = (state: RootState) => + state.intersectionMap.value.filteredSurroundingEvents +export const selectSurroundingNotifications = (state: RootState) => state.intersectionMap.value.surroundingNotifications +export const selectFilteredSurroundingNotifications = (state: RootState) => + state.intersectionMap.value.filteredSurroundingNotifications +export const selectViewState = (state: RootState) => state.intersectionMap.value.viewState +export const selectTimeWindowSeconds = (state: RootState) => state.intersectionMap.value.timeWindowSeconds +export const selectSliderValue = (state: RootState) => state.intersectionMap.value.sliderValue +export const selectRenderTimeInterval = (state: RootState) => state.intersectionMap.value.renderTimeInterval +export const selectHoveredFeature = (state: RootState) => state.intersectionMap.value.hoveredFeature +export const selectSelectedFeature = (state: RootState) => state.intersectionMap.value.selectedFeature +export const selectRawData = (state: RootState) => state.intersectionMap.value.rawData +export const selectMapSpatTimes = (state: RootState) => state.intersectionMap.value.mapSpatTimes +export const selectSigGroupLabelsVisible = (state: RootState) => state.intersectionMap.value.sigGroupLabelsVisible +export const selectLaneLabelsVisible = (state: RootState) => state.intersectionMap.value.laneLabelsVisible +export const selectShowPopupOnHover = (state: RootState) => state.intersectionMap.value.showPopupOnHover +export const selectImportedMessageData = (state: RootState) => state.intersectionMap.value.importedMessageData +export const selectCursor = (state: RootState) => state.intersectionMap.value.cursor +export const selectLoadInitialDataTimeoutId = (state: RootState) => state.intersectionMap.value.loadInitialDataTimeoutId +// export const selectWsClient = (state: RootState) => state.intersectionMap.value.wsClient; +export const selectLiveDataActive = (state: RootState) => state.intersectionMap.value.liveDataActive +export const selectCurrentMapData = (state: RootState) => state.intersectionMap.value.currentMapData +export const selectCurrentSpatData = (state: RootState) => state.intersectionMap.value.currentSpatData +export const selectCurrentBsmData = (state: RootState) => state.intersectionMap.value.currentBsmData +export const selectSliderTimeValue = (state: RootState) => state.intersectionMap.value.sliderTimeValue +export const selectBsmTrailLength = (state: RootState) => state.intersectionMap.value.bsmTrailLength + +export const { + setSurroundingEvents, + maybeUpdateSliderValue, + setViewState, + handleImportedMapMessageData, + updateQueryParams, + onTimeQueryChanged, + setSliderValue, + updateRenderTimeInterval, + onMapClick, + onMapMouseMove, + onMapMouseEnter, + onMapMouseLeave, + cleanUpLiveStreaming, + setLoadInitialdataTimeoutId, + clearSelectedFeature, + clearHoveredFeature, + setLaneLabelsVisible, + setSigGroupLabelsVisible, + setShowPopupOnHover, + toggleLiveDataActive, + setBsmTrailLength, + setRawData, + setMapProps, +} = intersectionMapSlice.actions + +export default intersectionMapSlice.reducer diff --git a/webapp/src/components/map/popup.tsx b/webapp/src/components/map/popup.tsx new file mode 100644 index 000000000..77b9c495a --- /dev/null +++ b/webapp/src/components/map/popup.tsx @@ -0,0 +1,93 @@ +import React, { useState, useEffect } from "react"; +import Map, { Source, Layer, Popup } from "react-map-gl"; + +import { Container, Col } from "reactstrap"; + +import { Paper, Box, Typography } from "@mui/material"; +import { CustomTable } from "./custom-table"; + +export const getSelectedLayerPopupContent = (feature: any) => { + switch (feature?.layer?.id) { + case "bsm": + let bsm = feature.properties; + return ( + + BSM + + + ); + case "mapMessage": + let map = feature.properties; + let connectedObjs: any[] = []; + JSON.parse(map?.connectsTo ?? "[]")?.forEach((connectsTo) => { + connectedObjs.push(["Connected Lane", connectsTo.connectingLane.lane]); + connectedObjs.push(["Signal Group", connectsTo.signalGroup]); + connectedObjs.push(["Connection ID", connectsTo.connectionID]); + }); + return ( + + MAP Lane + + + ); + + case "connectingLanes": + return ( + + Connecting Lane + + + ); + + case "signalStates": + return ( + + Signal State + + + ); + default: { + return {JSON.stringify(feature)}; + } + } + return No Data; +}; + +export const CustomPopup = (props) => { + return ( + {}} + maxWidth={"500px"} + closeOnClick={false} + > + {getSelectedLayerPopupContent(props.selectedFeature.feature)} + + ); +}; diff --git a/webapp/src/components/map/processed_map_v4.json b/webapp/src/components/map/processed_map_v4.json new file mode 100644 index 000000000..9b5c1c90e --- /dev/null +++ b/webapp/src/components/map/processed_map_v4.json @@ -0,0 +1,2459 @@ +{ + "mapFeatureCollection": { + "type": "FeatureCollection", + "features": [ + { + "type": "Feature", + "id": 2, + "geometry": { + "type": "LineString", + "coordinates": [ + [-105.0911523, 39.5953376], + [-105.0899239, 39.5953231], + [-105.0896005, 39.5953332], + [-105.0888831, 39.595317], + [-105.088108, 39.5953159], + [-105.0876637, 39.5953153] + ] + }, + "properties": { + "nodes": [ + { + "delta": [2225, 808], + "stopLine": null, + "dwidth": null, + "delevation": -10 + }, + { + "delta": [10517, -161], + "stopLine": null, + "dwidth": null, + "delevation": -60 + }, + { + "delta": [2769, 112], + "stopLine": null, + "dwidth": null, + "delevation": -10 + }, + { + "delta": [6142, -180], + "stopLine": null, + "dwidth": null, + "delevation": -30 + }, + { + "delta": [6636, -12], + "stopLine": null, + "dwidth": null, + "delevation": -20 + }, + { + "delta": [3804, -7], + "stopLine": null, + "dwidth": null, + "delevation": null + } + ], + "laneId": 2, + "sharedWith": { + "busVehicleTraffic": false, + "trackedVehicleTraffic": false, + "individualMotorizedVehicleTraffic": false, + "taxiVehicleTraffic": false, + "overlappingLaneDescriptionProvided": false, + "cyclistVehicleTraffic": false, + "otherNonMotorizedTrafficTypes": false, + "multipleLanesTreatedAsOneLane": false, + "pedestrianTraffic": false, + "pedestriansTraffic": false + }, + "egressApproach": 0, + "ingressApproach": 1, + "ingressPath": true, + "egressPath": false, + "maneuvers": { + "maneuverStraightAllowed": true, + "maneuverNoStoppingAllowed": false, + "goWithHalt": false, + "maneuverLeftAllowed": false, + "maneuverUTurnAllowed": false, + "maneuverLeftTurnOnRedAllowed": false, + "reserved1": false, + "maneuverRightAllowed": false, + "maneuverLaneChangeAllowed": false, + "yieldAllwaysRequired": false, + "maneuverRightTurnOnRedAllowed": false, + "caution": false + }, + "connectsTo": [ + { + "connectingLane": { + "lane": 19, + "maneuver": { + "maneuverStraightAllowed": true, + "maneuverNoStoppingAllowed": false, + "goWithHalt": false, + "maneuverLeftAllowed": false, + "maneuverUTurnAllowed": false, + "maneuverLeftTurnOnRedAllowed": false, + "reserved1": false, + "maneuverRightAllowed": false, + "maneuverLaneChangeAllowed": false, + "yieldAllwaysRequired": false, + "maneuverRightTurnOnRedAllowed": false, + "caution": false + } + }, + "remoteIntersection": null, + "signalGroup": 8, + "userClass": null, + "connectionID": 1 + } + ] + } + }, + { + "type": "Feature", + "id": 3, + "geometry": { + "type": "LineString", + "coordinates": [ + [-105.0911527, 39.5953113], + [-105.0908101, 39.5953039], + [-105.0900791, 39.5952962], + [-105.0896801, 39.5952874], + [-105.0888781, 39.5952839], + [-105.0885678, 39.5952789], + [-105.0880704, 39.5952849], + [-105.0876638, 39.5952849] + ] + }, + "properties": { + "nodes": [ + { + "delta": [2222, 515], + "stopLine": null, + "dwidth": null, + "delevation": -10 + }, + { + "delta": [2933, -82], + "stopLine": null, + "dwidth": null, + "delevation": -10 + }, + { + "delta": [6259, -85], + "stopLine": null, + "dwidth": null, + "delevation": -40 + }, + { + "delta": [3416, -98], + "stopLine": null, + "dwidth": null, + "delevation": -20 + }, + { + "delta": [6867, -39], + "stopLine": null, + "dwidth": null, + "delevation": -30 + }, + { + "delta": [2657, -55], + "stopLine": null, + "dwidth": null, + "delevation": -10 + }, + { + "delta": [4259, 67], + "stopLine": null, + "dwidth": null, + "delevation": -10 + }, + { + "delta": [3481, 0], + "stopLine": null, + "dwidth": null, + "delevation": null + } + ], + "laneId": 3, + "sharedWith": { + "busVehicleTraffic": false, + "trackedVehicleTraffic": false, + "individualMotorizedVehicleTraffic": false, + "taxiVehicleTraffic": false, + "overlappingLaneDescriptionProvided": false, + "cyclistVehicleTraffic": false, + "otherNonMotorizedTrafficTypes": false, + "multipleLanesTreatedAsOneLane": false, + "pedestrianTraffic": false, + "pedestriansTraffic": false + }, + "egressApproach": 0, + "ingressApproach": 1, + "ingressPath": true, + "egressPath": false, + "maneuvers": { + "maneuverStraightAllowed": true, + "maneuverNoStoppingAllowed": false, + "goWithHalt": false, + "maneuverLeftAllowed": false, + "maneuverUTurnAllowed": false, + "maneuverLeftTurnOnRedAllowed": false, + "reserved1": false, + "maneuverRightAllowed": false, + "maneuverLaneChangeAllowed": false, + "yieldAllwaysRequired": false, + "maneuverRightTurnOnRedAllowed": false, + "caution": false + }, + "connectsTo": [ + { + "connectingLane": { + "lane": 18, + "maneuver": { + "maneuverStraightAllowed": true, + "maneuverNoStoppingAllowed": false, + "goWithHalt": false, + "maneuverLeftAllowed": false, + "maneuverUTurnAllowed": false, + "maneuverLeftTurnOnRedAllowed": false, + "reserved1": false, + "maneuverRightAllowed": false, + "maneuverLaneChangeAllowed": false, + "yieldAllwaysRequired": false, + "maneuverRightTurnOnRedAllowed": false, + "caution": false + } + }, + "remoteIntersection": null, + "signalGroup": 8, + "userClass": null, + "connectionID": 1 + } + ] + } + }, + { + "type": "Feature", + "id": 4, + "geometry": { + "type": "LineString", + "coordinates": [ + [-105.0911537, 39.5952537], + [-105.0903143, 39.5952555], + [-105.0902511, 39.5952591], + [-105.0900995, 39.5952797] + ] + }, + "properties": { + "nodes": [ + { + "delta": [2213, -124], + "stopLine": null, + "dwidth": null, + "delevation": -10 + }, + { + "delta": [7187, 20], + "stopLine": null, + "dwidth": null, + "delevation": -40 + }, + { + "delta": [541, 40], + "stopLine": null, + "dwidth": null, + "delevation": -10 + }, + { + "delta": [1298, 229], + "stopLine": null, + "dwidth": null, + "delevation": null + } + ], + "laneId": 4, + "sharedWith": { + "busVehicleTraffic": false, + "trackedVehicleTraffic": false, + "individualMotorizedVehicleTraffic": false, + "taxiVehicleTraffic": false, + "overlappingLaneDescriptionProvided": false, + "cyclistVehicleTraffic": false, + "otherNonMotorizedTrafficTypes": false, + "multipleLanesTreatedAsOneLane": false, + "pedestrianTraffic": false, + "pedestriansTraffic": false + }, + "egressApproach": 0, + "ingressApproach": 1, + "ingressPath": true, + "egressPath": false, + "maneuvers": { + "maneuverStraightAllowed": false, + "maneuverNoStoppingAllowed": false, + "goWithHalt": false, + "maneuverLeftAllowed": true, + "maneuverUTurnAllowed": false, + "maneuverLeftTurnOnRedAllowed": false, + "reserved1": false, + "maneuverRightAllowed": false, + "maneuverLaneChangeAllowed": false, + "yieldAllwaysRequired": false, + "maneuverRightTurnOnRedAllowed": false, + "caution": false + }, + "connectsTo": [ + { + "connectingLane": { + "lane": 12, + "maneuver": { + "maneuverStraightAllowed": false, + "maneuverNoStoppingAllowed": false, + "goWithHalt": false, + "maneuverLeftAllowed": true, + "maneuverUTurnAllowed": false, + "maneuverLeftTurnOnRedAllowed": false, + "reserved1": false, + "maneuverRightAllowed": false, + "maneuverLaneChangeAllowed": false, + "yieldAllwaysRequired": false, + "maneuverRightTurnOnRedAllowed": false, + "caution": false + } + }, + "remoteIntersection": null, + "signalGroup": 3, + "userClass": null, + "connectionID": 1 + } + ] + } + }, + { + "type": "Feature", + "id": 6, + "geometry": { + "type": "LineString", + "coordinates": [ + [-105.0911474, 39.5951732], + [-105.0911048, 39.5951738] + ] + }, + "properties": { + "nodes": [ + { + "delta": [2267, -1019], + "stopLine": null, + "dwidth": null, + "delevation": -10 + }, + { + "delta": [365, 7], + "stopLine": null, + "dwidth": null, + "delevation": null + } + ], + "laneId": 6, + "sharedWith": { + "busVehicleTraffic": false, + "trackedVehicleTraffic": false, + "individualMotorizedVehicleTraffic": false, + "taxiVehicleTraffic": false, + "overlappingLaneDescriptionProvided": false, + "cyclistVehicleTraffic": false, + "otherNonMotorizedTrafficTypes": false, + "multipleLanesTreatedAsOneLane": false, + "pedestrianTraffic": false, + "pedestriansTraffic": false + }, + "egressApproach": 2, + "ingressApproach": 0, + "ingressPath": false, + "egressPath": true + } + }, + { + "type": "Feature", + "id": 5, + "geometry": { + "type": "LineString", + "coordinates": [ + [-105.0911474, 39.5952053], + [-105.0911079, 39.5952053] + ] + }, + "properties": { + "nodes": [ + { + "delta": [2267, -662], + "stopLine": null, + "dwidth": null, + "delevation": -10 + }, + { + "delta": [338, 0], + "stopLine": null, + "dwidth": null, + "delevation": null + } + ], + "laneId": 5, + "sharedWith": { + "busVehicleTraffic": false, + "trackedVehicleTraffic": false, + "individualMotorizedVehicleTraffic": false, + "taxiVehicleTraffic": false, + "overlappingLaneDescriptionProvided": false, + "cyclistVehicleTraffic": false, + "otherNonMotorizedTrafficTypes": false, + "multipleLanesTreatedAsOneLane": false, + "pedestrianTraffic": false, + "pedestriansTraffic": false + }, + "egressApproach": 2, + "ingressApproach": 0, + "ingressPath": false, + "egressPath": true + } + }, + { + "type": "Feature", + "id": 7, + "geometry": { + "type": "LineString", + "coordinates": [ + [-105.0911443, 39.5951332], + [-105.0911017, 39.5951356] + ] + }, + "properties": { + "nodes": [ + { + "delta": [2294, -1463], + "stopLine": null, + "dwidth": null, + "delevation": -10 + }, + { + "delta": [365, 27], + "stopLine": null, + "dwidth": null, + "delevation": null + } + ], + "laneId": 7, + "sharedWith": { + "busVehicleTraffic": false, + "trackedVehicleTraffic": false, + "individualMotorizedVehicleTraffic": false, + "taxiVehicleTraffic": false, + "overlappingLaneDescriptionProvided": false, + "cyclistVehicleTraffic": false, + "otherNonMotorizedTrafficTypes": false, + "multipleLanesTreatedAsOneLane": false, + "pedestrianTraffic": false, + "pedestriansTraffic": false + }, + "egressApproach": 2, + "ingressApproach": 0, + "ingressPath": false, + "egressPath": true + } + }, + { + "type": "Feature", + "id": 12, + "geometry": { + "type": "LineString", + "coordinates": [ + [-105.0915234, 39.5950941], + [-105.0915234, 39.5950602] + ] + }, + "properties": { + "nodes": [ + { + "delta": [-952, -1898], + "stopLine": null, + "dwidth": null, + "delevation": null + }, + { + "delta": [0, -377], + "stopLine": null, + "dwidth": null, + "delevation": null + } + ], + "laneId": 12, + "sharedWith": { + "busVehicleTraffic": false, + "trackedVehicleTraffic": false, + "individualMotorizedVehicleTraffic": false, + "taxiVehicleTraffic": false, + "overlappingLaneDescriptionProvided": false, + "cyclistVehicleTraffic": false, + "otherNonMotorizedTrafficTypes": false, + "multipleLanesTreatedAsOneLane": false, + "pedestrianTraffic": false, + "pedestriansTraffic": false + }, + "egressApproach": 4, + "ingressApproach": 0, + "ingressPath": false, + "egressPath": true + } + }, + { + "type": "Feature", + "id": 13, + "geometry": { + "type": "LineString", + "coordinates": [ + [-105.0915804, 39.5950941], + [-105.0915796, 39.595059], + [-105.0915796, 39.595059], + [-105.0915796, 39.5950596] + ] + }, + "properties": { + "nodes": [ + { + "delta": [-1440, -1898], + "stopLine": null, + "dwidth": null, + "delevation": null + }, + { + "delta": [7, -390], + "stopLine": null, + "dwidth": null, + "delevation": null + }, + { + "delta": [0, 0], + "stopLine": null, + "dwidth": null, + "delevation": null + }, + { + "delta": [0, 7], + "stopLine": null, + "dwidth": null, + "delevation": null + } + ], + "laneId": 13, + "sharedWith": { + "busVehicleTraffic": false, + "trackedVehicleTraffic": false, + "individualMotorizedVehicleTraffic": false, + "taxiVehicleTraffic": false, + "overlappingLaneDescriptionProvided": false, + "cyclistVehicleTraffic": false, + "otherNonMotorizedTrafficTypes": false, + "multipleLanesTreatedAsOneLane": false, + "pedestrianTraffic": false, + "pedestriansTraffic": false + }, + "egressApproach": 4, + "ingressApproach": 0, + "ingressPath": false, + "egressPath": true + } + }, + { + "type": "Feature", + "id": 10, + "geometry": { + "type": "LineString", + "coordinates": [ + [-105.0913299, 39.5950922], + [-105.09133, 39.5944158], + [-105.0913348, 39.5939413], + [-105.0913368, 39.592921], + [-105.0913348, 39.5923866], + [-105.0913092, 39.5916736], + [-105.091258, 39.5909296] + ] + }, + "properties": { + "nodes": [ + { + "delta": [705, -1919], + "stopLine": null, + "dwidth": null, + "delevation": null + }, + { + "delta": [-1, -7515], + "stopLine": null, + "dwidth": null, + "delevation": -40 + }, + { + "delta": [-41, -5272], + "stopLine": null, + "dwidth": null, + "delevation": 10 + }, + { + "delta": [-17, -11337], + "stopLine": null, + "dwidth": null, + "delevation": null + }, + { + "delta": [17, -5938], + "stopLine": null, + "dwidth": null, + "delevation": 10 + }, + { + "delta": [219, -7922], + "stopLine": null, + "dwidth": null, + "delevation": 20 + }, + { + "delta": [438, -8267], + "stopLine": null, + "dwidth": null, + "delevation": 70 + } + ], + "laneId": 10, + "sharedWith": { + "busVehicleTraffic": false, + "trackedVehicleTraffic": false, + "individualMotorizedVehicleTraffic": false, + "taxiVehicleTraffic": false, + "overlappingLaneDescriptionProvided": false, + "cyclistVehicleTraffic": false, + "otherNonMotorizedTrafficTypes": false, + "multipleLanesTreatedAsOneLane": false, + "pedestrianTraffic": false, + "pedestriansTraffic": false + }, + "egressApproach": 0, + "ingressApproach": 3, + "ingressPath": true, + "egressPath": false, + "maneuvers": { + "maneuverStraightAllowed": true, + "maneuverNoStoppingAllowed": false, + "goWithHalt": false, + "maneuverLeftAllowed": false, + "maneuverUTurnAllowed": false, + "maneuverLeftTurnOnRedAllowed": false, + "reserved1": false, + "maneuverRightAllowed": false, + "maneuverLaneChangeAllowed": false, + "yieldAllwaysRequired": false, + "maneuverRightTurnOnRedAllowed": false, + "caution": false + }, + "connectsTo": [ + { + "connectingLane": { + "lane": 26, + "maneuver": { + "maneuverStraightAllowed": true, + "maneuverNoStoppingAllowed": false, + "goWithHalt": false, + "maneuverLeftAllowed": false, + "maneuverUTurnAllowed": false, + "maneuverLeftTurnOnRedAllowed": false, + "reserved1": false, + "maneuverRightAllowed": false, + "maneuverLaneChangeAllowed": false, + "yieldAllwaysRequired": false, + "maneuverRightTurnOnRedAllowed": false, + "caution": false + } + }, + "remoteIntersection": null, + "signalGroup": 2, + "userClass": null, + "connectionID": 1 + } + ] + } + }, + { + "type": "Feature", + "id": 9, + "geometry": { + "type": "LineString", + "coordinates": [ + [-105.0912849, 39.5950912], + [-105.0912838, 39.5946624], + [-105.0912861, 39.5940862], + [-105.0912901, 39.5933704], + [-105.091279, 39.5921455], + [-105.0912541, 39.5915398], + [-105.0912097, 39.5909346] + ] + }, + "properties": { + "nodes": [ + { + "delta": [1090, -1930], + "stopLine": null, + "dwidth": null, + "delevation": -10 + }, + { + "delta": [9, -4764], + "stopLine": null, + "dwidth": null, + "delevation": -10 + }, + { + "delta": [-20, -6402], + "stopLine": null, + "dwidth": null, + "delevation": -20 + }, + { + "delta": [-34, -7953], + "stopLine": null, + "dwidth": null, + "delevation": -10 + }, + { + "delta": [95, -13610], + "stopLine": null, + "dwidth": null, + "delevation": 30 + }, + { + "delta": [213, -6730], + "stopLine": null, + "dwidth": null, + "delevation": 40 + }, + { + "delta": [380, -6724], + "stopLine": null, + "dwidth": null, + "delevation": 60 + } + ], + "laneId": 9, + "sharedWith": { + "busVehicleTraffic": false, + "trackedVehicleTraffic": false, + "individualMotorizedVehicleTraffic": false, + "taxiVehicleTraffic": false, + "overlappingLaneDescriptionProvided": false, + "cyclistVehicleTraffic": false, + "otherNonMotorizedTrafficTypes": false, + "multipleLanesTreatedAsOneLane": false, + "pedestrianTraffic": false, + "pedestriansTraffic": false + }, + "egressApproach": 0, + "ingressApproach": 3, + "ingressPath": true, + "egressPath": false, + "maneuvers": { + "maneuverStraightAllowed": true, + "maneuverNoStoppingAllowed": false, + "goWithHalt": false, + "maneuverLeftAllowed": false, + "maneuverUTurnAllowed": false, + "maneuverLeftTurnOnRedAllowed": false, + "reserved1": false, + "maneuverRightAllowed": false, + "maneuverLaneChangeAllowed": false, + "yieldAllwaysRequired": false, + "maneuverRightTurnOnRedAllowed": false, + "caution": false + }, + "connectsTo": [ + { + "connectingLane": { + "lane": 27, + "maneuver": { + "maneuverStraightAllowed": true, + "maneuverNoStoppingAllowed": false, + "goWithHalt": false, + "maneuverLeftAllowed": false, + "maneuverUTurnAllowed": false, + "maneuverLeftTurnOnRedAllowed": false, + "reserved1": false, + "maneuverRightAllowed": false, + "maneuverLaneChangeAllowed": false, + "yieldAllwaysRequired": false, + "maneuverRightTurnOnRedAllowed": false, + "caution": false + } + }, + "remoteIntersection": null, + "signalGroup": 2, + "userClass": null, + "connectionID": 1 + } + ] + } + }, + { + "type": "Feature", + "id": 11, + "geometry": { + "type": "LineString", + "coordinates": [ + [-105.0913713, 39.5950916], + [-105.091375, 39.5944888], + [-105.0913568, 39.5942534] + ] + }, + "properties": { + "nodes": [ + { + "delta": [350, -1925], + "stopLine": null, + "dwidth": null, + "delevation": null + }, + { + "delta": [-32, -6698], + "stopLine": null, + "dwidth": null, + "delevation": -30 + }, + { + "delta": [156, -2616], + "stopLine": null, + "dwidth": null, + "delevation": null + } + ], + "laneId": 11, + "sharedWith": { + "busVehicleTraffic": false, + "trackedVehicleTraffic": false, + "individualMotorizedVehicleTraffic": false, + "taxiVehicleTraffic": false, + "overlappingLaneDescriptionProvided": false, + "cyclistVehicleTraffic": false, + "otherNonMotorizedTrafficTypes": false, + "multipleLanesTreatedAsOneLane": false, + "pedestrianTraffic": false, + "pedestriansTraffic": false + }, + "egressApproach": 0, + "ingressApproach": 3, + "ingressPath": true, + "egressPath": false, + "maneuvers": { + "maneuverStraightAllowed": false, + "maneuverNoStoppingAllowed": false, + "goWithHalt": false, + "maneuverLeftAllowed": true, + "maneuverUTurnAllowed": false, + "maneuverLeftTurnOnRedAllowed": false, + "reserved1": false, + "maneuverRightAllowed": false, + "maneuverLaneChangeAllowed": false, + "yieldAllwaysRequired": false, + "maneuverRightTurnOnRedAllowed": false, + "caution": false + }, + "connectsTo": [ + { + "connectingLane": { + "lane": 18, + "maneuver": { + "maneuverStraightAllowed": false, + "maneuverNoStoppingAllowed": false, + "goWithHalt": false, + "maneuverLeftAllowed": true, + "maneuverUTurnAllowed": false, + "maneuverLeftTurnOnRedAllowed": false, + "reserved1": false, + "maneuverRightAllowed": false, + "maneuverLaneChangeAllowed": false, + "yieldAllwaysRequired": false, + "maneuverRightTurnOnRedAllowed": false, + "caution": false + } + }, + "remoteIntersection": null, + "signalGroup": 1, + "userClass": null, + "connectionID": 1 + } + ] + } + }, + { + "type": "Feature", + "id": 8, + "geometry": { + "type": "LineString", + "coordinates": [ + [-105.0912303, 39.5950889], + [-105.0912421, 39.5949129], + [-105.0912427, 39.5942475], + [-105.0912568, 39.5941003] + ] + }, + "properties": { + "nodes": [ + { + "delta": [1557, -1956], + "stopLine": null, + "dwidth": null, + "delevation": -10 + }, + { + "delta": [-101, -1956], + "stopLine": null, + "dwidth": null, + "delevation": null + }, + { + "delta": [-5, -7393], + "stopLine": null, + "dwidth": null, + "delevation": -40 + }, + { + "delta": [-121, -1636], + "stopLine": null, + "dwidth": null, + "delevation": null + } + ], + "laneId": 8, + "sharedWith": { + "busVehicleTraffic": false, + "trackedVehicleTraffic": false, + "individualMotorizedVehicleTraffic": false, + "taxiVehicleTraffic": false, + "overlappingLaneDescriptionProvided": false, + "cyclistVehicleTraffic": false, + "otherNonMotorizedTrafficTypes": false, + "multipleLanesTreatedAsOneLane": false, + "pedestrianTraffic": false, + "pedestriansTraffic": false + }, + "egressApproach": 0, + "ingressApproach": 3, + "ingressPath": true, + "egressPath": false, + "maneuvers": { + "maneuverStraightAllowed": false, + "maneuverNoStoppingAllowed": false, + "goWithHalt": false, + "maneuverLeftAllowed": false, + "maneuverUTurnAllowed": false, + "maneuverLeftTurnOnRedAllowed": false, + "reserved1": false, + "maneuverRightAllowed": true, + "maneuverLaneChangeAllowed": false, + "yieldAllwaysRequired": false, + "maneuverRightTurnOnRedAllowed": false, + "caution": false + }, + "connectsTo": [ + { + "connectingLane": { + "lane": 7, + "maneuver": { + "maneuverStraightAllowed": false, + "maneuverNoStoppingAllowed": false, + "goWithHalt": false, + "maneuverLeftAllowed": false, + "maneuverUTurnAllowed": false, + "maneuverLeftTurnOnRedAllowed": false, + "reserved1": false, + "maneuverRightAllowed": true, + "maneuverLaneChangeAllowed": false, + "yieldAllwaysRequired": false, + "maneuverRightTurnOnRedAllowed": false, + "caution": false + } + }, + "remoteIntersection": null, + "signalGroup": null, + "userClass": null, + "connectionID": 1 + } + ] + } + }, + { + "type": "Feature", + "id": 15, + "geometry": { + "type": "LineString", + "coordinates": [ + [-105.0916931, 39.5951733], + [-105.0927839, 39.5951756], + [-105.0941732, 39.5951722], + [-105.0952027, 39.5951734] + ] + }, + "properties": { + "nodes": [ + { + "delta": [-2405, -1018], + "stopLine": null, + "dwidth": null, + "delevation": 10 + }, + { + "delta": [-9339, 26], + "stopLine": null, + "dwidth": null, + "delevation": 30 + }, + { + "delta": [-11895, -38], + "stopLine": null, + "dwidth": null, + "delevation": 50 + }, + { + "delta": [-8814, 13], + "stopLine": null, + "dwidth": null, + "delevation": null + } + ], + "laneId": 15, + "sharedWith": { + "busVehicleTraffic": false, + "trackedVehicleTraffic": false, + "individualMotorizedVehicleTraffic": false, + "taxiVehicleTraffic": false, + "overlappingLaneDescriptionProvided": false, + "cyclistVehicleTraffic": false, + "otherNonMotorizedTrafficTypes": false, + "multipleLanesTreatedAsOneLane": false, + "pedestrianTraffic": false, + "pedestriansTraffic": false + }, + "egressApproach": 0, + "ingressApproach": 5, + "ingressPath": true, + "egressPath": false, + "maneuvers": { + "maneuverStraightAllowed": true, + "maneuverNoStoppingAllowed": false, + "goWithHalt": false, + "maneuverLeftAllowed": false, + "maneuverUTurnAllowed": false, + "maneuverLeftTurnOnRedAllowed": false, + "reserved1": false, + "maneuverRightAllowed": false, + "maneuverLaneChangeAllowed": false, + "yieldAllwaysRequired": false, + "maneuverRightTurnOnRedAllowed": false, + "caution": false + }, + "connectsTo": [ + { + "connectingLane": { + "lane": 6, + "maneuver": { + "maneuverStraightAllowed": true, + "maneuverNoStoppingAllowed": false, + "goWithHalt": false, + "maneuverLeftAllowed": false, + "maneuverUTurnAllowed": false, + "maneuverLeftTurnOnRedAllowed": false, + "reserved1": false, + "maneuverRightAllowed": false, + "maneuverLaneChangeAllowed": false, + "yieldAllwaysRequired": false, + "maneuverRightTurnOnRedAllowed": false, + "caution": false + } + }, + "remoteIntersection": null, + "signalGroup": 4, + "userClass": null, + "connectionID": 1 + } + ] + } + }, + { + "type": "Feature", + "id": 16, + "geometry": { + "type": "LineString", + "coordinates": [ + [-105.0916852, 39.5952059], + [-105.0931135, 39.5952073], + [-105.0944406, 39.595204], + [-105.0952041, 39.5952041] + ] + }, + "properties": { + "nodes": [ + { + "delta": [-2337, -656], + "stopLine": null, + "dwidth": null, + "delevation": 10 + }, + { + "delta": [-12229, 15], + "stopLine": null, + "dwidth": null, + "delevation": 50 + }, + { + "delta": [-11362, -37], + "stopLine": null, + "dwidth": null, + "delevation": 30 + }, + { + "delta": [-6537, 1], + "stopLine": null, + "dwidth": null, + "delevation": null + } + ], + "laneId": 16, + "sharedWith": { + "busVehicleTraffic": false, + "trackedVehicleTraffic": false, + "individualMotorizedVehicleTraffic": false, + "taxiVehicleTraffic": false, + "overlappingLaneDescriptionProvided": false, + "cyclistVehicleTraffic": false, + "otherNonMotorizedTrafficTypes": false, + "multipleLanesTreatedAsOneLane": false, + "pedestrianTraffic": false, + "pedestriansTraffic": false + }, + "egressApproach": 0, + "ingressApproach": 5, + "ingressPath": true, + "egressPath": false, + "maneuvers": { + "maneuverStraightAllowed": true, + "maneuverNoStoppingAllowed": false, + "goWithHalt": false, + "maneuverLeftAllowed": false, + "maneuverUTurnAllowed": false, + "maneuverLeftTurnOnRedAllowed": false, + "reserved1": false, + "maneuverRightAllowed": false, + "maneuverLaneChangeAllowed": false, + "yieldAllwaysRequired": false, + "maneuverRightTurnOnRedAllowed": false, + "caution": false + }, + "connectsTo": [ + { + "connectingLane": { + "lane": 5, + "maneuver": { + "maneuverStraightAllowed": true, + "maneuverNoStoppingAllowed": false, + "goWithHalt": false, + "maneuverLeftAllowed": false, + "maneuverUTurnAllowed": false, + "maneuverLeftTurnOnRedAllowed": false, + "reserved1": false, + "maneuverRightAllowed": false, + "maneuverLaneChangeAllowed": false, + "yieldAllwaysRequired": false, + "maneuverRightTurnOnRedAllowed": false, + "caution": false + } + }, + "remoteIntersection": null, + "signalGroup": 4, + "userClass": null, + "connectionID": 1 + } + ] + } + }, + { + "type": "Feature", + "id": 17, + "geometry": { + "type": "LineString", + "coordinates": [ + [-105.0916853, 39.5952393], + [-105.0927239, 39.5952377], + [-105.0941207, 39.5952375], + [-105.0943034, 39.5952213] + ] + }, + "properties": { + "nodes": [ + { + "delta": [-2338, -284], + "stopLine": null, + "dwidth": null, + "delevation": 10 + }, + { + "delta": [-8892, -18], + "stopLine": null, + "dwidth": null, + "delevation": 30 + }, + { + "delta": [-11959, -2], + "stopLine": null, + "dwidth": null, + "delevation": 50 + }, + { + "delta": [-1564, -180], + "stopLine": null, + "dwidth": null, + "delevation": null + } + ], + "laneId": 17, + "sharedWith": { + "busVehicleTraffic": false, + "trackedVehicleTraffic": false, + "individualMotorizedVehicleTraffic": false, + "taxiVehicleTraffic": false, + "overlappingLaneDescriptionProvided": false, + "cyclistVehicleTraffic": false, + "otherNonMotorizedTrafficTypes": false, + "multipleLanesTreatedAsOneLane": false, + "pedestrianTraffic": false, + "pedestriansTraffic": false + }, + "egressApproach": 0, + "ingressApproach": 5, + "ingressPath": true, + "egressPath": false, + "maneuvers": { + "maneuverStraightAllowed": false, + "maneuverNoStoppingAllowed": false, + "goWithHalt": false, + "maneuverLeftAllowed": true, + "maneuverUTurnAllowed": false, + "maneuverLeftTurnOnRedAllowed": false, + "reserved1": false, + "maneuverRightAllowed": false, + "maneuverLaneChangeAllowed": false, + "yieldAllwaysRequired": false, + "maneuverRightTurnOnRedAllowed": false, + "caution": false + }, + "connectsTo": [ + { + "connectingLane": { + "lane": 26, + "maneuver": { + "maneuverStraightAllowed": false, + "maneuverNoStoppingAllowed": false, + "goWithHalt": false, + "maneuverLeftAllowed": true, + "maneuverUTurnAllowed": false, + "maneuverLeftTurnOnRedAllowed": false, + "reserved1": false, + "maneuverRightAllowed": false, + "maneuverLaneChangeAllowed": false, + "yieldAllwaysRequired": false, + "maneuverRightTurnOnRedAllowed": false, + "caution": false + } + }, + "remoteIntersection": null, + "signalGroup": 7, + "userClass": null, + "connectionID": 1 + } + ] + } + }, + { + "type": "Feature", + "id": 18, + "geometry": { + "type": "LineString", + "coordinates": [ + [-105.0916766, 39.595308], + [-105.0917443, 39.5953067] + ] + }, + "properties": { + "nodes": [ + { + "delta": [-2264, 479], + "stopLine": null, + "dwidth": null, + "delevation": 20 + }, + { + "delta": [-580, -14], + "stopLine": null, + "dwidth": null, + "delevation": null + } + ], + "laneId": 18, + "sharedWith": { + "busVehicleTraffic": false, + "trackedVehicleTraffic": false, + "individualMotorizedVehicleTraffic": false, + "taxiVehicleTraffic": false, + "overlappingLaneDescriptionProvided": false, + "cyclistVehicleTraffic": false, + "otherNonMotorizedTrafficTypes": false, + "multipleLanesTreatedAsOneLane": false, + "pedestrianTraffic": false, + "pedestriansTraffic": false + }, + "egressApproach": 6, + "ingressApproach": 0, + "ingressPath": false, + "egressPath": true + } + }, + { + "type": "Feature", + "id": 19, + "geometry": { + "type": "LineString", + "coordinates": [ + [-105.0916758, 39.5953365], + [-105.0917363, 39.5953365] + ] + }, + "properties": { + "nodes": [ + { + "delta": [-2257, 796], + "stopLine": null, + "dwidth": null, + "delevation": 20 + }, + { + "delta": [-518, 0], + "stopLine": null, + "dwidth": null, + "delevation": null + } + ], + "laneId": 19, + "sharedWith": { + "busVehicleTraffic": false, + "trackedVehicleTraffic": false, + "individualMotorizedVehicleTraffic": false, + "taxiVehicleTraffic": false, + "overlappingLaneDescriptionProvided": false, + "cyclistVehicleTraffic": false, + "otherNonMotorizedTrafficTypes": false, + "multipleLanesTreatedAsOneLane": false, + "pedestrianTraffic": false, + "pedestriansTraffic": false + }, + "egressApproach": 6, + "ingressApproach": 0, + "ingressPath": false, + "egressPath": true + } + }, + { + "type": "Feature", + "id": 20, + "geometry": { + "type": "LineString", + "coordinates": [ + [-105.0916734, 39.5953755], + [-105.0917299, 39.5953755] + ] + }, + "properties": { + "nodes": [ + { + "delta": [-2236, 1229], + "stopLine": null, + "dwidth": null, + "delevation": 20 + }, + { + "delta": [-484, 0], + "stopLine": null, + "dwidth": null, + "delevation": null + } + ], + "laneId": 20, + "sharedWith": { + "busVehicleTraffic": false, + "trackedVehicleTraffic": false, + "individualMotorizedVehicleTraffic": false, + "taxiVehicleTraffic": false, + "overlappingLaneDescriptionProvided": false, + "cyclistVehicleTraffic": false, + "otherNonMotorizedTrafficTypes": false, + "multipleLanesTreatedAsOneLane": false, + "pedestrianTraffic": false, + "pedestriansTraffic": false + }, + "egressApproach": 6, + "ingressApproach": 0, + "ingressPath": false, + "egressPath": true + } + }, + { + "type": "Feature", + "id": 23, + "geometry": { + "type": "LineString", + "coordinates": [ + [-105.091519, 39.5954362], + [-105.0914927, 39.5965421], + [-105.091488, 39.5974092], + [-105.0914843, 39.5981324], + [-105.0914843, 39.5981324], + [-105.09148, 39.599322] + ] + }, + "properties": { + "nodes": [ + { + "delta": [-914, 1903], + "stopLine": null, + "dwidth": null, + "delevation": 20 + }, + { + "delta": [225, 12288], + "stopLine": null, + "dwidth": null, + "delevation": 30 + }, + { + "delta": [40, 9634], + "stopLine": null, + "dwidth": null, + "delevation": -20 + }, + { + "delta": [32, 8035], + "stopLine": null, + "dwidth": null, + "delevation": -30 + }, + { + "delta": [0, 0], + "stopLine": null, + "dwidth": null, + "delevation": null + }, + { + "delta": [37, 13218], + "stopLine": null, + "dwidth": null, + "delevation": -10 + } + ], + "laneId": 23, + "sharedWith": { + "busVehicleTraffic": false, + "trackedVehicleTraffic": false, + "individualMotorizedVehicleTraffic": false, + "taxiVehicleTraffic": false, + "overlappingLaneDescriptionProvided": false, + "cyclistVehicleTraffic": false, + "otherNonMotorizedTrafficTypes": false, + "multipleLanesTreatedAsOneLane": false, + "pedestrianTraffic": false, + "pedestriansTraffic": false + }, + "egressApproach": 0, + "ingressApproach": 7, + "ingressPath": true, + "egressPath": false, + "maneuvers": { + "maneuverStraightAllowed": true, + "maneuverNoStoppingAllowed": false, + "goWithHalt": false, + "maneuverLeftAllowed": false, + "maneuverUTurnAllowed": false, + "maneuverLeftTurnOnRedAllowed": false, + "reserved1": false, + "maneuverRightAllowed": false, + "maneuverLaneChangeAllowed": false, + "yieldAllwaysRequired": false, + "maneuverRightTurnOnRedAllowed": false, + "caution": false + }, + "connectsTo": [ + { + "connectingLane": { + "lane": 12, + "maneuver": { + "maneuverStraightAllowed": true, + "maneuverNoStoppingAllowed": false, + "goWithHalt": false, + "maneuverLeftAllowed": false, + "maneuverUTurnAllowed": false, + "maneuverLeftTurnOnRedAllowed": false, + "reserved1": false, + "maneuverRightAllowed": false, + "maneuverLaneChangeAllowed": false, + "yieldAllwaysRequired": false, + "maneuverRightTurnOnRedAllowed": false, + "caution": false + } + }, + "remoteIntersection": null, + "signalGroup": 6, + "userClass": null, + "connectionID": 1 + } + ] + } + }, + { + "type": "Feature", + "id": 22, + "geometry": { + "type": "LineString", + "coordinates": [ + [-105.0915548, 39.5954372], + [-105.0915441, 39.59638], + [-105.0915337, 39.5974224], + [-105.0915232, 39.5993262] + ] + }, + "properties": { + "nodes": [ + { + "delta": [-1221, 1914], + "stopLine": null, + "dwidth": null, + "delevation": 20 + }, + { + "delta": [92, 10475], + "stopLine": null, + "dwidth": null, + "delevation": 30 + }, + { + "delta": [89, 11582], + "stopLine": null, + "dwidth": null, + "delevation": -20 + }, + { + "delta": [90, 21153], + "stopLine": null, + "dwidth": null, + "delevation": -40 + } + ], + "laneId": 22, + "sharedWith": { + "busVehicleTraffic": false, + "trackedVehicleTraffic": false, + "individualMotorizedVehicleTraffic": false, + "taxiVehicleTraffic": false, + "overlappingLaneDescriptionProvided": false, + "cyclistVehicleTraffic": false, + "otherNonMotorizedTrafficTypes": false, + "multipleLanesTreatedAsOneLane": false, + "pedestrianTraffic": false, + "pedestriansTraffic": false + }, + "egressApproach": 0, + "ingressApproach": 7, + "ingressPath": true, + "egressPath": false, + "maneuvers": { + "maneuverStraightAllowed": true, + "maneuverNoStoppingAllowed": false, + "goWithHalt": false, + "maneuverLeftAllowed": false, + "maneuverUTurnAllowed": false, + "maneuverLeftTurnOnRedAllowed": false, + "reserved1": false, + "maneuverRightAllowed": false, + "maneuverLaneChangeAllowed": false, + "yieldAllwaysRequired": false, + "maneuverRightTurnOnRedAllowed": false, + "caution": false + }, + "connectsTo": [ + { + "connectingLane": { + "lane": 13, + "maneuver": { + "maneuverStraightAllowed": true, + "maneuverNoStoppingAllowed": false, + "goWithHalt": false, + "maneuverLeftAllowed": false, + "maneuverUTurnAllowed": false, + "maneuverLeftTurnOnRedAllowed": false, + "reserved1": false, + "maneuverRightAllowed": false, + "maneuverLaneChangeAllowed": false, + "yieldAllwaysRequired": false, + "maneuverRightTurnOnRedAllowed": false, + "caution": false + } + }, + "remoteIntersection": null, + "signalGroup": 6, + "userClass": null, + "connectionID": 1 + } + ] + } + }, + { + "type": "Feature", + "id": 21, + "geometry": { + "type": "LineString", + "coordinates": [ + [-105.0916048, 39.5954391], + [-105.0916029, 39.5957255], + [-105.0916004, 39.5962647], + [-105.0915885, 39.5966976], + [-105.0915786, 39.5977599], + [-105.0915764, 39.5981344] + ] + }, + "properties": { + "nodes": [ + { + "delta": [-1649, 1935], + "stopLine": null, + "dwidth": null, + "delevation": 20 + }, + { + "delta": [16, 3182], + "stopLine": null, + "dwidth": null, + "delevation": 10 + }, + { + "delta": [21, 5991], + "stopLine": null, + "dwidth": null, + "delevation": 20 + }, + { + "delta": [102, 4810], + "stopLine": null, + "dwidth": null, + "delevation": null + }, + { + "delta": [85, 11803], + "stopLine": null, + "dwidth": null, + "delevation": -30 + }, + { + "delta": [19, 4161], + "stopLine": null, + "dwidth": null, + "delevation": -20 + } + ], + "laneId": 21, + "sharedWith": { + "busVehicleTraffic": false, + "trackedVehicleTraffic": false, + "individualMotorizedVehicleTraffic": false, + "taxiVehicleTraffic": false, + "overlappingLaneDescriptionProvided": false, + "cyclistVehicleTraffic": false, + "otherNonMotorizedTrafficTypes": false, + "multipleLanesTreatedAsOneLane": false, + "pedestrianTraffic": false, + "pedestriansTraffic": false + }, + "egressApproach": 0, + "ingressApproach": 7, + "ingressPath": true, + "egressPath": false, + "maneuvers": { + "maneuverStraightAllowed": false, + "maneuverNoStoppingAllowed": false, + "goWithHalt": false, + "maneuverLeftAllowed": false, + "maneuverUTurnAllowed": false, + "maneuverLeftTurnOnRedAllowed": false, + "reserved1": false, + "maneuverRightAllowed": true, + "maneuverLaneChangeAllowed": false, + "yieldAllwaysRequired": false, + "maneuverRightTurnOnRedAllowed": false, + "caution": false + }, + "connectsTo": [ + { + "connectingLane": { + "lane": 20, + "maneuver": { + "maneuverStraightAllowed": false, + "maneuverNoStoppingAllowed": false, + "goWithHalt": false, + "maneuverLeftAllowed": false, + "maneuverUTurnAllowed": false, + "maneuverLeftTurnOnRedAllowed": false, + "reserved1": false, + "maneuverRightAllowed": true, + "maneuverLaneChangeAllowed": false, + "yieldAllwaysRequired": false, + "maneuverRightTurnOnRedAllowed": false, + "caution": false + } + }, + "remoteIntersection": null, + "signalGroup": null, + "userClass": null, + "connectionID": 1 + } + ] + } + }, + { + "type": "Feature", + "id": 24, + "geometry": { + "type": "LineString", + "coordinates": [ + [-105.0914747, 39.5954382], + [-105.0914714, 39.5958506], + [-105.0914611, 39.596192], + [-105.0914778, 39.5963599] + ] + }, + "properties": { + "nodes": [ + { + "delta": [-535, 1926], + "stopLine": null, + "dwidth": null, + "delevation": 20 + }, + { + "delta": [28, 4582], + "stopLine": null, + "dwidth": null, + "delevation": 10 + }, + { + "delta": [88, 3793], + "stopLine": null, + "dwidth": null, + "delevation": 10 + }, + { + "delta": [-143, 1866], + "stopLine": null, + "dwidth": null, + "delevation": 10 + } + ], + "laneId": 24, + "sharedWith": { + "busVehicleTraffic": false, + "trackedVehicleTraffic": false, + "individualMotorizedVehicleTraffic": false, + "taxiVehicleTraffic": false, + "overlappingLaneDescriptionProvided": false, + "cyclistVehicleTraffic": false, + "otherNonMotorizedTrafficTypes": false, + "multipleLanesTreatedAsOneLane": false, + "pedestrianTraffic": false, + "pedestriansTraffic": false + }, + "egressApproach": 0, + "ingressApproach": 7, + "ingressPath": true, + "egressPath": false, + "maneuvers": { + "maneuverStraightAllowed": false, + "maneuverNoStoppingAllowed": false, + "goWithHalt": false, + "maneuverLeftAllowed": true, + "maneuverUTurnAllowed": false, + "maneuverLeftTurnOnRedAllowed": false, + "reserved1": false, + "maneuverRightAllowed": false, + "maneuverLaneChangeAllowed": false, + "yieldAllwaysRequired": false, + "maneuverRightTurnOnRedAllowed": false, + "caution": false + }, + "connectsTo": [ + { + "connectingLane": { + "lane": 6, + "maneuver": { + "maneuverStraightAllowed": false, + "maneuverNoStoppingAllowed": false, + "goWithHalt": false, + "maneuverLeftAllowed": true, + "maneuverUTurnAllowed": false, + "maneuverLeftTurnOnRedAllowed": false, + "reserved1": false, + "maneuverRightAllowed": false, + "maneuverLaneChangeAllowed": false, + "yieldAllwaysRequired": false, + "maneuverRightTurnOnRedAllowed": false, + "caution": false + } + }, + "remoteIntersection": null, + "signalGroup": 1, + "userClass": null, + "connectionID": 1 + } + ] + } + }, + { + "type": "Feature", + "id": 25, + "geometry": { + "type": "LineString", + "coordinates": [ + [-105.0914371, 39.5954371], + [-105.0914355, 39.5958506], + [-105.0914461, 39.596052] + ] + }, + "properties": { + "nodes": [ + { + "delta": [-213, 1913], + "stopLine": null, + "dwidth": null, + "delevation": 20 + }, + { + "delta": [14, 4594], + "stopLine": null, + "dwidth": null, + "delevation": 10 + }, + { + "delta": [-91, 2238], + "stopLine": null, + "dwidth": null, + "delevation": 10 + } + ], + "laneId": 25, + "sharedWith": { + "busVehicleTraffic": false, + "trackedVehicleTraffic": false, + "individualMotorizedVehicleTraffic": false, + "taxiVehicleTraffic": false, + "overlappingLaneDescriptionProvided": false, + "cyclistVehicleTraffic": false, + "otherNonMotorizedTrafficTypes": false, + "multipleLanesTreatedAsOneLane": false, + "pedestrianTraffic": false, + "pedestriansTraffic": false + }, + "egressApproach": 0, + "ingressApproach": 7, + "ingressPath": true, + "egressPath": false, + "maneuvers": { + "maneuverStraightAllowed": false, + "maneuverNoStoppingAllowed": false, + "goWithHalt": false, + "maneuverLeftAllowed": true, + "maneuverUTurnAllowed": false, + "maneuverLeftTurnOnRedAllowed": false, + "reserved1": false, + "maneuverRightAllowed": false, + "maneuverLaneChangeAllowed": false, + "yieldAllwaysRequired": false, + "maneuverRightTurnOnRedAllowed": false, + "caution": false + }, + "connectsTo": [ + { + "connectingLane": { + "lane": 5, + "maneuver": { + "maneuverStraightAllowed": false, + "maneuverNoStoppingAllowed": false, + "goWithHalt": false, + "maneuverLeftAllowed": true, + "maneuverUTurnAllowed": false, + "maneuverLeftTurnOnRedAllowed": false, + "reserved1": false, + "maneuverRightAllowed": false, + "maneuverLaneChangeAllowed": false, + "yieldAllwaysRequired": false, + "maneuverRightTurnOnRedAllowed": false, + "caution": false + } + }, + "remoteIntersection": null, + "signalGroup": 1, + "userClass": null, + "connectionID": 1 + } + ] + } + }, + { + "type": "Feature", + "id": 26, + "geometry": { + "type": "LineString", + "coordinates": [ + [-105.0913276, 39.5954361], + [-105.0913284, 39.5954678] + ] + }, + "properties": { + "nodes": [ + { + "delta": [724, 1902], + "stopLine": null, + "dwidth": null, + "delevation": 10 + }, + { + "delta": [-7, 352], + "stopLine": null, + "dwidth": null, + "delevation": 10 + } + ], + "laneId": 26, + "sharedWith": { + "busVehicleTraffic": false, + "trackedVehicleTraffic": false, + "individualMotorizedVehicleTraffic": false, + "taxiVehicleTraffic": false, + "overlappingLaneDescriptionProvided": false, + "cyclistVehicleTraffic": false, + "otherNonMotorizedTrafficTypes": false, + "multipleLanesTreatedAsOneLane": false, + "pedestrianTraffic": false, + "pedestriansTraffic": false + }, + "egressApproach": 8, + "ingressApproach": 0, + "ingressPath": false, + "egressPath": true + } + }, + { + "type": "Feature", + "id": 27, + "geometry": { + "type": "LineString", + "coordinates": [ + [-105.0912831, 39.5954348], + [-105.0912839, 39.5954647] + ] + }, + "properties": { + "nodes": [ + { + "delta": [1105, 1888], + "stopLine": null, + "dwidth": null, + "delevation": 10 + }, + { + "delta": [-7, 332], + "stopLine": null, + "dwidth": null, + "delevation": 10 + } + ], + "laneId": 27, + "sharedWith": { + "busVehicleTraffic": false, + "trackedVehicleTraffic": false, + "individualMotorizedVehicleTraffic": false, + "taxiVehicleTraffic": false, + "overlappingLaneDescriptionProvided": false, + "cyclistVehicleTraffic": false, + "otherNonMotorizedTrafficTypes": false, + "multipleLanesTreatedAsOneLane": false, + "pedestrianTraffic": false, + "pedestriansTraffic": false + }, + "egressApproach": 8, + "ingressApproach": 0, + "ingressPath": false, + "egressPath": true + } + }, + { + "type": "Feature", + "id": 28, + "geometry": { + "type": "LineString", + "coordinates": [ + [-105.0912264, 39.5954336], + [-105.091228, 39.5954647], + [-105.091228, 39.5954653] + ] + }, + "properties": { + "nodes": [ + { + "delta": [1591, 1874], + "stopLine": null, + "dwidth": null, + "delevation": 10 + }, + { + "delta": [-14, 346], + "stopLine": null, + "dwidth": null, + "delevation": null + }, + { + "delta": [0, 7], + "stopLine": null, + "dwidth": null, + "delevation": null + } + ], + "laneId": 28, + "sharedWith": { + "busVehicleTraffic": false, + "trackedVehicleTraffic": false, + "individualMotorizedVehicleTraffic": false, + "taxiVehicleTraffic": false, + "overlappingLaneDescriptionProvided": false, + "cyclistVehicleTraffic": false, + "otherNonMotorizedTrafficTypes": false, + "multipleLanesTreatedAsOneLane": false, + "pedestrianTraffic": false, + "pedestriansTraffic": false + }, + "egressApproach": 8, + "ingressApproach": 0, + "ingressPath": false, + "egressPath": true + } + } + ] + }, + "connectingLanesFeatureCollection": { + "type": "FeatureCollection", + "features": [ + { + "type": "Feature", + "id": "2-19", + "geometry": { + "type": "LineString", + "coordinates": [ + [-105.0911523, 39.5953376], + [-105.0916758, 39.5953365] + ] + }, + "properties": { + "signalGroupId": 8, + "ingressLaneId": 2, + "egressLaneId": 19 + } + }, + { + "type": "Feature", + "id": "3-18", + "geometry": { + "type": "LineString", + "coordinates": [ + [-105.0911527, 39.5953113], + [-105.0916766, 39.595308] + ] + }, + "properties": { + "signalGroupId": 8, + "ingressLaneId": 3, + "egressLaneId": 18 + } + }, + { + "type": "Feature", + "id": "4-12", + "geometry": { + "type": "LineString", + "coordinates": [ + [-105.0911537, 39.5952537], + [-105.0915234, 39.5950941] + ] + }, + "properties": { + "signalGroupId": 3, + "ingressLaneId": 4, + "egressLaneId": 12 + } + }, + { + "type": "Feature", + "id": "10-26", + "geometry": { + "type": "LineString", + "coordinates": [ + [-105.0913299, 39.5950922], + [-105.0913276, 39.5954361] + ] + }, + "properties": { + "signalGroupId": 2, + "ingressLaneId": 10, + "egressLaneId": 26 + } + }, + { + "type": "Feature", + "id": "9-27", + "geometry": { + "type": "LineString", + "coordinates": [ + [-105.0912849, 39.5950912], + [-105.0912831, 39.5954348] + ] + }, + "properties": { + "signalGroupId": 2, + "ingressLaneId": 9, + "egressLaneId": 27 + } + }, + { + "type": "Feature", + "id": "11-18", + "geometry": { + "type": "LineString", + "coordinates": [ + [-105.0913713, 39.5950916], + [-105.0916766, 39.595308] + ] + }, + "properties": { + "signalGroupId": 1, + "ingressLaneId": 11, + "egressLaneId": 18 + } + }, + { + "type": "Feature", + "id": "8-7", + "geometry": { + "type": "LineString", + "coordinates": [ + [-105.0912303, 39.5950889], + [-105.0911443, 39.5951332] + ] + }, + "properties": { + "signalGroupId": null, + "ingressLaneId": 8, + "egressLaneId": 7 + } + }, + { + "type": "Feature", + "id": "15-6", + "geometry": { + "type": "LineString", + "coordinates": [ + [-105.0916931, 39.5951733], + [-105.0911474, 39.5951732] + ] + }, + "properties": { + "signalGroupId": 4, + "ingressLaneId": 15, + "egressLaneId": 6 + } + }, + { + "type": "Feature", + "id": "16-5", + "geometry": { + "type": "LineString", + "coordinates": [ + [-105.0916852, 39.5952059], + [-105.0911474, 39.5952053] + ] + }, + "properties": { + "signalGroupId": 4, + "ingressLaneId": 16, + "egressLaneId": 5 + } + }, + { + "type": "Feature", + "id": "17-26", + "geometry": { + "type": "LineString", + "coordinates": [ + [-105.0916853, 39.5952393], + [-105.0913276, 39.5954361] + ] + }, + "properties": { + "signalGroupId": 7, + "ingressLaneId": 17, + "egressLaneId": 26 + } + }, + { + "type": "Feature", + "id": "23-12", + "geometry": { + "type": "LineString", + "coordinates": [ + [-105.091519, 39.5954362], + [-105.0915234, 39.5950941] + ] + }, + "properties": { + "signalGroupId": 6, + "ingressLaneId": 23, + "egressLaneId": 12 + } + }, + { + "type": "Feature", + "id": "22-13", + "geometry": { + "type": "LineString", + "coordinates": [ + [-105.0915548, 39.5954372], + [-105.0915804, 39.5950941] + ] + }, + "properties": { + "signalGroupId": 6, + "ingressLaneId": 22, + "egressLaneId": 13 + } + }, + { + "type": "Feature", + "id": "21-20", + "geometry": { + "type": "LineString", + "coordinates": [ + [-105.0916048, 39.5954391], + [-105.0916734, 39.5953755] + ] + }, + "properties": { + "signalGroupId": null, + "ingressLaneId": 21, + "egressLaneId": 20 + } + }, + { + "type": "Feature", + "id": "24-6", + "geometry": { + "type": "LineString", + "coordinates": [ + [-105.0914747, 39.5954382], + [-105.0911474, 39.5951732] + ] + }, + "properties": { + "signalGroupId": 1, + "ingressLaneId": 24, + "egressLaneId": 6 + } + }, + { + "type": "Feature", + "id": "25-5", + "geometry": { + "type": "LineString", + "coordinates": [ + [-105.0914371, 39.5954371], + [-105.0911474, 39.5952053] + ] + }, + "properties": { + "signalGroupId": 1, + "ingressLaneId": 25, + "egressLaneId": 5 + } + } + ] + }, + "properties": { + "messageType": "MAP", + "odeReceivedAt": "2022-12-08T16:47:18.558068Z", + "originIp": "172.19.0.1", + "intersectionId": 12110, + "msgIssueRevision": 2, + "revision": 2, + "refPoint": { + "latitude": 39.5952649, + "longitude": -105.0914122, + "elevation": 1677 + }, + "cti4501Conformant": false, + "validationMessages": [ + { + "message": "$.payload.data.intersections.intersectionGeometry[0].id.region: null found, integer expected", + "jsonPath": "$.payload.data.intersections.intersectionGeometry[0].id.region", + "schemaPath": "#/$defs/J2735RoadRegulatorID/type" + }, + { + "message": "$.payload.data.intersections.intersectionGeometry[0].laneSet.GenericLane[3].connectsTo: null found, object expected", + "jsonPath": "$.payload.data.intersections.intersectionGeometry[0].laneSet.GenericLane[3].connectsTo", + "schemaPath": "#/$defs/J2735ConnectsToList_Wrapper/type" + }, + { + "message": "$.payload.data.intersections.intersectionGeometry[0].laneSet.GenericLane[3].maneuvers: null found, object expected", + "jsonPath": "$.payload.data.intersections.intersectionGeometry[0].laneSet.GenericLane[3].maneuvers", + "schemaPath": "#/$defs/J2735AllowedManeuvers/type" + }, + { + "message": "$.payload.data.intersections.intersectionGeometry[0].laneSet.GenericLane[4].connectsTo: null found, object expected", + "jsonPath": "$.payload.data.intersections.intersectionGeometry[0].laneSet.GenericLane[4].connectsTo", + "schemaPath": "#/$defs/J2735ConnectsToList_Wrapper/type" + }, + { + "message": "$.payload.data.intersections.intersectionGeometry[0].laneSet.GenericLane[4].maneuvers: null found, object expected", + "jsonPath": "$.payload.data.intersections.intersectionGeometry[0].laneSet.GenericLane[4].maneuvers", + "schemaPath": "#/$defs/J2735AllowedManeuvers/type" + }, + { + "message": "$.payload.data.intersections.intersectionGeometry[0].laneSet.GenericLane[5].connectsTo: null found, object expected", + "jsonPath": "$.payload.data.intersections.intersectionGeometry[0].laneSet.GenericLane[5].connectsTo", + "schemaPath": "#/$defs/J2735ConnectsToList_Wrapper/type" + }, + { + "message": "$.payload.data.intersections.intersectionGeometry[0].laneSet.GenericLane[5].maneuvers: null found, object expected", + "jsonPath": "$.payload.data.intersections.intersectionGeometry[0].laneSet.GenericLane[5].maneuvers", + "schemaPath": "#/$defs/J2735AllowedManeuvers/type" + }, + { + "message": "$.payload.data.intersections.intersectionGeometry[0].laneSet.GenericLane[6].connectsTo: null found, object expected", + "jsonPath": "$.payload.data.intersections.intersectionGeometry[0].laneSet.GenericLane[6].connectsTo", + "schemaPath": "#/$defs/J2735ConnectsToList_Wrapper/type" + }, + { + "message": "$.payload.data.intersections.intersectionGeometry[0].laneSet.GenericLane[6].maneuvers: null found, object expected", + "jsonPath": "$.payload.data.intersections.intersectionGeometry[0].laneSet.GenericLane[6].maneuvers", + "schemaPath": "#/$defs/J2735AllowedManeuvers/type" + }, + { + "message": "$.payload.data.intersections.intersectionGeometry[0].laneSet.GenericLane[7].connectsTo: null found, object expected", + "jsonPath": "$.payload.data.intersections.intersectionGeometry[0].laneSet.GenericLane[7].connectsTo", + "schemaPath": "#/$defs/J2735ConnectsToList_Wrapper/type" + }, + { + "message": "$.payload.data.intersections.intersectionGeometry[0].laneSet.GenericLane[7].maneuvers: null found, object expected", + "jsonPath": "$.payload.data.intersections.intersectionGeometry[0].laneSet.GenericLane[7].maneuvers", + "schemaPath": "#/$defs/J2735AllowedManeuvers/type" + }, + { + "message": "$.payload.data.intersections.intersectionGeometry[0].laneSet.GenericLane[11].connectsTo.connectsTo[0].signalGroup: null found, integer expected", + "jsonPath": "$.payload.data.intersections.intersectionGeometry[0].laneSet.GenericLane[11].connectsTo.connectsTo[0].signalGroup", + "schemaPath": "#/$defs/J2735SignalGroupID/type" + }, + { + "message": "$.payload.data.intersections.intersectionGeometry[0].laneSet.GenericLane[15].connectsTo: null found, object expected", + "jsonPath": "$.payload.data.intersections.intersectionGeometry[0].laneSet.GenericLane[15].connectsTo", + "schemaPath": "#/$defs/J2735ConnectsToList_Wrapper/type" + }, + { + "message": "$.payload.data.intersections.intersectionGeometry[0].laneSet.GenericLane[15].maneuvers: null found, object expected", + "jsonPath": "$.payload.data.intersections.intersectionGeometry[0].laneSet.GenericLane[15].maneuvers", + "schemaPath": "#/$defs/J2735AllowedManeuvers/type" + }, + { + "message": "$.payload.data.intersections.intersectionGeometry[0].laneSet.GenericLane[16].connectsTo: null found, object expected", + "jsonPath": "$.payload.data.intersections.intersectionGeometry[0].laneSet.GenericLane[16].connectsTo", + "schemaPath": "#/$defs/J2735ConnectsToList_Wrapper/type" + }, + { + "message": "$.payload.data.intersections.intersectionGeometry[0].laneSet.GenericLane[16].maneuvers: null found, object expected", + "jsonPath": "$.payload.data.intersections.intersectionGeometry[0].laneSet.GenericLane[16].maneuvers", + "schemaPath": "#/$defs/J2735AllowedManeuvers/type" + }, + { + "message": "$.payload.data.intersections.intersectionGeometry[0].laneSet.GenericLane[17].connectsTo: null found, object expected", + "jsonPath": "$.payload.data.intersections.intersectionGeometry[0].laneSet.GenericLane[17].connectsTo", + "schemaPath": "#/$defs/J2735ConnectsToList_Wrapper/type" + }, + { + "message": "$.payload.data.intersections.intersectionGeometry[0].laneSet.GenericLane[17].maneuvers: null found, object expected", + "jsonPath": "$.payload.data.intersections.intersectionGeometry[0].laneSet.GenericLane[17].maneuvers", + "schemaPath": "#/$defs/J2735AllowedManeuvers/type" + }, + { + "message": "$.payload.data.intersections.intersectionGeometry[0].laneSet.GenericLane[20].connectsTo.connectsTo[0].signalGroup: null found, integer expected", + "jsonPath": "$.payload.data.intersections.intersectionGeometry[0].laneSet.GenericLane[20].connectsTo.connectsTo[0].signalGroup", + "schemaPath": "#/$defs/J2735SignalGroupID/type" + }, + { + "message": "$.payload.data.intersections.intersectionGeometry[0].laneSet.GenericLane[23].connectsTo: null found, object expected", + "jsonPath": "$.payload.data.intersections.intersectionGeometry[0].laneSet.GenericLane[23].connectsTo", + "schemaPath": "#/$defs/J2735ConnectsToList_Wrapper/type" + }, + { + "message": "$.payload.data.intersections.intersectionGeometry[0].laneSet.GenericLane[23].maneuvers: null found, object expected", + "jsonPath": "$.payload.data.intersections.intersectionGeometry[0].laneSet.GenericLane[23].maneuvers", + "schemaPath": "#/$defs/J2735AllowedManeuvers/type" + }, + { + "message": "$.payload.data.intersections.intersectionGeometry[0].laneSet.GenericLane[24].connectsTo: null found, object expected", + "jsonPath": "$.payload.data.intersections.intersectionGeometry[0].laneSet.GenericLane[24].connectsTo", + "schemaPath": "#/$defs/J2735ConnectsToList_Wrapper/type" + }, + { + "message": "$.payload.data.intersections.intersectionGeometry[0].laneSet.GenericLane[24].maneuvers: null found, object expected", + "jsonPath": "$.payload.data.intersections.intersectionGeometry[0].laneSet.GenericLane[24].maneuvers", + "schemaPath": "#/$defs/J2735AllowedManeuvers/type" + }, + { + "message": "$.payload.data.intersections.intersectionGeometry[0].laneSet.GenericLane[25].connectsTo: null found, object expected", + "jsonPath": "$.payload.data.intersections.intersectionGeometry[0].laneSet.GenericLane[25].connectsTo", + "schemaPath": "#/$defs/J2735ConnectsToList_Wrapper/type" + }, + { + "message": "$.payload.data.intersections.intersectionGeometry[0].laneSet.GenericLane[25].maneuvers: null found, object expected", + "jsonPath": "$.payload.data.intersections.intersectionGeometry[0].laneSet.GenericLane[25].maneuvers", + "schemaPath": "#/$defs/J2735AllowedManeuvers/type" + }, + { + "message": "$.payload.data.intersections.intersectionGeometry[0].speedLimits: null found, object expected", + "jsonPath": "$.payload.data.intersections.intersectionGeometry[0].speedLimits", + "schemaPath": "#/$defs/J2735SpeedLimitList_Wrapper/type" + } + ], + "laneWidth": 366, + "mapSource": "RSU", + "timeStamp": "2022-12-08T16:47:18.558068Z" + } +} diff --git a/webapp/src/components/map/side-panel.tsx b/webapp/src/components/map/side-panel.tsx new file mode 100644 index 000000000..16081f0bf --- /dev/null +++ b/webapp/src/components/map/side-panel.tsx @@ -0,0 +1,245 @@ +import React, { useState } from "react"; + +import { Paper, Box, IconButton, Typography } from "@mui/material"; +import ChevronLeftIcon from "@mui/icons-material/ChevronLeft"; +import ChevronRightIcon from "@mui/icons-material/ChevronRight"; +import MuiAccordion, { AccordionProps } from "@mui/material/Accordion"; +import MuiAccordionSummary, { AccordionSummaryProps } from "@mui/material/AccordionSummary"; +import MuiAccordionDetails from "@mui/material/AccordionDetails"; +import ArrowForwardIosSharpIcon from "@mui/icons-material/ArrowForwardIosSharp"; +import { styled } from "@mui/material/styles"; +import { CustomTable } from "./custom-table"; +import { format } from "date-fns"; +import { ExpandableTable } from "./expandable-table"; + +const Accordion = styled((props: AccordionProps) => )( + ({ theme }) => ({ + //border: `1px solid ${theme.palette.divider}`, + // '&:not(:last-child)': { + // borderBottom: 0, + // }, + // '&:before': { + // display: 'none', + // }, + }) +); + +const AccordionSummary = styled((props: AccordionSummaryProps) => ( + } {...props} /> +))(({ theme }) => ({ + minHeight: 0, + paddingLeft: 10, + flexDirection: "row-reverse", + "& .MuiAccordionSummary-expandIconWrapper.Mui-expanded": { + transform: "rotate(90deg)", + }, + "& .MuiAccordionSummary-content": { + marginLeft: theme.spacing(1), + marginTop: 0, + marginBottom: 0, + }, +})); + +const AccordionDetails = styled(MuiAccordionDetails)(({ theme }) => ({})); + +interface SidePanelProps { + laneInfo: ConnectingLanesFeatureCollection | undefined; + signalGroups: SpatSignalGroup[] | undefined; + bsms: BsmUiFeatureCollection; + events: MessageMonitor.Event[]; + notifications: MessageMonitor.Notification[]; + sourceData: MessageMonitor.Notification | MessageMonitor.Event | Assessment | { timestamp: number } | undefined; + sourceDataType: "notification" | "event" | "assessment" | "timestamp" | undefined; +} + +export const SidePanel = (props: SidePanelProps) => { + const { laneInfo, signalGroups, bsms, events, notifications, sourceData, sourceDataType } = props; + + const [open, setOpen] = useState(false); + + const getDataTable = ( + sourceData: MessageMonitor.Notification | MessageMonitor.Event | Assessment | { timestamp: number } | undefined, + sourceDataType: "notification" | "event" | "assessment" | "timestamp" | undefined + ) => { + switch (sourceDataType) { + case "notification": + return getNotificationTable(sourceData as MessageMonitor.Notification); + case "event": + return No Data; //getNotificationTableFromEvent(sourceData as MessageMonitor.Event); + case "assessment": + return No Data; //getNotificationTableFromAssessment(sourceData as Assessment); + case "timestamp": + return ( + {format((sourceData as { timestamp: number }).timestamp, "MM/dd/yyyy HH:mm:ss")} + ); //getNotificationTableFromAssessment(sourceData as Assessment); + default: + return No Data; + } + }; + + const getNotificationTable = (notification: MessageMonitor.Notification) => { + const fields = [["time", format(new Date(notification.notificationGeneratedAt), "yyyy-MM-dd HH:mm:ss")]]; + switch (notification.notificationType) { + case "SpatBroadcastRateNotification": + break; + case "SignalStateConflictNotification": + break; + case "SignalGroupAlignmentNotification": + break; + case "MapBroadcastRateNotification": + break; + case "LaneDirectionOfTravelNotification": + break; + case "IntersectionReferenceAlignmentNotification": + break; + case "ConnectionOfTravelNotification": + const connectionOfTravelNotification = notification as ConnectionOfTravelNotification; + fields.push([ + "ingress Lane ID", + // connectionOfTravelNotification?.assessment?.connectionOfTravelAssessmentGroups?.[0]?.ingressLaneID.toString(), + connectionOfTravelNotification?.ingressLane.toString(), + ]); + fields.push([ + "egress Lane ID", + // connectionOfTravelNotification?.assessment?.connectionOfTravelAssessmentGroups?.[0]?.egressLaneID.toString(), + connectionOfTravelNotification?.egressLane.toString(), + ]); + fields.push([ + "event count", + connectionOfTravelNotification?.assessment?.connectionOfTravelAssessmentGroups?.[0]?.eventCount.toString(), + ]); + break; + } + return ( + <> + {notification?.notificationText} + + + + + ); + }; + + return ( +
+ + + + { + setOpen((prev) => !prev); + }} + > + {open ? : } + + {!open ? ( + + ) : ( + <> + + Information Panel + + + + Lanes + + + + [ + lane.properties.ingressLaneId, + lane.properties.egressLaneId, + signalGroups?.find((grp) => grp.signalGroup == lane.properties.signalGroupId)?.state ?? + "no data", + ]) ?? [] + } + /> + + + + + + BSMs + + + + [ + bsm.properties.secMark / 1000, + bsm.properties.id, + bsm.properties.speed, + bsm.properties.heading, + ]) ?? [] + } + /> + + + + + + Events + + + + [ + format(event.eventGeneratedAt, "MM/dd/yyyy HH:mm:ss"), + event.eventType, + ]) ?? [] + } + details={events?.map((event) => JSON.stringify(event, null, 2)) ?? []} + /> + + + + + + Notifications + + + + [ + format(notification.notificationGeneratedAt, "MM/dd/yyyy HH:mm:ss"), + notification.notificationType, + ]) ?? [] + } + details={notifications?.map((notification) => JSON.stringify(notification, null, 2)) ?? []} + /> + + + + + + {sourceDataType} + + {getDataTable(sourceData, sourceDataType)} + + + )} + + + +
+ ); +}; diff --git a/webapp/src/components/map/utilities/colors.ts b/webapp/src/components/map/utilities/colors.ts new file mode 100644 index 000000000..d9f7764b2 --- /dev/null +++ b/webapp/src/components/map/utilities/colors.ts @@ -0,0 +1,24 @@ +export function generateColorDictionary(inputSet: Set): { [key: string]: string } { + const keysArray = Array.from(inputSet); + const colorDictionary: { [key: string]: string } = {}; + + const colorStep = 360 / keysArray.length; // Divide the color wheel into equal parts + + keysArray.forEach((key, index) => { + const hue = index * colorStep; // Calculate hue for each key + const color = `hsl(${hue}, 100%, 35%)`; // Create an HSL color + colorDictionary[key] = color; + }); + + return colorDictionary; +} + +export function generateMapboxStyleExpression(colors: { [key: string]: string }): mapboxgl.Expression { + const layerStyle: mapboxgl.Expression = ["match", ["get", "id"]]; + for (const [key, value] of Object.entries(colors)) { + layerStyle.push(key); + layerStyle.push(value); + } + layerStyle.push("#000000"); // other + return layerStyle; +} diff --git a/webapp/src/components/map/utilities/file-utilities.ts b/webapp/src/components/map/utilities/file-utilities.ts new file mode 100644 index 000000000..522fd915c --- /dev/null +++ b/webapp/src/components/map/utilities/file-utilities.ts @@ -0,0 +1,28 @@ +import JSZip from "jszip"; +import { MAP_QUERY_PARAMS } from "../map-slice"; +import FileSaver from "file-saver"; + +const downloadJsonFile = (contents: any, name: string) => { + const element = document.createElement("a"); + const file = new Blob([JSON.stringify(contents)], { + type: "text/plain", + }); + element.href = URL.createObjectURL(file); + element.download = name; + document.body.appendChild(element); // Required for this to work in FireFox + element.click(); +}; + +const downloadAllData = (queryParams: MAP_QUERY_PARAMS, rawData: any) => { + var zip = new JSZip(); + zip.file(`intersection_${queryParams.intersectionId}_MAP_data.json`, JSON.stringify(rawData["map"])); + zip.file(`intersection_${queryParams.intersectionId}_SPAT_data.json`, JSON.stringify(rawData["spat"])); + zip.file(`intersection_${queryParams.intersectionId}_BSM_data.json`, JSON.stringify(rawData["bsm"])); + zip.file( + `intersection_${queryParams.intersectionId}_Notification_data.json`, + JSON.stringify(rawData["notification"]) + ); + zip.generateAsync({ type: "blob" }).then(function (content) { + FileSaver.saveAs(content, `intersection_${queryParams.intersectionId}_data.zip`); + }); +}; diff --git a/webapp/src/components/map/utilities/map-utils.ts b/webapp/src/components/map/utilities/map-utils.ts new file mode 100644 index 000000000..f46ec92b4 --- /dev/null +++ b/webapp/src/components/map/utilities/map-utils.ts @@ -0,0 +1,25 @@ +export function deg2rad(deg: number) { + return deg * (Math.PI / 180); +} + +export function rad2deg(rad: number) { + return rad * (180 / Math.PI); +} + +// get bearing between two lat/long points +export function getBearingBetweenPoints(start: number[], end: number[]) { + if (!start || !end) return 0; + const lat1 = deg2rad(start[1]!); + const lon1 = deg2rad(start[0]!); + const lat2 = deg2rad(end[1]!); + const lon2 = deg2rad(end[0]!); + const dLon = lon2 - lon1; + const y = Math.sin(dLon) * Math.cos(lat2); + const x = Math.cos(lat1) * Math.sin(lat2) - Math.sin(lat1) * Math.cos(lat2) * Math.cos(dLon); + const brng = Math.atan2(y, x); + return rad2deg(brng); +} + +export const getTimeRange = (startDate: Date, endDate: Date) => { + return (endDate.getTime() - startDate.getTime()) / 1000; +}; diff --git a/webapp/src/components/map/utilities/message-utils.ts b/webapp/src/components/map/utilities/message-utils.ts new file mode 100644 index 000000000..59edb02bb --- /dev/null +++ b/webapp/src/components/map/utilities/message-utils.ts @@ -0,0 +1,199 @@ +import { getBearingBetweenPoints } from "./map-utils"; + +export const parseMapSignalGroups = (mapMessage: ProcessedMap): SignalStateFeatureCollection => { + const features: SignalStateFeature[] = []; + + mapMessage?.mapFeatureCollection?.features?.forEach((mapFeature: MapFeature) => { + if (!mapFeature.properties.ingressApproach || !mapFeature?.properties?.connectsTo?.[0]?.signalGroup) { + return; + } + const coords = mapFeature.geometry.coordinates.slice(0, 2); + features.push({ + type: "Feature", + properties: { + signalGroup: mapFeature.properties.connectsTo[0].signalGroup, + intersectionId: mapMessage.properties.intersectionId, + orientation: getBearingBetweenPoints(coords[1], coords[0]), + signalState: "UNAVAILABLE", + }, + geometry: { + type: "Point", + coordinates: mapFeature.geometry.coordinates[0], + }, + }); + }); + + return { + type: "FeatureCollection" as "FeatureCollection", + features: features, + }; +}; + +export const createMarkerForNotification = ( + center: number[], + notification: MessageMonitor.Notification, + connectingLanes: MapFeatureCollection +) => { + const features: any[] = []; + const markerCollection = { + type: "FeatureCollection" as "FeatureCollection", + features: features, + }; + switch (notification.notificationType) { + case "ConnectionOfTravelNotification": + // TODO: Re-add once more notification data is available + // const connTravelNotification = notification as ConnectionOfTravelNotification; + // const connTravelAssessmentGroups = connTravelNotification.assessment.connectionOfTravelAssessmentGroups; + // connTravelAssessmentGroups?.forEach((assessmentGroup) => { + // const ingressLocation: number[] | undefined = connectingLanes.features.find( + // (connectingLaneFeature: MapFeature) => { + // return connectingLaneFeature.properties.laneId === assessmentGroup.ingressLaneID; + // } + // )?.geometry.coordinates[0]; + // const egressLocation: number[] | undefined = connectingLanes.features.find( + // (connectingLaneFeature: MapFeature) => { + // return connectingLaneFeature.properties.laneId === assessmentGroup.egressLaneID; + // } + // )?.geometry.coordinates[0]; + // if (!ingressLocation || !egressLocation) return; + // const marker = { + // type: "Feature", + // properties: { + // description: `${connTravelNotification.notificationText}, egress lane ${assessmentGroup.egressLaneID}, ingress lane ${assessmentGroup.ingressLaneID}, connection ID ${assessmentGroup.connectionID}, event count ${assessmentGroup.eventCount}`, + // title: connTravelNotification.notificationType, + // }, + // geometry: { + // type: "LineString", + // coordinates: [ingressLocation, egressLocation], + // }, + // }; + // markerCollection.features.push(marker); + // }); + break; + case "IntersectionReferenceAlignmentNotification": + // No markers for this notification + break; + case "LaneDirectionOfTravelNotification": + const laneDirTravelNotification = notification as LaneDirectionOfTravelNotification; + const laneDirTravelAssessmentGroups = laneDirTravelNotification.assessment.laneDirectionOfTravelAssessmentGroup; + laneDirTravelAssessmentGroups?.forEach((assessmentGroup) => { + const laneLocation: number[] | undefined = connectingLanes.features.find( + (connectingLaneFeature: MapFeature) => { + return connectingLaneFeature.properties.laneId === assessmentGroup.laneID; + } + )?.geometry.coordinates[0]; + if (!laneLocation) return; + const numEvents = assessmentGroup.inToleranceEvents + assessmentGroup.outOfToleranceEvents; + const eventsRatio = assessmentGroup.inToleranceEvents / numEvents; + const marker = { + type: "Feature", + properties: { + description: `${laneDirTravelNotification.notificationText}, lane ID ${assessmentGroup.laneID}, in tolerance events ${eventsRatio} (${assessmentGroup.inToleranceEvents}/${numEvents})`, + title: laneDirTravelNotification.notificationType, + }, + geometry: { + type: "Point", + coordinates: laneLocation, + }, + }; + markerCollection.features.push(marker); + }); + break; + case "SignalGroupAlignmentNotification": + // No markers for this notification + break; + case "SignalStateConflictNotification": + const sigStateConflictNotification = notification as SignalStateConflictNotification; + const sigStateConflictEvent = sigStateConflictNotification.event; + const sigStateConflictMarker = { + type: "Feature", + properties: { + description: `${sigStateConflictNotification.notificationText}, Conflict type ${sigStateConflictEvent.conflictType}, First conflicting signal state ${sigStateConflictEvent.firstConflictingSignalState} of group ${sigStateConflictEvent.firstConflictingSignalGroup}, Second conflicting signal state ${sigStateConflictEvent.secondConflictingSignalState} of group ${sigStateConflictEvent.secondConflictingSignalGroup}`, + title: sigStateConflictNotification.notificationType, + }, + geometry: { + type: "Point", + coordinates: center, + }, + }; + markerCollection.features.push(sigStateConflictMarker); + break; + case "TimeChangeDetailsNotification": + // No markers for this notification + break; + case "KafkaStreamsAnomalyNotification": + // No markers for this notification + break; + case "BroadcastRateNotification": + // No markers for this notification + break; + } + return markerCollection; +}; + +export const addConnections = ( + connectingLanes: ConnectingLanesFeatureCollection, + signalGroups: SpatSignalGroup[] +): ConnectingLanesUiFeatureCollection => { + return { + ...connectingLanes, + features: connectingLanes.features.map((feature) => ({ + ...feature, + properties: { + ...feature.properties, + signalState: signalGroups.find((signalGroup) => signalGroup.signalGroup == feature.properties.signalGroupId) + ?.state, + }, + })), + }; +}; + +export const generateSignalStateFeatureCollection = ( + prevSignalStates: SignalStateFeatureCollection, + signalGroups: SpatSignalGroup[] +): SignalStateFeatureCollection => { + return { + ...prevSignalStates, + features: (prevSignalStates?.features ?? []).map((feature) => ({ + ...feature, + properties: { + ...feature.properties, + signalState: + signalGroups?.find((signalGroup) => signalGroup.signalGroup == feature.properties.signalGroup)?.state ?? + "UNAVAILABLE", + }, + })), + }; +}; + +export const parseSpatSignalGroups = (spats: ProcessedSpat[]): SpatSignalGroups => { + const timedSignalGroups: SpatSignalGroups = {}; + spats?.forEach((spat: ProcessedSpat) => { + timedSignalGroups[Date.parse(spat.odeReceivedAt)] = spat.states.map((state) => { + return { + signalGroup: state.signalGroup, + state: state.stateTimeSpeed?.[0]?.eventState as SignalState, + }; + }); + }); + return timedSignalGroups; +}; + +export const parseBsmToGeojson = (bsmData: OdeBsmData[]): BsmFeatureCollection => { + return { + type: "FeatureCollection" as "FeatureCollection", + features: bsmData.map((bsm) => { + return { + type: "Feature", + properties: { + ...bsm.payload.data.coreData, + odeReceivedAt: new Date(bsm.metadata.odeReceivedAt as string).getTime() / 1000, + }, + geometry: { + type: "Point", + coordinates: [bsm.payload.data.coreData.position.longitude, bsm.payload.data.coreData.position.latitude], + }, + }; + }), + }; +}; diff --git a/webapp/src/features/adminAddRsu/adminAddRsuSlice.tsx b/webapp/src/features/adminAddRsu/adminAddRsuSlice.tsx index 1f8dcf7d2..fe6c4b78d 100644 --- a/webapp/src/features/adminAddRsu/adminAddRsuSlice.tsx +++ b/webapp/src/features/adminAddRsu/adminAddRsuSlice.tsx @@ -5,7 +5,7 @@ import apiHelper from '../../apis/api-helper' import { updateTableData as updateRsuTableData } from '../adminRsuTab/adminRsuTabSlice' import { RootState } from '../../store' import { AdminOrgRsu } from '../adminOrganizationTab/adminOrganizationTabSlice' -import { AdminRsu } from '../../types/Rsu' +import { AdminRsu } from '../../models/Rsu' import { AdminAddRsuForm } from './AdminAddRsu' export type AdminRsuCreationInfo = { diff --git a/webapp/src/features/adminEditRsu/AdminEditRsu.tsx b/webapp/src/features/adminEditRsu/AdminEditRsu.tsx index d31fee0a5..3ecedebee 100644 --- a/webapp/src/features/adminEditRsu/AdminEditRsu.tsx +++ b/webapp/src/features/adminEditRsu/AdminEditRsu.tsx @@ -39,7 +39,7 @@ import { useSelector, useDispatch } from 'react-redux' import '../adminRsuTab/Admin.css' import { AnyAction, ThunkDispatch } from '@reduxjs/toolkit' import { RootState } from '../../store' -import { AdminRsu } from '../../types/Rsu' +import { AdminRsu } from '../../models/Rsu' import { useParams } from 'react-router-dom' export type AdminEditRsuFormType = { diff --git a/webapp/src/features/adminOrganizationTab/adminOrganizationTabSlice.tsx b/webapp/src/features/adminOrganizationTab/adminOrganizationTabSlice.tsx index d511a5061..9909cae47 100644 --- a/webapp/src/features/adminOrganizationTab/adminOrganizationTabSlice.tsx +++ b/webapp/src/features/adminOrganizationTab/adminOrganizationTabSlice.tsx @@ -4,7 +4,7 @@ import EnvironmentVars from '../../EnvironmentVars' import apiHelper from '../../apis/api-helper' import { RootState } from '../../store' import { ApiMsgRespWithCodes } from '../../apis/rsu-api-types' -import { AdminRsu } from '../../types/Rsu' +import { AdminRsu } from '../../models/Rsu' export type AdminOrgSummary = { name: string diff --git a/webapp/src/features/adminOrganizationTabRsu/adminOrganizationTabRsuSlice.tsx b/webapp/src/features/adminOrganizationTabRsu/adminOrganizationTabRsuSlice.tsx index 57a29cf19..eec6b24fb 100644 --- a/webapp/src/features/adminOrganizationTabRsu/adminOrganizationTabRsuSlice.tsx +++ b/webapp/src/features/adminOrganizationTabRsu/adminOrganizationTabRsuSlice.tsx @@ -3,7 +3,7 @@ import { selectToken } from '../../generalSlices/userSlice' import EnvironmentVars from '../../EnvironmentVars' import apiHelper from '../../apis/api-helper' import { RootState } from '../../store' -import { AdminRsu } from '../../types/Rsu' +import { AdminRsu } from '../../models/Rsu' import { AdminOrgRsuDeleteMultiple, AdminOrgRsuDeleteSingle, diff --git a/webapp/src/features/adminRsuTab/adminRsuTabSlice.tsx b/webapp/src/features/adminRsuTab/adminRsuTabSlice.tsx index bc036c6b4..3fd93d494 100644 --- a/webapp/src/features/adminRsuTab/adminRsuTabSlice.tsx +++ b/webapp/src/features/adminRsuTab/adminRsuTabSlice.tsx @@ -5,7 +5,7 @@ import apiHelper from '../../apis/api-helper' import { getRsuInfoOnly } from '../../generalSlices/rsuSlice' import { RootState } from '../../store' import { AdminEditRsuFormType } from '../adminEditRsu/AdminEditRsu' -import { AdminRsu } from '../../types/Rsu' +import { AdminRsu } from '../../models/Rsu' const initialState = { tableData: [] as AdminEditRsuFormType[], diff --git a/webapp/src/features/menu/DisplayCounts.tsx b/webapp/src/features/menu/DisplayCounts.tsx index 7a4cd3f2f..0da44c4a4 100644 --- a/webapp/src/features/menu/DisplayCounts.tsx +++ b/webapp/src/features/menu/DisplayCounts.tsx @@ -23,8 +23,8 @@ import { selectCurrentSort, selectSortedCountList, sortCountList, changeDate } f import '../../components/css/SnmpwalkMenu.css' import { AnyAction, ThunkDispatch } from '@reduxjs/toolkit' import { RootState } from '../../store' -import { CountsListElement } from '../../types/Rsu' -import { MessageType } from '../../types/MessageTypes' +import { CountsListElement } from '../../models/Rsu' +import { MessageType } from '../../models/MessageTypes' const messageTypeOptions = EnvironmentVars.getMessageTypes().map((type) => { return { value: type, label: type } diff --git a/webapp/src/features/menu/menuSlice.tsx b/webapp/src/features/menu/menuSlice.tsx index 2b20e7424..6cd744a9a 100644 --- a/webapp/src/features/menu/menuSlice.tsx +++ b/webapp/src/features/menu/menuSlice.tsx @@ -1,7 +1,7 @@ import { AnyAction, ThunkDispatch, createSlice } from '@reduxjs/toolkit' import { updateRowData } from '../../generalSlices/rsuSlice' import { RootState } from '../../store' -import { CountsListElement } from '../../types/Rsu' +import { CountsListElement } from '../../models/Rsu' const { DateTime } = require('luxon') const initialState = { diff --git a/webapp/src/generalSlices/rsuSlice.ts b/webapp/src/generalSlices/rsuSlice.ts index 8d4df692f..a06fa2800 100644 --- a/webapp/src/generalSlices/rsuSlice.ts +++ b/webapp/src/generalSlices/rsuSlice.ts @@ -14,9 +14,9 @@ import { } from '../apis/rsu-api-types' import { RootState } from '../store' import { selectToken, selectOrganizationName } from './userSlice' -import { SelectedSrm } from '../types/Srm' -import { CountsListElement } from '../types/Rsu' -import { MessageType } from '../types/MessageTypes' +import { SelectedSrm } from '../models/Srm' +import { CountsListElement } from '../models/Rsu' +import { MessageType } from '../models/MessageTypes' const { DateTime } = require('luxon') const initialState = { diff --git a/webapp/src/generalSlices/wzdxSlice.ts b/webapp/src/generalSlices/wzdxSlice.ts index 3995897db..294b24a37 100644 --- a/webapp/src/generalSlices/wzdxSlice.ts +++ b/webapp/src/generalSlices/wzdxSlice.ts @@ -1,7 +1,7 @@ import { createAsyncThunk, createSlice } from '@reduxjs/toolkit' import RsuApi from '../apis/rsu-api' import { RootState } from '../store' -import { WZDxWorkZoneFeed } from '../types/wzdx/WzdxWorkZoneFeed42' +import { WZDxWorkZoneFeed } from '../models/wzdx/WzdxWorkZoneFeed42' import { selectToken } from './userSlice' const initialState: WZDxWorkZoneFeed = { type: 'FeatureCollection', features: [] } diff --git a/webapp/src/icons/bell.tsx b/webapp/src/icons/bell.tsx new file mode 100644 index 000000000..7421dab20 --- /dev/null +++ b/webapp/src/icons/bell.tsx @@ -0,0 +1,8 @@ +import { createSvgIcon } from "@mui/material/utils"; + +export const Bell = createSvgIcon( + + + , + "Bell" +); diff --git a/webapp/src/icons/chart-bar.tsx b/webapp/src/icons/chart-bar.tsx new file mode 100644 index 000000000..85ff86953 --- /dev/null +++ b/webapp/src/icons/chart-bar.tsx @@ -0,0 +1,8 @@ +import { createSvgIcon } from "@mui/material/utils"; + +export const ChartBar = createSvgIcon( + + + , + "ChartBar" +); diff --git a/webapp/src/icons/chevron-down.tsx b/webapp/src/icons/chevron-down.tsx new file mode 100644 index 000000000..1d391c3c6 --- /dev/null +++ b/webapp/src/icons/chevron-down.tsx @@ -0,0 +1,12 @@ +import { createSvgIcon } from "@mui/material/utils"; + +export const ChevronDown = createSvgIcon( + + + , + "ChevronDown" +); diff --git a/webapp/src/icons/chevron-right.tsx b/webapp/src/icons/chevron-right.tsx new file mode 100644 index 000000000..120005f38 --- /dev/null +++ b/webapp/src/icons/chevron-right.tsx @@ -0,0 +1,12 @@ +import { createSvgIcon } from "@mui/material/utils"; + +export const ChevronRight = createSvgIcon( + + + , + "ChevronRight" +); diff --git a/webapp/src/icons/clock.tsx b/webapp/src/icons/clock.tsx new file mode 100644 index 000000000..3810e85ae --- /dev/null +++ b/webapp/src/icons/clock.tsx @@ -0,0 +1,12 @@ +import { createSvgIcon } from "@mui/material/utils"; + +export const Clock = createSvgIcon( + + + , + "Clock" +); diff --git a/webapp/src/icons/cog.tsx b/webapp/src/icons/cog.tsx new file mode 100644 index 000000000..5af2c1c54 --- /dev/null +++ b/webapp/src/icons/cog.tsx @@ -0,0 +1,12 @@ +import { createSvgIcon } from "@mui/material/utils"; + +export const Cog = createSvgIcon( + + + , + "Cog" +); diff --git a/webapp/src/icons/download.tsx b/webapp/src/icons/download.tsx new file mode 100644 index 000000000..95176a3cd --- /dev/null +++ b/webapp/src/icons/download.tsx @@ -0,0 +1,12 @@ +import { createSvgIcon } from "@mui/material/utils"; + +export const Download = createSvgIcon( + + + , + "Download" +); diff --git a/webapp/src/icons/facebook.tsx b/webapp/src/icons/facebook.tsx new file mode 100644 index 000000000..dbdd247af --- /dev/null +++ b/webapp/src/icons/facebook.tsx @@ -0,0 +1,8 @@ +import { createSvgIcon } from "@mui/material/utils"; + +export const Facebook = createSvgIcon( + + + , + "Facebook" +); diff --git a/webapp/src/icons/google.tsx b/webapp/src/icons/google.tsx new file mode 100644 index 000000000..388f35b5a --- /dev/null +++ b/webapp/src/icons/google.tsx @@ -0,0 +1,8 @@ +import { createSvgIcon } from "@mui/material/utils"; + +export const Google = createSvgIcon( + + + , + "Google" +); diff --git a/webapp/src/icons/lock.tsx b/webapp/src/icons/lock.tsx new file mode 100644 index 000000000..8f8ac4b72 --- /dev/null +++ b/webapp/src/icons/lock.tsx @@ -0,0 +1,12 @@ +import { createSvgIcon } from "@mui/material/utils"; + +export const Lock = createSvgIcon( + + + , + "Lock" +); diff --git a/webapp/src/images/logo.png b/webapp/src/icons/logo.png similarity index 100% rename from webapp/src/images/logo.png rename to webapp/src/icons/logo.png diff --git a/webapp/src/icons/menu.tsx b/webapp/src/icons/menu.tsx new file mode 100644 index 000000000..733d98599 --- /dev/null +++ b/webapp/src/icons/menu.tsx @@ -0,0 +1,12 @@ +import { createSvgIcon } from "@mui/material/utils"; + +export const Menu = createSvgIcon( + + + , + "Menu" +); diff --git a/webapp/src/icons/pencil-alt.tsx b/webapp/src/icons/pencil-alt.tsx new file mode 100644 index 000000000..5a48388ff --- /dev/null +++ b/webapp/src/icons/pencil-alt.tsx @@ -0,0 +1,13 @@ +import { createSvgIcon } from "@mui/material/utils"; + +export const PencilAlt = createSvgIcon( + + + + , + "PencilAlt" +); diff --git a/webapp/src/icons/plus.tsx b/webapp/src/icons/plus.tsx new file mode 100644 index 000000000..69feeaeb1 --- /dev/null +++ b/webapp/src/icons/plus.tsx @@ -0,0 +1,10 @@ +import { createSvgIcon } from '@mui/material/utils'; + +export const Plus = createSvgIcon( + + + , 'Plus'); diff --git a/webapp/src/icons/refresh.tsx b/webapp/src/icons/refresh.tsx new file mode 100644 index 000000000..a1fdb62c2 --- /dev/null +++ b/webapp/src/icons/refresh.tsx @@ -0,0 +1,12 @@ +import { createSvgIcon } from "@mui/material/utils"; + +export const Refresh = createSvgIcon( + + + , + "Refresh" +); diff --git a/webapp/src/images/rsu_configure.PNG b/webapp/src/icons/rsu_configure.PNG similarity index 100% rename from webapp/src/images/rsu_configure.PNG rename to webapp/src/icons/rsu_configure.PNG diff --git a/webapp/src/images/rsu_count.PNG b/webapp/src/icons/rsu_count.PNG similarity index 100% rename from webapp/src/images/rsu_count.PNG rename to webapp/src/icons/rsu_count.PNG diff --git a/webapp/src/images/rsu_heatmap.PNG b/webapp/src/icons/rsu_heatmap.PNG similarity index 100% rename from webapp/src/images/rsu_heatmap.PNG rename to webapp/src/icons/rsu_heatmap.PNG diff --git a/webapp/src/images/rsu_menu.PNG b/webapp/src/icons/rsu_menu.PNG similarity index 100% rename from webapp/src/images/rsu_menu.PNG rename to webapp/src/icons/rsu_menu.PNG diff --git a/webapp/src/images/rsu_popup.PNG b/webapp/src/icons/rsu_popup.PNG similarity index 100% rename from webapp/src/images/rsu_popup.PNG rename to webapp/src/icons/rsu_popup.PNG diff --git a/webapp/src/images/rsu_status.PNG b/webapp/src/icons/rsu_status.PNG similarity index 100% rename from webapp/src/images/rsu_status.PNG rename to webapp/src/icons/rsu_status.PNG diff --git a/webapp/src/icons/search.tsx b/webapp/src/icons/search.tsx new file mode 100644 index 000000000..f2591d51c --- /dev/null +++ b/webapp/src/icons/search.tsx @@ -0,0 +1,12 @@ +import { createSvgIcon } from "@mui/material/utils"; + +export const Search = createSvgIcon( + + + , + "Search" +); diff --git a/webapp/src/icons/selector.tsx b/webapp/src/icons/selector.tsx new file mode 100644 index 000000000..f49ff0224 --- /dev/null +++ b/webapp/src/icons/selector.tsx @@ -0,0 +1,12 @@ +import { createSvgIcon } from "@mui/material/utils"; + +export const Selector = createSvgIcon( + + + , + "ChartSquareBar" +); diff --git a/webapp/src/icons/shopping-bag.tsx b/webapp/src/icons/shopping-bag.tsx new file mode 100644 index 000000000..aabfa1d32 --- /dev/null +++ b/webapp/src/icons/shopping-bag.tsx @@ -0,0 +1,12 @@ +import { createSvgIcon } from "@mui/material/utils"; + +export const ShoppingBag = createSvgIcon( + + + , + "ShoppingBag" +); diff --git a/webapp/src/icons/traffic-light-green.png b/webapp/src/icons/traffic-light-green.png new file mode 100644 index 0000000000000000000000000000000000000000..7aab7c880e50178735be4df4ba8f92fd3fb1477c GIT binary patch literal 15065 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 Bp+Eot literal 0 HcmV?d00001 diff --git a/webapp/src/icons/traffic-light-red.png b/webapp/src/icons/traffic-light-red.png new file mode 100644 index 0000000000000000000000000000000000000000..16ec65676e8a98701edb200f5d70e5b79c3eadfb GIT binary patch literal 15751 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 + + , + "Upload" +); diff --git a/webapp/src/icons/user-add.tsx b/webapp/src/icons/user-add.tsx new file mode 100644 index 000000000..8cec0fc1c --- /dev/null +++ b/webapp/src/icons/user-add.tsx @@ -0,0 +1,8 @@ +import { createSvgIcon } from "@mui/material/utils"; + +export const UserAdd = createSvgIcon( + + + , + "UserAdd" +); diff --git a/webapp/src/icons/user-circle.tsx b/webapp/src/icons/user-circle.tsx new file mode 100644 index 000000000..02708e937 --- /dev/null +++ b/webapp/src/icons/user-circle.tsx @@ -0,0 +1,12 @@ +import { createSvgIcon } from "@mui/material/utils"; + +export const UserCircle = createSvgIcon( + + + , + "UserCircle" +); diff --git a/webapp/src/icons/user.tsx b/webapp/src/icons/user.tsx new file mode 100644 index 000000000..308092fff --- /dev/null +++ b/webapp/src/icons/user.tsx @@ -0,0 +1,12 @@ +import { createSvgIcon } from "@mui/material/utils"; + +export const User = createSvgIcon( + + + , + "User" +); diff --git a/webapp/src/icons/users.tsx b/webapp/src/icons/users.tsx new file mode 100644 index 000000000..04f01fa30 --- /dev/null +++ b/webapp/src/icons/users.tsx @@ -0,0 +1,8 @@ +import { createSvgIcon } from "@mui/material/utils"; + +export const Users = createSvgIcon( + + + , + "Users" +); diff --git a/webapp/src/icons/x-circle.tsx b/webapp/src/icons/x-circle.tsx new file mode 100644 index 000000000..11f8900aa --- /dev/null +++ b/webapp/src/icons/x-circle.tsx @@ -0,0 +1,12 @@ +import { createSvgIcon } from "@mui/material/utils"; + +export const XCircle = createSvgIcon( + + + , + "XCircle" +); diff --git a/webapp/src/models/GraphData.d.ts b/webapp/src/models/GraphData.d.ts new file mode 100644 index 000000000..590f1b6f2 --- /dev/null +++ b/webapp/src/models/GraphData.d.ts @@ -0,0 +1,32 @@ +type GraphArrayDataType = { + id: number + ConnectionOfTravelEventCount: number + IntersectionReferenceAlignmentEventCount: number + LaneDirectionOfTravelEventCount: number + ProcessingTimePeriodCount: number + SignalGroupAlignmentEventCount: number + SignalStateConflictEventCount: number + SignalStateEventCount: number + SignalStateStopEventCount: number + TimeChangeDetailsEventCount: number + MapMinimumDataEventCount: number + SpatMinimumDataEventCount: number + MapBroadcastRateEventCount: number + SpatBroadcastRateEventCount: number +} + +type EVENT_TYPES = + | 'ALL' + | 'ConnectionOfTravelEvent' + | 'IntersectionReferenceAlignmentEvent' + | 'LaneDirectionOfTravelEvent' + | 'ProcessingTimePeriod' + | 'SignalGroupAlignmentEvent' + | 'SignalStateConflictEvent' + | 'SignalStateEvent' + | 'SignalStateStopEvent' + | 'TimeChangeDetailsEvent' + | 'MapMinimumDataEvent' + | 'SpatMinimumDataEvent' + | 'MapBroadcastRateEvent' + | 'SpatBroadcastRateEvent' diff --git a/webapp/src/models/IntersectionReferenceData.d.ts b/webapp/src/models/IntersectionReferenceData.d.ts new file mode 100644 index 000000000..247d12d84 --- /dev/null +++ b/webapp/src/models/IntersectionReferenceData.d.ts @@ -0,0 +1,7 @@ +type IntersectionReferenceData = { + intersectionID: number; + roadRegulatorID: number; + rsuIP: string; + latitude: number; + longitude: number; +}; diff --git a/webapp/src/models/IntersectionSummary.d.ts b/webapp/src/models/IntersectionSummary.d.ts new file mode 100644 index 000000000..63835cd69 --- /dev/null +++ b/webapp/src/models/IntersectionSummary.d.ts @@ -0,0 +1,4 @@ +type IntersectionSummary = { + id: number + location?: number[] +} diff --git a/webapp/src/models/MessageMonitorNotification.d.ts b/webapp/src/models/MessageMonitorNotification.d.ts new file mode 100644 index 000000000..66cfe8edc --- /dev/null +++ b/webapp/src/models/MessageMonitorNotification.d.ts @@ -0,0 +1,12 @@ +type MessageMonitorNotification = + | IntersectionReferenceAlignmentNotification + | SignalGroupAlignmentNotification + // | SpatMinimumDataNotification // + // | MapMinimumDataNotification // + | SpatBroadcastRateNotification + | MapBroadcastRateNotification + // | StopLineStopNotification // + // | LaneDirectionOfTravelAssessmentNotification + | ConnectionOfTravelNotification // + | SignalStateConflictNotification + | TimeChangeDetailsNotification diff --git a/webapp/src/types/MessageTypes.d.ts b/webapp/src/models/MessageTypes.d.ts similarity index 100% rename from webapp/src/types/MessageTypes.d.ts rename to webapp/src/models/MessageTypes.d.ts diff --git a/webapp/src/models/NotificationType.d.ts b/webapp/src/models/NotificationType.d.ts new file mode 100644 index 000000000..1f9c039db --- /dev/null +++ b/webapp/src/models/NotificationType.d.ts @@ -0,0 +1,12 @@ +type NotificationType = + | 'IntersectionReferenceAlignmentNotification' + | 'SignalGroupAlignmentNotification' + // | "SpatMinimumDataNotification" // + // | "MapMinimumDataNotification" // + | 'SpatBroadcastRateNotification' + | 'MapBroadcastRateNotification' + // | "SignalStateAssessmentNotification" // + | 'LaneDirectionOfTravelAssessmentNotification' + // | "ConnectionOfTravelNotification" // + | 'SignalStateConflictNotification' + | 'TimeChangeDetailsNotification' diff --git a/webapp/src/models/ParsedJwt.d.ts b/webapp/src/models/ParsedJwt.d.ts new file mode 100644 index 000000000..aa3837c29 --- /dev/null +++ b/webapp/src/models/ParsedJwt.d.ts @@ -0,0 +1,99 @@ +type ParsedJWT = { + exp: number; + iat: number; + jti: string; + iss: string; + aud: string[]; + sub: string; + typ: "Bearer"; + azp: string; + session_state: string; + acr: string; + "allowed-origins": string[]; + realm_access: { + roles: ROLES_TYPE[]; + }; + resource_access: { + "realm-management": { + roles: string[]; + }; + account: { + roles: string[]; + }; + }; + scope: string; + sid: string; + email_verified: boolean; + name: string; + preferred_username: string; + given_name: string; + family_name: string; +}; + +type ROLES_TYPE = "ADMIN" | "USER" | "default-roles-conflictvisualizer" | "offline_access" | "uma_authorization"; + +// { +// "exp": 1697731084, +// "iat": 1697727484, +// "jti": "ab4f4164-878b-4884-85cb-e469c25ff59f", +// "iss": "http://172.30.103.166:8084/realms/conflictvisualizer", +// "aud": [ +// "realm-management", +// "account" +// ], +// "sub": "a209224a-a59e-42bd-9bb1-527e76662b88", +// "typ": "Bearer", +// "azp": "conflictvisualizer-gui", +// "session_state": "49405f13-ccc1-4527-a2ee-515c926e0ac1", +// "acr": "1", +// "allowed-origins": [ +// "http://172.30.103.166:3000" +// ], +// "realm_access": { +// "roles": [ +// "offline_access", +// "default-roles-conflictvisualizer", +// "uma_authorization", +// "ADMIN" +// ] +// }, +// "resource_access": { +// "realm-management": { +// "roles": [ +// "view-realm", +// "view-identity-providers", +// "manage-identity-providers", +// "impersonation", +// "realm-admin", +// "create-client", +// "manage-users", +// "query-realms", +// "view-authorization", +// "query-clients", +// "query-users", +// "manage-events", +// "manage-realm", +// "view-events", +// "view-users", +// "view-clients", +// "manage-authorization", +// "manage-clients", +// "query-groups" +// ] +// }, +// "account": { +// "roles": [ +// "manage-account", +// "manage-account-links", +// "view-profile" +// ] +// } +// }, +// "scope": "profile email", +// "sid": "49405f13-ccc1-4527-a2ee-515c926e0ac1", +// "email_verified": true, +// "name": "admin cimms", +// "preferred_username": "admin@cimms.com", +// "given_name": "admin", +// "family_name": "cimms" +// } diff --git a/webapp/src/models/ReduxStateType.d.ts b/webapp/src/models/ReduxStateType.d.ts new file mode 100644 index 000000000..86a4e364f --- /dev/null +++ b/webapp/src/models/ReduxStateType.d.ts @@ -0,0 +1,11 @@ +type ReduxStateType = { + user: USER_SLICE_STATE_TYPE; +}; + +type USER_SLICE_STATE_TYPE = { + loading: boolean; + value: { + authToken?: string; + parsedJwt?: ParsedJWT; + }; +}; diff --git a/webapp/src/types/Rsu.d.ts b/webapp/src/models/Rsu.d.ts similarity index 100% rename from webapp/src/types/Rsu.d.ts rename to webapp/src/models/Rsu.d.ts diff --git a/webapp/src/types/Srm.d.ts b/webapp/src/models/Srm.d.ts similarity index 100% rename from webapp/src/types/Srm.d.ts rename to webapp/src/models/Srm.d.ts diff --git a/webapp/src/models/User.d.ts b/webapp/src/models/User.d.ts new file mode 100644 index 000000000..2a3dea7b7 --- /dev/null +++ b/webapp/src/models/User.d.ts @@ -0,0 +1,25 @@ +type User = { + email: string; + first_name: string; + last_name: string; + role: UserRole; + id: string; + email_preference: EmailPreferences; +}; + +type EmailPreferences = { + receiveAnnouncements: boolean; + notificationFrequency: EmailFrequency; + receiveCeaseBroadcastRecommendations: boolean; + receiveCriticalErrorMessages: boolean; + receiveNewUserRequests: boolean; +}; + +type UserRole = "ADMIN" | "USER"; + +type EmailFrequency = "ALWAYS" | "ONCE_PER_HOUR" | "ONCE_PER_DAY" | "ONCE_PER_WEEK" | "ONCE_PER_MONTH" | "NEVER"; + +type KeycloakRole = { + id: string; + name: string; +}; diff --git a/webapp/src/types/UserTypes.d.ts b/webapp/src/models/UserTypes.d.ts similarity index 100% rename from webapp/src/types/UserTypes.d.ts rename to webapp/src/models/UserTypes.d.ts diff --git a/webapp/src/models/bsmCollection.d.ts b/webapp/src/models/bsmCollection.d.ts new file mode 100644 index 000000000..dd92a255f --- /dev/null +++ b/webapp/src/models/bsmCollection.d.ts @@ -0,0 +1,37 @@ +type BsmFeatureCollection = { + type: "FeatureCollection"; + features: BsmFeature[]; +}; + +type BsmFeature = { + type: "Feature"; + properties: J2735BsmCoreData & bsmReceivedAt; + geometry: PointGemetry; +}; + +type PointGemetry = { + type: "Point"; + coordinates: number[]; +}; + +type bsmReceivedAt = { + odeReceivedAt: number; +}; + +type BsmUiFeatureCollection = { + type: "FeatureCollection"; + features: BsmUiFeature[]; +}; + +type BsmUiFeature = { + type: "Feature"; + properties: BsmUiProperties; + geometry: PointGemetry; +}; + +type BsmUiProperties = { + id: string; + secMark: number; + speed: number; + heading: number; +}; diff --git a/webapp/src/models/convert_java_to_ts.py b/webapp/src/models/convert_java_to_ts.py new file mode 100644 index 000000000..15f1cb1b4 --- /dev/null +++ b/webapp/src/models/convert_java_to_ts.py @@ -0,0 +1,117 @@ +import re +import os + +matching_expr = r"^ (?:[a-zA-Z@]+? )*(?:private )?(?:public )?(?:final )?(?:static )?([a-zA-Z0-9_<>]+?) ([a-zA-Z_]+).*?;" +matching_expr_extends = r"public class (.*?) extends (.*?)\{" +matching_expr_interface = r"public interface (.*?)\{" + +TYPE_MAPPINGS = { + # Simple types + '(^String)': 'str', + + '(^int)': 'number', + '(^Integer)': 'number', + '(^Double)': 'number', + '(^double)': 'number', + '(^long)': 'number', + '(^Long)': 'number', + 'final': 'any', + + '(^Boolean)': 'boolean', + '(^bool)': 'boolean', + + '^ArrayList<(.*?)>': r'\1[]', + '^List<(.*?)>': r'\1[]', + + '(^LineString)': 'number[][]', + '(^ZonedDateTime)': 'Date', + + # '(Set<(.*?)>)': r'Set<\1>', +} + +# IMPORTS_PATH = "./ts/imports" +TEMPLATE = """{imports} +type {class_name} = {extension}{{ +{contents} +}}""" + +# if not os.path.exists(IMPORTS_PATH): +# os.mkdir(IMPORTS_PATH) + +# INITIAL_PATH = "java/jpo-conflictmonitor/jpo-conflictmonitor/src/main/java/us/dot/its/jpo/conflictmonitor/monitor/models" +INITIAL_PATH = "../../../api/jpo-conflictmonitor/jpo-conflictmonitor/src/main/java/us/dot/its/jpo/conflictmonitor/monitor/models" +files = [os.path.join(dp, f) for dp, dn, filenames in os.walk("./" + INITIAL_PATH) + for f in filenames if os.path.splitext(f)[1] == '.java'] + +DIRECTORIES_TO_IGNORE = ["test", "jpo-s3-deposit", "jpo-ode-consumer-example", + "jpo-ode-svcs", "jpo-sdw-depositor", "jpo-security-scvs", "asn1_codec", "jpo-geojsonconverter"] + +CUSTOM_DIRECTORIES_TO_NOT_REGENERATE = ["config"] + +for file_path in files: + print(file_path) + imports = "" + + file_path = '.'.join(file_path.replace('\\', '/').split('.')[:-1]) + JAVA_PATH = '/'.join(file_path.split('/')[:-1]) + TS_PATH = JAVA_PATH.replace(INITIAL_PATH, 'jpo-conflictmonitor/') + CLASS_NAME = file_path.split('/')[-1] + + ignored = False + directories = file_path.split('/') + for ignored_dir in DIRECTORIES_TO_IGNORE: + if ignored_dir in directories: + ignored = True + for ignored_dir in CUSTOM_DIRECTORIES_TO_NOT_REGENERATE: + if ignored_dir in directories: + ignored = True + if ignored: + continue + + file_contents = open(f"{JAVA_PATH}/{CLASS_NAME}.java", 'r').read() + interface_match = re.findall(matching_expr_interface, file_contents) + if interface_match: + continue + + extension_match = re.findall(matching_expr_extends, file_contents) + extension = "" + if extension_match and extension_match[0][1]: + if extension_match[0][1].strip() == "Notification": + imports = '/// ' + extension = f"MessageMonitor.{extension_match[0][1]} & " if extension_match else "" + elif extension_match[0][1].strip() == "Event": + imports = '/// ' + extension = f"MessageMonitor.{extension_match[0][1]} & " if extension_match else "" + else: + extension = f"{extension_match[0][1]} & " if extension_match else "" + + matches = [] + lines = file_contents.split('\n') + for l in lines: + match = re.search(matching_expr, l) + if match: matches.append(match.groups()) + + print(matches) + lines = [] + for var in matches: + ts_type = var[0] + ts_name = var[1] + for k, v in TYPE_MAPPINGS.items(): + ts_type = re.sub(k, v, ts_type) + lines.append(f" {ts_name}: {ts_type}") + + contents = '\n'.join(lines) + if CLASS_NAME == "Notification": + imports = "declare namespace MessageMonitor {" + contents += "\n}" + if CLASS_NAME == "Event": + imports = "declare namespace MessageMonitor {" + contents += "\n}" + + ts_contents = TEMPLATE.format( + imports=imports, class_name=CLASS_NAME, extension=extension, contents=contents) + + if not os.path.exists(f"{TS_PATH}/"): + os.makedirs(f"{TS_PATH}/") + with open(f"{TS_PATH}/{CLASS_NAME}.d.ts", 'w') as f: + f.write(ts_contents) diff --git a/webapp/src/models/get_required_imports.py b/webapp/src/models/get_required_imports.py new file mode 100644 index 000000000..0a6279b3d --- /dev/null +++ b/webapp/src/models/get_required_imports.py @@ -0,0 +1,95 @@ +import re +import os +import glob +import json + +matching_expr = r" private ([a-zA-Z0-9_]*?) ([a-zA-Z0-9_]*)" + +KNOWN_TYPES = [ + '(^String)', + + '(^int)', + '(^Integer)', + '(^Double)', + '(^double)', + '(^long)', + '(^Long)', + + '(^Boolean)', + '(^bool)', + + '^ArrayList<(.*?)>', + '^List<(.*?)>', + + '(^LineString)', + '(^ZonedDateTime)', + + '(Set<(.*?)>)', +] + +KNOWN_ORGS = { + "us.dot.its.jpo.ode.plugin": "https://github.com/usdot-jpo-ode/jpo-ode/tree/dev/jpo-ode-plugins/src/main/java/us/dot/its/jpo/ode/plugin", + "us.dot.its.jpo.ode.model": "https://github.com/usdot-jpo-ode/jpo-ode/tree/dev/jpo-ode-core/src/main/java/us/dot/its/jpo/ode/model", + "org.locationtech.jts": "https://github.com/locationtech/jts/tree/master/modules/core/src/main/java/org/locationtech/jts", + "us.dot.its.jpo.geojsonconverter": "https://github.com/usdot-jpo-ode/jpo-geojsonconverter/tree/develop/jpo-geojsonconverter/src/main/java/us/dot/its/jpo/geojsonconverter", + "us.dot.its.jpo.conflictmonitor": "https://github.com/usdot-jpo-ode/jpo-conflictmonitor/tree/dev/jpo-conflictmonitor/src/main/java/us/dot/its/jpo/conflictmonitor", +} + +TEMPLATE = """type {class_name} = {{ +{contents} +}};""" + +files = [os.path.join(dp, f) for dp, dn, filenames in os.walk('./java/') + for f in filenames if os.path.splitext(f)[1] == '.java'] +print(files) + +types = {} +for file_path in files: + + file_path = '.'.join(file_path.replace('\\', '/').split('.')[:-1]) + JAVA_PATH = '/'.join(file_path.split('/')[:-1]) + TS_PATH = JAVA_PATH.replace('java', 'ts') + CLASS_NAME = file_path.split('/')[-1] + # print(file_path, JAVA_PATH, TS_PATH, CLASS_NAME) + + file_contents = open(f"{JAVA_PATH}/{CLASS_NAME}.java", 'r').read() + + matches = re.findall(matching_expr, file_contents) + # print(matches) + for var in matches: + java_type = var[0] + found = False + for known_type in KNOWN_TYPES: + if re.match(known_type, java_type): + found = True + break + if not found: + local_files = [i.replace('\\', '/').split('/')[-1].split('.')[0] + for i in glob.glob(f"{JAVA_PATH}/*")] + if java_type in local_files: + import_path = file_path[2:] + types[java_type] = f"./{import_path}/{java_type}.java" + else: + import_path = re.search( + f"import (.*?\\.{java_type});", file_contents).group(1) + found_org = False + for org, url in KNOWN_ORGS.items(): + print(org, java_type) + if org in import_path: + found_org = True + path = f"{url}{import_path.replace(org, '').replace('.', '/')}.java" + types[java_type] = path + break + if not found_org: + raise Exception( + f"Could not find organization for java type {java_type} in {file_path}") + +with open(f"./required_types.json", 'w') as f: + f.write(json.dumps(types, indent=2)) + +# ts_contents = TEMPLATE.format(class_name=CLASS_NAME, contents='\n'.join(lines)) + +# if not os.path.exists(f"{TS_PATH}/{CLASS_NAME}.ts"): +# os.makedirs(f"{TS_PATH}/") +# with open(f"{TS_PATH}/{CLASS_NAME}.ts", 'w') as f: +# f.write(ts_contents) diff --git a/webapp/src/models/imports/Coordinate.d.ts b/webapp/src/models/imports/Coordinate.d.ts new file mode 100644 index 000000000..41ac4d919 --- /dev/null +++ b/webapp/src/models/imports/Coordinate.d.ts @@ -0,0 +1,5 @@ +type Coordinate = { + x?: number; + y?: number; + z?: number; +}; diff --git a/webapp/src/models/imports/GeometryFactory.d.ts b/webapp/src/models/imports/GeometryFactory.d.ts new file mode 100644 index 000000000..1553b9bc7 --- /dev/null +++ b/webapp/src/models/imports/GeometryFactory.d.ts @@ -0,0 +1 @@ +type GeometryFactory = any; // TODO: check diff --git a/webapp/src/models/imports/J2735/J2735AdvisorySpeedList.d.ts b/webapp/src/models/imports/J2735/J2735AdvisorySpeedList.d.ts new file mode 100644 index 000000000..ebf3170fa --- /dev/null +++ b/webapp/src/models/imports/J2735/J2735AdvisorySpeedList.d.ts @@ -0,0 +1,23 @@ +type J2735AdvisorySpeedList = { + advisorySpeedList?: J2735AdvisorySpeed[]; +}; + +type J2735AdvisorySpeed = { + type?: J2735AdvisorySpeedType; + speed?: number; + confidence?: J2735SpeedConfidence; + distance?: number; + classId?: number; +}; + +type J2735AdvisorySpeedType = "NONE" | "GREENWAVE" | "ECODRIVE" | "TRANSIT"; + +type J2735SpeedConfidence = + | "UNAVAILABLE" + | "PREC100MS" + | "PREC10MS" + | "PREC5MS" + | "PREC1MS" + | "PREC0_1MS" + | "PREC0_05MS" + | "PREC0_01MS"; diff --git a/webapp/src/models/imports/J2735/J2735AllowedManeuvers.d.ts b/webapp/src/models/imports/J2735/J2735AllowedManeuvers.d.ts new file mode 100644 index 000000000..78b650edb --- /dev/null +++ b/webapp/src/models/imports/J2735/J2735AllowedManeuvers.d.ts @@ -0,0 +1,14 @@ +type J2735AllowedManeuvers = { + maneuverStraightAllowed: boolean; + maneuverLeftAllowed: boolean; + maneuverRightAllowed: boolean; + maneuverUTurnAllowed: boolean; + maneuverLeftTurnOnRedAllowed: boolean; + maneuverRightTurnOnRedAllowed: boolean; + maneuverLaneChangeAllowed: boolean; + maneuverNoStoppingAllowed: boolean; + yieldAllwaysRequired: boolean; + goWithHalt: boolean; + caution: boolean; + reserved1: boolean; +}; diff --git a/webapp/src/models/imports/J2735/J2735BsmCoreData.d.ts b/webapp/src/models/imports/J2735/J2735BsmCoreData.d.ts new file mode 100644 index 000000000..fec7864b2 --- /dev/null +++ b/webapp/src/models/imports/J2735/J2735BsmCoreData.d.ts @@ -0,0 +1,61 @@ +type J2735BsmCoreData = { + msgCnt: number; + id: string; + secMark: number; + + position: OdePosition3D; + accelSet: J2735AccelerationSet4Way; + accuracy: J2735PositionalAccuracy; + + transmission: J2735TransmissionState; + speed: number; + heading: number; + angle: number; + brakes: J2735BrakeSystemStatus; + size: J2735VehicleSize; +}; + +type J2735AccelerationSet4Way = { + accelLat: number; + accelLong: number; + accelVert: number; + accelYaw: number; +}; + +type J2735PositionalAccuracy = { + semiMajor: number; + semiMinor: number; + orientation: number; +}; + +type J2735TransmissionState = + | "NEUTRAL" + | "PARK" + | "FORWARDGEARS" + | "REVERSEGEARS" + | "RESERVED1" + | "RESERVED2" + | "RESERVED3" + | "UNAVAILABLE"; + +type J2735BrakeSystemStatus = { + wheelBrakes: J2735BitString; + traction: string; + abs: string; + scs: string; + brakeBoost: string; + auxBrakes: string; +}; + +type J2735WheelBrakes = { + unavailable: Boolean; + leftFront: Boolean; + leftRear: Boolean; + rightFront: Boolean; + rightRear: Boolean; +}; + +type J2735VehicleSize = { + width: number; + length: number; +}; diff --git a/webapp/src/models/imports/J2735/J2735GenericLane.d.ts b/webapp/src/models/imports/J2735/J2735GenericLane.d.ts new file mode 100644 index 000000000..3eefbeeb6 --- /dev/null +++ b/webapp/src/models/imports/J2735/J2735GenericLane.d.ts @@ -0,0 +1,186 @@ +type J2735GenericLane = { + laneID?: number; + name?: String; + ingressApproach?: number; + egressApproach?: number; + laneAttributes?: J2735LaneAttributes; + maneuvers?: J2735AllowedManeuvers; + nodeList?: J2735NodeListXY; + connectsTo?: J2735Connection[]; + overlays?: J2735OverlayLaneList; +}; + +type J2735OverlayLaneList = { + laneIds?: number[]; +}; + +type J2735ConnectsToList = { + connectsTo?: J2735Connection[]; +}; + +type J2735Connection = { + connectingLane?: J2735ConnectingLane; + remoteIntersection: J2735IntersectionReferenceID | null; + signalGroup: number | null; + userClass: number | null; + connectionID: number; +}; + +type J2735ConnectingLane = { + lane?: number; + maneuver: J2735AllowedManeuvers; +}; + +type J2735IntersectionReferenceID = { + region?: number; + id?: number; +}; + +type J2735LaneAttributes = { + directionalUse?: J2735BitString; + shareWith: J2735BitString; + laneType: J2735LaneTypeAttributes; +}; + +type J2735LaneTypeAttributes = { + vehicle?: J2735BitString; // motor vehicle lanes - J2735LaneAttributesVehicle + crosswalk: J2735BitString; // pedestrian crosswalks - J2735LaneAttributesCrosswalk + bikeLane: J2735BitString; // bike lanes - J2735LaneAttributesBike + sidewalk: J2735BitString; // pedestrian sidewalk paths - J2735LaneAttributesSidewalk + median: J2735BitString; // medians & channelization - J2735LaneAttributesBarrier + striping: J2735BitString; // roadway markings - J2735LaneAttributesStriping + trackedVehicle: J2735BitString; // trains and trolleys - J2735LaneAttributesTrackedVehicle + parking: J2735BitString; // parking and stopping lanes - J2735LaneAttributesParking +}; + +type J2735BitString = string; + +type J2735NodeListXY = { + nodes?: J2735NodeSetXY; + computed?: J2735ComputedLane; +}; + +type J2735ComputedLane = { + referenceLaneId?: number; + offsetXaxis?: number; // could have an object with min and max inside of it + offsetYaxis?: number; // could have an object with min and max inside of it + rotateXY?: number; + scaleXaxis?: number; + scaleYaxis?: number; +}; + +type J2735NodeSetXY = { + NodeXY?: J2735NodeXY[]; +}; + +type J2735NodeXY = { + delta?: J2735NodeOffsetPointXY; + attributes?: J2735NodeAttributeSetXY; +}; + +type J2735NodeOffsetPointXY = { + nodeXY1?: J2735Node_XY; + nodeXY2: J2735Node_XY; + nodeXY3: J2735Node_XY; + nodeXY4: J2735Node_XY; + nodeXY5: J2735Node_XY; + nodeXY6: J2735Node_XY; +}; + +type J2735Node_XY = { + x?: number; + y?: number; +}; + +type J2735NodeAttributeSetXY = { + localNode?: J2735NodeAttributeXYList; + disabled: J2735SegmentAttributeXYList; + enabled: J2735SegmentAttributeXYList; + data: J2735LaneDataAttributeList; + dWidth: number; + dElevation: number; +}; + +type J2735NodeAttributeXYList = { + localNode?: J2735NodeAttributeXYList; +}; + +type J2735SegmentAttributeXYList = { + segAttrList?: J2735SegmentAttributeXY[]; +}; + +type J2735LaneDataAttributeList = { + localNode?: J2735LaneDataAttribute; +}; + +type J2735LaneDataAttribute = { + pathEndPointAngle?: number; + laneCrownPointCenter: number; + laneCrownPointLeft: number; + laneCrownPointRight: number; + laneAngle: number; + speedLimits: J2735SpeedLimitList; +}; + +type J2735SpeedLimitList = { + speedLimits?: J2735RegulatorySpeedLimit[]; +}; + +type J2735RegulatorySpeedLimit = { + type?: J2735SpeedLimitType; + speed?: number; +}; + +type J2735SpeedLimitType = + | "unknown" + | "maxSpeedInSchoolZone" + | "maxSpeedInSchoolZoneWhenChildrenArePresent" + | "maxSpeedInConstructionZone" + | "vehicleMinSpeed" + | "vehicleMaxSpeed" + | "vehicleNightMaxSpeed" + | "truckMinSpeed" + | "truckMaxSpeed" + | "truckNightMaxSpeed" + | "vehiclesWithTrailersMinSpeed" + | "vehiclesWithTrailersMaxSpeed" + | "vehiclesWithTrailersNightMaxSpeed"; + +type J2735SegmentAttributeXY = + | "reserved" + | "doNotBlock" + | "whiteLine" + | "mergingLaneLeft" + | "mergingLaneRight" + | "curbOnLeft" + | "curbOnRight" + | "loadingzoneOnLeft" + | "loadingzoneOnRight" + | "turnOutPointOnLeft" + | "turnOutPointOnRight" + | "adjacentParkingOnLeft" + | "adjacentParkingOnRight" + | "adjacentBikeLaneOnLeft" + | "adjacentBikeLaneOnRight" + | "sharedBikeLane" + | "bikeBoxInFront" + | "transitStopOnLeft" + | "transitStopOnRight" + | "transitStopInLane" + | "sharedWithTrackedVehicle" + | "safeIsland" + | "lowCurbsPresent" + | "rumbleStripPresent" + | "audibleSignalingPresent" + | "adaptiveTimingPresent" + | "rfSignalRequestPresent" + | "partialCurbIntrusion" + | "taperToLeft" + | "taperToRight" + | "taperToCenterLine" + | "headInParking" + | "freeParking" + | "timeRestrictionsOnParking" + | "costToPark" + | "midBlockCurbPresent" + | "unEvenPavementPresent"; diff --git a/webapp/src/models/imports/J2735/J2735IntersectionGeometry.d.ts b/webapp/src/models/imports/J2735/J2735IntersectionGeometry.d.ts new file mode 100644 index 000000000..949ededf5 --- /dev/null +++ b/webapp/src/models/imports/J2735/J2735IntersectionGeometry.d.ts @@ -0,0 +1,13 @@ +type J2735IntersectionGeometry = { + name?: String; + id?: J2735IntersectionReferenceID; + revision?: number; + refPoint?: OdePosition3D; + laneWidth?: number; + speedLimits?: J2735SpeedLimitList; + laneSet?: J2735LaneList; +}; + +type J2735LaneList = { + genericLane?: J2735GenericLane[]; +}; diff --git a/webapp/src/models/imports/J2735/J2735LaneSharing.d.ts b/webapp/src/models/imports/J2735/J2735LaneSharing.d.ts new file mode 100644 index 000000000..b66fb3338 --- /dev/null +++ b/webapp/src/models/imports/J2735/J2735LaneSharing.d.ts @@ -0,0 +1,12 @@ +type J2735LaneSharing = { + overlappingLaneDescriptionProvided: boolean; + multipleLanesTreatedAsOneLane: boolean; + otherNonMotorizedTrafficTypes: boolean; + individualMotorizedVehicleTraffic: boolean; + busVehicleTraffic: boolean; + taxiVehicleTraffic: boolean; + pedestriansTraffic: boolean; + cyclistVehicleTraffic: boolean; + trackedVehicleTraffic: boolean; + pedestrianTraffic: boolean; +}; diff --git a/webapp/src/models/imports/J2735/J2735MovementPhaseState.d.ts b/webapp/src/models/imports/J2735/J2735MovementPhaseState.d.ts new file mode 100644 index 000000000..3a62865d5 --- /dev/null +++ b/webapp/src/models/imports/J2735/J2735MovementPhaseState.d.ts @@ -0,0 +1,11 @@ +type J2735MovementPhaseState = + | "UNAVAILABLE" + | "DARK" + | "STOP_THEN_PROCEED" + | "STOP_AND_REMAIN" + | "PRE_MOVEMENT" + | "PERMISSIVE_MOVEMENT_ALLOWED" + | "PROTECTED_MOVEMENT_ALLOWED" + | "PERMISSIVE_CLEARANCE" + | "PROTECTED_CLEARANCE" + | "CAUTION_CONFLICTING_TRAFFIC"; diff --git a/webapp/src/models/imports/OdeBsmData.d.ts b/webapp/src/models/imports/OdeBsmData.d.ts new file mode 100644 index 000000000..8c5591abb --- /dev/null +++ b/webapp/src/models/imports/OdeBsmData.d.ts @@ -0,0 +1,36 @@ +type OdeBsmData = { + metadata: OdeMsgMetadata; + payload: OdeMsgPayload; +}; + +type OdeMsgMetadata = { + payloadType: String; + serialId: SerialId; + odeReceivedAt: String; + schemaVersion: number; + maxDurationTime: number; + odePacketID: String; + odeTimStartDateTime: String; + recordGeneratedAt: String; + recordGeneratedBy: GeneratedBy; + sanitized: boolean; +}; + +type GeneratedBy = "TMC" | "OBU" | "RSU" | "TMC_VIA_SAT" | "TMC_VIA_SNMP" | "UNKNOWN"; + +type SerialId = { + streamId: string; + bundleSize: number; + bundleId: number; + recordId: number; + serialNumber: number; +}; + +type OdeMsgPayload = { + dataType: string; + data: OdeObject; +}; + +type OdeObject = { + coreData: J2735BsmCoreData; +}; // TODO: check diff --git a/webapp/src/models/imports/OdePosition3D.d.ts b/webapp/src/models/imports/OdePosition3D.d.ts new file mode 100644 index 000000000..32910c458 --- /dev/null +++ b/webapp/src/models/imports/OdePosition3D.d.ts @@ -0,0 +1,5 @@ +type OdePosition3D = { + latitude: number; + longitude: number; + elevation?: number; +}; diff --git a/webapp/src/models/imports/Point.d.ts b/webapp/src/models/imports/Point.d.ts new file mode 100644 index 000000000..903527d5b --- /dev/null +++ b/webapp/src/models/imports/Point.d.ts @@ -0,0 +1 @@ +type Point = number[]; diff --git a/webapp/src/models/imports/Polygon.d.ts b/webapp/src/models/imports/Polygon.d.ts new file mode 100644 index 000000000..48e96f616 --- /dev/null +++ b/webapp/src/models/imports/Polygon.d.ts @@ -0,0 +1 @@ +type Polygon = number[][]; // TODO: check diff --git a/webapp/src/models/imports/ProcessedMap.d.ts b/webapp/src/models/imports/ProcessedMap.d.ts new file mode 100644 index 000000000..f9e6088a7 --- /dev/null +++ b/webapp/src/models/imports/ProcessedMap.d.ts @@ -0,0 +1,101 @@ +type ProcessedMap = { + properties: MapSharedProperties; + mapFeatureCollection: MapFeatureCollection; + connectingLanesFeatureCollection: ConnectingLanesFeatureCollection; +}; + +type MapSharedProperties = { + messageType: string; + odeReceivedAt: string; + originIp: string; + intersectionName?: string; + region?: number; + intersectionId: number; + msgIssueRevision: number; + revision: number; + refPoint: OdePosition3D; + cti4501Conformant: Boolean; + validationMessages: ProcessedValidationMessage[]; + laneWidth: number; + speedLimits?: J2735RegulatorySpeedLimit[]; + mapSource: MapSource | string; //import us.dot.its.jpo.ode.model.OdeMapMetadata.MapSource; + timeStamp: string; +}; + +type MapSource = "RSU" | "V2X" | "MMITSS" | "unknown"; + +// enum MapSource { +// RSU, +// V2X, +// MMITSS, +// unknown, +// }; + +type MapFeatureCollection = { + type: "FeatureCollection"; + features: MapFeature[]; +}; + +type MapFeature = { + type: "Feature"; + id: number; + geometry: GeoJSON.LineString; + properties: MapProperties; +}; + +type Geometry = { + type: string; + coordinates: number[][]; +}; + +type MapProperties = { + nodes: MapNode[]; + laneId: number; + laneName?: string; + sharedWith: J2735LaneSharing; + egressApproach: number; + ingressApproach: number; + ingressPath: boolean; + egressPath: boolean; + maneuvers?: J2735AllowedManeuvers; + connectsTo?: J2735Connection[]; +}; + +type MapNode = { + delta: number[]; + dWidth?: number; + dElevation?: number; + stopLine: boolean | null; +}; + +type ConnectingLanesFeatureCollection = { + type: "FeatureCollection"; + features: ConnectingLanesFeature[]; +}; + +type ConnectingLanesUiFeatureCollection = { + type: "FeatureCollection"; + features: ConnectingLanesUiFeature[]; +}; + +type ConnectingLanesFeature = { + type: "Feature"; + id: number | string; + geometry: GeoJSON.LineString; + properties: ConnectingLanesProperties; +}; + +type ConnectingLanesUiFeature = { + type: "Feature"; + id: number | string; + geometry: GeoJSON.LineString; + properties: { + signalState?: SignalState; + }; +}; + +type ConnectingLanesProperties = { + signalGroupId: number | null; + ingressLaneId: number; + egressLaneId: number; +}; diff --git a/webapp/src/models/imports/ProcessedSpat.d.ts b/webapp/src/models/imports/ProcessedSpat.d.ts new file mode 100644 index 000000000..96b82fb17 --- /dev/null +++ b/webapp/src/models/imports/ProcessedSpat.d.ts @@ -0,0 +1,60 @@ +type ProcessedSpat = { + messageType: string; + odeReceivedAt: string; + originIp: string; + name?: string; + region?: number; + intersectionId: number; + cti4501Conformant: boolean; + validationMessages: ProcessedValidationMessage[]; + revision: number; + status: IntersectionStatusObject; + utcTimeStamp: string; + enabledLanes: number[]; + states: MovementState[]; +}; + +type ProcessedValidationMessage = { + message: string; + jsonPath: string; + schemaPath: string; + exception?: string; +}; + +type IntersectionStatusObject = { + manualControlIsEnabled: boolean; + stopTimeIsActivated: boolean; + failureFlash: boolean; + preemptIsActive: boolean; + signalPriorityIsActive: boolean; + fixedTimeOperation: boolean; + trafficDependentOperation: boolean; + standbyOperation: boolean; + failureMode: boolean; + off: boolean; + recentMAPmessageUpdate: boolean; + recentChangeInMAPassignedLanesIDsUsed: boolean; + noValidMAPisAvailableAtThisTime: boolean; + noValidSPATisAvailableAtThisTime: boolean; +}; + +type MovementState = { + movementName?: string; + signalGroup: number; + stateTimeSpeed: MovementEvent[]; +}; + +type MovementEvent = { + eventState: J2735MovementPhaseState | string; + timing: TimingChangeDetails; + speeds?: J2735AdvisorySpeedList; +}; + +type TimingChangeDetails = { + startTime: string | null; + minEndTime: string; + maxEndTime: string; + likelyTime: string | null; + confidence: number | null; + nextTime: string | null; +}; diff --git a/webapp/src/models/imports/README.md b/webapp/src/models/imports/README.md new file mode 100644 index 000000000..17d5c0d55 --- /dev/null +++ b/webapp/src/models/imports/README.md @@ -0,0 +1 @@ +# Manually Generated External Imports diff --git a/webapp/src/models/jpo-conflictmonitor/AllowConcurrentPermissiveList.d.ts b/webapp/src/models/jpo-conflictmonitor/AllowConcurrentPermissiveList.d.ts new file mode 100644 index 000000000..857e208e2 --- /dev/null +++ b/webapp/src/models/jpo-conflictmonitor/AllowConcurrentPermissiveList.d.ts @@ -0,0 +1,4 @@ + +type AllowConcurrentPermissiveList = { + allowedConcurrentList: AllowedConcurrentPermissive[] +} \ No newline at end of file diff --git a/webapp/src/models/jpo-conflictmonitor/AllowedConcurrentPermissive.d.ts b/webapp/src/models/jpo-conflictmonitor/AllowedConcurrentPermissive.d.ts new file mode 100644 index 000000000..0153e5106 --- /dev/null +++ b/webapp/src/models/jpo-conflictmonitor/AllowedConcurrentPermissive.d.ts @@ -0,0 +1,10 @@ + +type AllowedConcurrentPermissive = { + intersectionID: number + roadRegulatorID: number + firstIngressLane: number + secondIngressLane: number + firstEgressLane: number + secondEgressLane: number + allowConcurrent: booleanean +} \ No newline at end of file diff --git a/webapp/src/models/jpo-conflictmonitor/Intersection/Intersection.d.ts b/webapp/src/models/jpo-conflictmonitor/Intersection/Intersection.d.ts new file mode 100644 index 000000000..7e23763f2 --- /dev/null +++ b/webapp/src/models/jpo-conflictmonitor/Intersection/Intersection.d.ts @@ -0,0 +1,13 @@ + +type Intersection = { + ingressLanes: Lane[] + egressLanes: Lane[] + stopLines: IntersectionLine[] + startLines: IntersectionLine[] + referencePoint: Coordinate + intersectionId: number + roadRegulatorId: number + laneConnections: LaneConnection[] + ingressLaneId: number + egressLaneId: number +} \ No newline at end of file diff --git a/webapp/src/models/jpo-conflictmonitor/Intersection/IntersectionLine.d.ts b/webapp/src/models/jpo-conflictmonitor/Intersection/IntersectionLine.d.ts new file mode 100644 index 000000000..aaa299378 --- /dev/null +++ b/webapp/src/models/jpo-conflictmonitor/Intersection/IntersectionLine.d.ts @@ -0,0 +1,6 @@ + +type IntersectionLine = { + heading: number + stopLinePoint: Point + lane: Lane +} \ No newline at end of file diff --git a/webapp/src/models/jpo-conflictmonitor/Intersection/Lane.d.ts b/webapp/src/models/jpo-conflictmonitor/Intersection/Lane.d.ts new file mode 100644 index 000000000..bd6876014 --- /dev/null +++ b/webapp/src/models/jpo-conflictmonitor/Intersection/Lane.d.ts @@ -0,0 +1,9 @@ + +type Lane = { + id: number + points: number[][] + ingress: booleanean + geometryFactory: GeometryFactory + laneWidthCm: number + region: number +} \ No newline at end of file diff --git a/webapp/src/models/jpo-conflictmonitor/Intersection/LaneConnection.d.ts b/webapp/src/models/jpo-conflictmonitor/Intersection/LaneConnection.d.ts new file mode 100644 index 000000000..3750d78d5 --- /dev/null +++ b/webapp/src/models/jpo-conflictmonitor/Intersection/LaneConnection.d.ts @@ -0,0 +1,8 @@ + +type LaneConnection = { + ingressLane: Lane + egressLane: Lane + connectionId: number + signalGroup: number + connectingLineString: number[][] +} \ No newline at end of file diff --git a/webapp/src/models/jpo-conflictmonitor/Intersection/LaneSegment.d.ts b/webapp/src/models/jpo-conflictmonitor/Intersection/LaneSegment.d.ts new file mode 100644 index 000000000..a01cd6909 --- /dev/null +++ b/webapp/src/models/jpo-conflictmonitor/Intersection/LaneSegment.d.ts @@ -0,0 +1,9 @@ + +type LaneSegment = { + polygon: Polygon + startPoint: Point + endPoint: Point + centerLine: number[][] + heading: number + laneWidth: number +} \ No newline at end of file diff --git a/webapp/src/models/jpo-conflictmonitor/Intersection/LineVehicleIntersection.d.ts b/webapp/src/models/jpo-conflictmonitor/Intersection/LineVehicleIntersection.d.ts new file mode 100644 index 000000000..e196fc704 --- /dev/null +++ b/webapp/src/models/jpo-conflictmonitor/Intersection/LineVehicleIntersection.d.ts @@ -0,0 +1,5 @@ + +type LineVehicleIntersection = { + lane: Lane + bsm: OdeBsmData +} \ No newline at end of file diff --git a/webapp/src/models/jpo-conflictmonitor/Intersection/VehiclePath.d.ts b/webapp/src/models/jpo-conflictmonitor/Intersection/VehiclePath.d.ts new file mode 100644 index 000000000..52ffa5b64 --- /dev/null +++ b/webapp/src/models/jpo-conflictmonitor/Intersection/VehiclePath.d.ts @@ -0,0 +1,14 @@ + +type VehiclePath = { + logger: Logger + pathPoints: number[][] + bsms: BsmAggregator + intersection: Intersection + geometryFactory: GeometryFactory + ingressLane: Lane + egressLane: Lane + ingressBsm: OdeBsmData + egressBsm: OdeBsmData + minDistanceFeet: number + headingToleranceDegrees: number +} \ No newline at end of file diff --git a/webapp/src/models/jpo-conflictmonitor/LaneConnection.d.ts b/webapp/src/models/jpo-conflictmonitor/LaneConnection.d.ts new file mode 100644 index 000000000..3d3d67864 --- /dev/null +++ b/webapp/src/models/jpo-conflictmonitor/LaneConnection.d.ts @@ -0,0 +1,13 @@ + +type LaneConnection = { + referencePoint: OdePosition3D + ingress: J2735GenericLane + egress: J2735GenericLane + ingressPath: number[][] + connectingPath: number[][] + egressPath: number[][] + geometryFactory: GeometryFactory + signalGroup: number + interpolationPoints: number + DEFAULT_INTERPOLATION_POINTS: number +} \ No newline at end of file diff --git a/webapp/src/models/jpo-conflictmonitor/MapIntersection.d.ts b/webapp/src/models/jpo-conflictmonitor/MapIntersection.d.ts new file mode 100644 index 000000000..5f8fcdd0f --- /dev/null +++ b/webapp/src/models/jpo-conflictmonitor/MapIntersection.d.ts @@ -0,0 +1,6 @@ + +type MapIntersection = { + logger: Logger + intersectionGeometry: J2735IntersectionGeometry + laneConnections: LaneConnection[] +} \ No newline at end of file diff --git a/webapp/src/models/jpo-conflictmonitor/NotificationSource.d.ts b/webapp/src/models/jpo-conflictmonitor/NotificationSource.d.ts new file mode 100644 index 000000000..9ae058873 --- /dev/null +++ b/webapp/src/models/jpo-conflictmonitor/NotificationSource.d.ts @@ -0,0 +1,4 @@ + +type NotificationSource = { + getNotificationSourceString: str +} \ No newline at end of file diff --git a/webapp/src/models/jpo-conflictmonitor/RegulatorIntersectionId.d.ts b/webapp/src/models/jpo-conflictmonitor/RegulatorIntersectionId.d.ts new file mode 100644 index 000000000..0f1380fe9 --- /dev/null +++ b/webapp/src/models/jpo-conflictmonitor/RegulatorIntersectionId.d.ts @@ -0,0 +1,5 @@ + +type RegulatorIntersectionId = { + roadRegulatorId: number + intersectionId: number +} \ No newline at end of file diff --git a/webapp/src/models/jpo-conflictmonitor/SpatMap.d.ts b/webapp/src/models/jpo-conflictmonitor/SpatMap.d.ts new file mode 100644 index 000000000..8cac3f1d7 --- /dev/null +++ b/webapp/src/models/jpo-conflictmonitor/SpatMap.d.ts @@ -0,0 +1,5 @@ + +type SpatMap = { + spat: ProcessedSpat + map: ProcessedMap +} \ No newline at end of file diff --git a/webapp/src/models/jpo-conflictmonitor/VehicleEvent.d.ts b/webapp/src/models/jpo-conflictmonitor/VehicleEvent.d.ts new file mode 100644 index 000000000..5d78568b4 --- /dev/null +++ b/webapp/src/models/jpo-conflictmonitor/VehicleEvent.d.ts @@ -0,0 +1,7 @@ + +type VehicleEvent = { + bsms: BsmAggregator + spats: SpatAggregator + intersection: Intersection + source: str +} \ No newline at end of file diff --git a/webapp/src/models/jpo-conflictmonitor/assessments/Assessment.d.ts b/webapp/src/models/jpo-conflictmonitor/assessments/Assessment.d.ts new file mode 100644 index 000000000..ba4f189b0 --- /dev/null +++ b/webapp/src/models/jpo-conflictmonitor/assessments/Assessment.d.ts @@ -0,0 +1,8 @@ + +type Assessment = { + assessmentGeneratedAt: number + assessmentType: str + intersectionID: number + roadRegulatorID: number + source: str +} \ No newline at end of file diff --git a/webapp/src/models/jpo-conflictmonitor/assessments/ConnectionOfTravelAggregator.d.ts b/webapp/src/models/jpo-conflictmonitor/assessments/ConnectionOfTravelAggregator.d.ts new file mode 100644 index 000000000..92ea42077 --- /dev/null +++ b/webapp/src/models/jpo-conflictmonitor/assessments/ConnectionOfTravelAggregator.d.ts @@ -0,0 +1,6 @@ + +type ConnectionOfTravelAggregator = { + events: ConnectionOfTravelEvent[] + aggregatorCreationTime: number + messageDurationDays: number +} \ No newline at end of file diff --git a/webapp/src/models/jpo-conflictmonitor/assessments/ConnectionOfTravelAssessment.d.ts b/webapp/src/models/jpo-conflictmonitor/assessments/ConnectionOfTravelAssessment.d.ts new file mode 100644 index 000000000..ba34539d9 --- /dev/null +++ b/webapp/src/models/jpo-conflictmonitor/assessments/ConnectionOfTravelAssessment.d.ts @@ -0,0 +1,5 @@ + +type ConnectionOfTravelAssessment = Assessment & { + timestamp: number + connectionOfTravelAssessmentGroups: ConnectionOfTravelAssessmentGroup[] +} \ No newline at end of file diff --git a/webapp/src/models/jpo-conflictmonitor/assessments/ConnectionOfTravelAssessmentGroup.d.ts b/webapp/src/models/jpo-conflictmonitor/assessments/ConnectionOfTravelAssessmentGroup.d.ts new file mode 100644 index 000000000..d9bb13d47 --- /dev/null +++ b/webapp/src/models/jpo-conflictmonitor/assessments/ConnectionOfTravelAssessmentGroup.d.ts @@ -0,0 +1,7 @@ + +type ConnectionOfTravelAssessmentGroup = { + ingressLaneID: number + egressLaneID: number + connectionID: number + eventCount: number +} \ No newline at end of file diff --git a/webapp/src/models/jpo-conflictmonitor/assessments/LaneDirectionOfTravelAggregator.d.ts b/webapp/src/models/jpo-conflictmonitor/assessments/LaneDirectionOfTravelAggregator.d.ts new file mode 100644 index 000000000..8c010f880 --- /dev/null +++ b/webapp/src/models/jpo-conflictmonitor/assessments/LaneDirectionOfTravelAggregator.d.ts @@ -0,0 +1,8 @@ + +type LaneDirectionOfTravelAggregator = { + events: LaneDirectionOfTravelEvent[] + aggregatorCreationTime: number + tolerance: number + distanceFromCenterlineTolerance: number + messageDurationDays: number +} \ No newline at end of file diff --git a/webapp/src/models/jpo-conflictmonitor/assessments/LaneDirectionOfTravelAssessment.d.ts b/webapp/src/models/jpo-conflictmonitor/assessments/LaneDirectionOfTravelAssessment.d.ts new file mode 100644 index 000000000..a4977c820 --- /dev/null +++ b/webapp/src/models/jpo-conflictmonitor/assessments/LaneDirectionOfTravelAssessment.d.ts @@ -0,0 +1,5 @@ + +type LaneDirectionOfTravelAssessment = Assessment & { + timestamp: number + laneDirectionOfTravelAssessmentGroup: LaneDirectionOfTravelAssessmentGroup[] +} \ No newline at end of file diff --git a/webapp/src/models/jpo-conflictmonitor/assessments/LaneDirectionOfTravelAssessmentGroup.d.ts b/webapp/src/models/jpo-conflictmonitor/assessments/LaneDirectionOfTravelAssessmentGroup.d.ts new file mode 100644 index 000000000..9804b1545 --- /dev/null +++ b/webapp/src/models/jpo-conflictmonitor/assessments/LaneDirectionOfTravelAssessmentGroup.d.ts @@ -0,0 +1,14 @@ + +type LaneDirectionOfTravelAssessmentGroup = { + laneID: number + segmentID: number + inToleranceEvents: number + outOfToleranceEvents: number + medianInToleranceHeading: number + medianInToleranceCenterlineDistance: number + medianHeading: number + medianCenterlineDistance: number + expectedHeading: number + tolerance: number + distanceFromCenterlineTolerance: number +} \ No newline at end of file diff --git a/webapp/src/models/jpo-conflictmonitor/assessments/SignalStateEventAggregator.d.ts b/webapp/src/models/jpo-conflictmonitor/assessments/SignalStateEventAggregator.d.ts new file mode 100644 index 000000000..ffa7cff55 --- /dev/null +++ b/webapp/src/models/jpo-conflictmonitor/assessments/SignalStateEventAggregator.d.ts @@ -0,0 +1,7 @@ + +type SignalStateEventAggregator = { + events: StopLinePassageEvent[] + aggregatorCreationTime: number + tolerance: number + messageDurationDays: number +} \ No newline at end of file diff --git a/webapp/src/models/jpo-conflictmonitor/assessments/SignalStateEventAssessment.d.ts b/webapp/src/models/jpo-conflictmonitor/assessments/SignalStateEventAssessment.d.ts new file mode 100644 index 000000000..7de6a7065 --- /dev/null +++ b/webapp/src/models/jpo-conflictmonitor/assessments/SignalStateEventAssessment.d.ts @@ -0,0 +1,5 @@ + +type SignalStateEventAssessment = Assessment & { + timestamp: number + signalStateEventAssessmentGroup: SignalStateEventAssessmentGroup[] +} \ No newline at end of file diff --git a/webapp/src/models/jpo-conflictmonitor/assessments/SignalStateEventAssessmentGroup.d.ts b/webapp/src/models/jpo-conflictmonitor/assessments/SignalStateEventAssessmentGroup.d.ts new file mode 100644 index 000000000..c297b6a27 --- /dev/null +++ b/webapp/src/models/jpo-conflictmonitor/assessments/SignalStateEventAssessmentGroup.d.ts @@ -0,0 +1,8 @@ + +type SignalStateEventAssessmentGroup = { + signalGroup: number + darkEvents: number + redEvents: number + yellowEvents: number + greenEvents: number +} \ No newline at end of file diff --git a/webapp/src/models/jpo-conflictmonitor/assessments/StopLineStopAssessment.d.ts b/webapp/src/models/jpo-conflictmonitor/assessments/StopLineStopAssessment.d.ts new file mode 100644 index 000000000..5c3694db1 --- /dev/null +++ b/webapp/src/models/jpo-conflictmonitor/assessments/StopLineStopAssessment.d.ts @@ -0,0 +1,5 @@ + +type StopLineStopAssessment = Assessment & { + timestamp: number + stopLineStopAssessmentGroup: StopLineStopAssessmentGroup[] +} \ No newline at end of file diff --git a/webapp/src/models/jpo-conflictmonitor/assessments/StopLineStopAssessmentGroup.d.ts b/webapp/src/models/jpo-conflictmonitor/assessments/StopLineStopAssessmentGroup.d.ts new file mode 100644 index 000000000..1d4b2071d --- /dev/null +++ b/webapp/src/models/jpo-conflictmonitor/assessments/StopLineStopAssessmentGroup.d.ts @@ -0,0 +1,9 @@ + +type StopLineStopAssessmentGroup = { + signalGroup: number + numberOfEvents: number + timeStoppedOnRed: number + timeStoppedOnYellow: number + timeStoppedOnGreen: number + timeStoppedOnDark: number +} \ No newline at end of file diff --git a/webapp/src/models/jpo-conflictmonitor/bsm/BsmAggregator.d.ts b/webapp/src/models/jpo-conflictmonitor/bsm/BsmAggregator.d.ts new file mode 100644 index 000000000..04327d106 --- /dev/null +++ b/webapp/src/models/jpo-conflictmonitor/bsm/BsmAggregator.d.ts @@ -0,0 +1,4 @@ + +type BsmAggregator = { + bsms: OdeBsmData[] +} \ No newline at end of file diff --git a/webapp/src/models/jpo-conflictmonitor/bsm/BsmEvent.d.ts b/webapp/src/models/jpo-conflictmonitor/bsm/BsmEvent.d.ts new file mode 100644 index 000000000..fb592ce9e --- /dev/null +++ b/webapp/src/models/jpo-conflictmonitor/bsm/BsmEvent.d.ts @@ -0,0 +1,11 @@ + +type BsmEvent = { + startingBsm: OdeBsmData + endingBsm: OdeBsmData + startingBsmTimestamp: number + endingBsmTimestamp: number + wktPath: str + wktMapBoundingBox: str + inMapBoundingBox: booleanean + wallClockTimestamp: number +} \ No newline at end of file diff --git a/webapp/src/models/jpo-conflictmonitor/bsm/BsmEventIntersectionKey.d.ts b/webapp/src/models/jpo-conflictmonitor/bsm/BsmEventIntersectionKey.d.ts new file mode 100644 index 000000000..9438fc2e2 --- /dev/null +++ b/webapp/src/models/jpo-conflictmonitor/bsm/BsmEventIntersectionKey.d.ts @@ -0,0 +1,5 @@ + +type BsmEventIntersectionKey = BsmIntersectionKey & { + intersectionId: number + region: number +} \ No newline at end of file diff --git a/webapp/src/models/jpo-conflictmonitor/bsm/BsmIntersectionKey.d.ts b/webapp/src/models/jpo-conflictmonitor/bsm/BsmIntersectionKey.d.ts new file mode 100644 index 000000000..986bc41a7 --- /dev/null +++ b/webapp/src/models/jpo-conflictmonitor/bsm/BsmIntersectionKey.d.ts @@ -0,0 +1,6 @@ + +type BsmIntersectionKey = { + logger: Logger + rsuId: str + bsmId: str +} \ No newline at end of file diff --git a/webapp/src/models/jpo-conflictmonitor/bsm/BsmTimestampExtractor.d.ts b/webapp/src/models/jpo-conflictmonitor/bsm/BsmTimestampExtractor.d.ts new file mode 100644 index 000000000..a9c01d884 --- /dev/null +++ b/webapp/src/models/jpo-conflictmonitor/bsm/BsmTimestampExtractor.d.ts @@ -0,0 +1,4 @@ + +type BsmTimestampExtractor = { + logger: Logger +} \ No newline at end of file diff --git a/webapp/src/models/jpo-conflictmonitor/config/Config.d.ts b/webapp/src/models/jpo-conflictmonitor/config/Config.d.ts new file mode 100644 index 000000000..fd6019630 --- /dev/null +++ b/webapp/src/models/jpo-conflictmonitor/config/Config.d.ts @@ -0,0 +1,10 @@ +type Config = { + Logger: any; + key: string; + category: string; + value: any; + type: string; + units: UnitsEnum; + description: string; + updateType: UpdateType; +}; diff --git a/webapp/src/models/jpo-conflictmonitor/config/ConfigCollection.d.ts b/webapp/src/models/jpo-conflictmonitor/config/ConfigCollection.d.ts new file mode 100644 index 000000000..a1f693964 --- /dev/null +++ b/webapp/src/models/jpo-conflictmonitor/config/ConfigCollection.d.ts @@ -0,0 +1 @@ +type ConfigCollection = Config & {}; diff --git a/webapp/src/models/jpo-conflictmonitor/config/ConfigData.d.ts b/webapp/src/models/jpo-conflictmonitor/config/ConfigData.d.ts new file mode 100644 index 000000000..6d256dcf5 --- /dev/null +++ b/webapp/src/models/jpo-conflictmonitor/config/ConfigData.d.ts @@ -0,0 +1,8 @@ + +type ConfigData = { + key: str + category: str + units: UnitsEnum + description: str + updateType: UpdateType +} \ No newline at end of file diff --git a/webapp/src/models/jpo-conflictmonitor/config/ConfigDataClass.d.ts b/webapp/src/models/jpo-conflictmonitor/config/ConfigDataClass.d.ts new file mode 100644 index 000000000..01857c35b --- /dev/null +++ b/webapp/src/models/jpo-conflictmonitor/config/ConfigDataClass.d.ts @@ -0,0 +1,4 @@ + +type ConfigDataClass = { + +} \ No newline at end of file diff --git a/webapp/src/models/jpo-conflictmonitor/config/ConfigMap.d.ts b/webapp/src/models/jpo-conflictmonitor/config/ConfigMap.d.ts new file mode 100644 index 000000000..d471a9c32 --- /dev/null +++ b/webapp/src/models/jpo-conflictmonitor/config/ConfigMap.d.ts @@ -0,0 +1 @@ +type ConfigMap = ConcurrentSkipListMap & {}; diff --git a/webapp/src/models/jpo-conflictmonitor/config/DefaultBooleanConfig.d.ts b/webapp/src/models/jpo-conflictmonitor/config/DefaultBooleanConfig.d.ts new file mode 100644 index 000000000..c3de7ea1c --- /dev/null +++ b/webapp/src/models/jpo-conflictmonitor/config/DefaultBooleanConfig.d.ts @@ -0,0 +1 @@ +type DefaultBooleanConfig = DefaultConfig & {}; diff --git a/webapp/src/models/jpo-conflictmonitor/config/DefaultConfig.d.ts b/webapp/src/models/jpo-conflictmonitor/config/DefaultConfig.d.ts new file mode 100644 index 000000000..45998fdb8 --- /dev/null +++ b/webapp/src/models/jpo-conflictmonitor/config/DefaultConfig.d.ts @@ -0,0 +1 @@ +type DefaultConfig = Config & {}; diff --git a/webapp/src/models/jpo-conflictmonitor/config/DefaultConfigCollection.d.ts b/webapp/src/models/jpo-conflictmonitor/config/DefaultConfigCollection.d.ts new file mode 100644 index 000000000..a160ff2b6 --- /dev/null +++ b/webapp/src/models/jpo-conflictmonitor/config/DefaultConfigCollection.d.ts @@ -0,0 +1 @@ +type DefaultConfigCollection = ConfigCollection & {}; diff --git a/webapp/src/models/jpo-conflictmonitor/config/DefaultDoubleConfig.d.ts b/webapp/src/models/jpo-conflictmonitor/config/DefaultDoubleConfig.d.ts new file mode 100644 index 000000000..27c1fcae6 --- /dev/null +++ b/webapp/src/models/jpo-conflictmonitor/config/DefaultDoubleConfig.d.ts @@ -0,0 +1 @@ +type DefaultDoubleConfig = DefaultConfig & {}; diff --git a/webapp/src/models/jpo-conflictmonitor/config/DefaultIntConfig.d.ts b/webapp/src/models/jpo-conflictmonitor/config/DefaultIntConfig.d.ts new file mode 100644 index 000000000..1a8ec386b --- /dev/null +++ b/webapp/src/models/jpo-conflictmonitor/config/DefaultIntConfig.d.ts @@ -0,0 +1 @@ +type DefaultIntConfig = DefaultConfig & {}; diff --git a/webapp/src/models/jpo-conflictmonitor/config/DefaultLongConfig.d.ts b/webapp/src/models/jpo-conflictmonitor/config/DefaultLongConfig.d.ts new file mode 100644 index 000000000..c4b20fd76 --- /dev/null +++ b/webapp/src/models/jpo-conflictmonitor/config/DefaultLongConfig.d.ts @@ -0,0 +1 @@ +type DefaultLongConfig = DefaultConfig & {}; diff --git a/webapp/src/models/jpo-conflictmonitor/config/DefaultStringConfig.d.ts b/webapp/src/models/jpo-conflictmonitor/config/DefaultStringConfig.d.ts new file mode 100644 index 000000000..5d0fd4e42 --- /dev/null +++ b/webapp/src/models/jpo-conflictmonitor/config/DefaultStringConfig.d.ts @@ -0,0 +1 @@ +type DefaultStringConfig = DefaultConfig & {}; diff --git a/webapp/src/models/jpo-conflictmonitor/config/IntersectionConfig.d.ts b/webapp/src/models/jpo-conflictmonitor/config/IntersectionConfig.d.ts new file mode 100644 index 000000000..a5ff9750a --- /dev/null +++ b/webapp/src/models/jpo-conflictmonitor/config/IntersectionConfig.d.ts @@ -0,0 +1,5 @@ +type IntersectionConfig = Config & { + intersectionID: number; + roadRegulatorID?: number; + rsuID: str; +}; diff --git a/webapp/src/models/jpo-conflictmonitor/config/IntersectionConfigCollection.d.ts b/webapp/src/models/jpo-conflictmonitor/config/IntersectionConfigCollection.d.ts new file mode 100644 index 000000000..efc192f14 --- /dev/null +++ b/webapp/src/models/jpo-conflictmonitor/config/IntersectionConfigCollection.d.ts @@ -0,0 +1 @@ +type IntersectionConfigCollection = ConfigCollection & {}; diff --git a/webapp/src/models/jpo-conflictmonitor/config/RsuConfigKey.d.ts b/webapp/src/models/jpo-conflictmonitor/config/RsuConfigKey.d.ts new file mode 100644 index 000000000..6e4c5e339 --- /dev/null +++ b/webapp/src/models/jpo-conflictmonitor/config/RsuConfigKey.d.ts @@ -0,0 +1,4 @@ + +type RsuConfigKey = { + Logger: any +} \ No newline at end of file diff --git a/webapp/src/models/jpo-conflictmonitor/config/UnitsEnum.d.ts b/webapp/src/models/jpo-conflictmonitor/config/UnitsEnum.d.ts new file mode 100644 index 000000000..be352928e --- /dev/null +++ b/webapp/src/models/jpo-conflictmonitor/config/UnitsEnum.d.ts @@ -0,0 +1,11 @@ +type UnitsEnum = + | "DAYS" + | "HOURS" + | "MINUTES" + | "SECONDS" + | "MILLISECONDS" + | "PER_PERIOD" + | "DEGREES" + | "CENTIMETERS" + | "MILES_PER_HOUR" + | "NONE"; diff --git a/webapp/src/models/jpo-conflictmonitor/config/UpdateType.d.ts b/webapp/src/models/jpo-conflictmonitor/config/UpdateType.d.ts new file mode 100644 index 000000000..39369a68c --- /dev/null +++ b/webapp/src/models/jpo-conflictmonitor/config/UpdateType.d.ts @@ -0,0 +1 @@ +type UpdateType = "READ_ONLY" | "DEFAULT" | "INTERSECTION"; diff --git a/webapp/src/models/jpo-conflictmonitor/events/ConnectionOfTravelEvent.d.ts b/webapp/src/models/jpo-conflictmonitor/events/ConnectionOfTravelEvent.d.ts new file mode 100644 index 000000000..9b8a8799d --- /dev/null +++ b/webapp/src/models/jpo-conflictmonitor/events/ConnectionOfTravelEvent.d.ts @@ -0,0 +1,8 @@ +/// +type ConnectionOfTravelEvent = MessageMonitor.Event & { + timestamp: number + ingressLaneID: number + egressLaneID: number + source: str + connectionID: number +} \ No newline at end of file diff --git a/webapp/src/models/jpo-conflictmonitor/events/Event.d.ts b/webapp/src/models/jpo-conflictmonitor/events/Event.d.ts new file mode 100644 index 000000000..d2f01089a --- /dev/null +++ b/webapp/src/models/jpo-conflictmonitor/events/Event.d.ts @@ -0,0 +1,10 @@ +declare namespace MessageMonitor { +type Event = { + logger: Logger + eventGeneratedAt: number + eventType: str + intersectionID: number + roadRegulatorID: number + source: str +} +} \ No newline at end of file diff --git a/webapp/src/models/jpo-conflictmonitor/events/IntersectionReferenceAlignmentEvent.d.ts b/webapp/src/models/jpo-conflictmonitor/events/IntersectionReferenceAlignmentEvent.d.ts new file mode 100644 index 000000000..43651756c --- /dev/null +++ b/webapp/src/models/jpo-conflictmonitor/events/IntersectionReferenceAlignmentEvent.d.ts @@ -0,0 +1,7 @@ +/// +type IntersectionReferenceAlignmentEvent = MessageMonitor.Event & { + source: str + timestamp: number + spatRegulatorIntersectionIds: Set + mapRegulatorIntersectionIds: Set +} \ No newline at end of file diff --git a/webapp/src/models/jpo-conflictmonitor/events/LaneDirectionOfTravelEvent.d.ts b/webapp/src/models/jpo-conflictmonitor/events/LaneDirectionOfTravelEvent.d.ts new file mode 100644 index 000000000..75ed40c32 --- /dev/null +++ b/webapp/src/models/jpo-conflictmonitor/events/LaneDirectionOfTravelEvent.d.ts @@ -0,0 +1,15 @@ +/// +type LaneDirectionOfTravelEvent = MessageMonitor.Event & { + timestamp: number + laneID: number + laneSegmentNumber: number + laneSegmentInitialLatitude: number + laneSegmentInitialLongitude: number + laneSegmentFinalLatitude: number + laneSegmentFinalLongitude: number + expectedHeading: number + medianVehicleHeading: number + medianDistanceFromCenterline: number + aggregateBSMCount: number + source: str +} \ No newline at end of file diff --git a/webapp/src/models/jpo-conflictmonitor/events/ProcessingTimePeriod.d.ts b/webapp/src/models/jpo-conflictmonitor/events/ProcessingTimePeriod.d.ts new file mode 100644 index 000000000..f1311b757 --- /dev/null +++ b/webapp/src/models/jpo-conflictmonitor/events/ProcessingTimePeriod.d.ts @@ -0,0 +1,5 @@ + +type ProcessingTimePeriod = { + beginTimestamp: number + endTimestamp: number +} \ No newline at end of file diff --git a/webapp/src/models/jpo-conflictmonitor/events/SignalGroupAlignmentEvent.d.ts b/webapp/src/models/jpo-conflictmonitor/events/SignalGroupAlignmentEvent.d.ts new file mode 100644 index 000000000..77b64c9bb --- /dev/null +++ b/webapp/src/models/jpo-conflictmonitor/events/SignalGroupAlignmentEvent.d.ts @@ -0,0 +1,7 @@ +/// +type SignalGroupAlignmentEvent = MessageMonitor.Event & { + source: str + timestamp: number + spatSignalGroupIds: Set + mapSignalGroupIds: Set +} \ No newline at end of file diff --git a/webapp/src/models/jpo-conflictmonitor/events/SignalStateConflictEvent.d.ts b/webapp/src/models/jpo-conflictmonitor/events/SignalStateConflictEvent.d.ts new file mode 100644 index 000000000..09b0427a2 --- /dev/null +++ b/webapp/src/models/jpo-conflictmonitor/events/SignalStateConflictEvent.d.ts @@ -0,0 +1,10 @@ +/// +type SignalStateConflictEvent = MessageMonitor.Event & { + timestamp: number + conflictType: J2735MovementPhaseState + firstConflictingSignalGroup: number + firstConflictingSignalState: J2735MovementPhaseState + secondConflictingSignalGroup: number + secondConflictingSignalState: J2735MovementPhaseState + source: str +} \ No newline at end of file diff --git a/webapp/src/models/jpo-conflictmonitor/events/SignalStateEvent.d.ts b/webapp/src/models/jpo-conflictmonitor/events/SignalStateEvent.d.ts new file mode 100644 index 000000000..3423bc413 --- /dev/null +++ b/webapp/src/models/jpo-conflictmonitor/events/SignalStateEvent.d.ts @@ -0,0 +1,14 @@ +/// +type SignalStateEvent = MessageMonitor.Event & { + timestamp: number + ingressLane: number + egressLane: number + connectionID: number + eventState: J2735MovementPhaseState + vehicleID: str + latitude: number + longitude: number + heading: number + speed: number + signalGroup: number +} \ No newline at end of file diff --git a/webapp/src/models/jpo-conflictmonitor/events/SignalStateStopEvent.d.ts b/webapp/src/models/jpo-conflictmonitor/events/SignalStateStopEvent.d.ts new file mode 100644 index 000000000..eb40cdd7e --- /dev/null +++ b/webapp/src/models/jpo-conflictmonitor/events/SignalStateStopEvent.d.ts @@ -0,0 +1,13 @@ +/// +type SignalStateStopEvent = MessageMonitor.Event & { + timestamp: number + ingressLane: number + egressLane: number + connectionID: number + eventState: J2735MovementPhaseState + vehicleID: str + latitude: number + longitude: number + heading: number + speed: number +} \ No newline at end of file diff --git a/webapp/src/models/jpo-conflictmonitor/events/StopLinePassageEvent.d.ts b/webapp/src/models/jpo-conflictmonitor/events/StopLinePassageEvent.d.ts new file mode 100644 index 000000000..6c72a49bc --- /dev/null +++ b/webapp/src/models/jpo-conflictmonitor/events/StopLinePassageEvent.d.ts @@ -0,0 +1,15 @@ +/// +type StopLinePassageEvent = MessageMonitor.Event & { + source: str + timestamp: number + ingressLane: number + egressLane: number + connectionID: number + eventState: J2735MovementPhaseState + vehicleID: str + latitude: number + longitude: number + heading: number + speed: number + signalGroup: number +} \ No newline at end of file diff --git a/webapp/src/models/jpo-conflictmonitor/events/StopLineStopEvent.d.ts b/webapp/src/models/jpo-conflictmonitor/events/StopLineStopEvent.d.ts new file mode 100644 index 000000000..9b55254a6 --- /dev/null +++ b/webapp/src/models/jpo-conflictmonitor/events/StopLineStopEvent.d.ts @@ -0,0 +1,19 @@ +/// +type StopLineStopEvent = MessageMonitor.Event & { + source: str + ingressLane: number + egressLane: number + connectionID: number + initialEventState: J2735MovementPhaseState + initialTimestamp: number + finalEventState: J2735MovementPhaseState + finalTimestamp: number + vehicleID: str + latitude: number + longitude: number + heading: number + signalGroup: number + timeStoppedDuringRed: number + timeStoppedDuringYellow: number + timeStoppedDuringGreen: number +} \ No newline at end of file diff --git a/webapp/src/models/jpo-conflictmonitor/events/TimeChangeDetailsEvent.d.ts b/webapp/src/models/jpo-conflictmonitor/events/TimeChangeDetailsEvent.d.ts new file mode 100644 index 000000000..9b63af0f6 --- /dev/null +++ b/webapp/src/models/jpo-conflictmonitor/events/TimeChangeDetailsEvent.d.ts @@ -0,0 +1,15 @@ +/// +type TimeChangeDetailsEvent = MessageMonitor.Event & { + signalGroup: number + firstSpatTimestamp: number + secondSpatTimestamp: number + firstTimeMarkType: str + secondTimeMarkType: str + firstConflictingTimemark: number + secondConflictingTimemark: number + firstState: J2735MovementPhaseState + secondState: J2735MovementPhaseState + firstConflictingUtcTimestamp: number + secondConflictingUtcTimestamp: number + source: str +} \ No newline at end of file diff --git a/webapp/src/models/jpo-conflictmonitor/events/TimestampExtractors/ConnectionOfTravelTimestampExtractor.d.ts b/webapp/src/models/jpo-conflictmonitor/events/TimestampExtractors/ConnectionOfTravelTimestampExtractor.d.ts new file mode 100644 index 000000000..e67985928 --- /dev/null +++ b/webapp/src/models/jpo-conflictmonitor/events/TimestampExtractors/ConnectionOfTravelTimestampExtractor.d.ts @@ -0,0 +1,4 @@ + +type ConnectionOfTravelTimestampExtractor = { + +} \ No newline at end of file diff --git a/webapp/src/models/jpo-conflictmonitor/events/TimestampExtractors/LaneDirectionOfTravelTimestampExtractor.d.ts b/webapp/src/models/jpo-conflictmonitor/events/TimestampExtractors/LaneDirectionOfTravelTimestampExtractor.d.ts new file mode 100644 index 000000000..5b9f31f5b --- /dev/null +++ b/webapp/src/models/jpo-conflictmonitor/events/TimestampExtractors/LaneDirectionOfTravelTimestampExtractor.d.ts @@ -0,0 +1,4 @@ + +type LaneDirectionOfTravelTimestampExtractor = { + +} \ No newline at end of file diff --git a/webapp/src/models/jpo-conflictmonitor/events/TimestampExtractors/SignalStateTimestampExtractor.d.ts b/webapp/src/models/jpo-conflictmonitor/events/TimestampExtractors/SignalStateTimestampExtractor.d.ts new file mode 100644 index 000000000..a73ce9313 --- /dev/null +++ b/webapp/src/models/jpo-conflictmonitor/events/TimestampExtractors/SignalStateTimestampExtractor.d.ts @@ -0,0 +1,4 @@ + +type SignalStateTimestampExtractor = { + +} \ No newline at end of file diff --git a/webapp/src/models/jpo-conflictmonitor/events/app_health/KafkaStreamsEvent.d.ts b/webapp/src/models/jpo-conflictmonitor/events/app_health/KafkaStreamsEvent.d.ts new file mode 100644 index 000000000..1612a0543 --- /dev/null +++ b/webapp/src/models/jpo-conflictmonitor/events/app_health/KafkaStreamsEvent.d.ts @@ -0,0 +1,5 @@ + +type KafkaStreamsEvent = { + appId: str + topology: str +} \ No newline at end of file diff --git a/webapp/src/models/jpo-conflictmonitor/events/app_health/KafkaStreamsStateChangeEvent.d.ts b/webapp/src/models/jpo-conflictmonitor/events/app_health/KafkaStreamsStateChangeEvent.d.ts new file mode 100644 index 000000000..c4d5e3e1f --- /dev/null +++ b/webapp/src/models/jpo-conflictmonitor/events/app_health/KafkaStreamsStateChangeEvent.d.ts @@ -0,0 +1,5 @@ + +type KafkaStreamsStateChangeEvent = KafkaStreamsEvent & { + newState: str + oldState: str +} \ No newline at end of file diff --git a/webapp/src/models/jpo-conflictmonitor/events/app_health/KafkaStreamsUnhandledExceptionEvent.d.ts b/webapp/src/models/jpo-conflictmonitor/events/app_health/KafkaStreamsUnhandledExceptionEvent.d.ts new file mode 100644 index 000000000..e8a549140 --- /dev/null +++ b/webapp/src/models/jpo-conflictmonitor/events/app_health/KafkaStreamsUnhandledExceptionEvent.d.ts @@ -0,0 +1,4 @@ + +type KafkaStreamsUnhandledExceptionEvent = KafkaStreamsEvent & { + exception: Throwable +} \ No newline at end of file diff --git a/webapp/src/models/jpo-conflictmonitor/events/broadcast_rate/BroadcastRateEvent.d.ts b/webapp/src/models/jpo-conflictmonitor/events/broadcast_rate/BroadcastRateEvent.d.ts new file mode 100644 index 000000000..62d88e371 --- /dev/null +++ b/webapp/src/models/jpo-conflictmonitor/events/broadcast_rate/BroadcastRateEvent.d.ts @@ -0,0 +1,7 @@ + +type BroadcastRateEvent = { + topicName: str + source: str + timePeriod: ProcessingTimePeriod + numberOfMessages: number +} \ No newline at end of file diff --git a/webapp/src/models/jpo-conflictmonitor/events/broadcast_rate/MapBroadcastRateEvent.d.ts b/webapp/src/models/jpo-conflictmonitor/events/broadcast_rate/MapBroadcastRateEvent.d.ts new file mode 100644 index 000000000..1c8993a58 --- /dev/null +++ b/webapp/src/models/jpo-conflictmonitor/events/broadcast_rate/MapBroadcastRateEvent.d.ts @@ -0,0 +1,4 @@ + +type MapBroadcastRateEvent = { + +} \ No newline at end of file diff --git a/webapp/src/models/jpo-conflictmonitor/events/broadcast_rate/SpatBroadcastRateEvent.d.ts b/webapp/src/models/jpo-conflictmonitor/events/broadcast_rate/SpatBroadcastRateEvent.d.ts new file mode 100644 index 000000000..f843baa06 --- /dev/null +++ b/webapp/src/models/jpo-conflictmonitor/events/broadcast_rate/SpatBroadcastRateEvent.d.ts @@ -0,0 +1,4 @@ + +type SpatBroadcastRateEvent = { + +} \ No newline at end of file diff --git a/webapp/src/models/jpo-conflictmonitor/events/minimum_data/MapMinimumDataEvent.d.ts b/webapp/src/models/jpo-conflictmonitor/events/minimum_data/MapMinimumDataEvent.d.ts new file mode 100644 index 000000000..7a99f1768 --- /dev/null +++ b/webapp/src/models/jpo-conflictmonitor/events/minimum_data/MapMinimumDataEvent.d.ts @@ -0,0 +1,4 @@ + +type MapMinimumDataEvent = MinimumDataEvent & { + +} \ No newline at end of file diff --git a/webapp/src/models/jpo-conflictmonitor/events/minimum_data/MinimumDataEvent.d.ts b/webapp/src/models/jpo-conflictmonitor/events/minimum_data/MinimumDataEvent.d.ts new file mode 100644 index 000000000..2971e0ab9 --- /dev/null +++ b/webapp/src/models/jpo-conflictmonitor/events/minimum_data/MinimumDataEvent.d.ts @@ -0,0 +1,6 @@ + +type MinimumDataEvent = { + source: str + timePeriod: ProcessingTimePeriod + missingDataElements: String[] +} \ No newline at end of file diff --git a/webapp/src/models/jpo-conflictmonitor/events/minimum_data/SpatMinimumDataEvent.d.ts b/webapp/src/models/jpo-conflictmonitor/events/minimum_data/SpatMinimumDataEvent.d.ts new file mode 100644 index 000000000..03d3a83d8 --- /dev/null +++ b/webapp/src/models/jpo-conflictmonitor/events/minimum_data/SpatMinimumDataEvent.d.ts @@ -0,0 +1,4 @@ + +type SpatMinimumDataEvent = MinimumDataEvent & { + +} \ No newline at end of file diff --git a/webapp/src/models/jpo-conflictmonitor/map/IntersectionRegion.d.ts b/webapp/src/models/jpo-conflictmonitor/map/IntersectionRegion.d.ts new file mode 100644 index 000000000..a71083e2b --- /dev/null +++ b/webapp/src/models/jpo-conflictmonitor/map/IntersectionRegion.d.ts @@ -0,0 +1,5 @@ + +type IntersectionRegion = { + intersectionId: number + region: number +} \ No newline at end of file diff --git a/webapp/src/models/jpo-conflictmonitor/map/MapIndex.d.ts b/webapp/src/models/jpo-conflictmonitor/map/MapIndex.d.ts new file mode 100644 index 000000000..3e54442fb --- /dev/null +++ b/webapp/src/models/jpo-conflictmonitor/map/MapIndex.d.ts @@ -0,0 +1,4 @@ + +type MapIndex = { + quadtree: Quadtree +} \ No newline at end of file diff --git a/webapp/src/models/jpo-conflictmonitor/map/MapTimeChangeDetail.d.ts b/webapp/src/models/jpo-conflictmonitor/map/MapTimeChangeDetail.d.ts new file mode 100644 index 000000000..0ba9eb666 --- /dev/null +++ b/webapp/src/models/jpo-conflictmonitor/map/MapTimeChangeDetail.d.ts @@ -0,0 +1,4 @@ + +type MapTimeChangeDetail = { + +} \ No newline at end of file diff --git a/webapp/src/models/jpo-conflictmonitor/map/MapTimeChangeDetailAggregator.d.ts b/webapp/src/models/jpo-conflictmonitor/map/MapTimeChangeDetailAggregator.d.ts new file mode 100644 index 000000000..1730aecd8 --- /dev/null +++ b/webapp/src/models/jpo-conflictmonitor/map/MapTimeChangeDetailAggregator.d.ts @@ -0,0 +1,4 @@ + +type MapTimeChangeDetailAggregator = { + +} \ No newline at end of file diff --git a/webapp/src/models/jpo-conflictmonitor/map/MapTimeChangeDetailPair.d.ts b/webapp/src/models/jpo-conflictmonitor/map/MapTimeChangeDetailPair.d.ts new file mode 100644 index 000000000..38f0f3194 --- /dev/null +++ b/webapp/src/models/jpo-conflictmonitor/map/MapTimeChangeDetailPair.d.ts @@ -0,0 +1,4 @@ + +type MapTimeChangeDetailPair = { + +} \ No newline at end of file diff --git a/webapp/src/models/jpo-conflictmonitor/map/MapTimeChangeDetailState.d.ts b/webapp/src/models/jpo-conflictmonitor/map/MapTimeChangeDetailState.d.ts new file mode 100644 index 000000000..34f6ea9ac --- /dev/null +++ b/webapp/src/models/jpo-conflictmonitor/map/MapTimeChangeDetailState.d.ts @@ -0,0 +1,4 @@ + +type MapTimeChangeDetailState = { + +} \ No newline at end of file diff --git a/webapp/src/models/jpo-conflictmonitor/map/MapTimestampExtractor.d.ts b/webapp/src/models/jpo-conflictmonitor/map/MapTimestampExtractor.d.ts new file mode 100644 index 000000000..137008e1f --- /dev/null +++ b/webapp/src/models/jpo-conflictmonitor/map/MapTimestampExtractor.d.ts @@ -0,0 +1,4 @@ + +type MapTimestampExtractor = { + +} \ No newline at end of file diff --git a/webapp/src/models/jpo-conflictmonitor/notifications/ConnectionOfTravelNotification.d.ts b/webapp/src/models/jpo-conflictmonitor/notifications/ConnectionOfTravelNotification.d.ts new file mode 100644 index 000000000..9422692f0 --- /dev/null +++ b/webapp/src/models/jpo-conflictmonitor/notifications/ConnectionOfTravelNotification.d.ts @@ -0,0 +1,6 @@ +/// +type ConnectionOfTravelNotification = MessageMonitor.Notification & { + assessment: ConnectionOfTravelAssessment + ingressLane: number, + egressLane: number, +} \ No newline at end of file diff --git a/webapp/src/models/jpo-conflictmonitor/notifications/IntersectionReferenceAlignmentNotification.d.ts b/webapp/src/models/jpo-conflictmonitor/notifications/IntersectionReferenceAlignmentNotification.d.ts new file mode 100644 index 000000000..3f8d4e22e --- /dev/null +++ b/webapp/src/models/jpo-conflictmonitor/notifications/IntersectionReferenceAlignmentNotification.d.ts @@ -0,0 +1,4 @@ +/// +type IntersectionReferenceAlignmentNotification = MessageMonitor.Notification & { + event: IntersectionReferenceAlignmentEvent +} \ No newline at end of file diff --git a/webapp/src/models/jpo-conflictmonitor/notifications/LaneDirectionOfTravelNotification.d.ts b/webapp/src/models/jpo-conflictmonitor/notifications/LaneDirectionOfTravelNotification.d.ts new file mode 100644 index 000000000..19a3fd91d --- /dev/null +++ b/webapp/src/models/jpo-conflictmonitor/notifications/LaneDirectionOfTravelNotification.d.ts @@ -0,0 +1,4 @@ +/// +type LaneDirectionOfTravelNotification = MessageMonitor.Notification & { + assessment: LaneDirectionOfTravelAssessment +} \ No newline at end of file diff --git a/webapp/src/models/jpo-conflictmonitor/notifications/Notification.d.ts b/webapp/src/models/jpo-conflictmonitor/notifications/Notification.d.ts new file mode 100644 index 000000000..a9be8e724 --- /dev/null +++ b/webapp/src/models/jpo-conflictmonitor/notifications/Notification.d.ts @@ -0,0 +1,15 @@ +declare namespace MessageMonitor { +type Notification = { + logger: Logger + id: str + key: str + notificationGeneratedAt: number + notificationType: str + notificationText: str + notificationHeading: str + intersectionID: number + roadRegulatorID: number + notificationExpiresAt: Date + getUniqueId: str +} +} \ No newline at end of file diff --git a/webapp/src/models/jpo-conflictmonitor/notifications/SignalGroupAlignmentNotification.d.ts b/webapp/src/models/jpo-conflictmonitor/notifications/SignalGroupAlignmentNotification.d.ts new file mode 100644 index 000000000..19772ee63 --- /dev/null +++ b/webapp/src/models/jpo-conflictmonitor/notifications/SignalGroupAlignmentNotification.d.ts @@ -0,0 +1,4 @@ +/// +type SignalGroupAlignmentNotification = MessageMonitor.Notification & { + event: SignalGroupAlignmentEvent +} \ No newline at end of file diff --git a/webapp/src/models/jpo-conflictmonitor/notifications/SignalStateConflictNotification.d.ts b/webapp/src/models/jpo-conflictmonitor/notifications/SignalStateConflictNotification.d.ts new file mode 100644 index 000000000..dbb2b2bf0 --- /dev/null +++ b/webapp/src/models/jpo-conflictmonitor/notifications/SignalStateConflictNotification.d.ts @@ -0,0 +1,4 @@ +/// +type SignalStateConflictNotification = MessageMonitor.Notification & { + event: SignalStateConflictEvent +} \ No newline at end of file diff --git a/webapp/src/models/jpo-conflictmonitor/notifications/TimeChangeDetailsNotification.d.ts b/webapp/src/models/jpo-conflictmonitor/notifications/TimeChangeDetailsNotification.d.ts new file mode 100644 index 000000000..4d7113b71 --- /dev/null +++ b/webapp/src/models/jpo-conflictmonitor/notifications/TimeChangeDetailsNotification.d.ts @@ -0,0 +1,4 @@ +/// +type TimeChangeDetailsNotification = MessageMonitor.Notification & { + event: TimeChangeDetailsEvent +} \ No newline at end of file diff --git a/webapp/src/models/jpo-conflictmonitor/notifications/app_health/KafkaStreamsAnomalyNotification.d.ts b/webapp/src/models/jpo-conflictmonitor/notifications/app_health/KafkaStreamsAnomalyNotification.d.ts new file mode 100644 index 000000000..bcaef9013 --- /dev/null +++ b/webapp/src/models/jpo-conflictmonitor/notifications/app_health/KafkaStreamsAnomalyNotification.d.ts @@ -0,0 +1,5 @@ +/// +type KafkaStreamsAnomalyNotification = MessageMonitor.Notification & { + stateChange: KafkaStreamsStateChangeEvent + exceptionEvent: KafkaStreamsUnhandledExceptionEvent +} \ No newline at end of file diff --git a/webapp/src/models/jpo-conflictmonitor/notifications/broadcast_rate/BroadcastRateNotification.d.ts b/webapp/src/models/jpo-conflictmonitor/notifications/broadcast_rate/BroadcastRateNotification.d.ts new file mode 100644 index 000000000..4d2b55246 --- /dev/null +++ b/webapp/src/models/jpo-conflictmonitor/notifications/broadcast_rate/BroadcastRateNotification.d.ts @@ -0,0 +1,4 @@ + +type BroadcastRateNotification = { + event: T +} \ No newline at end of file diff --git a/webapp/src/models/jpo-conflictmonitor/notifications/broadcast_rate/MapBroadcastRateNotification.d.ts b/webapp/src/models/jpo-conflictmonitor/notifications/broadcast_rate/MapBroadcastRateNotification.d.ts new file mode 100644 index 000000000..1fb011363 --- /dev/null +++ b/webapp/src/models/jpo-conflictmonitor/notifications/broadcast_rate/MapBroadcastRateNotification.d.ts @@ -0,0 +1,4 @@ + +type MapBroadcastRateNotification = { + +} \ No newline at end of file diff --git a/webapp/src/models/jpo-conflictmonitor/notifications/broadcast_rate/SpatBroadcastRateNotification.d.ts b/webapp/src/models/jpo-conflictmonitor/notifications/broadcast_rate/SpatBroadcastRateNotification.d.ts new file mode 100644 index 000000000..0bec92cb3 --- /dev/null +++ b/webapp/src/models/jpo-conflictmonitor/notifications/broadcast_rate/SpatBroadcastRateNotification.d.ts @@ -0,0 +1,4 @@ + +type SpatBroadcastRateNotification = BroadcastRateNotification & { + +} \ No newline at end of file diff --git a/webapp/src/models/jpo-conflictmonitor/spat/SpatAggregator.d.ts b/webapp/src/models/jpo-conflictmonitor/spat/SpatAggregator.d.ts new file mode 100644 index 000000000..45fb5480f --- /dev/null +++ b/webapp/src/models/jpo-conflictmonitor/spat/SpatAggregator.d.ts @@ -0,0 +1,4 @@ + +type SpatAggregator = { + spats: ProcessedSpat[] +} \ No newline at end of file diff --git a/webapp/src/models/jpo-conflictmonitor/spat/SpatTimeChangeDetail.d.ts b/webapp/src/models/jpo-conflictmonitor/spat/SpatTimeChangeDetail.d.ts new file mode 100644 index 000000000..a918051d1 --- /dev/null +++ b/webapp/src/models/jpo-conflictmonitor/spat/SpatTimeChangeDetail.d.ts @@ -0,0 +1,8 @@ + +type SpatTimeChangeDetail = { + originIP: str + region: number + intersectionID: number + timestamp: number + states: SpatTimeChangeDetailState[] +} \ No newline at end of file diff --git a/webapp/src/models/jpo-conflictmonitor/spat/SpatTimeChangeDetailAggregator.d.ts b/webapp/src/models/jpo-conflictmonitor/spat/SpatTimeChangeDetailAggregator.d.ts new file mode 100644 index 000000000..fdb65f7ee --- /dev/null +++ b/webapp/src/models/jpo-conflictmonitor/spat/SpatTimeChangeDetailAggregator.d.ts @@ -0,0 +1,6 @@ + +type SpatTimeChangeDetailAggregator = { + logger: Logger + spatTimeChangeDetails: SpatTimeChangeDetail[] + messageBufferSize: number +} \ No newline at end of file diff --git a/webapp/src/models/jpo-conflictmonitor/spat/SpatTimeChangeDetailPair.d.ts b/webapp/src/models/jpo-conflictmonitor/spat/SpatTimeChangeDetailPair.d.ts new file mode 100644 index 000000000..a16ae7298 --- /dev/null +++ b/webapp/src/models/jpo-conflictmonitor/spat/SpatTimeChangeDetailPair.d.ts @@ -0,0 +1,5 @@ + +type SpatTimeChangeDetailPair = { + first: SpatTimeChangeDetail + second: SpatTimeChangeDetail +} \ No newline at end of file diff --git a/webapp/src/models/jpo-conflictmonitor/spat/SpatTimeChangeDetailState.d.ts b/webapp/src/models/jpo-conflictmonitor/spat/SpatTimeChangeDetailState.d.ts new file mode 100644 index 000000000..d67c2e40b --- /dev/null +++ b/webapp/src/models/jpo-conflictmonitor/spat/SpatTimeChangeDetailState.d.ts @@ -0,0 +1,7 @@ + +type SpatTimeChangeDetailState = { + minEndTime: number + maxEndTime: number + signalGroup: number + eventState: J2735MovementPhaseState +} \ No newline at end of file diff --git a/webapp/src/models/jpo-conflictmonitor/spat/SpatTimestampExtractor.d.ts b/webapp/src/models/jpo-conflictmonitor/spat/SpatTimestampExtractor.d.ts new file mode 100644 index 000000000..8a86247a6 --- /dev/null +++ b/webapp/src/models/jpo-conflictmonitor/spat/SpatTimestampExtractor.d.ts @@ -0,0 +1,4 @@ + +type SpatTimestampExtractor = { + +} \ No newline at end of file diff --git a/webapp/src/models/required_types.json b/webapp/src/models/required_types.json new file mode 100644 index 000000000..0944a0782 --- /dev/null +++ b/webapp/src/models/required_types.json @@ -0,0 +1,19 @@ +{ + "OdePosition3D": "https://github.com/usdot-jpo-ode/jpo-ode/tree/dev/jpo-ode-plugins/src/main/java/us/dot/its/jpo/ode/plugin/j2735/OdePosition3D.java", + "J2735GenericLane": "https://github.com/usdot-jpo-ode/jpo-ode/tree/dev/jpo-ode-plugins/src/main/java/us/dot/its/jpo/ode/plugin/j2735/J2735GenericLane.java", + "GeometryFactory": "https://github.com/locationtech/jts/tree/master/modules/core/src/main/java/org/locationtech/jts/geom/GeometryFactory.java", + "J2735IntersectionGeometry": "https://github.com/usdot-jpo-ode/jpo-ode/tree/dev/jpo-ode-plugins/src/main/java/us/dot/its/jpo/ode/plugin/j2735/J2735IntersectionGeometry.java", + "ProcessedSpat": "https://github.com/usdot-jpo-ode/jpo-geojsonconverter/tree/develop/jpo-geojsonconverter/src/main/java/us/dot/its/jpo/geojsonconverter/pojos/spat/ProcessedSpat.java", + "ProcessedMap": "https://github.com/usdot-jpo-ode/jpo-geojsonconverter/tree/develop/jpo-geojsonconverter/src/main/java/us/dot/its/jpo/geojsonconverter/pojos/geojson/map/ProcessedMap.java", + "BsmAggregator": "https://github.com/usdot-jpo-ode/jpo-conflictmonitor/tree/dev/jpo-conflictmonitor/src/main/java/us/dot/its/jpo/conflictmonitor/monitor/models/bsm/BsmAggregator.java", + "SpatAggregator": "https://github.com/usdot-jpo-ode/jpo-conflictmonitor/tree/dev/jpo-conflictmonitor/src/main/java/us/dot/its/jpo/conflictmonitor/monitor/models/spat/SpatAggregator.java", + "Intersection": "./java/Intersection/VehiclePath/Intersection.java", + "OdeBsmData": "https://github.com/usdot-jpo-ode/jpo-ode/tree/dev/jpo-ode-core/src/main/java/us/dot/its/jpo/ode/model/OdeBsmData.java", + "J2735MovementPhaseState": "https://github.com/usdot-jpo-ode/jpo-ode/tree/dev/jpo-ode-plugins/src/main/java/us/dot/its/jpo/ode/plugin/j2735/J2735MovementPhaseState.java", + "ProcessingTimePeriod": "https://github.com/usdot-jpo-ode/jpo-conflictmonitor/tree/dev/jpo-conflictmonitor/src/main/java/us/dot/its/jpo/conflictmonitor/monitor/models/events/ProcessingTimePeriod.java", + "Coordinate": "https://github.com/locationtech/jts/tree/master/modules/core/src/main/java/org/locationtech/jts/geom/Coordinate.java", + "Point": "https://github.com/locationtech/jts/tree/master/modules/core/src/main/java/org/locationtech/jts/geom/Point.java", + "Lane": "./java/Intersection/VehiclePath/Lane.java", + "Polygon": "https://github.com/locationtech/jts/tree/master/modules/core/src/main/java/org/locationtech/jts/geom/Polygon.java", + "SpatTimeChangeDetail": "./java/spat/SpatTimeChangeDetailPair/SpatTimeChangeDetail.java" +} diff --git a/webapp/src/models/spatSignalGroups.d.ts b/webapp/src/models/spatSignalGroups.d.ts new file mode 100644 index 000000000..202a57681 --- /dev/null +++ b/webapp/src/models/spatSignalGroups.d.ts @@ -0,0 +1,39 @@ +type SpatSignalGroups = { + [datetime: number]: SpatSignalGroup[] +} + +type SpatSignalGroup = { + signalGroup: number + state: SignalState +} + +type SignalState = + | null + | 'UNAVAILABLE' + | 'DARK' + | 'STOP_THEN_PROCEED' + | 'STOP_AND_REMAIN' + | 'PRE_MOVEMENT' + | 'PERMISSIVE_MOVEMENT_ALLOWED' + | 'PROTECTED_MOVEMENT_ALLOWED' + | 'PERMISSIVE_CLEARANCE' + | 'PROTECTED_CLEARANCE' + | 'CAUTION_CONFLICTING_TRAFFIC' + +type SignalStateFeatureCollection = { + type: 'FeatureCollection' + features: SignalStateFeature[] +} + +type SignalStateFeature = { + type: 'Feature' + properties: SignalStateProperties + geometry: PointGemetry +} + +type SignalStateProperties = { + signalGroup: number + intersectionId?: number + signalState: SignalState + orientation: number +} diff --git a/webapp/src/types/wzdx/WzdxWorkZoneFeed42.d.ts b/webapp/src/models/wzdx/WzdxWorkZoneFeed42.d.ts similarity index 100% rename from webapp/src/types/wzdx/WzdxWorkZoneFeed42.d.ts rename to webapp/src/models/wzdx/WzdxWorkZoneFeed42.d.ts diff --git a/webapp/src/types/wzdx/work_zone_feed_schema_42.json b/webapp/src/models/wzdx/work_zone_feed_schema_42.json similarity index 100% rename from webapp/src/types/wzdx/work_zone_feed_schema_42.json rename to webapp/src/models/wzdx/work_zone_feed_schema_42.json diff --git a/webapp/src/pages/IntersectionMapView.tsx b/webapp/src/pages/IntersectionMapView.tsx new file mode 100644 index 000000000..e69de29bb diff --git a/webapp/src/pages/Map.tsx b/webapp/src/pages/Map.tsx index 22314e495..80eb40022 100644 --- a/webapp/src/pages/Map.tsx +++ b/webapp/src/pages/Map.tsx @@ -77,7 +77,7 @@ import { import 'rc-slider/assets/index.css' import './css/BsmMap.css' import './css/Map.css' -import { WZDxFeature, WZDxWorkZoneFeed } from '../types/wzdx/WzdxWorkZoneFeed42' +import { WZDxFeature, WZDxWorkZoneFeed } from '../models/wzdx/WzdxWorkZoneFeed42' import { AnyAction, ThunkDispatch } from '@reduxjs/toolkit' import { RootState } from '../store' diff --git a/webapp/src/store.tsx b/webapp/src/store.tsx index eb1c076b8..6be05f977 100644 --- a/webapp/src/store.tsx +++ b/webapp/src/store.tsx @@ -1,4 +1,4 @@ -import { configureStore } from '@reduxjs/toolkit' +import { Action, ThunkAction, configureStore } from '@reduxjs/toolkit' import rsuReducer from './generalSlices/rsuSlice' import userReducer from './generalSlices/userSlice' import wzdxReducer from './generalSlices/wzdxSlice' @@ -15,6 +15,8 @@ import adminOrganizationTabRsuReducer from './features/adminOrganizationTabRsu/a import adminRsuTabReducer from './features/adminRsuTab/adminRsuTabSlice' import adminUserTabReducer from './features/adminUserTab/adminUserTabSlice' import menuReducer from './features/menu/menuSlice' +import intersectionMapReducer from './components/map/map-slice' +import intersectionMapLayerStyleReducer from './components/map/map-layer-style-slice' export const setupStore = (preloadedState: any) => { return configureStore({ @@ -35,11 +37,15 @@ export const setupStore = (preloadedState: any) => { adminRsuTab: adminRsuTabReducer, adminUserTab: adminUserTabReducer, menu: menuReducer, + intersectionMap: intersectionMapReducer, + intersectionMapLayerStyle: intersectionMapLayerStyleReducer, }, preloadedState, }) } -const tempState = setupStore({}).getState +type AppStore = ReturnType +export type AppState = ReturnType +type AppThunk = ThunkAction -export type RootState = ReturnType +export type RootState = ReturnType['getState']> diff --git a/webapp/yarn.lock b/webapp/yarn.lock index 4581822ab..3e7c4fecd 100644 --- a/webapp/yarn.lock +++ b/webapp/yarn.lock @@ -2423,6 +2423,11 @@ dependencies: "@sinonjs/commons" "^1.7.0" +"@stomp/stompjs@^7.0.0": + version "7.0.0" + resolved "https://registry.npmjs.org/@stomp/stompjs/-/stompjs-7.0.0.tgz" + integrity sha512-fGdq4wPDnSV/KyOsjq4P+zLc8MFWC3lMmP5FBgLWKPJTYcuCbAIrnRGjB7q2jHZdYCOD5vxLuFoKIYLy5/u8Pw== + "@surma/rollup-plugin-off-main-thread@^2.2.3": version "2.2.3" resolved "https://registry.npmjs.org/@surma/rollup-plugin-off-main-thread/-/rollup-plugin-off-main-thread-2.2.3.tgz" @@ -6114,6 +6119,11 @@ file-loader@^6.2.0: loader-utils "^2.0.0" schema-utils "^3.0.0" +file-saver@^2.0.5: + version "2.0.5" + resolved "https://registry.npmjs.org/file-saver/-/file-saver-2.0.5.tgz" + integrity sha512-P9bmyZ3h/PRG+Nzga+rbdI4OEpNDzAVyy74uVO9ATgzLK6VtAsYybF/+TOCvrc0MO793d6+42lLyZTw7/ArVzA== + filelist@^1.0.4: version "1.0.4" resolved "https://registry.npmjs.org/filelist/-/filelist-1.0.4.tgz" @@ -6880,6 +6890,11 @@ ignore@^5.2.0: resolved "https://registry.npmjs.org/ignore/-/ignore-5.3.0.tgz" integrity sha512-g7dmpshy+gD7mh88OC9NwSGTKoc3kyLAZQRU1mt53Aw/vnvfXnbC+F/7F7QoYVKbV+KNvJx8wArewKy1vXMtlg== +immediate@~3.0.5: + version "3.0.6" + resolved "https://registry.npmjs.org/immediate/-/immediate-3.0.6.tgz" + integrity sha512-XXOFtyqDjNDAQxVfYxuF7g9Il/IbWmmlQg2MYKOH8ExIT1qg6xc4zyS3HaEEATgs1btfzxq15ciUiY7gjSXRGQ== + immer@^9.0.16, immer@^9.0.7, immer@>=9.0: version "9.0.17" resolved "https://registry.npmjs.org/immer/-/immer-9.0.17.tgz" @@ -8594,6 +8609,16 @@ jss@^10.5.1, jss@10.10.0: object.assign "^4.1.4" object.values "^1.1.6" +jszip@^3.10.1: + version "3.10.1" + resolved "https://registry.npmjs.org/jszip/-/jszip-3.10.1.tgz" + integrity sha512-xXDvecyTpGLrqFrvkrUSoxxfJI5AH7U8zxxtVclpsUtMCq4JQ290LY8AW5c7Ggnr/Y/oK+bQMbqK2qmtk3pN4g== + dependencies: + lie "~3.3.0" + pako "~1.0.2" + readable-stream "~2.3.6" + setimmediate "^1.0.5" + kdbush@^4.0.1, kdbush@^4.0.2: version "4.0.2" resolved "https://registry.npmjs.org/kdbush/-/kdbush-4.0.2.tgz" @@ -8696,6 +8721,13 @@ levn@~0.3.0: prelude-ls "~1.1.2" type-check "~0.3.2" +lie@~3.3.0: + version "3.3.0" + resolved "https://registry.npmjs.org/lie/-/lie-3.3.0.tgz" + integrity sha512-UaiMJzeWRlEujzAuw5LokY1L5ecNQYZKfmyZ9L7wDHb/p5etKaxXhohBcrw0EYby+G/NA52vRSN4N39dxHAIwQ== + dependencies: + immediate "~3.0.5" + lilconfig@^2.0.3, lilconfig@^2.1.0: version "2.1.0" resolved "https://registry.npmjs.org/lilconfig/-/lilconfig-2.1.0.tgz" @@ -9493,6 +9525,11 @@ p-try@^2.0.0: resolved "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz" integrity sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ== +pako@~1.0.2: + version "1.0.11" + resolved "https://registry.npmjs.org/pako/-/pako-1.0.11.tgz" + integrity sha512-4hLB8Py4zZce5s4yd9XzopqwVv/yGNhV1Bl8NTmCq1763HeK2+EwVTv+leGeL13Dnh2wfbqowVPXCIO0z4taYw== + param-case@^3.0.4: version "3.0.4" resolved "https://registry.npmjs.org/param-case/-/param-case-3.0.4.tgz" @@ -10827,6 +10864,19 @@ readable-stream@^3.0.6: string_decoder "^1.1.1" util-deprecate "^1.0.1" +readable-stream@~2.3.6: + version "2.3.8" + resolved "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.8.tgz" + integrity sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA== + dependencies: + core-util-is "~1.0.0" + inherits "~2.0.3" + isarray "~1.0.0" + process-nextick-args "~2.0.0" + safe-buffer "~5.1.1" + string_decoder "~1.1.1" + util-deprecate "~1.0.1" + readdirp@~3.6.0: version "3.6.0" resolved "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz" From 1a4e3df2cf27bb133251e7c06fbb2fbaff17385f Mon Sep 17 00:00:00 2001 From: jacob6838 Date: Tue, 5 Mar 2024 08:57:14 -0700 Subject: [PATCH 002/189] sync'ing map properties with side panel --- webapp/src/components/map/side-panel.tsx | 165 +++++++++++------------ 1 file changed, 82 insertions(+), 83 deletions(-) diff --git a/webapp/src/components/map/side-panel.tsx b/webapp/src/components/map/side-panel.tsx index 16081f0bf..7c6f83648 100644 --- a/webapp/src/components/map/side-panel.tsx +++ b/webapp/src/components/map/side-panel.tsx @@ -1,16 +1,17 @@ -import React, { useState } from "react"; +import React, { useState } from 'react' -import { Paper, Box, IconButton, Typography } from "@mui/material"; -import ChevronLeftIcon from "@mui/icons-material/ChevronLeft"; -import ChevronRightIcon from "@mui/icons-material/ChevronRight"; -import MuiAccordion, { AccordionProps } from "@mui/material/Accordion"; -import MuiAccordionSummary, { AccordionSummaryProps } from "@mui/material/AccordionSummary"; -import MuiAccordionDetails from "@mui/material/AccordionDetails"; -import ArrowForwardIosSharpIcon from "@mui/icons-material/ArrowForwardIosSharp"; -import { styled } from "@mui/material/styles"; -import { CustomTable } from "./custom-table"; -import { format } from "date-fns"; -import { ExpandableTable } from "./expandable-table"; +import { Paper, Box, IconButton, Typography } from '@mui/material' +import ChevronLeftIcon from '@mui/icons-material/ChevronLeft' +import ChevronRightIcon from '@mui/icons-material/ChevronRight' +import MuiAccordion, { AccordionProps } from '@mui/material/Accordion' +import MuiAccordionSummary, { AccordionSummaryProps } from '@mui/material/AccordionSummary' +import MuiAccordionDetails from '@mui/material/AccordionDetails' +import ArrowForwardIosSharpIcon from '@mui/icons-material/ArrowForwardIosSharp' +import { styled } from '@mui/material/styles' +import { CustomTable } from './custom-table' +import { format } from 'date-fns' +import { ExpandableTable } from './expandable-table' +import { MAP_PROPS } from './map-slice' const Accordion = styled((props: AccordionProps) => )( ({ theme }) => ({ @@ -22,124 +23,122 @@ const Accordion = styled((props: AccordionProps) => ( - } {...props} /> + } {...props} /> ))(({ theme }) => ({ minHeight: 0, paddingLeft: 10, - flexDirection: "row-reverse", - "& .MuiAccordionSummary-expandIconWrapper.Mui-expanded": { - transform: "rotate(90deg)", + flexDirection: 'row-reverse', + '& .MuiAccordionSummary-expandIconWrapper.Mui-expanded': { + transform: 'rotate(90deg)', }, - "& .MuiAccordionSummary-content": { + '& .MuiAccordionSummary-content': { marginLeft: theme.spacing(1), marginTop: 0, marginBottom: 0, }, -})); +})) -const AccordionDetails = styled(MuiAccordionDetails)(({ theme }) => ({})); +const AccordionDetails = styled(MuiAccordionDetails)(({ theme }) => ({})) interface SidePanelProps { - laneInfo: ConnectingLanesFeatureCollection | undefined; - signalGroups: SpatSignalGroup[] | undefined; - bsms: BsmUiFeatureCollection; - events: MessageMonitor.Event[]; - notifications: MessageMonitor.Notification[]; - sourceData: MessageMonitor.Notification | MessageMonitor.Event | Assessment | { timestamp: number } | undefined; - sourceDataType: "notification" | "event" | "assessment" | "timestamp" | undefined; + laneInfo: ConnectingLanesFeatureCollection | undefined + signalGroups: SpatSignalGroup[] | undefined + bsms: BsmUiFeatureCollection + events: MessageMonitor.Event[] + notifications: MessageMonitor.Notification[] + sourceData: MAP_PROPS['sourceData'] + sourceDataType: MAP_PROPS['sourceDataType'] } export const SidePanel = (props: SidePanelProps) => { - const { laneInfo, signalGroups, bsms, events, notifications, sourceData, sourceDataType } = props; + const { laneInfo, signalGroups, bsms, events, notifications, sourceData, sourceDataType } = props - const [open, setOpen] = useState(false); + const [open, setOpen] = useState(false) const getDataTable = ( sourceData: MessageMonitor.Notification | MessageMonitor.Event | Assessment | { timestamp: number } | undefined, - sourceDataType: "notification" | "event" | "assessment" | "timestamp" | undefined + sourceDataType: 'notification' | 'event' | 'assessment' | 'timestamp' | undefined ) => { switch (sourceDataType) { - case "notification": - return getNotificationTable(sourceData as MessageMonitor.Notification); - case "event": - return No Data; //getNotificationTableFromEvent(sourceData as MessageMonitor.Event); - case "assessment": - return No Data; //getNotificationTableFromAssessment(sourceData as Assessment); - case "timestamp": - return ( - {format((sourceData as { timestamp: number }).timestamp, "MM/dd/yyyy HH:mm:ss")} - ); //getNotificationTableFromAssessment(sourceData as Assessment); + case 'notification': + return getNotificationTable(sourceData as MessageMonitor.Notification) + case 'event': + return No Data //getNotificationTableFromEvent(sourceData as MessageMonitor.Event); + case 'assessment': + return No Data //getNotificationTableFromAssessment(sourceData as Assessment); + case 'timestamp': + return {format((sourceData as { timestamp: number }).timestamp, 'MM/dd/yyyy HH:mm:ss')} //getNotificationTableFromAssessment(sourceData as Assessment); default: - return No Data; + return No Data } - }; + } const getNotificationTable = (notification: MessageMonitor.Notification) => { - const fields = [["time", format(new Date(notification.notificationGeneratedAt), "yyyy-MM-dd HH:mm:ss")]]; + const fields = [['time', format(new Date(notification.notificationGeneratedAt), 'yyyy-MM-dd HH:mm:ss')]] switch (notification.notificationType) { - case "SpatBroadcastRateNotification": - break; - case "SignalStateConflictNotification": - break; - case "SignalGroupAlignmentNotification": - break; - case "MapBroadcastRateNotification": - break; - case "LaneDirectionOfTravelNotification": - break; - case "IntersectionReferenceAlignmentNotification": - break; - case "ConnectionOfTravelNotification": - const connectionOfTravelNotification = notification as ConnectionOfTravelNotification; + case 'SpatBroadcastRateNotification': + break + case 'SignalStateConflictNotification': + break + case 'SignalGroupAlignmentNotification': + break + case 'MapBroadcastRateNotification': + break + case 'LaneDirectionOfTravelNotification': + break + case 'IntersectionReferenceAlignmentNotification': + break + case 'ConnectionOfTravelNotification': + const connectionOfTravelNotification = notification as ConnectionOfTravelNotification fields.push([ - "ingress Lane ID", + 'ingress Lane ID', // connectionOfTravelNotification?.assessment?.connectionOfTravelAssessmentGroups?.[0]?.ingressLaneID.toString(), connectionOfTravelNotification?.ingressLane.toString(), - ]); + ]) fields.push([ - "egress Lane ID", + 'egress Lane ID', // connectionOfTravelNotification?.assessment?.connectionOfTravelAssessmentGroups?.[0]?.egressLaneID.toString(), connectionOfTravelNotification?.egressLane.toString(), - ]); + ]) fields.push([ - "event count", + 'event count', connectionOfTravelNotification?.assessment?.connectionOfTravelAssessmentGroups?.[0]?.eventCount.toString(), - ]); - break; + ]) + break } return ( <> {notification?.notificationText} - + - ); - }; + ) + } return (
- - + + { - setOpen((prev) => !prev); + setOpen((prev) => !prev) }} > {open ? : } @@ -158,13 +157,13 @@ export const SidePanel = (props: SidePanelProps) => { [ lane.properties.ingressLaneId, lane.properties.egressLaneId, signalGroups?.find((grp) => grp.signalGroup == lane.properties.signalGroupId)?.state ?? - "no data", + 'no data', ]) ?? [] } /> @@ -178,7 +177,7 @@ export const SidePanel = (props: SidePanelProps) => { [ bsm.properties.secMark / 1000, @@ -198,10 +197,10 @@ export const SidePanel = (props: SidePanelProps) => { [ - format(event.eventGeneratedAt, "MM/dd/yyyy HH:mm:ss"), + format(event.eventGeneratedAt, 'MM/dd/yyyy HH:mm:ss'), event.eventType, ]) ?? [] } @@ -217,10 +216,10 @@ export const SidePanel = (props: SidePanelProps) => { [ - format(notification.notificationGeneratedAt, "MM/dd/yyyy HH:mm:ss"), + format(notification.notificationGeneratedAt, 'MM/dd/yyyy HH:mm:ss'), notification.notificationType, ]) ?? [] } @@ -241,5 +240,5 @@ export const SidePanel = (props: SidePanelProps) => {
- ); -}; + ) +} From 0460329293b5e082dff6edbdc56f1031e47f87d5 Mon Sep 17 00:00:00 2001 From: jacob6838 Date: Tue, 26 Mar 2024 08:08:31 -0600 Subject: [PATCH 003/189] Updating map source data type and adding rsu_ip --- webapp/src/components/map/map-component.tsx | 14 +- webapp/src/components/map/map-slice.tsx | 54 ++++--- webapp/src/components/map/side-panel.tsx | 29 +++- .../components/map/utilities/message-utils.ts | 149 ++++++++++-------- 4 files changed, 148 insertions(+), 98 deletions(-) diff --git a/webapp/src/components/map/map-component.tsx b/webapp/src/components/map/map-component.tsx index ece12d992..7fc458f37 100644 --- a/webapp/src/components/map/map-component.tsx +++ b/webapp/src/components/map/map-component.tsx @@ -65,11 +65,9 @@ import { addConnections, createMarkerForNotification } from './utilities/message import { AnyAction, ThunkDispatch } from '@reduxjs/toolkit' import { RootState } from '../../store' import { MapLegend } from './map-legend' +import { RsuInfo } from '../../apis/rsu-api-types' -const generateQueryParams = ( - source: MessageMonitor.Notification | MessageMonitor.Event | Assessment | timestamp | undefined, - sourceDataType: 'notification' | 'event' | 'assessment' | 'timestamp' | undefined -) => { +const generateQueryParams = (source: MAP_PROPS['sourceData'], sourceDataType: MAP_PROPS['sourceDataType']) => { const startOffset = 1000 * 60 * 1 const endOffset = 1000 * 60 * 1 @@ -106,6 +104,14 @@ const generateQueryParams = ( eventDate: new Date(ts), vehicleId: undefined, } + case 'rsu_ip': + const rsu_info = source as RsuInfo['rsuList'][0] + return { + startDate: new Date(Date.now() - startOffset), + endDate: new Date(Date.now() + endOffset), + eventDate: new Date(Date.now()), + vehicleId: undefined, + } default: return { startDate: new Date(Date.now() - startOffset), diff --git a/webapp/src/components/map/map-slice.tsx b/webapp/src/components/map/map-slice.tsx index 276da0218..8b0ca79af 100644 --- a/webapp/src/components/map/map-slice.tsx +++ b/webapp/src/components/map/map-slice.tsx @@ -19,7 +19,20 @@ import { ViewState } from 'react-map-gl' import JSZip from 'jszip' import FileSaver from 'file-saver' import { features } from 'process' -import { selectMapDate, selectMapList, selectRsuData, selectRsuIpv4, selectRsuMapData, selectSelectedRsu, selectSelectedSrm, selectSrmSsmList, selectSsmDisplay } from '../../generalSlices/rsuSlice' +import { + getMapData, + selectMapDate, + selectMapList, + selectRsu, + selectRsuData, + selectRsuIpv4, + selectRsuMapData, + selectSelectedRsu, + selectSelectedSrm, + selectSrmSsmList, + selectSsmDisplay, +} from '../../generalSlices/rsuSlice' +import { RsuInfo } from '../../apis/rsu-api-types' export type MAP_LAYERS = | 'mapMessage' @@ -52,7 +65,13 @@ type timestamp = { export type MAP_PROPS = { sourceApi: 'conflictvisualizer' | 'cvmanager' - sourceData: MessageMonitor.Notification | MessageMonitor.Event | Assessment | timestamp | string | undefined + sourceData: + | MessageMonitor.Notification + | MessageMonitor.Event + | Assessment + | timestamp + | RsuInfo['rsuList'][0] + | undefined sourceDataType: 'notification' | 'event' | 'assessment' | 'timestamp' | 'rsu_ip' | undefined intersectionId: number | undefined roadRegulatorId: number | undefined @@ -302,12 +321,6 @@ export const renderEntireMap = createAsyncThunk( rawData['event'] = sourceData as MessageMonitor.Event } else if (sourceDataType == 'assessment') { rawData['assessment'] = sourceData as Assessment - } else if (sourceDataType == 'rsu_ip') { - const rsuData = selectRsuData(currentState) - const mapList = selectMapList(currentState) - const rsu = rsuData.find((rsu) => rsu.properties.ipv4_address == sourceData) - if (rsu != null && mapList.includes(rsu.properties.ipv4_address)) { - } } return { connectingLanes: latestMapMessage.connectingLanesFeatureCollection, @@ -322,8 +335,7 @@ export const renderEntireMap = createAsyncThunk( getTimeRange(queryParams.startDate, queryParams.endDate) ), } - } - else { + } else if (sourceApi == 'cvmanager') { const queryParams = selectQueryParams(currentState) const sourceData = selectSourceData(currentState) const sourceDataType = selectSourceDataType(currentState) @@ -350,20 +362,18 @@ export const renderEntireMap = createAsyncThunk( rawData['spat'] = currentSpatData rawData['bsm'] = currentBsmData if (sourceDataType == 'rsu_ip') { - const rsuData = selectRsuData(currentState) const mapList = selectMapList(currentState) - const rsu = rsuData.find((rsu) => rsu.properties.ipv4_address == sourceData) + const rsu = sourceData as RsuInfo['rsuList'][0] if (rsu != null && mapList.includes(rsu.properties.ipv4_address)) { - - - const rsuMapData = selectRsuMapData(currentState); - const selectedRsu = selectSelectedRsu(currentState); - const selectedSrm = selectSelectedSrm(currentState); - const mapDate = selectMapDate(currentState); - const ssmDisplay = selectSsmDisplay(currentState); - const rsuIpv4 = selectRsuIpv4(currentState); - const srmSsmList = selectSrmSsmList(currentState); - + dispatch(getMapData()) + dispatch(selectRsu(rsu)) + //rsuMapData: set by getMapData, called here + //selectedRsu: set by selectRsu, called here + //selectedSrm: set by selectSrm, called in rsuMap page + //mapDate: filled by getMapData, called here + //ssmDisplay: filled by toggleSsmSrmDisplay, called in rsuMap page + //rsuIpv4: pulled from selectedRsu, called here + //srmSsmList: set by getSsmSrmData, called in rsuMap page } } return { diff --git a/webapp/src/components/map/side-panel.tsx b/webapp/src/components/map/side-panel.tsx index 7c6f83648..c896e27a8 100644 --- a/webapp/src/components/map/side-panel.tsx +++ b/webapp/src/components/map/side-panel.tsx @@ -12,6 +12,7 @@ import { CustomTable } from './custom-table' import { format } from 'date-fns' import { ExpandableTable } from './expandable-table' import { MAP_PROPS } from './map-slice' +import { RsuInfo } from '../../apis/rsu-api-types' const Accordion = styled((props: AccordionProps) => )( ({ theme }) => ({ @@ -58,10 +59,7 @@ export const SidePanel = (props: SidePanelProps) => { const [open, setOpen] = useState(false) - const getDataTable = ( - sourceData: MessageMonitor.Notification | MessageMonitor.Event | Assessment | { timestamp: number } | undefined, - sourceDataType: 'notification' | 'event' | 'assessment' | 'timestamp' | undefined - ) => { + const getDataTable = (sourceData: MAP_PROPS['sourceData'], sourceDataType: MAP_PROPS['sourceDataType']) => { switch (sourceDataType) { case 'notification': return getNotificationTable(sourceData as MessageMonitor.Notification) @@ -71,6 +69,8 @@ export const SidePanel = (props: SidePanelProps) => { return No Data //getNotificationTableFromAssessment(sourceData as Assessment); case 'timestamp': return {format((sourceData as { timestamp: number }).timestamp, 'MM/dd/yyyy HH:mm:ss')} //getNotificationTableFromAssessment(sourceData as Assessment); + case 'rsu_ip': + return getRsuInfoTable(sourceData as RsuInfo['rsuList'][0]) default: return No Data } @@ -119,6 +119,27 @@ export const SidePanel = (props: SidePanelProps) => { ) } + const getRsuInfoTable = (rsuInfo: RsuInfo['rsuList'][0]) => { + const fields = [ + ['id', rsuInfo?.properties?.rsu_id], + ['milepost', rsuInfo?.properties?.milepost], + ['geography', rsuInfo?.properties?.geography], + ['model_name', rsuInfo?.properties?.model_name], + ['ipv4_address', rsuInfo?.properties?.ipv4_address], + ['primary_route', rsuInfo?.properties?.primary_route], + ['serial_number', rsuInfo?.properties?.serial_number], + ['manufacturer_name', rsuInfo?.properties?.manufacturer_name], + ] + return ( + <> + {rsuInfo?.properties?.ipv4_address} + + + + + ) + } + return (
{ - const features: SignalStateFeature[] = []; + const features: SignalStateFeature[] = [] mapMessage?.mapFeatureCollection?.features?.forEach((mapFeature: MapFeature) => { if (!mapFeature.properties.ingressApproach || !mapFeature?.properties?.connectsTo?.[0]?.signalGroup) { - return; + return } - const coords = mapFeature.geometry.coordinates.slice(0, 2); + const coords = mapFeature.geometry.coordinates.slice(0, 2) features.push({ - type: "Feature", + type: 'Feature', properties: { signalGroup: mapFeature.properties.connectsTo[0].signalGroup, intersectionId: mapMessage.properties.intersectionId, orientation: getBearingBetweenPoints(coords[1], coords[0]), - signalState: "UNAVAILABLE", + signalState: 'UNAVAILABLE', }, geometry: { - type: "Point", + type: 'Point', coordinates: mapFeature.geometry.coordinates[0], }, - }); - }); + }) + }) return { - type: "FeatureCollection" as "FeatureCollection", + type: 'FeatureCollection' as 'FeatureCollection', features: features, - }; -}; + } +} export const createMarkerForNotification = ( center: number[], notification: MessageMonitor.Notification, connectingLanes: MapFeatureCollection ) => { - const features: any[] = []; + const features: any[] = [] const markerCollection = { - type: "FeatureCollection" as "FeatureCollection", + type: 'FeatureCollection' as 'FeatureCollection', features: features, - }; + } switch (notification.notificationType) { - case "ConnectionOfTravelNotification": + case 'ConnectionOfTravelNotification': // TODO: Re-add once more notification data is available // const connTravelNotification = notification as ConnectionOfTravelNotification; // const connTravelAssessmentGroups = connTravelNotification.assessment.connectionOfTravelAssessmentGroups; @@ -69,67 +70,79 @@ export const createMarkerForNotification = ( // }; // markerCollection.features.push(marker); // }); - break; - case "IntersectionReferenceAlignmentNotification": + break + case 'IntersectionReferenceAlignmentNotification': // No markers for this notification - break; - case "LaneDirectionOfTravelNotification": - const laneDirTravelNotification = notification as LaneDirectionOfTravelNotification; - const laneDirTravelAssessmentGroups = laneDirTravelNotification.assessment.laneDirectionOfTravelAssessmentGroup; + break + case 'LaneDirectionOfTravelNotification': + const laneDirTravelNotification = notification as LaneDirectionOfTravelNotification + const laneDirTravelAssessmentGroups = laneDirTravelNotification.assessment.laneDirectionOfTravelAssessmentGroup laneDirTravelAssessmentGroups?.forEach((assessmentGroup) => { const laneLocation: number[] | undefined = connectingLanes.features.find( (connectingLaneFeature: MapFeature) => { - return connectingLaneFeature.properties.laneId === assessmentGroup.laneID; + return connectingLaneFeature.properties.laneId === assessmentGroup.laneID } - )?.geometry.coordinates[0]; - if (!laneLocation) return; - const numEvents = assessmentGroup.inToleranceEvents + assessmentGroup.outOfToleranceEvents; - const eventsRatio = assessmentGroup.inToleranceEvents / numEvents; + )?.geometry.coordinates[0] + if (!laneLocation) return + const numEvents = assessmentGroup.inToleranceEvents + assessmentGroup.outOfToleranceEvents + const eventsRatio = assessmentGroup.inToleranceEvents / numEvents const marker = { - type: "Feature", + type: 'Feature', properties: { description: `${laneDirTravelNotification.notificationText}, lane ID ${assessmentGroup.laneID}, in tolerance events ${eventsRatio} (${assessmentGroup.inToleranceEvents}/${numEvents})`, title: laneDirTravelNotification.notificationType, }, geometry: { - type: "Point", + type: 'Point', coordinates: laneLocation, }, - }; - markerCollection.features.push(marker); - }); - break; - case "SignalGroupAlignmentNotification": + } + markerCollection.features.push(marker) + }) + break + case 'SignalGroupAlignmentNotification': // No markers for this notification - break; - case "SignalStateConflictNotification": - const sigStateConflictNotification = notification as SignalStateConflictNotification; - const sigStateConflictEvent = sigStateConflictNotification.event; + break + case 'SignalStateConflictNotification': + const sigStateConflictNotification = notification as SignalStateConflictNotification + const sigStateConflictEvent = sigStateConflictNotification.event const sigStateConflictMarker = { - type: "Feature", + type: 'Feature', properties: { description: `${sigStateConflictNotification.notificationText}, Conflict type ${sigStateConflictEvent.conflictType}, First conflicting signal state ${sigStateConflictEvent.firstConflictingSignalState} of group ${sigStateConflictEvent.firstConflictingSignalGroup}, Second conflicting signal state ${sigStateConflictEvent.secondConflictingSignalState} of group ${sigStateConflictEvent.secondConflictingSignalGroup}`, title: sigStateConflictNotification.notificationType, }, geometry: { - type: "Point", + type: 'Point', coordinates: center, }, - }; - markerCollection.features.push(sigStateConflictMarker); - break; - case "TimeChangeDetailsNotification": + } + markerCollection.features.push(sigStateConflictMarker) + break + case 'TimeChangeDetailsNotification': // No markers for this notification - break; - case "KafkaStreamsAnomalyNotification": + break + case 'KafkaStreamsAnomalyNotification': // No markers for this notification - break; - case "BroadcastRateNotification": + break + case 'BroadcastRateNotification': // No markers for this notification - break; + break + } + return markerCollection +} + +export const createMarkerForRsu = (rsuInfo: RsuInfo['rsuList'][0]) => { + const features: any[] = [] + const markerCollection = { + type: 'FeatureCollection' as 'FeatureCollection', + features: features, } - return markerCollection; -}; + + markerCollection.features.push(rsuInfo) + + return markerCollection +} export const addConnections = ( connectingLanes: ConnectingLanesFeatureCollection, @@ -145,8 +158,8 @@ export const addConnections = ( ?.state, }, })), - }; -}; + } +} export const generateSignalStateFeatureCollection = ( prevSignalStates: SignalStateFeatureCollection, @@ -160,40 +173,40 @@ export const generateSignalStateFeatureCollection = ( ...feature.properties, signalState: signalGroups?.find((signalGroup) => signalGroup.signalGroup == feature.properties.signalGroup)?.state ?? - "UNAVAILABLE", + 'UNAVAILABLE', }, })), - }; -}; + } +} export const parseSpatSignalGroups = (spats: ProcessedSpat[]): SpatSignalGroups => { - const timedSignalGroups: SpatSignalGroups = {}; + const timedSignalGroups: SpatSignalGroups = {} spats?.forEach((spat: ProcessedSpat) => { timedSignalGroups[Date.parse(spat.odeReceivedAt)] = spat.states.map((state) => { return { signalGroup: state.signalGroup, state: state.stateTimeSpeed?.[0]?.eventState as SignalState, - }; - }); - }); - return timedSignalGroups; -}; + } + }) + }) + return timedSignalGroups +} export const parseBsmToGeojson = (bsmData: OdeBsmData[]): BsmFeatureCollection => { return { - type: "FeatureCollection" as "FeatureCollection", + type: 'FeatureCollection' as 'FeatureCollection', features: bsmData.map((bsm) => { return { - type: "Feature", + type: 'Feature', properties: { ...bsm.payload.data.coreData, odeReceivedAt: new Date(bsm.metadata.odeReceivedAt as string).getTime() / 1000, }, geometry: { - type: "Point", + type: 'Point', coordinates: [bsm.payload.data.coreData.position.longitude, bsm.payload.data.coreData.position.latitude], }, - }; + } }), - }; -}; + } +} From f4ddb56ee4790e6519eacb1972b21f49ec832cce Mon Sep 17 00:00:00 2001 From: jacob6838 Date: Fri, 29 Mar 2024 09:21:17 -0600 Subject: [PATCH 004/189] Fixing notification type --- webapp/package-lock.json | 30 +++ webapp/package.json | 1 + webapp/src/components/Header.tsx | 2 +- webapp/src/components/Help.tsx | 12 +- webapp/src/components/map/map-component.tsx | 3 + .../components/map/map-layer-style-slice.ts | 16 +- webapp/src/components/map/map-slice.tsx | 26 +- webapp/src/icons/bell.tsx | 7 +- webapp/src/icons/chart-bar.tsx | 7 +- webapp/src/icons/chevron-down.tsx | 7 +- webapp/src/icons/chevron-right.tsx | 7 +- webapp/src/icons/clock.tsx | 7 +- webapp/src/icons/cog.tsx | 7 +- webapp/src/icons/download.tsx | 7 +- webapp/src/icons/facebook.tsx | 7 +- webapp/src/icons/google.tsx | 7 +- webapp/src/icons/lock.tsx | 7 +- webapp/src/icons/menu.tsx | 7 +- webapp/src/icons/pencil-alt.tsx | 7 +- webapp/src/icons/plus.tsx | 7 +- webapp/src/icons/refresh.tsx | 7 +- webapp/src/icons/search.tsx | 7 +- webapp/src/icons/selector.tsx | 7 +- webapp/src/icons/shopping-bag.tsx | 7 +- webapp/src/icons/upload.tsx | 7 +- webapp/src/icons/user-add.tsx | 7 +- webapp/src/icons/user-circle.tsx | 7 +- webapp/src/icons/user.tsx | 13 +- webapp/src/icons/users.tsx | 7 +- webapp/src/icons/x-circle.tsx | 7 +- .../notifications/Notification.d.ts | 26 +- webapp/src/pages/IntersectionMapView.tsx | 251 ++++++++++++++++++ webapp/yarn.lock | 19 +- 33 files changed, 449 insertions(+), 104 deletions(-) diff --git a/webapp/package-lock.json b/webapp/package-lock.json index 85a0ac8c7..4bd7f1e7a 100644 --- a/webapp/package-lock.json +++ b/webapp/package-lock.json @@ -43,6 +43,7 @@ "react-confirm-alert": "^2.8.0", "react-dom": "^17.0.2", "react-hook-form": "^7.41.5", + "react-hot-toast": "^2.4.1", "react-icons": "^4.3.1", "react-map-gl": "^7.0.21", "react-redux": "^8.0.5", @@ -21298,6 +21299,35 @@ "react": "^16.8.0 || ^17 || ^18" } }, + "node_modules/react-hot-toast": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/react-hot-toast/-/react-hot-toast-2.4.1.tgz", + "integrity": "sha512-j8z+cQbWIM5LY37pR6uZR6D4LfseplqnuAO4co4u8917hBUvXlEqyP1ZzqVLcqoyUesZZv/ImreoCeHVDpE5pQ==", + "dependencies": { + "goober": "^2.1.10" + }, + "engines": { + "node": ">=10" + }, + "peerDependencies": { + "react": ">=16", + "react-dom": ">=16" + } + }, + "node_modules/react-hot-toast/node_modules/csstype": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.3.tgz", + "integrity": "sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==", + "peer": true + }, + "node_modules/react-hot-toast/node_modules/goober": { + "version": "2.1.14", + "resolved": "https://registry.npmjs.org/goober/-/goober-2.1.14.tgz", + "integrity": "sha512-4UpC0NdGyAFqLNPnhCT2iHpza2q+RAY3GV85a/mRPdzyPQMsj0KmMMuetdIkzWRbJ+Hgau1EZztq8ImmiMGhsg==", + "peerDependencies": { + "csstype": "^3.0.10" + } + }, "node_modules/react-icons": { "version": "4.3.1", "resolved": "https://registry.npmjs.org/react-icons/-/react-icons-4.3.1.tgz", diff --git a/webapp/package.json b/webapp/package.json index 1f096a8ca..1745d14a8 100644 --- a/webapp/package.json +++ b/webapp/package.json @@ -38,6 +38,7 @@ "react-confirm-alert": "^2.8.0", "react-dom": "^17.0.2", "react-hook-form": "^7.41.5", + "react-hot-toast": "^2.4.1", "react-icons": "^4.3.1", "react-map-gl": "^7.0.21", "react-redux": "^8.0.5", diff --git a/webapp/src/components/Header.tsx b/webapp/src/components/Header.tsx index 5aed8227f..b79da6cdc 100644 --- a/webapp/src/components/Header.tsx +++ b/webapp/src/components/Header.tsx @@ -1,6 +1,6 @@ import React, { useEffect } from 'react' import Grid from '@material-ui/core/Grid' -import logo from '../images/logo.png' +import logo from '../icons/logo.png' import { useSelector, useDispatch } from 'react-redux' import EnvironmentVars from '../EnvironmentVars' import { diff --git a/webapp/src/components/Help.tsx b/webapp/src/components/Help.tsx index 1660d18b2..97baf117a 100644 --- a/webapp/src/components/Help.tsx +++ b/webapp/src/components/Help.tsx @@ -1,11 +1,11 @@ import React from 'react' import '../components/css/Help.css' -import popup from '../images/rsu_popup.PNG' -import status from '../images/rsu_status.PNG' -import table from '../images/rsu_count.PNG' -import menu from '../images/rsu_menu.PNG' -import heatmap from '../images/rsu_heatmap.PNG' -import configure from '../images/rsu_configure.PNG' +import popup from '../icons/rsu_popup.PNG' +import status from '../icons/rsu_status.PNG' +import table from '../icons/rsu_count.PNG' +import menu from '../icons/rsu_menu.PNG' +import heatmap from '../icons/rsu_heatmap.PNG' +import configure from '../icons/rsu_configure.PNG' import EnvironmentVars from '../EnvironmentVars' const Help = () => { diff --git a/webapp/src/components/map/map-component.tsx b/webapp/src/components/map/map-component.tsx index 7fc458f37..cb62b8f4c 100644 --- a/webapp/src/components/map/map-component.tsx +++ b/webapp/src/components/map/map-component.tsx @@ -362,6 +362,9 @@ const MapTab = (props: MAP_PROPS) => { > + {/* + + */} diff --git a/webapp/src/components/map/map-layer-style-slice.ts b/webapp/src/components/map/map-layer-style-slice.ts index b627b92de..7555471e4 100644 --- a/webapp/src/components/map/map-layer-style-slice.ts +++ b/webapp/src/components/map/map-layer-style-slice.ts @@ -133,6 +133,17 @@ const connectingLanesLabelsLayer: SymbolLayer = { }, } +const srmLayer: CircleLayer = { + id: 'srmMarker', + type: 'circle', + source: 'srmData', + minzoom: 12, + paint: { + 'circle-radius': 8, + 'circle-color': 'rgb(14, 32, 82)', + }, +} + const markerLayer: LineLayer = { id: 'invalid-lane-collection', type: 'line', @@ -232,6 +243,7 @@ export const initialState = { mapMessageLabelsLayerStyle: { ...mapMessageLabelsLayer, source: 'string' }, connectingLanesLayerStyle: { ...connectingLanesLayer, source: 'string' }, connectingLanesLabelsLayerStyle: { ...connectingLanesLabelsLayer, source: 'string' }, + srmLayerStyle: { ...srmLayer, source: 'string' }, markerLayerStyle: { ...markerLayer, source: 'string' }, bsmLayerStyle: { ...bsmLayerStyle, source: 'string' }, signalStateLayerStyle: { ...signalStateLayer, source: 'string' }, @@ -258,9 +270,6 @@ export const intersectionMapLayerStyleSlice = createSlice({ state.value.signalStateLayerStyle = { ...state.value.signalStateLayerStyle, layout: action.payload } }, }, - extraReducers: (builder) => { - builder - }, }) export const selectMapMessageLayerStyle = (state: RootState) => @@ -271,6 +280,7 @@ export const selectConnectingLanesLayerStyle = (state: RootState) => state.intersectionMapLayerStyle.value.connectingLanesLayerStyle export const selectConnectingLanesLabelsLayerStyle = (state: RootState) => state.intersectionMapLayerStyle.value.connectingLanesLabelsLayerStyle +export const selectSrmLayerStyle = (state: RootState) => state.intersectionMapLayerStyle.value.srmLayerStyle export const selectMarkerLayerStyle = (state: RootState) => state.intersectionMapLayerStyle.value.markerLayerStyle export const selectBsmLayerStyle = (state: RootState) => state.intersectionMapLayerStyle.value.bsmLayerStyle export const selectSignalStateLayerStyle = (state: RootState) => diff --git a/webapp/src/components/map/map-slice.tsx b/webapp/src/components/map/map-slice.tsx index 8b0ca79af..e2fe0e502 100644 --- a/webapp/src/components/map/map-slice.tsx +++ b/webapp/src/components/map/map-slice.tsx @@ -367,13 +367,6 @@ export const renderEntireMap = createAsyncThunk( if (rsu != null && mapList.includes(rsu.properties.ipv4_address)) { dispatch(getMapData()) dispatch(selectRsu(rsu)) - //rsuMapData: set by getMapData, called here - //selectedRsu: set by selectRsu, called here - //selectedSrm: set by selectSrm, called in rsuMap page - //mapDate: filled by getMapData, called here - //ssmDisplay: filled by toggleSsmSrmDisplay, called in rsuMap page - //rsuIpv4: pulled from selectedRsu, called here - //srmSsmList: set by getSsmSrmData, called in rsuMap page } } return { @@ -845,6 +838,25 @@ export const intersectionMapSlice = createSlice({ value: initialState, }, reducers: { + updateSsmSrmCounts: (state, action: PayloadAction) => { + // let localSrmCount = 0 + // let localSsmCount = 0 + // let localMsgList = [] + // // console.error('srmSsmList', state.value.srmSsmList) + // for (const elem of state.value.srmSsmList) { + // if (elem.ip === state.value.rsuIpv4) { + // localMsgList.push(elem) + // if (elem.type === 'srmTx') { + // localSrmCount += 1 + // } else { + // localSsmCount += 1 + // } + // } + // } + // state.value.srmCount = localSrmCount + // state.value.srmSsmCount = localSsmCount + // state.value.srmMsgList = localMsgList + }, setSurroundingEvents: (state, action: PayloadAction) => { state.value.surroundingEvents = action.payload }, diff --git a/webapp/src/icons/bell.tsx b/webapp/src/icons/bell.tsx index 7421dab20..b88a78f50 100644 --- a/webapp/src/icons/bell.tsx +++ b/webapp/src/icons/bell.tsx @@ -1,8 +1,9 @@ -import { createSvgIcon } from "@mui/material/utils"; +import { createSvgIcon } from '@mui/material/utils' +import React from 'react' export const Bell = createSvgIcon( , - "Bell" -); + 'Bell' +) diff --git a/webapp/src/icons/chart-bar.tsx b/webapp/src/icons/chart-bar.tsx index 85ff86953..9b5c7ffeb 100644 --- a/webapp/src/icons/chart-bar.tsx +++ b/webapp/src/icons/chart-bar.tsx @@ -1,8 +1,9 @@ -import { createSvgIcon } from "@mui/material/utils"; +import { createSvgIcon } from '@mui/material/utils' +import React from 'react' export const ChartBar = createSvgIcon( , - "ChartBar" -); + 'ChartBar' +) diff --git a/webapp/src/icons/chevron-down.tsx b/webapp/src/icons/chevron-down.tsx index 1d391c3c6..40f55e1d6 100644 --- a/webapp/src/icons/chevron-down.tsx +++ b/webapp/src/icons/chevron-down.tsx @@ -1,4 +1,5 @@ -import { createSvgIcon } from "@mui/material/utils"; +import { createSvgIcon } from '@mui/material/utils' +import React from 'react' export const ChevronDown = createSvgIcon( @@ -8,5 +9,5 @@ export const ChevronDown = createSvgIcon( clipRule="evenodd" /> , - "ChevronDown" -); + 'ChevronDown' +) diff --git a/webapp/src/icons/chevron-right.tsx b/webapp/src/icons/chevron-right.tsx index 120005f38..31805fc5d 100644 --- a/webapp/src/icons/chevron-right.tsx +++ b/webapp/src/icons/chevron-right.tsx @@ -1,4 +1,5 @@ -import { createSvgIcon } from "@mui/material/utils"; +import { createSvgIcon } from '@mui/material/utils' +import React from 'react' export const ChevronRight = createSvgIcon( @@ -8,5 +9,5 @@ export const ChevronRight = createSvgIcon( clipRule="evenodd" /> , - "ChevronRight" -); + 'ChevronRight' +) diff --git a/webapp/src/icons/clock.tsx b/webapp/src/icons/clock.tsx index 3810e85ae..0abbc61ac 100644 --- a/webapp/src/icons/clock.tsx +++ b/webapp/src/icons/clock.tsx @@ -1,4 +1,5 @@ -import { createSvgIcon } from "@mui/material/utils"; +import { createSvgIcon } from '@mui/material/utils' +import React from 'react' export const Clock = createSvgIcon( @@ -8,5 +9,5 @@ export const Clock = createSvgIcon( clipRule="evenodd" /> , - "Clock" -); + 'Clock' +) diff --git a/webapp/src/icons/cog.tsx b/webapp/src/icons/cog.tsx index 5af2c1c54..3d2440395 100644 --- a/webapp/src/icons/cog.tsx +++ b/webapp/src/icons/cog.tsx @@ -1,4 +1,5 @@ -import { createSvgIcon } from "@mui/material/utils"; +import { createSvgIcon } from '@mui/material/utils' +import React from 'react' export const Cog = createSvgIcon( @@ -8,5 +9,5 @@ export const Cog = createSvgIcon( clipRule="evenodd" /> , - "Cog" -); + 'Cog' +) diff --git a/webapp/src/icons/download.tsx b/webapp/src/icons/download.tsx index 95176a3cd..7d3ae4f0a 100644 --- a/webapp/src/icons/download.tsx +++ b/webapp/src/icons/download.tsx @@ -1,4 +1,5 @@ -import { createSvgIcon } from "@mui/material/utils"; +import { createSvgIcon } from '@mui/material/utils' +import React from 'react' export const Download = createSvgIcon( @@ -8,5 +9,5 @@ export const Download = createSvgIcon( clipRule="evenodd" /> , - "Download" -); + 'Download' +) diff --git a/webapp/src/icons/facebook.tsx b/webapp/src/icons/facebook.tsx index dbdd247af..d15cf37f3 100644 --- a/webapp/src/icons/facebook.tsx +++ b/webapp/src/icons/facebook.tsx @@ -1,8 +1,9 @@ -import { createSvgIcon } from "@mui/material/utils"; +import { createSvgIcon } from '@mui/material/utils' +import React from 'react' export const Facebook = createSvgIcon( , - "Facebook" -); + 'Facebook' +) diff --git a/webapp/src/icons/google.tsx b/webapp/src/icons/google.tsx index 388f35b5a..df1f945b9 100644 --- a/webapp/src/icons/google.tsx +++ b/webapp/src/icons/google.tsx @@ -1,8 +1,9 @@ -import { createSvgIcon } from "@mui/material/utils"; +import { createSvgIcon } from '@mui/material/utils' +import React from 'react' export const Google = createSvgIcon( , - "Google" -); + 'Google' +) diff --git a/webapp/src/icons/lock.tsx b/webapp/src/icons/lock.tsx index 8f8ac4b72..162de4b0a 100644 --- a/webapp/src/icons/lock.tsx +++ b/webapp/src/icons/lock.tsx @@ -1,4 +1,5 @@ -import { createSvgIcon } from "@mui/material/utils"; +import { createSvgIcon } from '@mui/material/utils' +import React from 'react' export const Lock = createSvgIcon( @@ -8,5 +9,5 @@ export const Lock = createSvgIcon( clipRule="evenodd" /> , - "Lock" -); + 'Lock' +) diff --git a/webapp/src/icons/menu.tsx b/webapp/src/icons/menu.tsx index 733d98599..715d91480 100644 --- a/webapp/src/icons/menu.tsx +++ b/webapp/src/icons/menu.tsx @@ -1,4 +1,5 @@ -import { createSvgIcon } from "@mui/material/utils"; +import { createSvgIcon } from '@mui/material/utils' +import React from 'react' export const Menu = createSvgIcon( @@ -8,5 +9,5 @@ export const Menu = createSvgIcon( clipRule="evenodd" /> , - "Menu" -); + 'Menu' +) diff --git a/webapp/src/icons/pencil-alt.tsx b/webapp/src/icons/pencil-alt.tsx index 5a48388ff..1481b8bb1 100644 --- a/webapp/src/icons/pencil-alt.tsx +++ b/webapp/src/icons/pencil-alt.tsx @@ -1,4 +1,5 @@ -import { createSvgIcon } from "@mui/material/utils"; +import { createSvgIcon } from '@mui/material/utils' +import React from 'react' export const PencilAlt = createSvgIcon( @@ -9,5 +10,5 @@ export const PencilAlt = createSvgIcon( clipRule="evenodd" /> , - "PencilAlt" -); + 'PencilAlt' +) diff --git a/webapp/src/icons/plus.tsx b/webapp/src/icons/plus.tsx index 69feeaeb1..eb30309de 100644 --- a/webapp/src/icons/plus.tsx +++ b/webapp/src/icons/plus.tsx @@ -1,4 +1,5 @@ -import { createSvgIcon } from '@mui/material/utils'; +import { createSvgIcon } from '@mui/material/utils' +import React from 'react' export const Plus = createSvgIcon( @@ -7,4 +8,6 @@ export const Plus = createSvgIcon( d="M10 5a1 1 0 011 1v3h3a1 1 0 110 2h-3v3a1 1 0 11-2 0v-3H6a1 1 0 110-2h3V6a1 1 0 011-1z" clipRule="evenodd" /> - , 'Plus'); + , + 'Plus' +) diff --git a/webapp/src/icons/refresh.tsx b/webapp/src/icons/refresh.tsx index a1fdb62c2..414b7d56d 100644 --- a/webapp/src/icons/refresh.tsx +++ b/webapp/src/icons/refresh.tsx @@ -1,4 +1,5 @@ -import { createSvgIcon } from "@mui/material/utils"; +import { createSvgIcon } from '@mui/material/utils' +import React from 'react' export const Refresh = createSvgIcon( @@ -8,5 +9,5 @@ export const Refresh = createSvgIcon( clipRule="evenodd" /> , - "Refresh" -); + 'Refresh' +) diff --git a/webapp/src/icons/search.tsx b/webapp/src/icons/search.tsx index f2591d51c..b06ca1af1 100644 --- a/webapp/src/icons/search.tsx +++ b/webapp/src/icons/search.tsx @@ -1,4 +1,5 @@ -import { createSvgIcon } from "@mui/material/utils"; +import { createSvgIcon } from '@mui/material/utils' +import React from 'react' export const Search = createSvgIcon( @@ -8,5 +9,5 @@ export const Search = createSvgIcon( clipRule="evenodd" /> , - "Search" -); + 'Search' +) diff --git a/webapp/src/icons/selector.tsx b/webapp/src/icons/selector.tsx index f49ff0224..10adbf9a0 100644 --- a/webapp/src/icons/selector.tsx +++ b/webapp/src/icons/selector.tsx @@ -1,4 +1,5 @@ -import { createSvgIcon } from "@mui/material/utils"; +import { createSvgIcon } from '@mui/material/utils' +import React from 'react' export const Selector = createSvgIcon( @@ -8,5 +9,5 @@ export const Selector = createSvgIcon( d="M4 0C4.26519 5.66374e-05 4.51951 0.105451 4.707 0.293L7.707 3.293C7.88916 3.4816 7.98995 3.7342 7.98767 3.9964C7.9854 4.2586 7.88023 4.50941 7.69482 4.69482C7.50941 4.88023 7.2586 4.9854 6.9964 4.98767C6.7342 4.98995 6.4816 4.88916 6.293 4.707L4 2.414L1.707 4.707C1.5184 4.88916 1.2658 4.98995 1.0036 4.98767C0.741402 4.9854 0.49059 4.88023 0.305182 4.69482C0.119773 4.50941 0.0146042 4.2586 0.0123258 3.9964C0.0100473 3.7342 0.110842 3.4816 0.293 3.293L3.293 0.293C3.48049 0.105451 3.73481 5.66374e-05 4 0ZM0.293 9.293C0.480528 9.10553 0.734836 9.00021 1 9.00021C1.26516 9.00021 1.51947 9.10553 1.707 9.293L4 11.586L6.293 9.293C6.4816 9.11084 6.7342 9.01005 6.9964 9.01233C7.2586 9.0146 7.50941 9.11977 7.69482 9.30518C7.88023 9.49059 7.9854 9.7414 7.98767 10.0036C7.98995 10.2658 7.88916 10.5184 7.707 10.707L4.707 13.707C4.51947 13.8945 4.26516 13.9998 4 13.9998C3.73484 13.9998 3.48053 13.8945 3.293 13.707L0.293 10.707C0.105529 10.5195 0.000213623 10.2652 0.000213623 10C0.000213623 9.73484 0.105529 9.48053 0.293 9.293Z" /> , - "ChartSquareBar" -); + 'ChartSquareBar' +) diff --git a/webapp/src/icons/shopping-bag.tsx b/webapp/src/icons/shopping-bag.tsx index aabfa1d32..10ee4ede5 100644 --- a/webapp/src/icons/shopping-bag.tsx +++ b/webapp/src/icons/shopping-bag.tsx @@ -1,4 +1,5 @@ -import { createSvgIcon } from "@mui/material/utils"; +import { createSvgIcon } from '@mui/material/utils' +import React from 'react' export const ShoppingBag = createSvgIcon( @@ -8,5 +9,5 @@ export const ShoppingBag = createSvgIcon( clipRule="evenodd" /> , - "ShoppingBag" -); + 'ShoppingBag' +) diff --git a/webapp/src/icons/upload.tsx b/webapp/src/icons/upload.tsx index d0a259818..86f9ababa 100644 --- a/webapp/src/icons/upload.tsx +++ b/webapp/src/icons/upload.tsx @@ -1,4 +1,5 @@ -import { createSvgIcon } from "@mui/material/utils"; +import { createSvgIcon } from '@mui/material/utils' +import React from 'react' export const Upload = createSvgIcon( @@ -8,5 +9,5 @@ export const Upload = createSvgIcon( clipRule="evenodd" /> , - "Upload" -); + 'Upload' +) diff --git a/webapp/src/icons/user-add.tsx b/webapp/src/icons/user-add.tsx index 8cec0fc1c..15cab5b0c 100644 --- a/webapp/src/icons/user-add.tsx +++ b/webapp/src/icons/user-add.tsx @@ -1,8 +1,9 @@ -import { createSvgIcon } from "@mui/material/utils"; +import { createSvgIcon } from '@mui/material/utils' +import React from 'react' export const UserAdd = createSvgIcon( , - "UserAdd" -); + 'UserAdd' +) diff --git a/webapp/src/icons/user-circle.tsx b/webapp/src/icons/user-circle.tsx index 02708e937..58e967c0f 100644 --- a/webapp/src/icons/user-circle.tsx +++ b/webapp/src/icons/user-circle.tsx @@ -1,4 +1,5 @@ -import { createSvgIcon } from "@mui/material/utils"; +import { createSvgIcon } from '@mui/material/utils' +import React from 'react' export const UserCircle = createSvgIcon( @@ -8,5 +9,5 @@ export const UserCircle = createSvgIcon( d="M19.5999 9.99999C19.5999 12.5461 18.5885 14.9879 16.7881 16.7882C14.9878 18.5886 12.546 19.6 9.9999 19.6C7.45382 19.6 5.01203 18.5886 3.21168 16.7882C1.41133 14.9879 0.399902 12.5461 0.399902 9.99999C0.399902 7.45392 1.41133 5.01212 3.21168 3.21177C5.01203 1.41142 7.45382 0.399994 9.9999 0.399994C12.546 0.399994 14.9878 1.41142 16.7881 3.21177C18.5885 5.01212 19.5999 7.45392 19.5999 9.99999ZM12.3999 6.39999C12.3999 7.03651 12.147 7.64696 11.697 8.09705C11.2469 8.54714 10.6364 8.79999 9.9999 8.79999C9.36338 8.79999 8.75293 8.54714 8.30285 8.09705C7.85276 7.64696 7.5999 7.03651 7.5999 6.39999C7.5999 5.76347 7.85276 5.15302 8.30285 4.70294C8.75293 4.25285 9.36338 3.99999 9.9999 3.99999C10.6364 3.99999 11.2469 4.25285 11.697 4.70294C12.147 5.15302 12.3999 5.76347 12.3999 6.39999ZM9.9999 11.2C8.85102 11.1998 7.72623 11.5294 6.7592 12.1497C5.79217 12.77 5.02355 13.6549 4.5447 14.6992C5.21991 15.4847 6.05703 16.1149 6.99864 16.5466C7.94024 16.9782 8.96407 17.2011 9.9999 17.2C11.0357 17.2011 12.0596 16.9782 13.0012 16.5466C13.9428 16.1149 14.7799 15.4847 15.4551 14.6992C14.9762 13.6549 14.2076 12.77 13.2406 12.1497C12.2736 11.5294 11.1488 11.1998 9.9999 11.2Z" /> , - "UserCircle" -); + 'UserCircle' +) diff --git a/webapp/src/icons/user.tsx b/webapp/src/icons/user.tsx index 308092fff..c9ebae91c 100644 --- a/webapp/src/icons/user.tsx +++ b/webapp/src/icons/user.tsx @@ -1,12 +1,9 @@ -import { createSvgIcon } from "@mui/material/utils"; +import { createSvgIcon } from '@mui/material/utils' +import React from 'react' export const User = createSvgIcon( - + , - "User" -); + 'User' +) diff --git a/webapp/src/icons/users.tsx b/webapp/src/icons/users.tsx index 04f01fa30..af6cd09a6 100644 --- a/webapp/src/icons/users.tsx +++ b/webapp/src/icons/users.tsx @@ -1,8 +1,9 @@ -import { createSvgIcon } from "@mui/material/utils"; +import { createSvgIcon } from '@mui/material/utils' +import React from 'react' export const Users = createSvgIcon( , - "Users" -); + 'Users' +) diff --git a/webapp/src/icons/x-circle.tsx b/webapp/src/icons/x-circle.tsx index 11f8900aa..fdf8ce579 100644 --- a/webapp/src/icons/x-circle.tsx +++ b/webapp/src/icons/x-circle.tsx @@ -1,4 +1,5 @@ -import { createSvgIcon } from "@mui/material/utils"; +import { createSvgIcon } from '@mui/material/utils' +import React from 'react' export const XCircle = createSvgIcon( @@ -8,5 +9,5 @@ export const XCircle = createSvgIcon( clipRule="evenodd" /> , - "XCircle" -); + 'XCircle' +) diff --git a/webapp/src/models/jpo-conflictmonitor/notifications/Notification.d.ts b/webapp/src/models/jpo-conflictmonitor/notifications/Notification.d.ts index a9be8e724..56fb6e45e 100644 --- a/webapp/src/models/jpo-conflictmonitor/notifications/Notification.d.ts +++ b/webapp/src/models/jpo-conflictmonitor/notifications/Notification.d.ts @@ -1,15 +1,15 @@ declare namespace MessageMonitor { -type Notification = { - logger: Logger - id: str - key: str - notificationGeneratedAt: number - notificationType: str - notificationText: str - notificationHeading: str - intersectionID: number - roadRegulatorID: number - notificationExpiresAt: Date - getUniqueId: str + type Notification = { + logger: any + id: string + key: string + notificationGeneratedAt: number + notificationType: string + notificationText: string + notificationHeading: string + intersectionID: number + roadRegulatorID: number + notificationExpiresAt: Date + getUniqueId: string + } } -} \ No newline at end of file diff --git a/webapp/src/pages/IntersectionMapView.tsx b/webapp/src/pages/IntersectionMapView.tsx index e69de29bb..ad1f67fa3 100644 --- a/webapp/src/pages/IntersectionMapView.tsx +++ b/webapp/src/pages/IntersectionMapView.tsx @@ -0,0 +1,251 @@ +import React, { useEffect, useState } from 'react' +import Map, { Source, Layer, LineLayer, CircleLayer } from 'react-map-gl' +import { Container } from 'reactstrap' +import EnvironmentVars from '../EnvironmentVars' +import './css/RsuMapView.css' +import SsmSrmItem from '../components/SsmSrmItem' +import { useSelector, useDispatch } from 'react-redux' +import { + selectRsuMapData, + selectSelectedRsu, + selectSelectedSrm, + selectMapDate, + selectSsmDisplay, + selectRsuIpv4, + selectSrmSsmList, + + // actions + toggleMapDisplay, + toggleSsmSrmDisplay, + getSsmSrmData, + setSelectedSrm, +} from '../generalSlices/rsuSlice' +import { AnyAction, ThunkDispatch } from '@reduxjs/toolkit' +import { RootState } from '../store' + +interface RsuMapViewProps { + auth: boolean +} + +function RsuMapView(props: RsuMapViewProps) { + const dispatch: ThunkDispatch = useDispatch() + + const rsuMapData = useSelector(selectRsuMapData) + const selectedRsu = useSelector(selectSelectedRsu) + const selectedSrm = useSelector(selectSelectedSrm) + const mapDate = useSelector(selectMapDate) + const ssmDisplay = useSelector(selectSsmDisplay) + const rsuIpv4 = useSelector(selectRsuIpv4) + const srmSsmList = useSelector(selectSrmSsmList) + + const [srmCount, setSrmCount] = useState(0) + const [ssmCount, setSsmCount] = useState(0) + const [msgList, setMsgList] = useState([]) + const [egressData, setEgressData] = useState>({ + type: 'FeatureCollection' as 'FeatureCollection', + features: [], + }) + const [ingressData, setIngressData] = useState>({ + type: 'FeatureCollection' as 'FeatureCollection', + features: [], + }) + + useEffect(() => { + dispatch(getSsmSrmData()) + }, [dispatch]) + + useEffect(() => { + let localSrmCount = 0 + let localSsmCount = 0 + let localMsgList = [] + console.error('srmSsmList', srmSsmList) + for (const elem of srmSsmList) { + if (elem.ip === rsuIpv4) { + localMsgList.push(elem) + if (elem.type === 'srmTx') { + localSrmCount += 1 + } else { + localSsmCount += 1 + } + } + } + setSrmCount(localSrmCount) + setSsmCount(localSsmCount) + setMsgList(localMsgList) + }, [srmSsmList, rsuIpv4]) + + useEffect(() => { + const ingressDataFeatures = [] as Array> + const egressDataFeatures = [] as Array> + + Object.entries(rsuMapData?.['features'] ?? []).map((feature) => { + if (feature[1].properties.ingressPath === 'true') { + ingressDataFeatures.push(feature[1]) + } + return null + }) + Object.entries(rsuMapData?.['features'] ?? []).map((feature) => { + if (feature[1].properties.egressPath === 'true') { + egressDataFeatures.push(feature[1]) + } + return null + }) + + setIngressData((prevIngressData) => { + return { ...prevIngressData, features: ingressDataFeatures } + }) + setEgressData((prevEgressData) => { + return { ...prevEgressData, features: egressDataFeatures } + }) + }, [rsuMapData]) + + const srmData: GeoJSON.FeatureCollection = { + type: 'FeatureCollection' as 'FeatureCollection', + features: [], + } + + if (selectedSrm.length > 0) { + srmData.features.push({ + type: 'Feature', + geometry: { + type: 'Point', + coordinates: [selectedSrm[0].long, selectedSrm[0].lat], + }, + properties: {}, + }) + } + + const ingressLayer: LineLayer = { + id: 'ingressLayer', + type: 'line', + minzoom: 14, + source: 'ingressData', + layout: { + 'line-join': 'round', + 'line-cap': 'round', + }, + paint: { + 'line-color': 'rgb(50,205,50)', + 'line-width': 3, + }, + } + + const egressLayer: LineLayer = { + id: 'egressLayer', + type: 'line', + minzoom: 14, + source: 'egressData', + layout: { + 'line-join': 'round', + 'line-cap': 'round', + }, + paint: { + 'line-color': 'rgb(203, 4, 4)', + 'line-width': 3, + }, + } + + const srmLayer: CircleLayer = { + id: 'srmMarker', + type: 'circle', + source: 'srmData', + minzoom: 12, + paint: { + 'circle-radius': 8, + 'circle-color': 'rgb(14, 32, 82)', + }, + } + + const [viewState, setViewState] = useState({ + latitude: selectedRsu.geometry.coordinates[1], + longitude: selectedRsu.geometry.coordinates[0], + zoom: 17, + }) + + return ( +
+ + { + setViewState(evt.viewState) + }} + > + + + + + + + + + + + + +
MAP data from {mapDate}
+ {ssmDisplay ? ( +
+ +

SSM / SRM Data For {rsuIpv4}

+
+

Time

+

Request Id

+

Role

+

Status

+

Display

+
+ {msgList.map((index) => ( + + ))} +

Total Counts

+
+

SSM: {ssmCount}

+

SRM: {srmCount}

+
+
+ ) : ( + + )} +
+
+

Reference

+
+
+
+

- Ingress Lane

+
+
+
+

- Egress Lane

+
+
+
+

- SSM (table)

+
+
+
+

- SRM (table)

+
+
+
+

- SRM (map)

+
+
+
+ ) +} + +export default RsuMapView diff --git a/webapp/yarn.lock b/webapp/yarn.lock index 3e7c4fecd..ce93615da 100644 --- a/webapp/yarn.lock +++ b/webapp/yarn.lock @@ -4918,6 +4918,11 @@ csstype@^2.5.2: resolved "https://registry.npmjs.org/csstype/-/csstype-2.6.17.tgz" integrity sha512-u1wmTI1jJGzCJzWndZo8mk4wnPTZd1eOIYTYvuEyOQGfmDl3TrabCCfKnOC86FZwW/9djqTl933UF/cS425i9A== +csstype@^3.0.10: + version "3.1.3" + resolved "https://registry.npmjs.org/csstype/-/csstype-3.1.3.tgz" + integrity sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw== + csstype@^3.0.2: version "3.1.1" resolved "https://registry.npmjs.org/csstype/-/csstype-3.1.1.tgz" @@ -6543,6 +6548,11 @@ globby@^11.0.4, globby@^11.1.0: merge2 "^1.4.1" slash "^3.0.0" +goober@^2.1.10: + version "2.1.14" + resolved "https://registry.npmjs.org/goober/-/goober-2.1.14.tgz" + integrity sha512-4UpC0NdGyAFqLNPnhCT2iHpza2q+RAY3GV85a/mRPdzyPQMsj0KmMMuetdIkzWRbJ+Hgau1EZztq8ImmiMGhsg== + gopd@^1.0.1: version "1.0.1" resolved "https://registry.npmjs.org/gopd/-/gopd-1.0.1.tgz" @@ -10545,7 +10555,7 @@ react-dev-utils@^12.0.0: strip-ansi "^6.0.1" text-table "^0.2.0" -"react-dom@^16.0.0 || ^17.0.0", "react-dom@^16.8 || ^17.0 || ^18.0", "react-dom@^16.8.0 || ^17 || ^18", "react-dom@^16.8.0 || ^17.0.0", "react-dom@^16.8.0 || ^17.0.0 || ^18.0.0", "react-dom@^16.8.5 || ^17.0.0 || ^18.0.0", "react-dom@^17.0.0 || ^18.0.0", react-dom@^17.0.2, "react-dom@^17.0.2 || ^18.0.0", react-dom@<18.0.0, "react-dom@>= 16.8.0", react-dom@>=0.16.0, react-dom@>=16.0.0, react-dom@>=16.14.0, react-dom@>=16.6.0, react-dom@>=16.8, react-dom@>=16.8.0, react-dom@>=16.9.0: +"react-dom@^16.0.0 || ^17.0.0", "react-dom@^16.8 || ^17.0 || ^18.0", "react-dom@^16.8.0 || ^17 || ^18", "react-dom@^16.8.0 || ^17.0.0", "react-dom@^16.8.0 || ^17.0.0 || ^18.0.0", "react-dom@^16.8.5 || ^17.0.0 || ^18.0.0", "react-dom@^17.0.0 || ^18.0.0", react-dom@^17.0.2, "react-dom@^17.0.2 || ^18.0.0", react-dom@<18.0.0, "react-dom@>= 16.8.0", react-dom@>=0.16.0, react-dom@>=16, react-dom@>=16.0.0, react-dom@>=16.14.0, react-dom@>=16.6.0, react-dom@>=16.8, react-dom@>=16.8.0, react-dom@>=16.9.0: version "17.0.2" resolved "https://registry.npmjs.org/react-dom/-/react-dom-17.0.2.tgz" integrity sha512-s4h96KtLDUQlsENhMn1ar8t2bEa+q/YAtj8pPPdIjPDGBDIVNsrD9aXNWqspUe6AzKCIG0C1HZZLqLV7qpOBGA== @@ -10574,6 +10584,13 @@ react-hook-form@^7.0.0, react-hook-form@^7.41.5: resolved "https://registry.npmjs.org/react-hook-form/-/react-hook-form-7.41.5.tgz" integrity sha512-DAKjSJ7X9f16oQrP3TW2/eD9N6HOgrmIahP4LOdFphEWVfGZ2LulFd6f6AQ/YS/0cx/5oc4j8a1PXxuaurWp/Q== +react-hot-toast@^2.4.1: + version "2.4.1" + resolved "https://registry.npmjs.org/react-hot-toast/-/react-hot-toast-2.4.1.tgz" + integrity sha512-j8z+cQbWIM5LY37pR6uZR6D4LfseplqnuAO4co4u8917hBUvXlEqyP1ZzqVLcqoyUesZZv/ImreoCeHVDpE5pQ== + dependencies: + goober "^2.1.10" + react-icons@^4.3.1: version "4.3.1" resolved "https://registry.npmjs.org/react-icons/-/react-icons-4.3.1.tgz" From 7f43edda18db22303190d492d310a1253ae30ba3 Mon Sep 17 00:00:00 2001 From: jacob6838 Date: Mon, 1 Apr 2024 14:24:13 -0600 Subject: [PATCH 005/189] Updating environment variables, breaking out intersection list/ids into shared slice --- webapp/Dockerfile | 12 + webapp/src/Dashboard.tsx | 5 +- webapp/src/apis/keycloak-api.ts | 4 +- webapp/src/components/map/control-panel.tsx | 60 +++- webapp/src/components/map/map-component.tsx | 2 +- webapp/src/components/map/map-legend.tsx | 12 +- webapp/src/components/map/map-slice.tsx | 10 +- .../src/generalSlices/intersectionSlice.tsx | 86 ++++++ webapp/src/icons/map/intersection_icon.png | Bin 0 -> 36642 bytes .../icons/map/traffic-light-icon-green-1.svg | 93 ++++++ .../icons/map/traffic-light-icon-green.svg | 93 ++++++ ...raffic-light-icon-proceed-with-caution.svg | 180 ++++++++++++ ...traffic-light-icon-protected-clearance.svg | 128 +++++++++ ...-light-icon-protected-movement-allowed.svg | 129 +++++++++ .../icons/map/traffic-light-icon-red-1.svg | 93 ++++++ .../map/traffic-light-icon-red-flashing.svg | 152 ++++++++++ .../src/icons/map/traffic-light-icon-red.svg | 93 ++++++ ...ffic-light-icon-stop-then-proceed-high.svg | 95 +++++++ ...affic-light-icon-stop-then-proceed-low.svg | 180 ++++++++++++ .../icons/map/traffic-light-icon-unknown.svg | 93 ++++++ .../icons/map/traffic-light-icon-yellow-1.svg | 1 + .../map/traffic-light-icon-yellow-red-1.svg | 93 ++++++ .../traffic-light-icon-yellow-red-green.svg | 227 +++++++++++++++ .../map/traffic-light-icon-yellow-red.svg | 93 ++++++ .../icons/map/traffic-light-icon-yellow.svg | 93 ++++++ webapp/src/pages/IntersectionMapView.tsx | 268 ++---------------- webapp/src/store.tsx | 2 + 27 files changed, 2039 insertions(+), 258 deletions(-) create mode 100644 webapp/src/generalSlices/intersectionSlice.tsx create mode 100644 webapp/src/icons/map/intersection_icon.png create mode 100644 webapp/src/icons/map/traffic-light-icon-green-1.svg create mode 100644 webapp/src/icons/map/traffic-light-icon-green.svg create mode 100644 webapp/src/icons/map/traffic-light-icon-proceed-with-caution.svg create mode 100644 webapp/src/icons/map/traffic-light-icon-protected-clearance.svg create mode 100644 webapp/src/icons/map/traffic-light-icon-protected-movement-allowed.svg create mode 100644 webapp/src/icons/map/traffic-light-icon-red-1.svg create mode 100644 webapp/src/icons/map/traffic-light-icon-red-flashing.svg create mode 100644 webapp/src/icons/map/traffic-light-icon-red.svg create mode 100644 webapp/src/icons/map/traffic-light-icon-stop-then-proceed-high.svg create mode 100644 webapp/src/icons/map/traffic-light-icon-stop-then-proceed-low.svg create mode 100644 webapp/src/icons/map/traffic-light-icon-unknown.svg create mode 100644 webapp/src/icons/map/traffic-light-icon-yellow-1.svg create mode 100644 webapp/src/icons/map/traffic-light-icon-yellow-red-1.svg create mode 100644 webapp/src/icons/map/traffic-light-icon-yellow-red-green.svg create mode 100644 webapp/src/icons/map/traffic-light-icon-yellow-red.svg create mode 100644 webapp/src/icons/map/traffic-light-icon-yellow.svg diff --git a/webapp/Dockerfile b/webapp/Dockerfile index 596709251..63d82e9ac 100644 --- a/webapp/Dockerfile +++ b/webapp/Dockerfile @@ -30,6 +30,18 @@ ENV REACT_APP_MAPBOX_INIT_LONGITUDE $MAPBOX_INIT_LONGITUDE ARG MAPBOX_INIT_ZOOM ENV REACT_APP_MAPBOX_INIT_ZOOM $MAPBOX_INIT_ZOOM +ARG CVIZ_API_SERVER_URL +ENV REACT_APP_CVIZ_API_SERVER_URL $CVIZ_API_SERVER_URL + +ARG CVIZ_API_WS_URL +ENV REACT_APP_CVIZ_API_WS_URL $CVIZ_API_WS_URL + +ARG KEYCLOAK_URL +ENV KEYCLOAK_URL $KEYCLOAK_URL + +ARG KEYCLOAK_REALM +ENV KEYCLOAK_REALM $KEYCLOAK_REALM + RUN npm ci RUN npm run build diff --git a/webapp/src/Dashboard.tsx b/webapp/src/Dashboard.tsx index faea444d3..f3375107d 100644 --- a/webapp/src/Dashboard.tsx +++ b/webapp/src/Dashboard.tsx @@ -27,6 +27,7 @@ import { RootState } from './store' import { AnyAction } from '@reduxjs/toolkit' import { Routes, Route, Navigate } from 'react-router-dom' import { NotFoundRedirect } from './pages/404' +import IntersectionMapView from './pages/IntersectionMapView' let loginDispatched = false @@ -79,7 +80,7 @@ const Dashboard = () => { <> - + @@ -96,7 +97,7 @@ const Dashboard = () => { } /> - } /> + } /> } /> } /> } /> diff --git a/webapp/src/apis/keycloak-api.ts b/webapp/src/apis/keycloak-api.ts index b3b694e95..33e2bd03c 100644 --- a/webapp/src/apis/keycloak-api.ts +++ b/webapp/src/apis/keycloak-api.ts @@ -1,7 +1,7 @@ import { authApiHelper } from './api-helper-cviz' -const KEYCLOAK_ADMIN_ENDPOINT = `${process.env.CVIZ_AUTH_SERVER_URL}/admin/realms/${process.env.CVIZ_KEYCLOAK_REALM}` -const KEYCLOAK_AUTH_ENDPOINT = `${process.env.CVIZ_AUTH_SERVER_URL}/realms/${process.env.CVIZ_KEYCLOAK_REALM}` +const KEYCLOAK_ADMIN_ENDPOINT = `${process.env.KEYCLOAK_URL}/admin/realms/${process.env.KEYCLOAK_REALM}` +const KEYCLOAK_AUTH_ENDPOINT = `${process.env.KEYCLOAK_URL}/realms/${process.env.KEYCLOAK_REALM}` class KeycloakApi { getEmailPreferences(attributes: Record): EmailPreferences { diff --git a/webapp/src/components/map/control-panel.tsx b/webapp/src/components/map/control-panel.tsx index 7adbd5343..bc8a36498 100644 --- a/webapp/src/components/map/control-panel.tsx +++ b/webapp/src/components/map/control-panel.tsx @@ -4,7 +4,19 @@ import dayjs from 'dayjs' import { LocalizationProvider } from '@mui/x-date-pickers/LocalizationProvider' import { AdapterDayjs } from '@mui/x-date-pickers/AdapterDayjs' import { DateTimePicker } from '@mui/x-date-pickers/DateTimePicker' -import { Box, Typography, TextField, Button, Checkbox, InputAdornment, Chip } from '@mui/material' +import { + Box, + Typography, + TextField, + Button, + Checkbox, + InputAdornment, + Chip, + FormControl, + InputLabel, + Select, + MenuItem, +} from '@mui/material' import MuiAccordion, { AccordionProps } from '@mui/material/Accordion' import MuiAccordionSummary, { AccordionSummaryProps } from '@mui/material/AccordionSummary' import MuiAccordionDetails from '@mui/material/AccordionDetails' @@ -16,16 +28,21 @@ import { AnyAction, ThunkDispatch } from '@reduxjs/toolkit' import { RootState } from '../../store' import { useDispatch, useSelector } from 'react-redux' import { + MAP_PROPS, + MAP_PROPS_SOURCE_API, downloadMapData, handleImportedMapMessageData, onTimeQueryChanged, selectBsmTrailLength, + selectIntersectionId, selectSliderTimeValue, + selectSourceApi, setBsmTrailLength, setLaneLabelsVisible, setShowPopupOnHover, setSigGroupLabelsVisible, setSliderValue, + setSourceApi, toggleLiveDataActive, } from './map-slice' import { @@ -42,7 +59,6 @@ import { selectFilteredSurroundingNotifications, selectHoveredFeature, selectImportedMessageData, - selectIntersectionId, selectLaneLabelsVisible, selectLayersVisible, selectLiveDataActive, @@ -70,6 +86,7 @@ import { import { selectToken } from '../../generalSlices/userSlice' import { selectSignalStateLayerStyle, setSignalLayerLayout } from './map-layer-style-slice' import { getTimeRange } from './utilities/map-utils' +import { selectIntersections, setSelectedIntersection } from '../../generalSlices/intersectionSlice' const Accordion = styled((props: AccordionProps) => )( ({ theme }) => ({ @@ -152,6 +169,9 @@ function ControlPanel() { const liveDataActive = useSelector(selectLiveDataActive) const sliderTimeValue = useSelector(selectSliderTimeValue) const bsmTrailLength = useSelector(selectBsmTrailLength) + const dataSourceApi = useSelector(selectSourceApi) + const selectedIntersection = useSelector(selectIntersectionId) + const intersectionsList = useSelector(selectIntersections) const getQueryParams = ({ startDate, @@ -380,6 +400,42 @@ function ControlPanel() { padding: '10px 10px 10px 10px', }} > + + + Data Sources + + + + + Data Source + + + + Intersection + + + + + diff --git a/webapp/src/components/map/map-component.tsx b/webapp/src/components/map/map-component.tsx index cb62b8f4c..6be76b7db 100644 --- a/webapp/src/components/map/map-component.tsx +++ b/webapp/src/components/map/map-component.tsx @@ -309,7 +309,7 @@ const MapTab = (props: MAP_PROPS) => { {...viewState} ref={mapRef} onLoad={() => {}} - mapStyle={process.env.MAPBOX_STYLE_URL!} + mapStyle={process.env.CVIZ_MAPBOX_STYLE_URL!} mapboxAccessToken={MAPBOX_API_TOKEN} attributionControl={true} customAttribution={['© CDOT']} diff --git a/webapp/src/components/map/map-legend.tsx b/webapp/src/components/map/map-legend.tsx index 07a24b2b0..908679553 100644 --- a/webapp/src/components/map/map-legend.tsx +++ b/webapp/src/components/map/map-legend.tsx @@ -17,12 +17,12 @@ import MuiAccordionSummary, { AccordionSummaryProps } from '@mui/material/Accord import MuiAccordionDetails from '@mui/material/AccordionDetails' import ArrowForwardIosSharpIcon from '@mui/icons-material/ArrowForwardIosSharp' import { styled } from '@mui/material/styles' -import trafficLightIconUnknown from '../../../public/icons/traffic-light-icon-unknown.svg' -import trafficLightIconRedFlashing from '../../../public/icons/traffic-light-icon-red-flashing.svg' -import trafficLightIconRed1 from '../../../public/icons/traffic-light-icon-red-1.svg' -import trafficLightIconYellowRed1 from '../../../public/icons/traffic-light-icon-yellow-red-1.svg' -import trafficLightIconYellow1 from '../../../public/icons/traffic-light-icon-yellow-1.svg' -import trafficLightIconGreen1 from '../../../public/icons/traffic-light-icon-green-1.svg' +import trafficLightIconUnknown from '../../icons/map/traffic-light-icon-unknown.svg' +import trafficLightIconRedFlashing from '../../icons/map/traffic-light-icon-red-flashing.svg' +import trafficLightIconRed1 from '../../icons/map/traffic-light-icon-red-1.svg' +import trafficLightIconYellowRed1 from '../../icons/map/traffic-light-icon-yellow-red-1.svg' +import trafficLightIconYellow1 from '../../icons/map/traffic-light-icon-yellow-1.svg' +import trafficLightIconGreen1 from '../../icons/map/traffic-light-icon-green-1.svg' import { selectMapLegendColors } from './map-layer-style-slice' import { useSelector } from 'react-redux' diff --git a/webapp/src/components/map/map-slice.tsx b/webapp/src/components/map/map-slice.tsx index e2fe0e502..a304f8b0f 100644 --- a/webapp/src/components/map/map-slice.tsx +++ b/webapp/src/components/map/map-slice.tsx @@ -63,8 +63,10 @@ type timestamp = { timestamp: number } +export const MAP_PROPS_SOURCE_API = ['conflictvisualizer', 'cvmanager'] as const + export type MAP_PROPS = { - sourceApi: 'conflictvisualizer' | 'cvmanager' + sourceApi: (typeof MAP_PROPS_SOURCE_API)[number] sourceData: | MessageMonitor.Notification | MessageMonitor.Event @@ -1087,10 +1089,11 @@ export const intersectionMapSlice = createSlice({ setMapProps: (state, action: PayloadAction) => { state.value.sourceData = action.payload.sourceData state.value.sourceDataType = action.payload.sourceDataType - state.value.intersectionId = action.payload.intersectionId - state.value.roadRegulatorId = action.payload.roadRegulatorId state.value.loadOnNull = action.payload.loadOnNull }, + setSourceApi: (state, action: PayloadAction) => { + state.value.sourceApi = action.payload + }, }, extraReducers: (builder) => { builder @@ -1309,6 +1312,7 @@ export const { setBsmTrailLength, setRawData, setMapProps, + setSourceApi, } = intersectionMapSlice.actions export default intersectionMapSlice.reducer diff --git a/webapp/src/generalSlices/intersectionSlice.tsx b/webapp/src/generalSlices/intersectionSlice.tsx new file mode 100644 index 000000000..985beeca8 --- /dev/null +++ b/webapp/src/generalSlices/intersectionSlice.tsx @@ -0,0 +1,86 @@ +import { createAsyncThunk, createSlice, PayloadAction, ThunkDispatch } from '@reduxjs/toolkit' +import { RootState } from '../store' +import MessageMonitorApi from '../apis/mm-api' +import { selectToken } from './userSlice' + +export const initialState = { + intersections: [] as IntersectionReferenceData[], + selectedIntersection: null as IntersectionReferenceData | null, + selectedRoadRegulatorId: -1, + selectedIntersectionId: -1, +} + +export const getIntersections = createAsyncThunk( + 'intersection/getIntersections', + async (_, { getState }) => { + const currentState = getState() as RootState + const authToken = selectToken(currentState)! + + const intersections = await MessageMonitorApi.getIntersections({ token: authToken }) + intersections.push({ + intersectionID: -1, + roadRegulatorID: -1, + rsuIP: '0.0.0.0', + latitude: 0, + longitude: 0, + }) + return intersections + }, + { + condition: (_, { getState }) => selectToken(getState() as RootState) != undefined, + } +) + +export const intersectionSlice = createSlice({ + name: 'intersection', + initialState: { + loading: false, + value: initialState, + }, + reducers: { + setSelectedIntersection: (state, action: PayloadAction) => { + const intersection = state.value.intersections.find((i) => i.intersectionID === action.payload) + if (intersection) { + state.value.selectedIntersection = intersection + state.value.selectedIntersectionId = action.payload + } else { + console.error('Intersection ' + action.payload + ' not found in list:', state.value.intersections) + } + }, + setSelectedIntersectionId: (state, action: PayloadAction) => { + state.value.selectedIntersectionId = action.payload + }, + setSelectedRoadRegulatorId: (state, action: PayloadAction) => { + state.value.selectedRoadRegulatorId = action.payload + }, + setIntersectionManual: (state, action: PayloadAction) => { + state.value.intersections = [action.payload] + state.value.selectedIntersection = action.payload + state.value.selectedIntersectionId = action.payload[0].intersectionID + state.value.selectedRoadRegulatorId = action.payload[0].roadRegulatorID + }, + }, + extraReducers: (builder) => { + builder + .addCase(getIntersections.pending, (state) => { + state.loading = true + }) + .addCase(getIntersections.fulfilled, (state, action: PayloadAction) => { + state.value.intersections = action.payload + state.loading = false + }) + .addCase(getIntersections.rejected, (state) => { + state.loading = false + }) + }, +}) + +export const selectIntersections = (state: RootState) => state.intersection.value.intersections +export const selectSelectedIntersection = (state: RootState) => state.intersection.value.selectedIntersection +export const selectSelectedIntersectionId = (state: RootState) => state.intersection.value.selectedIntersectionId +export const selectSelectedRoadRegulatorId = (state: RootState) => state.intersection.value.selectedRoadRegulatorId + +export const { setSelectedIntersection, setSelectedIntersectionId, setSelectedRoadRegulatorId } = + intersectionSlice.actions + +export default intersectionSlice.reducer diff --git a/webapp/src/icons/map/intersection_icon.png b/webapp/src/icons/map/intersection_icon.png new file mode 100644 index 0000000000000000000000000000000000000000..9f6d43faa8e92b7cfd307786f3fc08e2f80e7f0b GIT binary patch literal 36642 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≠x24H?dP886W_`hzbah7(6}u3`U@ zfK{}9AnTRoOF>9hxvCn2;CwM0q_4-<_sgj3Y`_b;YGD8cmk%DD1AVkYr&y8=APYRZ0+n-`wY;v^!t zAU}+#-c9TyWH~Jp)jQqPI7?vOR`UqIUH%rY&(#5PFZ`Bl08twm!PH>Vbe$958&Cv@ z19y|d(1T#hASMuN$Vf`a(ATal?JEQ2qrKiW`IT4d$y+{dvJvwO>yRygXaL~M)|luw z@!x~Gh{SP_<~275zYF9)FcmklzB(l3iL;T}&hCxtASc3xIPy$aaE0B8GI}_BDdbxc zJ!U%iWYQkKI3)j3T^!iS)C&c-;$&#RU-#KdAc44kwet74-r6ezjFHbLqGk|PuvBaP zaO^rIjms%A0y%_L?YmFTprIJ>dIkRqi6~g>40uk6G&G$v*4(Gz5H{lurek>XQ-P62 zPX~D6j|2uH;AK+30X7hI_(DQ5NH4A}bW}xxZ>99#F^hC|GB1Wo+dF$#8aw4Qe@x&Y zz+Bh9qfvgizl~7-k|$?B#nO7m)9`_sf(Iy~(MHdbQMjY`fkEtnZXxFwy1`yplXp%o zEw`yT%F1)3&V+ar2}N+?QrRCbyuOFxquS)M$d&T_C))#X+#V7Q{wHQJ1b`pGfRB8hjUON*$8yT=*EK=NRQjJ<0|E`m z=I@Mcdn^6-MyLaQVJjwQNb)r5JL`TuEEA8yJ|m?kqSc~mFT=ch0$t+2>IVW{_=`Xk zy9k@%P#!_Z2G=Xe5t2Vi2z5qKk$DwSx(&JN03wjE5!Ez*rp}ICw+ToQnR>uIBAs;2Pt5HZn@DN2cNrb z&l9)?8X69v$8<_SQOG~qt@N8Z-wF}YfFOSnuCM?!k&zkF{6G+f$OaPrx+1=514nMl2Kscn0qc5~H8p)Cd zXaPV;$*k|n@eFj!mPQ?26H2sZ^MgBUK;-lhG;njD0?p3=lFXys*EAmoz4E^H3OO4q z`_(||oc1M@!IRp|B&th0mwd7EC^OHGP z6}0ta*i=6&XAkg@FWe%Y#H<+Ilk^v4NE@>_+MTpVa_>4sK5|b3fTSc5E}htq`ET?o zC->84usRfh#ZV$v5!Bd^Z2taP?mHXM#yg5bN>ni&Dk`56XDJc013Fd%>&~iC{*%+I z3qyM(a=>c}3_!xzHiYE-B6q6-pX^WFYE#`S8it_dvZYb$Vz(yPPfAN3FF5xekt3gT z*RxhE(_s|p`IGHiwmp5f)cD5F>0*H2ia?%+U@CO_^u{7=YUZvfhCeTBJd<)$xKdsS z|9_dBz^;PAr~F%xGKpi`_-FH*F$q)#aNoHbV*Y6{N3wpD7)gd=Ou@~!P$YYWqC?9- zqJyg&B>G)XnPsLXZhM9F`FVH#;p>~T*lvjSKuYEN*-J~b50bzUBO~N%Y z3HVclds|&M94mOu?WEp&j0dC^{|PetdGa;Y$b5M`0N#crSS5U9GEI&oX2YYC)mm$<;8w z{Bh~~%N*QxoA+7aVVZgkkg|Ve_?>AlUY^`}Fo6d6pv+x8rcmC{@R`2IEki$ac>$Lf z6vv(sD7ptSWCH7&5!X*+vl}noI)_Da=$t-=@y~x>g4SZyU|&_Fm=g&x$&}|$6=e)= zlC>T971OAo70#dF9p+1K-6X8wczm; z(4G2%QC$ms3!cQP*%QWH_D&=bPv2^Rr^QJ9K4yrD1!5YD@TlGS^nNR6zZBf9egf@m z_sl+`toglX0Y{cd0OiutR_JdR3Pe?1_;Jrlx7g;?Bosa~MO|76F&eleAdp5Rp8SJ_ zBc5iewbNl1lxA5e=LbOmxl9yw&trw(Ea!)LXU3nbg4DYRv`BD{JF2=TLr_Jf`S0w(gCo)*s zHgXG-aOrQZh6Q-Se-_D!RHI!Gb&r3CES<$=7O-9cChw$8dW?q}2UHW;PMD-OC(xk+TH--884xDS%xK zAl}E}1DtdjJIn4~e1~yWRn~8ves=0j2sBnJCSNhL7V`}t_s$O>uMP~(LK6~;8Oe0~ z`liMCIMTWGMrF5XI=xGMdne~j#MT44jFq0=o5vbQ`t2UShkik?b4-I3hs%P*;d&6w z_YJ)@pzFQGE*vQI60C3Vl`%8O=DWtKtE+8~!EGT7RHEqCB~76?IYST5*KeV#?>jVw zC84)ZV>r`%`{boyyZc!Gc5j-+U)uWam?!&N3j@>@!EUHi0{z{7y>^MDP$GZv@Xw%S zMw5gCMvRn5Mk-Ut5&*dikD^-UN|zQNAJ!2cjjYbKNPa4gT8RQ;L81hQB5*E_E9bJ4 zd8uR{X+$*3N2F?CpnnN=28N#;{4yOp!Qm?l$L#kzd8RV}&ir-Rn`7<%C%X1BWxa!H zt`_M#&nTSS*ikGMhZ>tYRj*HBwQJ0a%lJTe$~Oo0|7Yh!#hHV#xK#If?h-jVf?8> zP^1P@G@nA!GO;8%@SQ-1D2NbziT>`3JvS%~{gqV?@a_j2y@|*>kvQ`p)g2*2uub$XLjU+Y^5N)%6-J+fC2E6KeH5nR8f+`~as|Aidk@y-_BOqH}4Cds*Zl zb3MQDJ+JYClAR(CZs&pL?hhmPv>$&=zcl3PbL0QO6KcmVRd&73=jN#rFtxS(Fd)Vl zgiahXFNAofIG436Qv;H~ZW-Ja$&9n3hYQaAMgq1@SnRo7j%H=2)j zZQlU+X6C=11JwT*A?5{#tG5-F{%RtHPxWlp@M8#*sZip?ft{&P9Ey7Q4$(-ci>r~v zn(J6Q=o78FTFvxTX57C&bIi#i<%dw~0{9v$x9MfnA|JQKY@I2`So><5E8yTLYtYHy zC%SuM94r`a42`NToZhdayKWg$yx*xSao(IcXld{2@f%y5G(n2Pbjb zI#v$C27pb1<}KrAF$C($Zuqj{;^-YrS-cqu*v_C`lex4Y69<5f{N_NUvjNgl;o8}} zfb97EWsvYgs*MUG%`rugY)C}@8<6c=Z``@8`&gFDzm0dtn_kH7IlK2?!+^W~2Y66= zgM6nY@W6q;5zACIv$IGXQvP^MjAt`8j0hV{kId$f#YITtQhObs{~OYs1=A?y+M312 z7GX|+g(y^(4(laYz(cLy~r;sMkUjeH#+|$DO0Eb!Ox#n$ZDAFz`UB$WScAd_)!xZ-34=AZq2-# zX8sNCePbFq$=}^H+zh+p#S~SjB)uD_r}cn`$BHSe2DM7dVq4RmUz?J6>P;Jo?@9AZd(Pvi^uPCppR+zq2rT{SpUh)n^gmmJ) zZ_53P?Qnh3aa-vzx%e7?T`hsPMb!Y%xe@+VnT>$Ja zJ39At%n7GFjT|NYdZsN4!R5gSp=P zLQMCFhXTx1M$$m-3L*>jRAcckGQP!wVG)shXW4MQq0J?U*T%LXR74$=N{}ZQDPB0! zMo1}yY-GB#1%3p28>5e1L0Ls$z8bR;onqoJUh><;2hwC1ORzkjqcZ&Oo%LyE+Gzr{ z1H{N2ip|o^y!Te@l1iu9d=TL--`3>q=6(Ljn7f=V1pC6ydarNh8U(E)S*a97glENM z{bd=-V^T1Jr1HL7SJ4~hOOq5d^mnda~{2X03P z%8GXNT&X^NSq`Pw?%vhZn~PzKTT?X5tQlZBd@jww2EJ%8zQS#r-3KMTs}T0MiTy4| z&I^Q?3HvHSl0fe@d_7-EMY;x=`h{59o z#a&qj8=4>t!ae5p)S%r&eI*_f4Xp~SZMe;7l*GSuDhSxJN}O>G5arO4iF1`tK$4`C zZgiR3d{R+;j95(&4~&^SqZWkXI%z(liFJLkue337(3JoyYbq$c#J9JFtr}6OMIFdfgifDovpY2KOL6p+rE z)67ci_QL3LGJKg`W3%cHVh%ZAaWs*hFk*tlzYW^`X@{?jYN>K7*&h9BNQ@K3+4qG= z@e{72Jo@z`=vujOo*PW#VrMR^{oC^9l}88>3O*BxYkiek>^sdVZ5<_Kz zb&*I-4JcpcC5b{5Dv^}Ioz(Bo{ZT%LF85Y&n!EmlV`=Cuc+sQM>|9`dKSFT1#a_3B zH%NRyV%Go#D`Zzp#l{=2ZpM|a zD6eB;vVk!8*doU%v)B%4Z;1?3SuV4dO-C{6ZB2eV4*xQdJ90oAnRzfYQOdqo2w>Wn zyX)TO%$Pz-r`&m{YzL^N{CYlfCtg*k-l>Zt#07dmaw3BGFOi7Dp?J*$e;lwz zfhn$^>2R~{?|2XTGz0A4h&;0^vHel^HBg>0U8{*x*LxB&tb*rL*Sj>*wpfjz)T6)EvFCNdWDDWzQVMS$LYpbi{RU()F8cFJnp-e z2;XnRF)l?2O0==KoAh6w$b&pHfOLvrv5|P6o1d~<@A=En!q69ZI=8jUSG{nzg)xwz)1^t0sp%#waB z8H4>T|9&{7AAYr1de^Et)4)#l9TMeQ1U0Qh*y*hqAA4-969`rpdoe1m>N)DJ-2OK+GBWKb$L3p3BbCE&r#ZS4^$W_|tOWL6pE$`=~=|O@VroE5XU~ zfo(xT!Tk@AoFg81?X)hL&nJj8*wDE0rAu(scI7d{zyxsfj15R7r{_=Gr!3q*6u`=C zCv~KlOM15`8d656G)2jw=4fPW+Zt08#57PzxEnz9pwZrTeD8q6uoTzg5+M zXMGiwwoRJCm`*c_j&_FPR}e$gd9Dt*)aWY*(#ui}+l{}e<6q!+JB#+40cN`?KBMR` z)6NYF2N9?NvGFYVeiA3iYKxbKl{@}Wgrek=;$1W%5UE%N|UOTTu9OP zAfhxJS2h_8gd+*~*KrDOVrh#%%6Ke&Yxnpyq@@zQa;CfNCcDWb_SnzUs;wQP-)vDx0!~p1u$*Ggb#F;v`5M)_S?h9FRfQ7jMXmd}Z1ddd>E$>QZIGB}13j3PU zr2r35Odq(7>0^|{3t6*P_n1LR5RWYZOg|&=Fiqk~(XU0bRRASdgoJzr zRHQvEo>o5kbZhN_jL)aS+^Y(SV>rRld%>XATe<9)pWBbo3M=N+n18MiJXmfqT`KX5 zn^CEId!INHvVVGb=oiw!;qaljG}JCh=pBIAUl<_OSAvjoges4PVCq7(1=G3JEw-JE z)e2_tTp*s~K1DLL>jZ!i*>hoa;2Ia4O1)+}w7OLJd7_F#;%A4Z{azwcsKy`OK)5E$ zHWVVxB5~M%`YcOzPyTHB=EnL>0*Kd^)u;C%pAjV0ytzMr-W`ynUz#3No5zUJqh;7T zm2si!2HzR(s%w&gE#iym4?N}*;4Vcxy9aKnBw;GK=g8>VLg6lhwjQ6T|;EiN&)>*yJ-fLhDbFyLjFmh1@! zZ_JkJ)YpJ1r_)S6&(HrCfB7J+k7)XdV36k6XPwbF8DD{#xRQay|SAh=GvcAHQ`ZnjaL8GeP*Mz^T~a_dn3+n}m7t*UP01@}y7|arJ?xzdxRAw-toAf!>bN|{!?;*0Sb@Pyr`cs*RcOT#Q2kA0>J}5#W>H47>9_-b(gcUmp5_~oj=4eTbe1*6oF}`w zZCwGfanY7J^7#4iiB+PT29m5?J$qo7!77nu;@2lN958)I2A={6tsWxcpA(o}`5Kql z^qkpVL!qjDd>DMZ&72cTIZtgaO3$y#BY=eBqgP{no;6QgWNvrWtGNfM)_v+0#``9Z zd7s_{J}QOCvTAm`P=l8H_2GJb=VGlze_qp`TcVted);)-%TP+m)$2K0x!?pr#;$Pg z5EEvoiBRZdi+x9?!$=V3O+1(*iVyk|9U`hQLc9F{FTiy(Y})FS3#5!806t&>to0)@ zz90GIT;KG|SZ&WE3qMh-Ol`(JVkdl^!PwMYk&+VM*8Y;kuViVGC#{7H#=z<9fMZki z8T8w3{d%hJKL#y2@-qOd>wwU}KkL2sRusnn!gUG@5v^8rAbeH988e^S8Gnx_$Q?1)B{^5_$&pNSw%LC-egW{kFO=IXHQ|&u zjZcgkKnz<9!%nW(!21uh@$N+EM->_UphnZmwPTCZT>bLBDpo1JTOh4G6Wis z5n$kVf+>8qN<&{REM<_>Sko!#qSCcQt^EesH8?T@Xf`6zofw zIkV@9o+?lrfHeM`!zlyj_1`dq#Hi(C{t{AJ!iTxJ1YCZR>JPimLzjyMLkloi*M!P<);`j3X-pwUq{)dO9q*FHE;UZ)c9(qPZ$`piHNXtJuOR|Z$sTUcs#(_CT6jO*r|}fyFQzn<-FL{viRYZ z)Fkz)afTB-5LIG4hIGSQE z3}o@UUpd14o58Cfb2Z5ooFMU8A_8yM5GhK@d2ishf9`oJSB~;$8v~hppVzST+w|Ef z7?ENOHG=zLpi*V0qZORc-|qt zMdf(LgHpYT`X2yFTL}-pwTIGa9{YtMnBsw%o2FafO64seXot{)ow^IsdS~9kqb7bF zls|FBsI0V=)f>up8juv;iVyad7>72J34U8BC2E}qPV@>o1jez_0;u=zTjRtm(N7J1y-7f5%TGLGlD|~zN{X>0a(UWtUQP#*+wtf3Y7Y98Oj08z?LEu8vazBK%R6k7 zqUlLVkHDY%8EoVkBOq|uX+2{`$DKl*{AAiNtA7?O<1d ztkR)8*rT;E2aw0G98B83>U-cy$ij`MNct+G=AuHJG=T+MkO&_1Vjsb}`CkdNZ1EO1 zJMHoUW}RK<7%c*UA za9!S8I6x)16Ii{V5C4M5DhrBPUaIC^$Qsm(?)r|ln&z-xvrcmM%qF#ZHyw1)3pnAV z_^2a9V`(3NLmSzC=cn~fQoQ+_|0Fdm<;QxY)WcOthXE!lBn_TWsbrl%%ILqWK47_r zM|(Jhvt$l86@GprDSms!Gdj^5)8WcmS8 zOLb<6mLKz2rAO8>f;LWRX5#`Ij^Z-?Wkwqqr5d|PZNggd;LBO=rPP6z0uq21e&x(5 zTTjJ-8!ZzUIso(}z1M|cQT95{!o?E_AaSY7gT={+%(Sm)8d0;;XVy*mbw9l%e1%Hb zQt$_~11@FFcxdRhyLijRPPk&AXG}Sd>yonEcI=O+9Vd0}$`@BC9Hk(J{p( zTdew^xaIz8+HV$d7X&hp`DO9|nSqAPHhP+PiCraL2y#q{;zJ^yzH4`(U1O&p@lj|C zV(rrYT3K83hzmuD;p6}8@zdw7`DL}& zJmIeq3YYujLfbUrFbXrPXHET&!!^xP6b5uFyl=@c9M>=%pZ$6hoi?v=^vu-dJ2i^u z;FCUOJqrz;cIEWftIF0Wf+?y>*Eha+w@l{=wfD(T7)xfAp#B?x&s{8D*+X`-1!>SnxX- zz{n^;kiP)ijd%RYsdLvq2Axd33bdkfF;&p)PHnz7Zqi4Ddk-7KdiF)GSKesdN6@Kc z0uPkZMXnklWmd?h@EB%te*B}wy^lSgP1<&9e>N>iQ`Vs4dpjtFB-;7Tu^!kqQm@+Z z8TzoZT#TiuPWxRe?8OyA)VZ<_xm7lw<5Rb_V7xeX68ucx-Z_ox*@hftI-3g8ud34t zSOIO#E;}8eSEkwZ3{9Gx$|r8PGx8we2OUems%@b!IQdDv5=r*Woi!@5_DOu$VR>J8 zN4tL^jTch^va)no*YmWbNZ!JDObOxFp?8xu^u>+CHh!R3aB);&=03L(2LL3TWdN+V zL{1e@Rpk#Fg)Hbg&EY)UT6ESNZPfyzHC4{K5`DUVBgU|$R?E#lG~%sU(@^K~5lAjP ziNRzuoTy-k;2MH$L27@)h?-y&FcF#;OKmk>kmT<1#Ce{fJrydjEX1aOgjw5$;1Y#$ zpbgHlq>aIBXsJD58KL+OMy6J|8uGl5|3)?pE>#Cac$EoZMfxqIq54f;&Vtrm7Y2&; zxpUBXUdfTv4ILtK9tvvO*iV_`v4fdx&FHc?(CQ%Ty$$SB5`@Jkrg~6YJ`@%tlhL~-PVlI)a=$u+tQ|q5*fn8cC2B&3;0CiUDG8{y@irtQ z8-NLD0{aM#?FU~cE(2_NeigO>L)*{@>Q!XWuA5c()J43&n248Gn-QXu0byLjKBRPH zi#+&Dju;b`bwVeAcge24R|P)DMeuQoKq)hjhbn_3TmbKH7jbjSRfT@%k&1S39EsY1 zmjemfv4Q=_1{e$*SoaZ4v<*}}$ON^&uc$czkQ74nt9Jk?&Hh6qK6J-nX$Zz+c6Qf< z#vI$X39hU@{x9#s2see_bWPmPa2QL>HbNl|WK9}{AIvrL8NY&zPmcKFHa>rgG;ETJ z9j8=A^@ji@uNpS_Ok^a7mYJCpTE*5Ol+;cC@(^%0>^#oRRiNsL>1I|M|(W{;w6sbc1Z|)fgFSnLF=;=qT61XA-YOFLhbDrvoJHO3-H|>Dve9SnmZg^C6Uh~H?WjdhQZ}8Y-9q` z#=V}bc~xs^@1Rk8wl&V@mTEahl(^t>bBs{l7thhswx@T>%AP!n+El)&L;2&Y5z9+( z?RCU_y8mUQn`Qix!n5a!B=}VeI&Q3Uf8-C2uGnZ(|$yHVAmy7DZ5Mm}a`QM+9b;)9|+!szP01 z{y5DvQ?2e7+7Nm@74O&1gwD@3ew<=~QHP;I6``&?{+(w7#2YANT{>8)!=009%vP2N zU>e}5{tn_6H@irp=HXE;drDLVx5=l#uG$wO-xjQd+D?I!fpDbFXv^ftoIwuEMh|d$W zO?56-->9XilUo_hWE%k*$!Ey1l^&uvgxXw*G-y_%ANJ( zHAP;aN$B7p4fn$>#Jh~c%t?bdNo)q_!>&j&8--1figcvD3_?fzhvr8aIiapDpRM}+ z7M2te+G>gpMYpHr2Ksv)BBT!s&G|w|;OgNrD1sIY>Q;YP@C152buN%$F`F+C>}<_( zFvVXf>p@3@$pzI?jjkxgi`14n>G{ zjS+#RS}^8^MKO`w8KQ|KOA-H5yZYlCZK=JsC=5Fsi{}*&RTAMPe7zk%H~%P7FIf;U9HuBp=cau7{7H2 zH4L9=b(Nk-qm36`@lLt+$9FE?k0c_aiXu8$r4#B;-e0AkP;VncwZJV+b-9sM`|L*y zOMQ#;Ig~~od!k@k0Y`_!TcSD{yI@lKZdAb;G=IpgiqHy%r>t_WUf1vbs9mMGw@;U( zan`lNyz*#Ng!^@>4Wa2-FBHa*2#b<;O|Uc;`J#3`Tl$&ur|6^PAI97vKe*)9-ml(} zyB4CU+(yQ-)W7QQ1w*CM9Uw~E531Q%xdX_3dngDV(YD(IQW%8C`{}0Ph@zVeR literal 0 HcmV?d00001 diff --git a/webapp/src/icons/map/traffic-light-icon-green-1.svg b/webapp/src/icons/map/traffic-light-icon-green-1.svg new file mode 100644 index 000000000..dbdf15986 --- /dev/null +++ b/webapp/src/icons/map/traffic-light-icon-green-1.svg @@ -0,0 +1,93 @@ + + + + + + + + + + + + + + + + diff --git a/webapp/src/icons/map/traffic-light-icon-green.svg b/webapp/src/icons/map/traffic-light-icon-green.svg new file mode 100644 index 000000000..457fb9968 --- /dev/null +++ b/webapp/src/icons/map/traffic-light-icon-green.svg @@ -0,0 +1,93 @@ + + + + + + + + + + + + + + + + diff --git a/webapp/src/icons/map/traffic-light-icon-proceed-with-caution.svg b/webapp/src/icons/map/traffic-light-icon-proceed-with-caution.svg new file mode 100644 index 000000000..6f6866474 --- /dev/null +++ b/webapp/src/icons/map/traffic-light-icon-proceed-with-caution.svg @@ -0,0 +1,180 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/webapp/src/icons/map/traffic-light-icon-protected-clearance.svg b/webapp/src/icons/map/traffic-light-icon-protected-clearance.svg new file mode 100644 index 000000000..e167d28ad --- /dev/null +++ b/webapp/src/icons/map/traffic-light-icon-protected-clearance.svg @@ -0,0 +1,128 @@ + + + + + + + + + + + + + + + + + + + + + diff --git a/webapp/src/icons/map/traffic-light-icon-protected-movement-allowed.svg b/webapp/src/icons/map/traffic-light-icon-protected-movement-allowed.svg new file mode 100644 index 000000000..46e943f0d --- /dev/null +++ b/webapp/src/icons/map/traffic-light-icon-protected-movement-allowed.svg @@ -0,0 +1,129 @@ + + + + + + + + + + + + + + + + + + + + + diff --git a/webapp/src/icons/map/traffic-light-icon-red-1.svg b/webapp/src/icons/map/traffic-light-icon-red-1.svg new file mode 100644 index 000000000..e132f11e4 --- /dev/null +++ b/webapp/src/icons/map/traffic-light-icon-red-1.svg @@ -0,0 +1,93 @@ + + + + + + + + + + + + + + + + diff --git a/webapp/src/icons/map/traffic-light-icon-red-flashing.svg b/webapp/src/icons/map/traffic-light-icon-red-flashing.svg new file mode 100644 index 000000000..cedf9c813 --- /dev/null +++ b/webapp/src/icons/map/traffic-light-icon-red-flashing.svg @@ -0,0 +1,152 @@ + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/webapp/src/icons/map/traffic-light-icon-red.svg b/webapp/src/icons/map/traffic-light-icon-red.svg new file mode 100644 index 000000000..05f27c199 --- /dev/null +++ b/webapp/src/icons/map/traffic-light-icon-red.svg @@ -0,0 +1,93 @@ + + + + + + + + + + + + + + + + diff --git a/webapp/src/icons/map/traffic-light-icon-stop-then-proceed-high.svg b/webapp/src/icons/map/traffic-light-icon-stop-then-proceed-high.svg new file mode 100644 index 000000000..1e591a3e9 --- /dev/null +++ b/webapp/src/icons/map/traffic-light-icon-stop-then-proceed-high.svg @@ -0,0 +1,95 @@ + + + + + + + + + + + + + + + + diff --git a/webapp/src/icons/map/traffic-light-icon-stop-then-proceed-low.svg b/webapp/src/icons/map/traffic-light-icon-stop-then-proceed-low.svg new file mode 100644 index 000000000..5f991d6d5 --- /dev/null +++ b/webapp/src/icons/map/traffic-light-icon-stop-then-proceed-low.svg @@ -0,0 +1,180 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/webapp/src/icons/map/traffic-light-icon-unknown.svg b/webapp/src/icons/map/traffic-light-icon-unknown.svg new file mode 100644 index 000000000..b185903d1 --- /dev/null +++ b/webapp/src/icons/map/traffic-light-icon-unknown.svg @@ -0,0 +1,93 @@ + + + + + + + + + + + + + + + + diff --git a/webapp/src/icons/map/traffic-light-icon-yellow-1.svg b/webapp/src/icons/map/traffic-light-icon-yellow-1.svg new file mode 100644 index 000000000..80d4a1c3d --- /dev/null +++ b/webapp/src/icons/map/traffic-light-icon-yellow-1.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/webapp/src/icons/map/traffic-light-icon-yellow-red-1.svg b/webapp/src/icons/map/traffic-light-icon-yellow-red-1.svg new file mode 100644 index 000000000..866ca53f0 --- /dev/null +++ b/webapp/src/icons/map/traffic-light-icon-yellow-red-1.svg @@ -0,0 +1,93 @@ + + + + + + + + + + + + + + + + diff --git a/webapp/src/icons/map/traffic-light-icon-yellow-red-green.svg b/webapp/src/icons/map/traffic-light-icon-yellow-red-green.svg new file mode 100644 index 000000000..445f7adac --- /dev/null +++ b/webapp/src/icons/map/traffic-light-icon-yellow-red-green.svg @@ -0,0 +1,227 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/webapp/src/icons/map/traffic-light-icon-yellow-red.svg b/webapp/src/icons/map/traffic-light-icon-yellow-red.svg new file mode 100644 index 000000000..8f05c4bfc --- /dev/null +++ b/webapp/src/icons/map/traffic-light-icon-yellow-red.svg @@ -0,0 +1,93 @@ + + + + + + + + + + + + + + + + diff --git a/webapp/src/icons/map/traffic-light-icon-yellow.svg b/webapp/src/icons/map/traffic-light-icon-yellow.svg new file mode 100644 index 000000000..d80f3ee41 --- /dev/null +++ b/webapp/src/icons/map/traffic-light-icon-yellow.svg @@ -0,0 +1,93 @@ + + + + + + + + + + + + + + + + diff --git a/webapp/src/pages/IntersectionMapView.tsx b/webapp/src/pages/IntersectionMapView.tsx index ad1f67fa3..c82a0e5cf 100644 --- a/webapp/src/pages/IntersectionMapView.tsx +++ b/webapp/src/pages/IntersectionMapView.tsx @@ -1,251 +1,35 @@ -import React, { useEffect, useState } from 'react' -import Map, { Source, Layer, LineLayer, CircleLayer } from 'react-map-gl' -import { Container } from 'reactstrap' -import EnvironmentVars from '../EnvironmentVars' -import './css/RsuMapView.css' -import SsmSrmItem from '../components/SsmSrmItem' -import { useSelector, useDispatch } from 'react-redux' -import { - selectRsuMapData, - selectSelectedRsu, - selectSelectedSrm, - selectMapDate, - selectSsmDisplay, - selectRsuIpv4, - selectSrmSsmList, +import React from 'react' +import { useSelector } from 'react-redux' +import { Box, Container } from '@mui/material' +import MapTab from '../components/map/map-component' +import { selectSelectedIntersectionId, selectSelectedRoadRegulatorId } from '../generalSlices/intersectionSlice' - // actions - toggleMapDisplay, - toggleSsmSrmDisplay, - getSsmSrmData, - setSelectedSrm, -} from '../generalSlices/rsuSlice' -import { AnyAction, ThunkDispatch } from '@reduxjs/toolkit' -import { RootState } from '../store' - -interface RsuMapViewProps { - auth: boolean -} - -function RsuMapView(props: RsuMapViewProps) { - const dispatch: ThunkDispatch = useDispatch() - - const rsuMapData = useSelector(selectRsuMapData) - const selectedRsu = useSelector(selectSelectedRsu) - const selectedSrm = useSelector(selectSelectedSrm) - const mapDate = useSelector(selectMapDate) - const ssmDisplay = useSelector(selectSsmDisplay) - const rsuIpv4 = useSelector(selectRsuIpv4) - const srmSsmList = useSelector(selectSrmSsmList) - - const [srmCount, setSrmCount] = useState(0) - const [ssmCount, setSsmCount] = useState(0) - const [msgList, setMsgList] = useState([]) - const [egressData, setEgressData] = useState>({ - type: 'FeatureCollection' as 'FeatureCollection', - features: [], - }) - const [ingressData, setIngressData] = useState>({ - type: 'FeatureCollection' as 'FeatureCollection', - features: [], - }) - - useEffect(() => { - dispatch(getSsmSrmData()) - }, [dispatch]) - - useEffect(() => { - let localSrmCount = 0 - let localSsmCount = 0 - let localMsgList = [] - console.error('srmSsmList', srmSsmList) - for (const elem of srmSsmList) { - if (elem.ip === rsuIpv4) { - localMsgList.push(elem) - if (elem.type === 'srmTx') { - localSrmCount += 1 - } else { - localSsmCount += 1 - } - } - } - setSrmCount(localSrmCount) - setSsmCount(localSsmCount) - setMsgList(localMsgList) - }, [srmSsmList, rsuIpv4]) - - useEffect(() => { - const ingressDataFeatures = [] as Array> - const egressDataFeatures = [] as Array> - - Object.entries(rsuMapData?.['features'] ?? []).map((feature) => { - if (feature[1].properties.ingressPath === 'true') { - ingressDataFeatures.push(feature[1]) - } - return null - }) - Object.entries(rsuMapData?.['features'] ?? []).map((feature) => { - if (feature[1].properties.egressPath === 'true') { - egressDataFeatures.push(feature[1]) - } - return null - }) - - setIngressData((prevIngressData) => { - return { ...prevIngressData, features: ingressDataFeatures } - }) - setEgressData((prevEgressData) => { - return { ...prevEgressData, features: egressDataFeatures } - }) - }, [rsuMapData]) - - const srmData: GeoJSON.FeatureCollection = { - type: 'FeatureCollection' as 'FeatureCollection', - features: [], - } - - if (selectedSrm.length > 0) { - srmData.features.push({ - type: 'Feature', - geometry: { - type: 'Point', - coordinates: [selectedSrm[0].long, selectedSrm[0].lat], - }, - properties: {}, - }) - } - - const ingressLayer: LineLayer = { - id: 'ingressLayer', - type: 'line', - minzoom: 14, - source: 'ingressData', - layout: { - 'line-join': 'round', - 'line-cap': 'round', - }, - paint: { - 'line-color': 'rgb(50,205,50)', - 'line-width': 3, - }, - } - - const egressLayer: LineLayer = { - id: 'egressLayer', - type: 'line', - minzoom: 14, - source: 'egressData', - layout: { - 'line-join': 'round', - 'line-cap': 'round', - }, - paint: { - 'line-color': 'rgb(203, 4, 4)', - 'line-width': 3, - }, - } - - const srmLayer: CircleLayer = { - id: 'srmMarker', - type: 'circle', - source: 'srmData', - minzoom: 12, - paint: { - 'circle-radius': 8, - 'circle-color': 'rgb(14, 32, 82)', - }, - } - - const [viewState, setViewState] = useState({ - latitude: selectedRsu.geometry.coordinates[1], - longitude: selectedRsu.geometry.coordinates[0], - zoom: 17, - }) +function IntersectionMapView() { + const intersectionId = useSelector(selectSelectedIntersectionId) + const roadRegulatorId = useSelector(selectSelectedRoadRegulatorId) return ( -
+ - { - setViewState(evt.viewState) - }} - > - - - - - - - - - - + - -
MAP data from {mapDate}
- {ssmDisplay ? ( -
- -

SSM / SRM Data For {rsuIpv4}

-
-

Time

-

Request Id

-

Role

-

Status

-

Display

-
- {msgList.map((index) => ( - - ))} -

Total Counts

-
-

SSM: {ssmCount}

-

SRM: {srmCount}

-
-
- ) : ( - - )} -
-
-

Reference

-
-
-
-

- Ingress Lane

-
-
-
-

- Egress Lane

-
-
-
-

- SSM (table)

-
-
-
-

- SRM (table)

-
-
-
-

- SRM (map)

-
-
-
+ ) } -export default RsuMapView +export default IntersectionMapView diff --git a/webapp/src/store.tsx b/webapp/src/store.tsx index 6be05f977..ef7fcfbd0 100644 --- a/webapp/src/store.tsx +++ b/webapp/src/store.tsx @@ -3,6 +3,7 @@ import rsuReducer from './generalSlices/rsuSlice' import userReducer from './generalSlices/userSlice' import wzdxReducer from './generalSlices/wzdxSlice' import configReducer from './generalSlices/configSlice' +import intersectionReducer from './generalSlices/intersectionSlice' import adminAddOrganizationReducer from './features/adminAddOrganization/adminAddOrganizationSlice' import adminAddRsuReducer from './features/adminAddRsu/adminAddRsuSlice' import adminAddUserReducer from './features/adminAdduser/adminAddUserSlice' @@ -25,6 +26,7 @@ export const setupStore = (preloadedState: any) => { user: userReducer, wzdx: wzdxReducer, config: configReducer, + intersection: intersectionReducer, adminAddOrganization: adminAddOrganizationReducer, adminAddRsu: adminAddRsuReducer, adminAddUser: adminAddUserReducer, From da59af9d68bc96bc76aca70fe19f9a995c1e6644 Mon Sep 17 00:00:00 2001 From: jacob6838 Date: Mon, 1 Apr 2024 23:26:17 -0600 Subject: [PATCH 006/189] Updating keycloak config with conflictvisualizer roles --- resources/keycloak/realm.json | 44 +++++++++++++++- webapp/Dockerfile | 12 ----- webapp/src/EnvironmentVars.tsx | 5 ++ webapp/src/apis/api-helper-cviz.ts | 3 +- webapp/src/apis/keycloak-api.ts | 5 +- webapp/src/components/map/map-component.tsx | 19 ++++--- webapp/src/components/map/map-slice.tsx | 5 +- webapp/src/pages/IntersectionMapView.tsx | 58 +++++++++++++-------- 8 files changed, 102 insertions(+), 49 deletions(-) diff --git a/resources/keycloak/realm.json b/resources/keycloak/realm.json index 277974aab..563fe737a 100644 --- a/resources/keycloak/realm.json +++ b/resources/keycloak/realm.json @@ -4789,6 +4789,24 @@ "failureFactor": 30, "roles": { "realm": [ + { + "id": "cb80b6bc-eafa-4f58-80ca-1e38ac8af5a1", + "name": "USER", + "description": "Conflictvisualizer User", + "composite": false, + "clientRole": false, + "containerId": "d0dc22ea-63fe-42ee-81d0-beed32b15ea7", + "attributes": {} + }, + { + "id": "eeb12ecf-17c1-45f0-9bc4-957a618039f9", + "name": "ADMIN", + "description": "Conflictvisualizer Administrator", + "composite": false, + "clientRole": false, + "containerId": "d0dc22ea-63fe-42ee-81d0-beed32b15ea7", + "attributes": {} + }, { "id": "998ccb10-fd0a-4e8a-9e56-e1c7962bf20c", "name": "default-roles-cvmanager", @@ -5133,7 +5151,28 @@ ] } }, - "groups": [], + "groups": [ + { + "id": "1d271cc0-96b8-434d-9c87-375cd789897a", + "name": "ADMIN", + "path": "/ADMIN", + "attributes": {}, + "realmRoles": ["ADMIN"], + "clientRoles": { + "realm-management": ["manage-users", "realm-admin"] + }, + "subGroups": [] + }, + { + "id": "3385cd0b-1ddf-460e-8c67-c4cf138868ec", + "name": "USER", + "path": "/USER", + "attributes": {}, + "realmRoles": ["USER"], + "clientRoles": {}, + "subGroups": [] + } + ], "defaultRole": { "id": "998ccb10-fd0a-4e8a-9e56-e1c7962bf20c", "name": "default-roles-cvmanager", @@ -5142,6 +5181,7 @@ "clientRole": false, "containerId": "d0dc22ea-63fe-42ee-81d0-beed32b15ea7" }, + "defaultGroups": ["/USER"], "requiredCredentials": ["password"], "otpPolicyType": "totp", "otpPolicyAlgorithm": "HmacSHA1", @@ -5196,7 +5236,7 @@ "requiredActions": [], "realmRoles": ["default-roles-cvmanager"], "notBefore": 0, - "groups": [] + "groups": ["/ADMIN"] } ], "scopeMappings": [ diff --git a/webapp/Dockerfile b/webapp/Dockerfile index 63d82e9ac..596709251 100644 --- a/webapp/Dockerfile +++ b/webapp/Dockerfile @@ -30,18 +30,6 @@ ENV REACT_APP_MAPBOX_INIT_LONGITUDE $MAPBOX_INIT_LONGITUDE ARG MAPBOX_INIT_ZOOM ENV REACT_APP_MAPBOX_INIT_ZOOM $MAPBOX_INIT_ZOOM -ARG CVIZ_API_SERVER_URL -ENV REACT_APP_CVIZ_API_SERVER_URL $CVIZ_API_SERVER_URL - -ARG CVIZ_API_WS_URL -ENV REACT_APP_CVIZ_API_WS_URL $CVIZ_API_WS_URL - -ARG KEYCLOAK_URL -ENV KEYCLOAK_URL $KEYCLOAK_URL - -ARG KEYCLOAK_REALM -ENV KEYCLOAK_REALM $KEYCLOAK_REALM - RUN npm ci RUN npm run build diff --git a/webapp/src/EnvironmentVars.tsx b/webapp/src/EnvironmentVars.tsx index 5b9d7f5f9..796f654f6 100644 --- a/webapp/src/EnvironmentVars.tsx +++ b/webapp/src/EnvironmentVars.tsx @@ -27,7 +27,12 @@ class EnvironmentVars { } static MAPBOX_TOKEN = process.env.REACT_APP_MAPBOX_TOKEN + static CVIZ_MAPBOX_TOKEN = process.env.REACT_APP_CVIZ_MAPBOX_TOKEN + static CVIZ_MAPBOX_STYLE_URL = process.env.REACT_APP_CVIZ_MAPBOX_STYLE_URL + static CVIZ_API_SERVER_URL = process.env.REACT_APP_CVIZ_API_SERVER_URL + static CVIZ_API_WS_URL = process.env.REACT_APP_CVIZ_API_WS_URL static KEYCLOAK_HOST_URL = process.env.REACT_APP_KEYCLOAK_URL + static KEYCLOAK_REALM = process.env.REACT_APP_KEYCLOAK_REALM static DOT_NAME = process.env.REACT_APP_DOT_NAME static rsuInfoEndpoint = `${this.getBaseApiUrl()}/rsuinfo` diff --git a/webapp/src/apis/api-helper-cviz.ts b/webapp/src/apis/api-helper-cviz.ts index 218d7dfe6..0eca407d4 100644 --- a/webapp/src/apis/api-helper-cviz.ts +++ b/webapp/src/apis/api-helper-cviz.ts @@ -1,4 +1,5 @@ import toast from 'react-hot-toast' +import EnvironmentVars from '../EnvironmentVars' class CvizApiHelper { formatQueryParams(query_params?: Record): string { @@ -37,7 +38,7 @@ class CvizApiHelper { successMessage?: string failureMessage?: string }): Promise { - const url = (basePath ?? process.env.CVIZ_API_SERVER_URL!) + path + this.formatQueryParams(queryParams) + const url = (basePath ?? EnvironmentVars.CVIZ_API_SERVER_URL!) + path + this.formatQueryParams(queryParams) const localHeaders: HeadersInit = { ...headers } if (token) localHeaders['Authorization'] = `Bearer ${token}` diff --git a/webapp/src/apis/keycloak-api.ts b/webapp/src/apis/keycloak-api.ts index 33e2bd03c..7ce92f281 100644 --- a/webapp/src/apis/keycloak-api.ts +++ b/webapp/src/apis/keycloak-api.ts @@ -1,7 +1,8 @@ import { authApiHelper } from './api-helper-cviz' +import EnvironmentVars from '../EnvironmentVars' -const KEYCLOAK_ADMIN_ENDPOINT = `${process.env.KEYCLOAK_URL}/admin/realms/${process.env.KEYCLOAK_REALM}` -const KEYCLOAK_AUTH_ENDPOINT = `${process.env.KEYCLOAK_URL}/realms/${process.env.KEYCLOAK_REALM}` +const KEYCLOAK_ADMIN_ENDPOINT = `${EnvironmentVars.KEYCLOAK_HOST_URL}/admin/realms/${EnvironmentVars.KEYCLOAK_REALM}` +const KEYCLOAK_AUTH_ENDPOINT = `${EnvironmentVars.KEYCLOAK_HOST_URL}/realms/${EnvironmentVars.KEYCLOAK_REALM}` class KeycloakApi { getEmailPreferences(attributes: Record): EmailPreferences { diff --git a/webapp/src/components/map/map-component.tsx b/webapp/src/components/map/map-component.tsx index 6be76b7db..37be808b3 100644 --- a/webapp/src/components/map/map-component.tsx +++ b/webapp/src/components/map/map-component.tsx @@ -61,6 +61,7 @@ import { updateRenderTimeInterval, updateRenderedMapState, } from './map-slice' +import EnvironmentVars from '../../EnvironmentVars' import { addConnections, createMarkerForNotification } from './utilities/message-utils' import { AnyAction, ThunkDispatch } from '@reduxjs/toolkit' import { RootState } from '../../store' @@ -127,7 +128,6 @@ type timestamp = { } const MapTab = (props: MAP_PROPS) => { - const MAPBOX_API_TOKEN = process.env.MAPBOX_TOKEN! const dispatch: ThunkDispatch = useDispatch() // userSlice @@ -216,7 +216,10 @@ const MapTab = (props: MAP_PROPS) => { if (loadInitialDataTimeoutId) { clearTimeout(loadInitialDataTimeoutId) } - const timeoutId = setTimeout(() => dispatch(pullInitialData()), 500) + const timeoutId = setTimeout(() => { + console.log('Loading Initial Data') + dispatch(pullInitialData()) + }, 500) dispatch(setLoadInitialdataTimeoutId(timeoutId)) }, [queryParams]) @@ -261,8 +264,8 @@ const MapTab = (props: MAP_PROPS) => { }, [liveDataActive]) return ( - - + +
{ zIndex: 10, top: 0, left: 0, - // width: 1200, - width: 'calc(100% - 500px)', + width: 1200, + // width: 'calc(100% - 500px)', borderRadius: '4px', fontSize: '16px', maxHeight: 'calc(100vh - 120px)', @@ -309,8 +312,8 @@ const MapTab = (props: MAP_PROPS) => { {...viewState} ref={mapRef} onLoad={() => {}} - mapStyle={process.env.CVIZ_MAPBOX_STYLE_URL!} - mapboxAccessToken={MAPBOX_API_TOKEN} + mapStyle={EnvironmentVars.CVIZ_MAPBOX_STYLE_URL} + mapboxAccessToken={EnvironmentVars.CVIZ_MAPBOX_TOKEN} attributionControl={true} customAttribution={['© CDOT']} styleDiffing diff --git a/webapp/src/components/map/map-slice.tsx b/webapp/src/components/map/map-slice.tsx index a304f8b0f..37c817441 100644 --- a/webapp/src/components/map/map-slice.tsx +++ b/webapp/src/components/map/map-slice.tsx @@ -33,6 +33,7 @@ import { selectSsmDisplay, } from '../../generalSlices/rsuSlice' import { RsuInfo } from '../../apis/rsu-api-types' +import EnvironmentVars from '../../EnvironmentVars' export type MAP_LAYERS = | 'mapMessage' @@ -599,7 +600,7 @@ export const initializeLiveStreaming = createAsyncThunk( let protocols = ['v10.stomp', 'v11.stomp'] protocols.push(token) - const url = `${process.env.CVIZ_API_WS_URL}/stomp` + const url = `${EnvironmentVars.CVIZ_API_WS_URL}/stomp` console.debug('Connecting to STOMP endpoint: ' + url + ' with token: ' + token) // Stomp Client Documentation: https://stomp-js.github.io/stomp-websocket/codo/extra/docs-src/Usage.md.html @@ -936,12 +937,12 @@ export const intersectionMapSlice = createSlice({ intersectionId: mapData[0].properties.intersectionId, roadRegulatorId: -1, } + state.value.timeWindowSeconds = 60 state.value.sliderTimeValue = getNewSliderTimeValue( state.value.queryParams.startDate, state.value.sliderValue, state.value.timeWindowSeconds ) - state.value.timeWindowSeconds = 60 }, updateQueryParams: ( state, diff --git a/webapp/src/pages/IntersectionMapView.tsx b/webapp/src/pages/IntersectionMapView.tsx index c82a0e5cf..0f75a4cb0 100644 --- a/webapp/src/pages/IntersectionMapView.tsx +++ b/webapp/src/pages/IntersectionMapView.tsx @@ -1,34 +1,48 @@ -import React from 'react' -import { useSelector } from 'react-redux' +import React, { useEffect } from 'react' +import { useDispatch, useSelector } from 'react-redux' import { Box, Container } from '@mui/material' import MapTab from '../components/map/map-component' -import { selectSelectedIntersectionId, selectSelectedRoadRegulatorId } from '../generalSlices/intersectionSlice' +import { + getIntersections, + selectSelectedIntersectionId, + selectSelectedRoadRegulatorId, +} from '../generalSlices/intersectionSlice' +import { AnyAction, ThunkDispatch } from '@reduxjs/toolkit' +import { RootState } from '../store' function IntersectionMapView() { + const dispatch: ThunkDispatch = useDispatch() + const intersectionId = useSelector(selectSelectedIntersectionId) const roadRegulatorId = useSelector(selectSelectedRoadRegulatorId) + useEffect(() => { + dispatch(getIntersections()) + }, []) + return ( - - + - - - + + + + +
) } From f5317bd08ceeeb4e7b81e204f0d72ff56429f53f Mon Sep 17 00:00:00 2001 From: jacob6838 Date: Tue, 2 Apr 2024 08:55:50 -0600 Subject: [PATCH 007/189] Updating documentation for conflictvisualizer integration --- webapp/README.md | 8 ++++++++ webapp/sample.env.local | 6 ++++++ webapp/src/components/map/map-slice.tsx | 4 ++-- 3 files changed, 16 insertions(+), 2 deletions(-) diff --git a/webapp/README.md b/webapp/README.md index c56b13c3b..ff7ee116f 100644 --- a/webapp/README.md +++ b/webapp/README.md @@ -106,6 +106,14 @@ Re-factoring RSU manager to utilize Redux Toolkit for state management - References - WzdxMap.js - read and load WZDx data +## ConflictVisualizer Integration + +The usdot [jpo-conflictvisualizer](https://github.com/usdot-jpo-ode/jpo-conflictvisualizer) is a tool that can be used to visualize the conflicts between Basic Safety Messages (BSMs), Signal Phase and Timing (SPaT) messages, and MAP messages. The CV Manager Web Application has been integrated with the ConflictVisualizer tool to allow users to view data directly from a [jpo-conflictmonitor](https://github.com/usdot-jpo-ode/jpo-conflictmonitor) instance. This integration currently requires an additional jpo-conflictvisualizer api to be deployed alongside the jpo-cvmanager api. This allows the webapp to make authenticated requests to the jpo-conflictvisualizer api to retrieve the conflict monitor data. + +### Changes Made + +These changes were tested running locally in docker. These changes require a jpo-conflictvisualizer api to be running, and to be connected to the cvmanager keycloak server (and cvmanager keycloak realm). This API also requires the jpo-conflictmonitor and jpo-geojsonconverter to be running, so that there is data available. Once + ## Unit Testing The unit tests for this repository are written using Jest. These tests can be run with the following command: diff --git a/webapp/sample.env.local b/webapp/sample.env.local index eb05322e2..0c2086f24 100644 --- a/webapp/sample.env.local +++ b/webapp/sample.env.local @@ -3,6 +3,8 @@ BUILD_PATH='./build' # mapbox access token REACT_APP_MAPBOX_TOKEN="" +REACT_APP_CVIZ_MAPBOX_TOKEN="" +REACT_APP_CVIZ_MAPBOX_STYLE_URL="" # used to swap endpoints for various environments or leave blank REACT_APP_ENV="dev" @@ -12,10 +14,14 @@ REACT_APP_GATEWAY_BASE_URL="http://cvmanager.local.com:8081" # base url or IP for keycloak REACT_APP_KEYCLOAK_URL="http://cvmanager.auth.com:8084/" +REACT_APP_KEYCLOAK_REALM="cvmanager" COUNT_MESSAGE_TYPES='BSM,SSM,SPAT,SRM,MAP' DOT_NAME="CDOT" +REACT_APP_CVIZ_API_SERVER_URL='http://localhost:8085' +REACT_APP_CVIZ_API_WS_URL='ws://localhost:8085' + # initial mapbox view REACT_APP_MAPBOX_INIT_LATITUDE="39.7392" REACT_APP_MAPBOX_INIT_LONGITUDE="-104.9903" diff --git a/webapp/src/components/map/map-slice.tsx b/webapp/src/components/map/map-slice.tsx index 37c817441..7dff1f924 100644 --- a/webapp/src/components/map/map-slice.tsx +++ b/webapp/src/components/map/map-slice.tsx @@ -120,7 +120,7 @@ const initialState = { sourceDataType: undefined as MAP_PROPS['sourceDataType'] | undefined, intersectionId: undefined as MAP_PROPS['intersectionId'] | undefined, roadRegulatorId: undefined as MAP_PROPS['roadRegulatorId'] | undefined, - loadOnNull: undefined as MAP_PROPS['loadOnNull'] | undefined, + loadOnNull: true as MAP_PROPS['loadOnNull'] | undefined, mapData: undefined as ProcessedMap | undefined, mapSignalGroups: undefined as SignalStateFeatureCollection | undefined, signalStateData: undefined as SignalStateFeatureCollection | undefined, @@ -1090,7 +1090,7 @@ export const intersectionMapSlice = createSlice({ setMapProps: (state, action: PayloadAction) => { state.value.sourceData = action.payload.sourceData state.value.sourceDataType = action.payload.sourceDataType - state.value.loadOnNull = action.payload.loadOnNull + state.value.loadOnNull = action.payload.loadOnNull ?? true }, setSourceApi: (state, action: PayloadAction) => { state.value.sourceApi = action.payload From fb04c08b3213e77accc73693335c41bc17a47d49 Mon Sep 17 00:00:00 2001 From: jacob6838 Date: Tue, 30 Apr 2024 11:44:21 -0600 Subject: [PATCH 008/189] Adding intersections to main map page --- webapp/README.md | 6 + webapp/public/icons/intersection_icon.png | Bin 0 -> 36642 bytes .../icons/traffic-light-icon-green-1.svg | 93 +++++++ .../public/icons/traffic-light-icon-green.svg | 93 +++++++ ...raffic-light-icon-proceed-with-caution.svg | 180 ++++++++++++++ ...traffic-light-icon-protected-clearance.svg | 128 ++++++++++ ...-light-icon-protected-movement-allowed.svg | 129 ++++++++++ .../public/icons/traffic-light-icon-red-1.svg | 93 +++++++ .../icons/traffic-light-icon-red-flashing.svg | 152 ++++++++++++ .../public/icons/traffic-light-icon-red.svg | 93 +++++++ ...ffic-light-icon-stop-then-proceed-high.svg | 95 ++++++++ ...affic-light-icon-stop-then-proceed-low.svg | 180 ++++++++++++++ .../icons/traffic-light-icon-unknown.svg | 93 +++++++ .../icons/traffic-light-icon-yellow-1.svg | 1 + .../icons/traffic-light-icon-yellow-red-1.svg | 93 +++++++ .../traffic-light-icon-yellow-red-green.svg | 227 ++++++++++++++++++ .../icons/traffic-light-icon-yellow-red.svg | 93 +++++++ .../icons/traffic-light-icon-yellow.svg | 93 +++++++ webapp/src/App.tsx | 2 + webapp/src/pages/Map.tsx | 67 +++++- 20 files changed, 1910 insertions(+), 1 deletion(-) create mode 100644 webapp/public/icons/intersection_icon.png create mode 100644 webapp/public/icons/traffic-light-icon-green-1.svg create mode 100644 webapp/public/icons/traffic-light-icon-green.svg create mode 100644 webapp/public/icons/traffic-light-icon-proceed-with-caution.svg create mode 100644 webapp/public/icons/traffic-light-icon-protected-clearance.svg create mode 100644 webapp/public/icons/traffic-light-icon-protected-movement-allowed.svg create mode 100644 webapp/public/icons/traffic-light-icon-red-1.svg create mode 100644 webapp/public/icons/traffic-light-icon-red-flashing.svg create mode 100644 webapp/public/icons/traffic-light-icon-red.svg create mode 100644 webapp/public/icons/traffic-light-icon-stop-then-proceed-high.svg create mode 100644 webapp/public/icons/traffic-light-icon-stop-then-proceed-low.svg create mode 100644 webapp/public/icons/traffic-light-icon-unknown.svg create mode 100644 webapp/public/icons/traffic-light-icon-yellow-1.svg create mode 100644 webapp/public/icons/traffic-light-icon-yellow-red-1.svg create mode 100644 webapp/public/icons/traffic-light-icon-yellow-red-green.svg create mode 100644 webapp/public/icons/traffic-light-icon-yellow-red.svg create mode 100644 webapp/public/icons/traffic-light-icon-yellow.svg diff --git a/webapp/README.md b/webapp/README.md index ff7ee116f..55121e273 100644 --- a/webapp/README.md +++ b/webapp/README.md @@ -12,6 +12,12 @@ This is a web application that is made with React JS that is a front-end for int - Download instructions: https://docs.npmjs.com/downloading-and-installing-node-js-and-npm - Nodejs - Download instructions: https://nodejs.org/en/download/ +- jpo-conflictvisualizer api (modified to connect with the jpo-cvmanager keycloak server) + - Repository: https://github.com/usdot-jpo-ode/jpo-conflictvisualizer + - Follow the instructions in the README to build/deploy the api +- jpo-conflictmonitor (required to have data for the conflict visualizer) + - Repository: https://github.com/usdot-jpo-ode/jpo-conflictmonitor + - Follow the instructions in the README to build/deploy the tool ## Building The Application diff --git a/webapp/public/icons/intersection_icon.png b/webapp/public/icons/intersection_icon.png new file mode 100644 index 0000000000000000000000000000000000000000..9f6d43faa8e92b7cfd307786f3fc08e2f80e7f0b GIT binary patch literal 36642 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≠x24H?dP886W_`hzbah7(6}u3`U@ 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 literal 0 HcmV?d00001 diff --git a/webapp/public/icons/traffic-light-icon-green-1.svg b/webapp/public/icons/traffic-light-icon-green-1.svg new file mode 100644 index 000000000..95b13c475 --- /dev/null +++ b/webapp/public/icons/traffic-light-icon-green-1.svg @@ -0,0 +1,93 @@ + + + + + + + + + + + + + + + + diff --git a/webapp/public/icons/traffic-light-icon-green.svg b/webapp/public/icons/traffic-light-icon-green.svg new file mode 100644 index 000000000..0afebded0 --- /dev/null +++ b/webapp/public/icons/traffic-light-icon-green.svg @@ -0,0 +1,93 @@ + + + + + + + + + + + + + + + + diff --git a/webapp/public/icons/traffic-light-icon-proceed-with-caution.svg b/webapp/public/icons/traffic-light-icon-proceed-with-caution.svg new file mode 100644 index 000000000..d8e0580cb --- /dev/null +++ b/webapp/public/icons/traffic-light-icon-proceed-with-caution.svg @@ -0,0 +1,180 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/webapp/public/icons/traffic-light-icon-protected-clearance.svg b/webapp/public/icons/traffic-light-icon-protected-clearance.svg new file mode 100644 index 000000000..186926285 --- /dev/null +++ b/webapp/public/icons/traffic-light-icon-protected-clearance.svg @@ -0,0 +1,128 @@ + + + + + + + + + + + + + + + + + + + + + diff --git a/webapp/public/icons/traffic-light-icon-protected-movement-allowed.svg b/webapp/public/icons/traffic-light-icon-protected-movement-allowed.svg new file mode 100644 index 000000000..1e36128b8 --- /dev/null +++ b/webapp/public/icons/traffic-light-icon-protected-movement-allowed.svg @@ -0,0 +1,129 @@ + + + + + + + + + + + + + + + + + + + + + diff --git a/webapp/public/icons/traffic-light-icon-red-1.svg b/webapp/public/icons/traffic-light-icon-red-1.svg new file mode 100644 index 000000000..10eaad8fc --- /dev/null +++ b/webapp/public/icons/traffic-light-icon-red-1.svg @@ -0,0 +1,93 @@ + + + + + + + + + + + + + + + + diff --git a/webapp/public/icons/traffic-light-icon-red-flashing.svg b/webapp/public/icons/traffic-light-icon-red-flashing.svg new file mode 100644 index 000000000..1342114ca --- /dev/null +++ b/webapp/public/icons/traffic-light-icon-red-flashing.svg @@ -0,0 +1,152 @@ + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/webapp/public/icons/traffic-light-icon-red.svg b/webapp/public/icons/traffic-light-icon-red.svg new file mode 100644 index 000000000..5a3baf721 --- /dev/null +++ b/webapp/public/icons/traffic-light-icon-red.svg @@ -0,0 +1,93 @@ + + + + + + + + + + + + + + + + diff --git a/webapp/public/icons/traffic-light-icon-stop-then-proceed-high.svg b/webapp/public/icons/traffic-light-icon-stop-then-proceed-high.svg new file mode 100644 index 000000000..3a71b58fc --- /dev/null +++ b/webapp/public/icons/traffic-light-icon-stop-then-proceed-high.svg @@ -0,0 +1,95 @@ + + + + + + + + + + + + + + + + diff --git a/webapp/public/icons/traffic-light-icon-stop-then-proceed-low.svg b/webapp/public/icons/traffic-light-icon-stop-then-proceed-low.svg new file mode 100644 index 000000000..4d3de7857 --- /dev/null +++ b/webapp/public/icons/traffic-light-icon-stop-then-proceed-low.svg @@ -0,0 +1,180 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/webapp/public/icons/traffic-light-icon-unknown.svg b/webapp/public/icons/traffic-light-icon-unknown.svg new file mode 100644 index 000000000..214b9ed86 --- /dev/null +++ b/webapp/public/icons/traffic-light-icon-unknown.svg @@ -0,0 +1,93 @@ + + + + + + + + + + + + + + + + diff --git a/webapp/public/icons/traffic-light-icon-yellow-1.svg b/webapp/public/icons/traffic-light-icon-yellow-1.svg new file mode 100644 index 000000000..1ea18850c --- /dev/null +++ b/webapp/public/icons/traffic-light-icon-yellow-1.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/webapp/public/icons/traffic-light-icon-yellow-red-1.svg b/webapp/public/icons/traffic-light-icon-yellow-red-1.svg new file mode 100644 index 000000000..947879c7b --- /dev/null +++ b/webapp/public/icons/traffic-light-icon-yellow-red-1.svg @@ -0,0 +1,93 @@ + + + + + + + + + + + + + + + + diff --git a/webapp/public/icons/traffic-light-icon-yellow-red-green.svg b/webapp/public/icons/traffic-light-icon-yellow-red-green.svg new file mode 100644 index 000000000..c65b7e4be --- /dev/null +++ b/webapp/public/icons/traffic-light-icon-yellow-red-green.svg @@ -0,0 +1,227 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/webapp/public/icons/traffic-light-icon-yellow-red.svg b/webapp/public/icons/traffic-light-icon-yellow-red.svg new file mode 100644 index 000000000..2295902c7 --- /dev/null +++ b/webapp/public/icons/traffic-light-icon-yellow-red.svg @@ -0,0 +1,93 @@ + + + + + + + + + + + + + + + + diff --git a/webapp/public/icons/traffic-light-icon-yellow.svg b/webapp/public/icons/traffic-light-icon-yellow.svg new file mode 100644 index 000000000..b3e03adb3 --- /dev/null +++ b/webapp/public/icons/traffic-light-icon-yellow.svg @@ -0,0 +1,93 @@ + + + + + + + + + + + + + + + + diff --git a/webapp/src/App.tsx b/webapp/src/App.tsx index a06739431..74d2466cf 100644 --- a/webapp/src/App.tsx +++ b/webapp/src/App.tsx @@ -16,6 +16,7 @@ import Dashboard from './Dashboard' import NotFound, { NotFoundRedirect } from './pages/404' import { theme } from './styles' import { ThemeProvider } from '@mui/material' +import { getIntersections } from './generalSlices/intersectionSlice' const App = () => { const dispatch: ThunkDispatch = useDispatch() @@ -41,6 +42,7 @@ const App = () => { // Refresh Data console.debug('Authorizing the user with the API') dispatch(getRsuData()) + dispatch(getIntersections()) }, [authLoginData, dispatch]) return ( diff --git a/webapp/src/pages/Map.tsx b/webapp/src/pages/Map.tsx index 34834365c..5f8f60dfc 100644 --- a/webapp/src/pages/Map.tsx +++ b/webapp/src/pages/Map.tsx @@ -81,6 +81,12 @@ import './css/Map.css' import { WZDxFeature, WZDxWorkZoneFeed } from '../models/wzdx/WzdxWorkZoneFeed42' import { AnyAction, ThunkDispatch } from '@reduxjs/toolkit' import { RootState } from '../store' +import { + intersectionMapLabelsLayer, + selectIntersections, + selectSelectedIntersection, + setSelectedIntersectionId, +} from '../generalSlices/intersectionSlice' // @ts-ignore: workerClass does not exist in typed mapboxgl // eslint-disable-next-line import/no-webpack-loader-syntax @@ -123,6 +129,9 @@ function MapPage(props: MapPageProps) { const wzdxData = useSelector(selectWzdxData) + const intersectionsList = useSelector(selectIntersections) + const selectedIntersection = useSelector(selectSelectedIntersection) + // Mapbox local state variables const [viewState, setViewState] = useState(EnvironmentVars.getMapboxInitViewState()) @@ -545,6 +554,11 @@ function MapPage(props: MapPageProps) { 'line-width': 8, }, }, + { + id: 'intersection-layer', + label: 'Intersections', + type: 'symbol', + }, ] const Legend = () => { @@ -861,6 +875,57 @@ function MapPage(props: MapPageProps) {
{selectedWZDxMarker.props.feature.properties.table}
) : null} + {intersectionsList + .filter((intersection) => intersection.latitude != 0) + .map((intersection) => { + return ( + { + e.originalEvent.preventDefault() + dispatch(setSelectedIntersectionId(intersection.intersectionID)) + }} + > + + + ) + })} + {selectedIntersection && ( + +
SELECTED {selectedIntersection.intersectionID}
+
+ )} + {activeLayers.includes('intersection-layer') && ( +
+ {intersectionsList} + ({ + type: 'Feature', + properties: { + intersectionId: intersection.intersectionID, + intersectionName: intersection.intersectionID, + }, + geometry: { + type: 'Point', + coordinates: [intersection.longitude, intersection.latitude], + }, + })), + }} + > + + +
+ )} {selectedRsu ? ( { - dispatch(setBsmFilterOffset(e)) + dispatch(setBsmFilterOffset(e as number)) }} />
From e7f726ac8156bc1f7b400667a827875996caa423 Mon Sep 17 00:00:00 2001 From: jacob6838 Date: Tue, 30 Apr 2024 11:44:41 -0600 Subject: [PATCH 009/189] Adding payload types to rsu slice --- webapp/src/features/menu/ConfigureRSU.tsx | 2 +- webapp/src/generalSlices/rsuSlice.test.ts | 37 +++++++++++++++++------ webapp/src/generalSlices/rsuSlice.ts | 20 ++++++------ 3 files changed, 38 insertions(+), 21 deletions(-) diff --git a/webapp/src/features/menu/ConfigureRSU.tsx b/webapp/src/features/menu/ConfigureRSU.tsx index 126a6c33f..e41170ee4 100644 --- a/webapp/src/features/menu/ConfigureRSU.tsx +++ b/webapp/src/features/menu/ConfigureRSU.tsx @@ -35,7 +35,7 @@ const ConfigureRSU = () => {
+
+ +
Date: Fri, 3 May 2024 16:15:18 -0600 Subject: [PATCH 015/189] updating ota server unit tests --- .../images/obu_ota_server/obu_ota_server.py | 92 +++++---- .../obu_ota_server/test_obu_ota_server.py | 177 +++++++++++++++++- services/pytest.ini | 4 +- services/requirements.txt | 3 +- 4 files changed, 228 insertions(+), 48 deletions(-) diff --git a/services/addons/images/obu_ota_server/obu_ota_server.py b/services/addons/images/obu_ota_server/obu_ota_server.py index a56831f72..a24ea8f55 100644 --- a/services/addons/images/obu_ota_server/obu_ota_server.py +++ b/services/addons/images/obu_ota_server/obu_ota_server.py @@ -14,8 +14,11 @@ @app.get("/") -def read_root(request: Request): - return {"message": "Hello World", "root_path": request.scope.get("root_path")} +async def read_root(request: Request): + return { + "message": "obu ota server root path", + "root_path": request.scope.get("root_path"), + } def get_firmware_list(): @@ -30,60 +33,75 @@ def get_firmware_list(): @app.get("/firmwares") -def get_manifest(request: Request): +async def get_manifest(request: Request): try: files = get_firmware_list() - logging.debug(files) host_name = os.getenv("SERVER_HOST", "localhost") response_manifest = commsginia_manifest.add_contents(host_name, files) - logging.debug(response_manifest) return response_manifest except Exception as e: raise HTTPException(status_code=500, detail=str(e)) def get_firmware(firmware_id: str, local_file_path: str): - blob_storage_provider = os.getenv("BLOB_STORAGE_PROVIDER", "LOCAL") - # checks if firmware exists locally - if not os.path.exists(local_file_path): - # If configured to only use local storage, return False as firmware does not exist - if blob_storage_provider.upper() == "LOCAL": - return False - # If configured to use GCP storage, download the firmware from GCP - elif blob_storage_provider.upper() == "GCP": - # Download blob will attempt to download the firmware and return True if successful - return gcs_utils(firmware_id, local_file_path) - return True + try: + blob_storage_provider = os.getenv("BLOB_STORAGE_PROVIDER", "LOCAL") + # checks if firmware exists locally + if not os.path.exists(local_file_path): + # If configured to only use local storage, return False as firmware does not exist + if blob_storage_provider.upper() == "LOCAL": + return False + # If configured to use GCP storage, download the firmware from GCP + elif blob_storage_provider.upper() == "GCP": + # Download blob will attempt to download the firmware and return True if successful + return gcs_utils.download_gcp_blob(firmware_id, local_file_path) + return True + except Exception as e: + logging.error(f"Error getting firmware: {e}") + raise HTTPException(status_code=500, detail="Error getting firmware") + + +def parse_range_header(range_header): + start, end = 0, None + try: + if range_header: + start_end = range_header.partition("=")[2].split("-") + start = int(start_end[0]) + end = int(start_end[1]) if start_end[1] else None + except ValueError: + raise HTTPException(status_code=400, detail="Invalid Range header") + if end is not None and start > end: + raise HTTPException( + status_code=400, + detail="Invalid Range header: start cannot be greater than end", + ) + return start, end + + +async def read_file(file_path, start, end): + try: + async with aiofiles.open(file_path, mode="rb") as file: + file_size = os.path.getsize(file_path) + if end is None: + end = file_size + await file.seek(start) + data = await file.read(end - start) + return data, file_size + except Exception as e: + logging.error(f"Error reading file: {e}") + raise HTTPException(status_code=500, detail="Error reading file") @app.get("/firmwares/{firmware_id}") async def get_fw(request: Request, firmware_id: str): - logging.debug(f"Method: {request.method}") - logging.debug(f"URL: {request.url}") - logging.debug(f"Headers: {request.headers}") - logging.debug(f"Query Parameters: {request.query_params}") - logging.debug(f"Client: {request.client}") - logging.debug(f"Cookies: {request.cookies}") file_path = f"/firmwares/{firmware_id}" # Checks if firmware exists locally or downloads it from GCS if not get_firmware(firmware_id, file_path): raise HTTPException(status_code=404, detail="Firmware not found") - range_header = request.headers.get("Range") - start, end = 0, None - - if range_header: - start_end = range_header.partition("=")[2].split("-") - start = int(start_end[0]) - end = int(start_end[1]) if start_end[1] else None - - async with aiofiles.open(file_path, mode="rb") as file: - file_size = os.path.getsize(file_path) - if end is None: - end = file_size - await file.seek(start) - data = await file.read(end - start) + start, end = parse_range_header(request.headers.get("Range")) + data, file_size = await read_file(file_path, start, end) headers = { "Content-Range": f"bytes {start}-{end-1}/{file_size}", @@ -91,7 +109,7 @@ async def get_fw(request: Request, firmware_id: str): "Accept-Ranges": "bytes", } - logging.debug(f"Completed Firmware Response for lient: {request.client}") + logging.debug(f"Completed Firmware Response for client: {request.client}") return Response( content=data, media_type="application/octet-stream", headers=headers ) diff --git a/services/addons/tests/obu_ota_server/test_obu_ota_server.py b/services/addons/tests/obu_ota_server/test_obu_ota_server.py index 90b9ba880..2e3fd7bf6 100644 --- a/services/addons/tests/obu_ota_server/test_obu_ota_server.py +++ b/services/addons/tests/obu_ota_server/test_obu_ota_server.py @@ -1,21 +1,31 @@ +import asyncio import pytest +import aiofiles +import json +import tempfile import os -import glob -from unittest.mock import patch -from addons.images.obu_ota_server.obu_ota_server import get_firmware_list + +from httpx import AsyncClient +from fastapi import HTTPException, Request, Response + +from unittest.mock import MagicMock, AsyncMock, patch +from addons.images.obu_ota_server.obu_ota_server import ( + get_firmware_list, + get_firmware, + parse_range_header, + read_file, + app, +) @patch("os.getenv") @patch("glob.glob") def test_get_firmware_list_local(mock_glob, mock_getenv): - # Arrange mock_getenv.return_value = "LOCAL" mock_glob.return_value = ["/firmwares/test1.tar.sig", "/firmwares/test2.tar.sig"] - # Act result = get_firmware_list() - # Assert mock_getenv.assert_called_once_with("BLOB_STORAGE_PROVIDER", "LOCAL") mock_glob.assert_called_once_with("/firmwares/*.tar.sig") assert result == ["/firmwares/test1.tar.sig", "/firmwares/test2.tar.sig"] @@ -24,21 +34,170 @@ def test_get_firmware_list_local(mock_glob, mock_getenv): @patch("os.getenv") @patch("services.common.gcs_utils.list_gcs_blobs") def test_get_firmware_list_gcs(mock_list_gcs_blobs, mock_getenv): - # Arrange mock_getenv.return_value = "GCP" mock_list_gcs_blobs.return_value = [ "/firmwares/test1.tar.sig", "/firmwares/test2.tar.sig", ] - # Act result = get_firmware_list() - # Assert mock_getenv.assert_called_once_with("BLOB_STORAGE_PROVIDER", "LOCAL") mock_list_gcs_blobs.assert_called_once_with("firmwares", ".tar.sig") assert result == ["/firmwares/test1.tar.sig", "/firmwares/test2.tar.sig"] +@patch("os.getenv") +@patch("os.path.exists") +@patch("services.common.gcs_utils.list_gcs_blobs") +def test_get_firmware_local_fail(mock_gcs_utils, mock_os_path_exists, mock_os_getenv): + mock_os_getenv.return_value = "LOCAL" + mock_os_path_exists.return_value = False + + firmware_id = "test_firmware_id" + local_file_path = "test_local_file_path" + result = get_firmware(firmware_id, local_file_path) + + mock_os_getenv.assert_called_once_with("BLOB_STORAGE_PROVIDER", "LOCAL") + mock_os_path_exists.assert_called_once_with(local_file_path) + mock_gcs_utils.assert_not_called() + + assert result == False + + +@patch("os.getenv") +@patch("os.path.exists") +@patch("services.common.gcs_utils.list_gcs_blobs") +def test_get_firmware_local_success( + mock_gcs_utils, mock_os_path_exists, mock_os_getenv +): + mock_os_getenv.return_value = "LOCAL" + mock_os_path_exists.return_value = True + + firmware_id = "test_firmware_id" + local_file_path = "test_local_file_path" + result = get_firmware(firmware_id, local_file_path) + + mock_os_getenv.assert_called_once_with("BLOB_STORAGE_PROVIDER", "LOCAL") + mock_os_path_exists.assert_called_once_with(local_file_path) + mock_gcs_utils.assert_not_called() + + assert result == True + + +@patch("os.getenv") +@patch("os.path.exists") +@patch("services.common.gcs_utils.download_gcp_blob") +def test_get_firmware_gcs_success( + mock_download_gcp_blob, mock_os_path_exists, mock_os_getenv +): + mock_os_getenv.return_value = "GCP" + mock_os_path_exists.return_value = False + mock_download_gcp_blob.return_value = True + + firmware_id = "test_firmware_id" + local_file_path = "test_local_file_path" + result = get_firmware(firmware_id, local_file_path) + + mock_os_getenv.assert_called_with("BLOB_STORAGE_PROVIDER", "LOCAL") + mock_os_path_exists.assert_called_with(local_file_path) + mock_download_gcp_blob.assert_called_once_with(firmware_id, local_file_path) + + assert result == True + + +@patch("os.getenv") +@patch("os.path.exists") +@patch("services.common.gcs_utils.download_gcp_blob") +def test_get_firmware_gcs_failure( + mock_download_gcp_blob, mock_os_path_exists, mock_os_getenv +): + mock_os_getenv.return_value = "GCP" + mock_os_path_exists.return_value = False + mock_download_gcp_blob.return_value = False + + firmware_id = "test_firmware_id" + local_file_path = "test_local_file_path" + result = get_firmware(firmware_id, local_file_path) + + mock_os_getenv.assert_called_with("BLOB_STORAGE_PROVIDER", "LOCAL") + mock_os_path_exists.assert_called_with(local_file_path) + mock_download_gcp_blob.assert_called_once_with(firmware_id, local_file_path) + + assert result == False + + +def test_parse_range_header_valid(): + assert parse_range_header(None) == (0, None) + assert parse_range_header("bytes=0-499") == (0, 499) + assert parse_range_header("bytes=500-") == (500, None) + + +def test_parse_range_header_invalid(): + with pytest.raises(HTTPException) as excinfo: + parse_range_header("invalid range header") + assert str(excinfo.value) == "400: Invalid Range header" + + with pytest.raises(HTTPException) as excinfo: + parse_range_header("bytes=0-abc") + assert str(excinfo.value) == "400: Invalid Range header" + + with pytest.raises(HTTPException) as excinfo: + parse_range_header("bytes=-500") + assert str(excinfo.value) == "400: Invalid Range header" + + with pytest.raises(HTTPException) as excinfo: + parse_range_header("bytes=500-499") + assert ( + str(excinfo.value) + == "400: Invalid Range header: start cannot be greater than end" + ) + + +@pytest.mark.asyncio +async def test_read_file(): + with tempfile.NamedTemporaryFile(delete=False) as temp: + temp.write(b"Test data") + temp_path = temp.name + + data, file_size = await read_file(temp_path, 0, 9) + + assert data == b"Test data" + assert file_size == 9 + + # Clean up the temporary file + os.remove(temp_path) + + +@pytest.mark.asyncio +async def test_read_file_failure(): + with pytest.raises(HTTPException) as excinfo: + data, file_size = await read_file("fake_path", 0, 9) + assert str(excinfo.value) == "500: Error reading file" + + +@pytest.mark.anyio +async def test_read_root(): + async with AsyncClient(app=app, base_url="http://test") as ac: + response = await ac.get("/") + assert response.status_code == 200 + assert response.json() == {"message": "obu ota server root path", "root_path": ""} + + +@pytest.mark.anyio +@patch("addons.images.obu_ota_server.obu_ota_server.get_firmware_list") +@patch("addons.images.obu_ota_server.obu_ota_server.commsginia_manifest.add_contents") +async def test_get_manifest(mock_commsginia_manifest, mock_get_firmware_list): + mock_get_firmware_list.return_value = [ + "/firmwares/test1.tar.sig", + "/firmwares/test2.tar.sig", + ] + mock_commsginia_manifest.return_value = {"json": "data"} + async with AsyncClient(app=app, base_url="http://test") as ac: + response = await ac.get("/firmwares") + assert response.status_code == 200 + assert response.json() == {"json": "data"} + + if __name__ == "__main__": pytest.main() diff --git a/services/pytest.ini b/services/pytest.ini index 33dcfeca0..2c53bb63b 100644 --- a/services/pytest.ini +++ b/services/pytest.ini @@ -8,5 +8,7 @@ pythonpath = . addons/images/obu_ota_server api/src common +asyncio_mode=auto env = - CORS_DOMAIN=test.com \ No newline at end of file + CORS_DOMAIN=test.com + diff --git a/services/requirements.txt b/services/requirements.txt index cd24a9045..e6aaff7e9 100644 --- a/services/requirements.txt +++ b/services/requirements.txt @@ -32,4 +32,5 @@ waitress==2.1.2 fastapi==0.110.0 pydantic==2.6.2 uvicorn==0.27.1 -aiofiles==23.2.1 \ No newline at end of file +aiofiles==23.2.1 +httpx==0.27.0 \ No newline at end of file From dcfff8749a243109cf6ad81e2b9c09c81eb9bd9a Mon Sep 17 00:00:00 2001 From: jacob6838 Date: Mon, 6 May 2024 16:11:40 -0600 Subject: [PATCH 016/189] Fixing various warnings --- webapp/src/App.tsx | 1 - webapp/src/Dashboard.tsx | 5 +- webapp/src/apis/api-helper-cviz.ts | 2 +- webapp/src/apis/api-helper.ts | 3 +- webapp/src/apis/events-api.ts | 4 +- webapp/src/apis/mm-api.ts | 3 +- webapp/src/apis/notification-api.ts | 3 +- webapp/src/components/RsuFirmwareMenu.tsx | 4 +- webapp/src/components/SnmpsetMenu.tsx | 4 +- webapp/src/components/map/control-panel.tsx | 65 ++++++--------------- 10 files changed, 31 insertions(+), 63 deletions(-) diff --git a/webapp/src/App.tsx b/webapp/src/App.tsx index 900bbc55a..66db04bdf 100644 --- a/webapp/src/App.tsx +++ b/webapp/src/App.tsx @@ -1,5 +1,4 @@ import React, { useEffect } from 'react' -import { css } from '@emotion/react' import './App.css' import { useSelector, useDispatch } from 'react-redux' import { diff --git a/webapp/src/Dashboard.tsx b/webapp/src/Dashboard.tsx index f3375107d..2fc1b541b 100644 --- a/webapp/src/Dashboard.tsx +++ b/webapp/src/Dashboard.tsx @@ -8,16 +8,13 @@ import Admin from './pages/Admin' import Grid from '@material-ui/core/Grid' import Tabs, { TabItem } from './components/Tabs' import Map from './pages/Map' -import RsuMapView from './pages/RsuMapView' import './App.css' import { useSelector, useDispatch } from 'react-redux' import { - selectDisplayMap, - // Actions getRsuData, } from './generalSlices/rsuSlice' -import { selectAuthLoginData, selectRole, selectLoadingGlobal, setRouteNotFound } from './generalSlices/userSlice' +import { selectAuthLoginData, selectLoadingGlobal } from './generalSlices/userSlice' import { SecureStorageManager } from './managers' import { ReactKeycloakProvider } from '@react-keycloak/web' import keycloak from './keycloak-config' diff --git a/webapp/src/apis/api-helper-cviz.ts b/webapp/src/apis/api-helper-cviz.ts index 0eca407d4..a7fbbfda0 100644 --- a/webapp/src/apis/api-helper-cviz.ts +++ b/webapp/src/apis/api-helper-cviz.ts @@ -57,7 +57,7 @@ class CvizApiHelper { method: method, headers: localHeaders, body: body - ? localHeaders['Content-Type'] == 'application/x-www-form-urlencoded' + ? localHeaders['Content-Type'] === 'application/x-www-form-urlencoded' ? (body as string) : JSON.stringify(body) : undefined, diff --git a/webapp/src/apis/api-helper.ts b/webapp/src/apis/api-helper.ts index cfe4e1094..e930dc163 100644 --- a/webapp/src/apis/api-helper.ts +++ b/webapp/src/apis/api-helper.ts @@ -275,4 +275,5 @@ class ApiHelper { } } -export default new ApiHelper() +const apiHelper = new ApiHelper() +export default apiHelper diff --git a/webapp/src/apis/events-api.ts b/webapp/src/apis/events-api.ts index 08584759a..17f974ac5 100644 --- a/webapp/src/apis/events-api.ts +++ b/webapp/src/apis/events-api.ts @@ -1,4 +1,3 @@ -import { number } from 'prop-types' import { authApiHelper } from './api-helper-cviz' interface Item { @@ -104,4 +103,5 @@ class EventsApi { } } -export default new EventsApi() +const eventsApi = new EventsApi() +export default eventsApi diff --git a/webapp/src/apis/mm-api.ts b/webapp/src/apis/mm-api.ts index 7ccfe717c..f0f5c355a 100644 --- a/webapp/src/apis/mm-api.ts +++ b/webapp/src/apis/mm-api.ts @@ -104,4 +104,5 @@ class MessageMonitorApi { } } -export default new MessageMonitorApi() +const messageMonitorApi = new MessageMonitorApi() +export default messageMonitorApi diff --git a/webapp/src/apis/notification-api.ts b/webapp/src/apis/notification-api.ts index cf778a8c7..aabf2c228 100644 --- a/webapp/src/apis/notification-api.ts +++ b/webapp/src/apis/notification-api.ts @@ -100,4 +100,5 @@ class NotificationApi { } } -export default new NotificationApi() +const notificationApi = new NotificationApi() +export default notificationApi diff --git a/webapp/src/components/RsuFirmwareMenu.tsx b/webapp/src/components/RsuFirmwareMenu.tsx index 78aa48314..4dab22487 100644 --- a/webapp/src/components/RsuFirmwareMenu.tsx +++ b/webapp/src/components/RsuFirmwareMenu.tsx @@ -58,7 +58,7 @@ const RsuFirmwareMenu = (props: RsuFirmwareMenuProps) => {

Firmware Upgrade

- {props.type == 'single_rsu' && ( + {props.type === 'single_rsu' && (
{firmwareUpgradeAvailable ? (
@@ -102,7 +102,7 @@ const RsuFirmwareMenu = (props: RsuFirmwareMenuProps) => {
)} - {props.type == 'multi_rsu' && ( + {props.type === 'multi_rsu' && (

diff --git a/webapp/src/components/SnmpsetMenu.tsx b/webapp/src/components/SnmpsetMenu.tsx index 1b86cabe4..f850d1fe9 100644 --- a/webapp/src/components/SnmpsetMenu.tsx +++ b/webapp/src/components/SnmpsetMenu.tsx @@ -23,12 +23,12 @@ import { RootState } from '../store' import './css/SnmpwalkMenu.css' -export type SnmpsetMenu = { +export type SnmpsetMenuProps = { type: string rsuIpList: string[] } -const SnmpsetMenu = (props: SnmpsetMenu) => { +const SnmpsetMenu = (props: SnmpsetMenuProps) => { const { type, rsuIpList } = props const dispatch: ThunkDispatch = useDispatch() diff --git a/webapp/src/components/map/control-panel.tsx b/webapp/src/components/map/control-panel.tsx index e07a65e38..c7ac25ab3 100644 --- a/webapp/src/components/map/control-panel.tsx +++ b/webapp/src/components/map/control-panel.tsx @@ -35,7 +35,6 @@ import { onTimeQueryChanged, selectBsmEventsByMinute, selectBsmTrailLength, - selectIntersectionId, selectPlaybackModeActive, selectSliderTimeValue, selectSourceApi, @@ -49,44 +48,15 @@ import { togglePlaybackModeActive, } from './map-slice' import { - selectAllInteractiveLayerIds, - selectBsmData, - selectConnectingLanes, - selectCurrentBsmData, - selectCurrentBsms, - selectCurrentMapData, - selectCurrentSignalGroups, - selectCurrentSpatData, - selectCursor, - selectFilteredSurroundingEvents, - selectFilteredSurroundingNotifications, - selectHoveredFeature, - selectImportedMessageData, selectLaneLabelsVisible, - selectLayersVisible, selectLiveDataActive, - selectLoadInitialDataTimeoutId, - selectLoadOnNull, - selectMapData, - selectMapSignalGroups, selectMapSpatTimes, selectQueryParams, - selectRawData, - selectRenderTimeInterval, - selectRoadRegulatorId, - selectSelectedFeature, selectShowPopupOnHover, selectSigGroupLabelsVisible, - selectSignalStateData, selectSliderValue, - selectSourceData, - selectSourceDataType, - selectSpatSignalGroups, - selectSurroundingNotifications, selectTimeWindowSeconds, - selectViewState, } from './map-slice' -import { selectToken } from '../../generalSlices/userSlice' import { selectSignalStateLayerStyle, setSignalLayerLayout } from './map-layer-style-slice' import { getTimeRange } from './utilities/map-utils' import { @@ -95,10 +65,9 @@ import { selectSelectedIntersectionId, } from '../../generalSlices/intersectionSlice' import { selectRsu, selectRsuData, selectSelectedRsu } from '../../generalSlices/rsuSlice' -import { RsuInfo } from '../../apis/rsu-api-types' import pauseIcon from '../../icons/pause.png' import playIcon from '../../icons/play.png' -import { BarChart, XAxis, Bar, Cell, ResponsiveContainer, Tooltip } from 'recharts' +import { BarChart, XAxis, Bar, ResponsiveContainer, Tooltip } from 'recharts' const Accordion = styled((props: AccordionProps) => )( ({ theme }) => ({ @@ -232,26 +201,26 @@ function ControlPanel() { useEffect(() => { const newDateParams = getQueryParams({ ...queryParams, timeWindowSeconds }) - if (newDateParams.eventTime.getTime() != oldDateParams.eventTime.getTime()) { + if (newDateParams.eventTime.getTime() !== oldDateParams.eventTime.getTime()) { setShouldReRenderEventTime(false) setEventTime(dayjs(newDateParams.eventTime)) } - if (newDateParams.timeBefore != oldDateParams.timeBefore) { + if (newDateParams.timeBefore !== oldDateParams.timeBefore) { setShouldReRenderTimeBefore(false) setTimeBefore(newDateParams.timeBefore.toString()) } - if (newDateParams.timeAfter != oldDateParams.timeAfter) { + if (newDateParams.timeAfter !== oldDateParams.timeAfter) { setShouldReRenderTimeAfter(false) setTimeAfter(newDateParams.timeAfter.toString()) } - if (newDateParams.timeWindowSeconds != oldDateParams.timeWindowSeconds) { + if (newDateParams.timeWindowSeconds !== oldDateParams.timeWindowSeconds) { setShouldReRenderTimeWindowSeconds(false) setTimeWindowSeconds(newDateParams.timeWindowSeconds.toString()) } }, [{ ...queryParams, timeWindowSeconds }]) useEffect(() => { - if (bsmTrailLength != prevBsmTrailLength) { + if (bsmTrailLength !== prevBsmTrailLength) { setShouldReRenderTimeAfter(false) setPrevBsmTrailLength(bsmTrailLength) setBsmTrailLengthLocal(bsmTrailLength.toString()) @@ -343,7 +312,7 @@ function ControlPanel() { }, [timeWindowSeconds]) useEffect(() => { - if (shouldReRenderBsmTrail && getNumber(bsmTrailLengthLocal) != null) { + if (shouldReRenderBsmTrail && getNumber(bsmTrailLengthLocal) !== null) { dispatch(setBsmTrailLength(getNumber(bsmTrailLengthLocal)!)) } else { setShouldReRenderBsmTrail(true) @@ -355,9 +324,9 @@ function ControlPanel() { const d = eventTime?.toDate().getTime()! return ( !isNaN(d) && - getNumber(timeBefore) != null && - getNumber(timeAfter) != null && - getNumber(timeWindowSecondsLocal) != null + getNumber(timeBefore) !== null && + getNumber(timeAfter) !== null && + getNumber(timeWindowSecondsLocal) !== null ) } catch (e) { return false @@ -418,7 +387,7 @@ function ControlPanel() { y={y - 1} width={12} height={height + 3} - fill={bsmEventsByMinute != null && bsmEventsByMinute.length > 0 ? '#10B981' : 'transparent'} + fill={bsmEventsByMinute !== null && bsmEventsByMinute.length > 0 ? '#10B981' : 'transparent'} style={{ pointerEvents: 'none' }} /> ) @@ -506,7 +475,7 @@ function ControlPanel() { ))} - {dataSourceApi == 'conflictvisualizer' && ( + {dataSourceApi === 'conflictvisualizer' && ( Intersection { - dispatch(selectRsu(rsuData.find((v) => v.properties.ipv4_address == e.target.value))) + dispatch(selectRsu(rsuData.find((v) => v.properties.ipv4_address === e.target.value))) }} > {rsuData.map((rsu) => ( @@ -638,12 +607,12 @@ function ControlPanel() {

MAP Message Time:{' '} - {mapSpatTimes.mapTime == 0 ? 'No Data' : format(mapSpatTimes.mapTime * 1000, 'MM/dd/yyyy HH:mm:ss')} + {mapSpatTimes.mapTime === 0 ? 'No Data' : format(mapSpatTimes.mapTime * 1000, 'MM/dd/yyyy HH:mm:ss')}

SPAT Message Time:{' '} - {mapSpatTimes.spatTime == 0 ? 'No Data' : format(mapSpatTimes.spatTime * 1000, 'MM/dd/yyyy HH:mm:ss')} + {mapSpatTimes.spatTime === 0 ? 'No Data' : format(mapSpatTimes.spatTime * 1000, 'MM/dd/yyyy HH:mm:ss')}

Activity Chart for {format(sliderTimeValue.start, 'MM/dd/yyyy')}:

@@ -710,7 +679,7 @@ function ControlPanel() {

Rotate Signal Head Icons With Map

dispatch( setSignalLayerLayout({ From d8b9f69e4a655ed1956d3a52977b61b5d97f320b Mon Sep 17 00:00:00 2001 From: jacob6838 Date: Mon, 6 May 2024 16:32:41 -0600 Subject: [PATCH 017/189] Updating BSM counts start/end time text color --- webapp/src/components/map/custom-table.css | 3 +++ webapp/src/components/map/custom-table.tsx | 4 +++- webapp/src/pages/Map.tsx | 6 ++++-- 3 files changed, 10 insertions(+), 3 deletions(-) create mode 100644 webapp/src/components/map/custom-table.css diff --git a/webapp/src/components/map/custom-table.css b/webapp/src/components/map/custom-table.css new file mode 100644 index 000000000..7650a7142 --- /dev/null +++ b/webapp/src/components/map/custom-table.css @@ -0,0 +1,3 @@ +.mapSideTable { + min-width: 50px; +} diff --git a/webapp/src/components/map/custom-table.tsx b/webapp/src/components/map/custom-table.tsx index 5597e9a8b..55c4b214b 100644 --- a/webapp/src/components/map/custom-table.tsx +++ b/webapp/src/components/map/custom-table.tsx @@ -12,6 +12,8 @@ import { TableCell, } from '@mui/material' +import './custom-table.css' + interface CustomTableProps { headers: string[] data: (string | number)[][] @@ -25,7 +27,7 @@ export const CustomTable = (props: CustomTableProps) => { return ( - +
{headers.map((head) => ( diff --git a/webapp/src/pages/Map.tsx b/webapp/src/pages/Map.tsx index 1a77e672c..af56073f5 100644 --- a/webapp/src/pages/Map.tsx +++ b/webapp/src/pages/Map.tsx @@ -1031,7 +1031,8 @@ function MapPage(props: MapPageProps) { dateChanged(e.toDate(), 'start') } }} - renderInput={(params) => } + renderInput={(params) => } + InputLabelProps={{ style: { color: 'black' } }} /> @@ -1047,7 +1048,8 @@ function MapPage(props: MapPageProps) { dateChanged(e.toDate(), 'end') } }} - renderInput={(params) => } + renderInput={(params) => } + InputLabelProps={{ style: { color: 'black' } }} /> From 6691d938339e883d268da73141cfafbea0b9e0cf Mon Sep 17 00:00:00 2001 From: Michael7371 <40476797+Michael7371@users.noreply.github.com> Date: Wed, 8 May 2024 14:07:16 -0600 Subject: [PATCH 018/189] updates with unit tests added unit tests added docker compose to spin up local deployment tested on k8 cluster --- docker-compose-obu-ota-server.yml | 48 +++++++ resources/kubernetes/obu-ota-server.yaml | 118 ++++++++++++++++++ resources/ota/firmwares/.gitignore | 1 + resources/ota/firmwares/README.md | 3 + resources/ota/nginx/Dockerfile | 9 ++ resources/ota/nginx/gen_dhparam.sh | 1 + resources/ota/nginx/nginx-plain.conf | 53 ++++++++ resources/ota/nginx/nginx-ssl.conf | 92 ++++++++++++++ resources/ota/nginx/set_up_users.sh | 24 ++++ resources/ota/nginx/ssl/.gitignore | 2 + resources/ota/nginx/ssl/README.md | 3 + services/Dockerfile.obu_ota_server | 11 ++ .../images/firmware_manager/upgrader.py | 2 +- .../images/obu_ota_server/obu_ota_server.py | 16 +-- .../images/obu_ota_server/requirements.txt | 3 +- .../addons/images/obu_ota_server/sample.env | 21 +++- .../firmware_manager/test_download_blob.py | 30 ----- .../tests/firmware_manager/test_upgrader.py | 7 +- .../test_commsignia_manifest.py | 58 +++++++++ .../obu_ota_server/test_obu_ota_server.py | 54 ++++++-- services/common/gcs_utils.py | 17 ++- services/common/tests/test_gcs_utils.py | 84 +++++++++++++ services/requirements.txt | 1 + 23 files changed, 597 insertions(+), 61 deletions(-) create mode 100644 docker-compose-obu-ota-server.yml create mode 100644 resources/kubernetes/obu-ota-server.yaml create mode 100644 resources/ota/firmwares/.gitignore create mode 100644 resources/ota/firmwares/README.md create mode 100644 resources/ota/nginx/Dockerfile create mode 100644 resources/ota/nginx/gen_dhparam.sh create mode 100644 resources/ota/nginx/nginx-plain.conf create mode 100644 resources/ota/nginx/nginx-ssl.conf create mode 100644 resources/ota/nginx/set_up_users.sh create mode 100644 resources/ota/nginx/ssl/.gitignore create mode 100644 resources/ota/nginx/ssl/README.md create mode 100644 services/Dockerfile.obu_ota_server delete mode 100644 services/addons/tests/firmware_manager/test_download_blob.py create mode 100644 services/addons/tests/obu_ota_server/test_commsignia_manifest.py create mode 100644 services/common/tests/test_gcs_utils.py diff --git a/docker-compose-obu-ota-server.yml b/docker-compose-obu-ota-server.yml new file mode 100644 index 000000000..828305cc3 --- /dev/null +++ b/docker-compose-obu-ota-server.yml @@ -0,0 +1,48 @@ +version: '3' +services: + # OBU OTA Server and Nginx proxy services + jpo_ota_backend: + build: + context: ./services + dockerfile: Dockerfile.obu_ota_server + image: obu_ota_server:latest + restart: unless-stopped + ports: + - 8085:8085 + environment: + SERVER_HOST: ${OBU_OTA_SERVER_HOST} + LOGGING_LEVEL: ${OBU_OTA_SERVER_LOGGING_LEVEL} + BLOB_STORAGE_PROVIDER: ${BLOB_STORAGE_PROVIDER} + volumes: + - ./resources/ota:/firmwares + logging: + options: + max-size: '10m' + max-file: '5' + + jpo_ota_nginx: + build: + context: resources/ota/nginx + image: obu_ota_nginx:latest + restart: unless-stopped + ports: + - 80:80 + - 443:443 + environment: + NGINX_ENVSUBST_OUTPUT_DIR: /etc/nginx + + SERVER_HOST: ${OBU_OTA_SERVER_HOST} + OTA_USERNAME: ${OTA_USERNAME} + OTA_PASSWORD: ${OTA_PASSWORD} + volumes: + - ./resources/ota/nginx/nginx-${NGINX_ENCRYPTION}.conf:/etc/nginx/templates/nginx.conf.template + - ./resources/ota/nginx/set_up_users.sh:/docker-entrypoint.d/set_up_users.sh + - ./resources/ota/nginx/gen_dhparam.sh:/docker-entrypoint.d/gen_dhparam.sh + - ./resources/ota/nginx/ssl/${SERVER_CERT_FILE}:/etc/ssl/certs/ota_server.crt + - ./resources/ota/nginx/ssl/${SERVER_KEY_FILE}:/etc/ssl/private/ota_server.key + depends_on: + - jpo_ota_backend + logging: + options: + max-size: '10m' + max-file: '5' diff --git a/resources/kubernetes/obu-ota-server.yaml b/resources/kubernetes/obu-ota-server.yaml new file mode 100644 index 000000000..6c6b94f3b --- /dev/null +++ b/resources/kubernetes/obu-ota-server.yaml @@ -0,0 +1,118 @@ +# K8s ManagedCertificate for using SSL/TLS with a domain +# Domain and DNS forwarding to the Ingress endpoint must be configured +apiVersion: networking.gke.io/v1 +kind: ManagedCertificate +metadata: + name: obu-ota-server-managed-cert +spec: + domains: + - your.domain.com +--- +# K8s FrontendConfig for applying SSL certificate to Ingress +# Requires 'obu-ota-server-ssl-policy' SSL policy to exist +apiVersion: networking.gke.io/v1beta1 +kind: FrontendConfig +metadata: + name: obu-ota-server-frontend + labels: + app: obu-ota-server +spec: + redirectToHttps: + enabled: true + sslPolicy: obu-ota-server-ssl-policy +--- +# NodePort to expose CV Manager web application +apiVersion: v1 +kind: Service +metadata: + labels: + app: obu-ota-server + name: obu-ota-server-service-internal +spec: + ports: + - port: 80 + protocol: TCP + targetPort: 80 + selector: + app: obu-ota-server + type: NodePort +--- +# External HTTP/HTTPS Ingress to internal NodePort +# Requires 'cv-manager-ip' as a global static external IP to be reserved +apiVersion: networking.k8s.io/v1 +kind: Ingress +metadata: + name: obu-ota-server-ingress + labels: + app: obu-ota-server + annotations: + kubernetes.io/ingress.regional-static-ip-name: 'cv-manager-ip' + networking.gke.io/managed-certificates: 'obu-ota-server-managed-cert' + networking.gke.io/v1beta1.FrontendConfig: 'obu-ota-server-frontend' + # basic auth config: + nginx.ingress.kubernetes.io/auth-type: basic + nginx.ingress.kubernetes.io/auth-secret: basic-auth + nginx.ingress.kubernetes.io/auth-realm: 'Authentication Required - foo' +spec: + defaultBackend: + service: + name: obu-ota-server-service-internal + port: + number: 80 + + ingressClassName: nginx + rules: + - host: foo.bar.com + http: + paths: + - path: / + pathType: Prefix + backend: + service: + name: http-svc + port: + number: 80 +--- +# Limits the number of pods that are down simultaneously +apiVersion: policy/v1 +kind: PodDisruptionBudget +metadata: + name: obu-ota-server-pdb +spec: + selector: + matchLabels: + app: obu-ota-server + maxUnavailable: 1 +--- +apiVersion: 'apps/v1' +kind: 'Deployment' +metadata: + name: obu-ota-server +spec: + replicas: 1 + selector: + matchLabels: + app: obu-ota-server + template: + metadata: + labels: + app: obu-ota-server + spec: + containers: + - name: obu-ota-server + imagePullPolicy: Always + # Replace image with an actual built jpo-cvmanager webapp image from an image repository + image: jpoode-cvmanager-webapp:tag + resources: + requests: + memory: '4Gi' + cpu: '2' + ephemeral-storage: '4Gi' + limits: + memory: '4Gi' + cpu: '2' + ephemeral-storage: '4Gi' + tty: true + stdin: true + ports: + - containerPort: 80 diff --git a/resources/ota/firmwares/.gitignore b/resources/ota/firmwares/.gitignore new file mode 100644 index 000000000..df6ae8cdc --- /dev/null +++ b/resources/ota/firmwares/.gitignore @@ -0,0 +1 @@ +*.tar.sig diff --git a/resources/ota/firmwares/README.md b/resources/ota/firmwares/README.md new file mode 100644 index 000000000..6414fcba1 --- /dev/null +++ b/resources/ota/firmwares/README.md @@ -0,0 +1,3 @@ +# OTA Firmwares + +This folder is used as an attached volume to the OBU OTA server and stores the OBU firmware files either stored locally or downloaded from GCP. diff --git a/resources/ota/nginx/Dockerfile b/resources/ota/nginx/Dockerfile new file mode 100644 index 000000000..65aba528e --- /dev/null +++ b/resources/ota/nginx/Dockerfile @@ -0,0 +1,9 @@ +FROM nginx:1.25.4-alpine + +RUN apk update && apk add \ + apache2-utils \ + jq \ + openssl \ + bash + +CMD ["nginx", "-g", "daemon off;"] \ No newline at end of file diff --git a/resources/ota/nginx/gen_dhparam.sh b/resources/ota/nginx/gen_dhparam.sh new file mode 100644 index 000000000..824c4cd2b --- /dev/null +++ b/resources/ota/nginx/gen_dhparam.sh @@ -0,0 +1 @@ +openssl dhparam -out /etc/nginx/dhparam.pem 2048 \ No newline at end of file diff --git a/resources/ota/nginx/nginx-plain.conf b/resources/ota/nginx/nginx-plain.conf new file mode 100644 index 000000000..0b5d43234 --- /dev/null +++ b/resources/ota/nginx/nginx-plain.conf @@ -0,0 +1,53 @@ +events { + worker_connections 768; +} + +http { + + upstream api { + server jpo_ota_backend:8085; + } + + sendfile on; + tcp_nopush on; + tcp_nodelay on; + keepalive_timeout 65; + types_hash_max_size 2048; + + + server { + listen 80; + server_name http://$SERVER_HOST; + + auth_basic "Basic Protected Area"; + auth_basic_user_file /var/www/.htpasswd; + + client_max_body_size 300M; + + location ~ /\.ht { + deny all; + } + + # location ^~ /.well-known/acme-challenge { + # default_type text/plain; + # root /var/www/letsencrypt; + # } + + location / { + proxy_pass http://api/; + proxy_set_header Host $http_host; + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + proxy_set_header X-Forwarded-Proto $scheme; + proxy_set_header Range $http_range; + + proxy_http_version 1.1; + proxy_set_header Connection ''; + proxy_buffering off; + proxy_cache off; + proxy_read_timeout 86400; + + access_log /var/log/nginx/access_manifest.log; + } + } +} \ No newline at end of file diff --git a/resources/ota/nginx/nginx-ssl.conf b/resources/ota/nginx/nginx-ssl.conf new file mode 100644 index 000000000..e93ba5b67 --- /dev/null +++ b/resources/ota/nginx/nginx-ssl.conf @@ -0,0 +1,92 @@ +events { + worker_connections 768; +} + +http { + + upstream api { + server jpo_ota_backend:8085; + } + + sendfile on; + tcp_nopush on; + tcp_nodelay on; + keepalive_timeout 65; + types_hash_max_size 2048; + + ## + # SSL Settings + ## + + ssl_protocols TLSv1 TLSv1.1 TLSv1.2 TLSv1.3; # Dropping SSLv3, ref: POODLE + ssl_prefer_server_ciphers on; + ssl_dhparam "/etc/nginx/dhparam.pem"; + ssl_ciphers ECDHE-RSA-AES256-GCM-SHA384:ECDHE-RSA-CHACHA20-POLY1305:ECDHE-ARIA256-GCM-SHA384:DHE-RSA-AES256-GCM-SHA384:DHE-RSA-CHACHA20-POLY1305:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ARIA128-GCM-SHA256:DHE-RSA-AES128-GCM-SHA256; + add_header Strict-Transport-Security "max-age=31557600; includeSubDomains"; + ssl_stapling on; + ssl_stapling_verify on; + + ## + # General Logging Settings + ## + + access_log /var/log/nginx/access.log; + + + # HTTP forwards everything to HTTPS + # + server { + listen 80; + + server_name http://$SERVER_HOST; + + location / { + return 301 https://$host$request_uri; + } + + # deny access to .htaccess files, if Apache's document root + # concurs with nginx's one + location ~ /\.ht { + deny all; + } + } + + server { + listen 443 default_server ssl; + server_name https://$SERVER_HOST; + + ssl_certificate /etc/ssl/certs/ota_server.crt; + ssl_certificate_key /etc/ssl/private/ota_server.key; + + auth_basic "Basic Protected Area"; + auth_basic_user_file /var/www/.htpasswd; + + client_max_body_size 300M; + + location ~ /\.ht { + deny all; + } + + # location ^~ /.well-known/acme-challenge { + # default_type text/plain; + # root /var/www/letsencrypt; + # } + + location / { + proxy_pass http://api/; + proxy_set_header Host $http_host; + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + proxy_set_header X-Forwarded-Proto $scheme; + proxy_set_header Range $http_range; + + proxy_http_version 1.1; + proxy_set_header Connection ''; + proxy_buffering off; + proxy_cache off; + proxy_read_timeout 86400; + + access_log /var/log/nginx/access.log; + } + } +} \ No newline at end of file diff --git a/resources/ota/nginx/set_up_users.sh b/resources/ota/nginx/set_up_users.sh new file mode 100644 index 000000000..e9696bd82 --- /dev/null +++ b/resources/ota/nginx/set_up_users.sh @@ -0,0 +1,24 @@ +#!/usr/bin/env bash + +function generate_user() { + htpasswd -c -b /var/www/.htpasswd "${OTA_USERNAME}" "${OTA_PASSWORD}" +} + +function main() { + mkdir -p /var/www/ + chmod 755 /var/www/ + touch /var/www/.htpasswd + generate_user +} + +if [[ -z "${OTA_USERNAME}" ]]; then + echo "Error: USERNAME environment variable is not set." + exit 1 +fi + +if [[ -z "${OTA_PASSWORD}" ]]; then + echo "Error: PASSWORD environment variable is not set." + exit 1 +fi + +main \ No newline at end of file diff --git a/resources/ota/nginx/ssl/.gitignore b/resources/ota/nginx/ssl/.gitignore new file mode 100644 index 000000000..592bac37d --- /dev/null +++ b/resources/ota/nginx/ssl/.gitignore @@ -0,0 +1,2 @@ +*.crt +*.key \ No newline at end of file diff --git a/resources/ota/nginx/ssl/README.md b/resources/ota/nginx/ssl/README.md new file mode 100644 index 000000000..39172c5ed --- /dev/null +++ b/resources/ota/nginx/ssl/README.md @@ -0,0 +1,3 @@ +# SSL + +This folder is used as an attached volume to the NGINX service and must include the server's .crt and .key file. All .crt and .key files are not tracked through git. \ No newline at end of file diff --git a/services/Dockerfile.obu_ota_server b/services/Dockerfile.obu_ota_server new file mode 100644 index 000000000..b3afbf2bb --- /dev/null +++ b/services/Dockerfile.obu_ota_server @@ -0,0 +1,11 @@ +FROM python:3.12-alpine + +WORKDIR /home + +ADD addons/images/obu_ota_server/requirements.txt . +ADD addons/images/obu_ota_server/*.py . +ADD common/*.py ./common/ + +RUN pip install --no-cache-dir --upgrade -r requirements.txt + +CMD ["uvicorn", "obu_ota_server:app", "--host", "0.0.0.0", "--port", "8085"] \ No newline at end of file diff --git a/services/addons/images/firmware_manager/upgrader.py b/services/addons/images/firmware_manager/upgrader.py index bb52e7fef..5a77d74e4 100644 --- a/services/addons/images/firmware_manager/upgrader.py +++ b/services/addons/images/firmware_manager/upgrader.py @@ -2,7 +2,7 @@ import abc import subprocess import time -from services.common import gcs_utils +from common import gcs_utils import logging import os import requests diff --git a/services/addons/images/obu_ota_server/obu_ota_server.py b/services/addons/images/obu_ota_server/obu_ota_server.py index a24ea8f55..4c2842a2a 100644 --- a/services/addons/images/obu_ota_server/obu_ota_server.py +++ b/services/addons/images/obu_ota_server/obu_ota_server.py @@ -1,5 +1,5 @@ from fastapi import FastAPI, Request, Response, HTTPException -from services.common import gcs_utils +from common import gcs_utils import commsginia_manifest import os import glob @@ -28,7 +28,8 @@ def get_firmware_list(): if blob_storage_provider.upper() == "LOCAL": files = glob.glob(f"/firmwares/*{file_extension}") elif blob_storage_provider.upper() == "GCP": - files = gcs_utils.list_gcs_blobs("firmwares", file_extension) + blob_storage_path = os.getenv("BLOB_STORAGE_PATH", "LOCAL") + files = gcs_utils.list_gcs_blobs(blob_storage_path, file_extension) return files @@ -86,7 +87,7 @@ async def read_file(file_path, start, end): end = file_size await file.seek(start) data = await file.read(end - start) - return data, file_size + return data, file_size, end except Exception as e: logging.error(f"Error reading file: {e}") raise HTTPException(status_code=500, detail="Error reading file") @@ -100,16 +101,15 @@ async def get_fw(request: Request, firmware_id: str): if not get_firmware(firmware_id, file_path): raise HTTPException(status_code=404, detail="Firmware not found") - start, end = parse_range_header(request.headers.get("Range")) - data, file_size = await read_file(file_path, start, end) + header_start, header_end = parse_range_header(request.headers.get("Range")) + data, file_size, end = await read_file(file_path, header_start, header_end) headers = { - "Content-Range": f"bytes {start}-{end-1}/{file_size}", - "Content-Length": str(end - start), + "Content-Range": f"bytes {header_start}-{end-1}/{file_size}", + "Content-Length": str(end - header_start), "Accept-Ranges": "bytes", } - logging.debug(f"Completed Firmware Response for client: {request.client}") return Response( content=data, media_type="application/octet-stream", headers=headers ) diff --git a/services/addons/images/obu_ota_server/requirements.txt b/services/addons/images/obu_ota_server/requirements.txt index 39405fc15..d4e91c8ff 100644 --- a/services/addons/images/obu_ota_server/requirements.txt +++ b/services/addons/images/obu_ota_server/requirements.txt @@ -1,4 +1,5 @@ fastapi==0.110.0 pydantic==2.6.2 uvicorn==0.27.1 -aiofiles==23.2.1 \ No newline at end of file +aiofiles==23.2.1 +google-cloud-storage==2.14.0 diff --git a/services/addons/images/obu_ota_server/sample.env b/services/addons/images/obu_ota_server/sample.env index dd6263afa..8d7817f5d 100644 --- a/services/addons/images/obu_ota_server/sample.env +++ b/services/addons/images/obu_ota_server/sample.env @@ -1,12 +1,27 @@ +# Python logging ptions: "INFO", "DEBUG", "WARN", "ERROR" LOGGING_LEVEL="INFO" -SERVER_HOST="localhost" +# Routeable host name for the server +SERVER_HOST = "localhost" # Blob storage variables # For local deployments set the BLOB_STORAGE_PROVIDER to "LOCAL" -BLOB_STORAGE_PROVIDER="GCP" +BLOB_STORAGE_PROVIDER="LOCAL" # For users using GCP cloud storage BLOB_STORAGE_BUCKET="" GCP_PROJECT="" -GOOGLE_APPLICATION_CREDENTIALS="" \ No newline at end of file +GOOGLE_APPLICATION_CREDENTIALS="" + + +# Nginx basic auth username and password +OTA_USERNAME = "admin" +OTA_PASSWORD = "admin" + +# Nginx encryption options: "plain", "ssl" +# Note that this just changes the config file attached as a volume to the Nginx container +NGINX_ENCRYPTION="plain" + +# SSL file name in path /docker/nginx/ssl/ +SERVER_CERT_FILE="ota_server.crt" +SERVER_KEY_FILE="ota_server.key" \ No newline at end of file diff --git a/services/addons/tests/firmware_manager/test_download_blob.py b/services/addons/tests/firmware_manager/test_download_blob.py deleted file mode 100644 index cc44ced9d..000000000 --- a/services/addons/tests/firmware_manager/test_download_blob.py +++ /dev/null @@ -1,30 +0,0 @@ -from unittest.mock import patch -import os - -from addons.images.firmware_manager import download_blob - - -@patch.dict( - os.environ, {"GCP_PROJECT": "test-project", "BLOB_STORAGE_BUCKET": "test-bucket"} -) -@patch("addons.images.firmware_manager.download_blob.logging") -@patch("addons.images.firmware_manager.download_blob.storage.Client") -def test_download_gcp_blob(mock_storage_client, mock_logging): - # mock - mock_client = mock_storage_client.return_value - mock_bucket = mock_client.get_bucket.return_value - mock_blob = mock_bucket.blob.return_value - - # run - download_blob.download_gcp_blob( - blob_name="test.blob", destination_file_name="/home/test/" - ) - - # validate - mock_storage_client.assert_called_with("test-project") - mock_client.get_bucket.assert_called_with("test-bucket") - mock_bucket.blob.assert_called_with("test.blob") - mock_blob.download_to_filename.assert_called_with("/home/test/") - mock_logging.info.assert_called_with( - "Downloaded storage object test.blob from bucket test-bucket to local file /home/test/." - ) diff --git a/services/addons/tests/firmware_manager/test_upgrader.py b/services/addons/tests/firmware_manager/test_upgrader.py index 417c2b084..b83b64a94 100644 --- a/services/addons/tests/firmware_manager/test_upgrader.py +++ b/services/addons/tests/firmware_manager/test_upgrader.py @@ -71,7 +71,7 @@ def test_cleanup_not_exist(mock_Path, mock_shutil): @patch.dict(os.environ, {"BLOB_STORAGE_PROVIDER": "GCP"}) -@patch("addons.images.firmware_manager.upgrader.download_blob.download_gcp_blob") +@patch("common.gcs_utils.download_gcp_blob") @patch("addons.images.firmware_manager.upgrader.Path") def test_download_blob_gcp(mock_Path, mock_download_gcp_blob): mock_path_obj = mock_Path.return_value @@ -88,7 +88,7 @@ def test_download_blob_gcp(mock_Path, mock_download_gcp_blob): @patch.dict(os.environ, {"BLOB_STORAGE_PROVIDER": "Test"}) @patch("addons.images.firmware_manager.upgrader.logging") -@patch("addons.images.firmware_manager.upgrader.download_blob.download_gcp_blob") +@patch("common.gcs_utils.download_gcp_blob") @patch("addons.images.firmware_manager.upgrader.Path") def test_download_blob_not_supported(mock_Path, mock_download_gcp_blob, mock_logging): mock_path_obj = mock_Path.return_value @@ -143,6 +143,7 @@ def test_notify_firmware_manager_exception(mock_requests, mock_logging): "Failed to connect to the Firmware Manager API for '8.8.8.8': Exception occurred during upgrade" ) + @patch("addons.images.firmware_manager.upgrader.time") @patch("addons.images.firmware_manager.upgrader.subprocess") def test_upgrader_wait_until_online_success(mock_subprocess, mock_time): @@ -168,4 +169,4 @@ def test_upgrader_wait_until_online_timeout(mock_subprocess, mock_time): code = test_upgrader.wait_until_online() assert code == -1 - assert mock_time.sleep.call_count == 180 \ No newline at end of file + assert mock_time.sleep.call_count == 180 diff --git a/services/addons/tests/obu_ota_server/test_commsignia_manifest.py b/services/addons/tests/obu_ota_server/test_commsignia_manifest.py new file mode 100644 index 000000000..999f7b657 --- /dev/null +++ b/services/addons/tests/obu_ota_server/test_commsignia_manifest.py @@ -0,0 +1,58 @@ +import pytest +import unittest.mock as mock +from addons.images.obu_ota_server.commsginia_manifest import add_contents + + +def test_add_contents(): + # Mock server and firmware_list + server = "http://localhost" + firmware_list = [ + "/path/to/ob4-generic-ro-secureboot-yZZ.XX.YY-b012345.tar.sig", + "/path/to/ob4-generic-ro-secureboot-yZZ.XX.YY-b012346.tar.sig", + "/path/to/bad-firmware-file-name.tar.sig", + ] + + # Mock os.path and os.stat + with mock.patch("os.path.getmtime", return_value=1633022452), mock.patch( + "os.stat", return_value=mock.Mock(st_size=1024) + ): + + # Call the function with the mock data + manifest = add_contents(server, firmware_list) + + # Check the manifest content + assert manifest["totalElements"] == 2 + assert manifest["numberOfElements"] == 2 + assert len(manifest["content"]) == 2 + + # Check the content of the first firmware + assert ( + manifest["content"][0]["name"] + == "ob4-generic-ro-secureboot-yZZ.XX.YY-b012345.tar.sig" + ) + assert manifest["content"][0]["size"] == 1024 + + # Check the content of the second firmware + assert ( + manifest["content"][1]["name"] + == "ob4-generic-ro-secureboot-yZZ.XX.YY-b012346.tar.sig" + ) + assert manifest["content"][1]["size"] == 1024 + + +def test_add_contents_max_firmwares(): + # Mock server and firmware_list with more firmwares than the size of the manifest + server = "http://localhost" + firmware_list = [ + f"/path/to/ob4-generic-ro-secureboot-yZZ.XX.YY-b{i}.tar.sig" + for i in range(1, 35) + ] + + # Mock os.path and os.stat + with mock.patch("os.path.getmtime", return_value=1633022452), mock.patch( + "os.stat", return_value=mock.Mock(st_size=1024) + ): + + # Call the function with the mock data and check if it raises an AttributeError + with pytest.raises(AttributeError): + add_contents(server, firmware_list) diff --git a/services/addons/tests/obu_ota_server/test_obu_ota_server.py b/services/addons/tests/obu_ota_server/test_obu_ota_server.py index 2e3fd7bf6..94baa12c3 100644 --- a/services/addons/tests/obu_ota_server/test_obu_ota_server.py +++ b/services/addons/tests/obu_ota_server/test_obu_ota_server.py @@ -1,14 +1,10 @@ -import asyncio import pytest -import aiofiles -import json import tempfile import os - from httpx import AsyncClient -from fastapi import HTTPException, Request, Response +from fastapi import HTTPException +from unittest.mock import patch -from unittest.mock import MagicMock, AsyncMock, patch from addons.images.obu_ota_server.obu_ota_server import ( get_firmware_list, get_firmware, @@ -32,7 +28,7 @@ def test_get_firmware_list_local(mock_glob, mock_getenv): @patch("os.getenv") -@patch("services.common.gcs_utils.list_gcs_blobs") +@patch("common.gcs_utils.list_gcs_blobs") def test_get_firmware_list_gcs(mock_list_gcs_blobs, mock_getenv): mock_getenv.return_value = "GCP" mock_list_gcs_blobs.return_value = [ @@ -49,7 +45,7 @@ def test_get_firmware_list_gcs(mock_list_gcs_blobs, mock_getenv): @patch("os.getenv") @patch("os.path.exists") -@patch("services.common.gcs_utils.list_gcs_blobs") +@patch("common.gcs_utils.list_gcs_blobs") def test_get_firmware_local_fail(mock_gcs_utils, mock_os_path_exists, mock_os_getenv): mock_os_getenv.return_value = "LOCAL" mock_os_path_exists.return_value = False @@ -67,7 +63,7 @@ def test_get_firmware_local_fail(mock_gcs_utils, mock_os_path_exists, mock_os_ge @patch("os.getenv") @patch("os.path.exists") -@patch("services.common.gcs_utils.list_gcs_blobs") +@patch("common.gcs_utils.list_gcs_blobs") def test_get_firmware_local_success( mock_gcs_utils, mock_os_path_exists, mock_os_getenv ): @@ -87,7 +83,7 @@ def test_get_firmware_local_success( @patch("os.getenv") @patch("os.path.exists") -@patch("services.common.gcs_utils.download_gcp_blob") +@patch("common.gcs_utils.download_gcp_blob") def test_get_firmware_gcs_success( mock_download_gcp_blob, mock_os_path_exists, mock_os_getenv ): @@ -108,7 +104,7 @@ def test_get_firmware_gcs_success( @patch("os.getenv") @patch("os.path.exists") -@patch("services.common.gcs_utils.download_gcp_blob") +@patch("common.gcs_utils.download_gcp_blob") def test_get_firmware_gcs_failure( mock_download_gcp_blob, mock_os_path_exists, mock_os_getenv ): @@ -160,10 +156,11 @@ async def test_read_file(): temp.write(b"Test data") temp_path = temp.name - data, file_size = await read_file(temp_path, 0, 9) + data, file_size, end = await read_file(temp_path, 0, 9) assert data == b"Test data" assert file_size == 9 + assert end == 9 # Clean up the temporary file os.remove(temp_path) @@ -172,10 +169,26 @@ async def test_read_file(): @pytest.mark.asyncio async def test_read_file_failure(): with pytest.raises(HTTPException) as excinfo: - data, file_size = await read_file("fake_path", 0, 9) + data, file_size, end = await read_file("fake_path", 0, 9) assert str(excinfo.value) == "500: Error reading file" +@pytest.mark.asyncio +async def test_read_file_no_end_range(): + with tempfile.NamedTemporaryFile(delete=False) as temp: + temp.write(b"Test data") + temp_path = temp.name + + data, file_size, end = await read_file(temp_path, 0, None) + + assert data == b"Test data" + assert file_size == 9 + assert end == 9 + + # Clean up the temporary file + os.remove(temp_path) + + @pytest.mark.anyio async def test_read_root(): async with AsyncClient(app=app, base_url="http://test") as ac: @@ -199,5 +212,20 @@ async def test_get_manifest(mock_commsginia_manifest, mock_get_firmware_list): assert response.json() == {"json": "data"} +@pytest.mark.anyio +@patch("addons.images.obu_ota_server.obu_ota_server.get_firmware") +@patch("addons.images.obu_ota_server.obu_ota_server.parse_range_header") +@patch("addons.images.obu_ota_server.obu_ota_server.read_file") +async def test_get_fw(mock_read_file, mock_parse_range_header, mock_get_firmware): + mock_get_firmware.return_value = True + mock_parse_range_header.return_value = 0, 100 + mock_read_file.return_value = b"Test data", 100, 100 + async with AsyncClient(app=app, base_url="http://test") as ac: + response = await ac.get("/firmwares/test_firmware_id") + assert response.status_code == 200 + assert response.content == b"Test data" + assert response.headers["Content-Length"] == "100" + + if __name__ == "__main__": pytest.main() diff --git a/services/common/gcs_utils.py b/services/common/gcs_utils.py index 2a5c9cd35..9c41d8930 100644 --- a/services/common/gcs_utils.py +++ b/services/common/gcs_utils.py @@ -23,9 +23,22 @@ def list_gcs_blobs(gcs_prefix, file_extension): files = [] gcp_project = os.environ.get("GCP_PROJECT") bucket_name = os.environ.get("BLOB_STORAGE_BUCKET") + logging.debug(f"Listing blobs in bucket {bucket_name} with prefix {gcs_prefix}.") storage_client = storage.Client(gcp_project) - blobs = storage_client.list_blobs(bucket_name=bucket_name, prefix=gcs_prefix) + blobs = storage_client.list_blobs(bucket_name, prefix=gcs_prefix, delimiter="/") + blob_count = 0 + download_count = 0 for blob in blobs: + logging.debug(f"Blob: {blob.name}") if blob.name.endswith(file_extension): - files.append(blob.name) + path = f"/firmwares/{blob.name.split(gcs_prefix)[-1]}" + files.append(path) + blob.download_to_filename(path) + logging.debug(f"Downloaded blob {blob.name} to {path}") + download_count += 1 + else: + logging.debug(f"Blob {blob.name} does not end with {file_extension}") + blob_count += 1 + logging.debug(f"Found {blob_count} blobs, and downloaded {download_count} files.") + logging.debug(f"Found {len(files)} files with extension {file_extension}.") return files diff --git a/services/common/tests/test_gcs_utils.py b/services/common/tests/test_gcs_utils.py new file mode 100644 index 000000000..e769e2303 --- /dev/null +++ b/services/common/tests/test_gcs_utils.py @@ -0,0 +1,84 @@ +from unittest.mock import patch, MagicMock +import os + +from common import gcs_utils + + +@patch.dict( + os.environ, {"GCP_PROJECT": "test-project", "BLOB_STORAGE_BUCKET": "test-bucket"} +) +@patch("common.gcs_utils.logging") +@patch("common.gcs_utils.storage.Client") +def test_download_gcp_blob(mock_storage_client, mock_logging): + # mock + mock_client = mock_storage_client.return_value + mock_bucket = mock_client.get_bucket.return_value + mock_blob = mock_bucket.blob.return_value + + # run + gcs_utils.download_gcp_blob( + blob_name="test.blob", destination_file_name="/home/test/" + ) + + # validate + mock_storage_client.assert_called_with("test-project") + mock_client.get_bucket.assert_called_with("test-bucket") + mock_bucket.blob.assert_called_with("test.blob") + mock_blob.download_to_filename.assert_called_with("/home/test/") + mock_logging.info.assert_called_with( + "Downloaded storage object test.blob from bucket test-bucket to local file /home/test/." + ) + + +@patch.dict( + os.environ, {"GCP_PROJECT": "test-project", "BLOB_STORAGE_BUCKET": "test-bucket"} +) +@patch("common.gcs_utils.storage.Client") +def test_list_gcs_blobs(mock_storage_client): + # mock + mock_client = mock_storage_client.return_value + # mock_bucket = mock_client.get_bucket.return_value + mock_blobs = [] + for filename in ["file1.txt", "file2.jpg", "file3.txt"]: + mock_blob = MagicMock() + mock_blob.name = filename + mock_blobs.append(mock_blob) + + mock_client.list_blobs.return_value = mock_blobs + + # run + result = gcs_utils.list_gcs_blobs( + gcs_prefix="path/to/files/", file_extension=".txt" + ) + + # validate + mock_storage_client.assert_called_with("test-project") + mock_client.list_blobs.assert_called_with( + bucket_name="test-bucket", prefix="path/to/files/" + ) + assert result == ["file1.txt", "file3.txt"] + + +@patch.dict( + os.environ, {"GCP_PROJECT": "test-project", "BLOB_STORAGE_BUCKET": "test-bucket"} +) +@patch("common.gcs_utils.storage.Client") +def test_list_gcs_blobs_empty(mock_storage_client): + # mock + mock_client = mock_storage_client.return_value + # mock_bucket = mock_client.get_bucket.return_value + mock_blobs = [] + + mock_client.list_blobs.return_value = mock_blobs + + # run + result = gcs_utils.list_gcs_blobs( + gcs_prefix="path/to/files/", file_extension=".txt" + ) + + # validate + mock_storage_client.assert_called_with("test-project") + mock_client.list_blobs.assert_called_with( + bucket_name="test-bucket", prefix="path/to/files/" + ) + assert result == [] diff --git a/services/requirements.txt b/services/requirements.txt index e6aaff7e9..a70888602 100644 --- a/services/requirements.txt +++ b/services/requirements.txt @@ -32,5 +32,6 @@ waitress==2.1.2 fastapi==0.110.0 pydantic==2.6.2 uvicorn==0.27.1 +pytest-asyncio==0.23.3 aiofiles==23.2.1 httpx==0.27.0 \ No newline at end of file From d3e7377adac0942f789246c560d67d85bc4489e7 Mon Sep 17 00:00:00 2001 From: Marc Wodahl Date: Wed, 15 May 2024 14:49:25 -0600 Subject: [PATCH 019/189] Add docker-compose-mongo.yml for local MongoDB support --- README.md | 65 ++++++----- docker-compose-mongo.yml | 54 +++++++++ mongo_scripts/create_indexes.js | 197 ++++++++++++++++++++++++++++++++ mongo_scripts/setup_mongo.sh | 11 ++ sample.env | 16 ++- 5 files changed, 316 insertions(+), 27 deletions(-) create mode 100644 docker-compose-mongo.yml create mode 100644 mongo_scripts/create_indexes.js create mode 100644 mongo_scripts/setup_mongo.sh diff --git a/README.md b/README.md index ff9a01f81..49a4254d1 100644 --- a/README.md +++ b/README.md @@ -88,44 +88,47 @@ The following steps are intended to help get a new user up and running the JPO C ### Debugging -Note that it is recommended to work with the Python API from a [virtual environment](https://docs.python.org/3/library/venv.html). +Note that it is recommended to work with the Python API from a [virtual environment](https://docs.python.org/3/library/venv.html). #### Setting up a virtual environment from the command line + 1. Verify that you have Python 3.12.2 installed on your machine by running the following command: - ```bash - python3.12 --version - ``` - ```cmd - python --version - ``` - If you have a different version installed, download and install Python 3.12.2 from the [Python website](https://www.python.org/downloads/). + ```bash + python3.12 --version + ``` + ```cmd + python --version + ``` + If you have a different version installed, download and install Python 3.12.2 from the [Python website](https://www.python.org/downloads/). 2. Open a terminal and navigate to the root of the project. 3. Run the following command to create a virtual environment in the project root: - ```bash - python3.12 -m venv .venv - ``` - ```cmd - python -m venv .venv - ``` + ```bash + python3.12 -m venv .venv + ``` + ```cmd + python -m venv .venv + ``` 4. Activate the virtual environment: - ```bash - source .venv/bin/activate - ``` - ```cmd - .venv\Scripts\activate - ``` + ```bash + source .venv/bin/activate + ``` + ```cmd + .venv\Scripts\activate + ``` 5. Install the required packages: - ```bash - pip3.12 install -r services/requirements.txt - ``` - ```cmd - pip install -r services/requirements.txt - ``` + ```bash + pip3.12 install -r services/requirements.txt + ``` + ```cmd + pip install -r services/requirements.txt + ``` #### Setting up a virtual environment with VSCode + See [Visual Studio Code](https://code.visualstudio.com/docs/python/environments) documentation for information on how to set up a virtual environment with VS Code. #### Debugging Profile + A debugging profile has been set up for use with VSCode to allow ease of debugging with this application. To use this profile, simply open the project in VSCode and select the "Debug" tab on the left side of the screen. Then, select the "Debug Solution" profile and click the green play button. This will spin up a postgresql instance as well as the keycloak auth solution within docker containers. Once running, this will also start the debugger and attach it to the running API container. You can then set breakpoints and step through the code as needed. For the "Debug Solution" to run properly on Windows 10/11 using WSL, the following must be configured: @@ -208,6 +211,16 @@ For the "Debug Solution" to run properly on Windows 10/11 using WSL, the followi - MONGO_DB_URI: URI for the MongoDB connection. - MONGO_DB_NAME: Database name for RSU counts. +- MONGO_ADMIN_DB_USER: Admin Username for MongoDB +- MONGO_ADMIN_DB_PASS: Admin Password for MongoDB +- MONGO_CV_MANAGER_DB_USER: CV Manager Username for MongoDB +- MONGO_CV_MANAGER_DB_PASS: CV Manager Password for MongoDB + +- MONGO_IP: IP Address of the MongoDB (Defaults to $DOCKER_HOST_IP) +- MONGO_DB_USER: Username of the account used to connect to MongoDB +- MONGO_DB_PASS: Password of the account used to connect to MongoDB +- MONGO_PORT: Port number of MongoDB (default is 27017) +- MONGO_COLLECTION_TTL: Number of days documents will be kept in a MongoDB collection Keycloak Variables diff --git a/docker-compose-mongo.yml b/docker-compose-mongo.yml new file mode 100644 index 000000000..0f50df4b1 --- /dev/null +++ b/docker-compose-mongo.yml @@ -0,0 +1,54 @@ +version: '3' + +include: + - docker-compose.yml + +services: + mongo: + image: mongo:7 + container_name: mongo + restart: always + ports: + - '27017:27017' + environment: + MONGO_INITDB_ROOT_USERNAME: ${MONGO_ADMIN_DB_USER} + MONGO_INITDB_ROOT_PASSWORD: ${MONGO_ADMIN_DB_PASS} + MONGO_INITDB_DATABASE: admin + entrypoint: + - bash + - -c + - | + openssl rand -base64 741 > /mongo_keyfile + chmod 400 /mongo_keyfile + chown 999:999 /mongo_keyfile + exec docker-entrypoint.sh $$@ + command: 'mongod --bind_ip_all --replSet rs0 --keyFile /mongo_keyfile' + volumes: + - mongo_data:/data/db + healthcheck: + test: | + echo 'db.runCommand("ping").ok' | mongosh localhost:27017/test --quiet + interval: 10s + start_period: 30s + + mongo-setup: + image: arm64v8/mongo:7 + platform: linux/arm64/v8 + container_name: mongo_setup + depends_on: + - mongo + restart: on-failure + environment: + MONGO_ADMIN_DB_USER: ${MONGO_ADMIN_DB_USER} + MONGO_ADMIN_DB_PASS: ${MONGO_ADMIN_DB_PASS} + MONGO_DB_NAME: ${MONGO_DB_NAME} + MONGO_CV_MANAGER_DB_USER: ${MONGO_CV_MANAGER_DB_USER} + MONGO_CV_MANAGER_DB_PASS: ${MONGO_CV_MANAGER_DB_PASS} + MONGO_COLLECTION_TTL: ${MONGO_COLLECTION_TTL} + entrypoint: ['/bin/bash', 'setup_mongo.sh'] + volumes: + - ./mongo_scripts/setup_mongo.sh:/setup_mongo.sh + - ./mongo_scripts/create_indexes.js:/create_indexes.js + +volumes: + mongo_data: diff --git a/mongo_scripts/create_indexes.js b/mongo_scripts/create_indexes.js new file mode 100644 index 000000000..dbb07dc12 --- /dev/null +++ b/mongo_scripts/create_indexes.js @@ -0,0 +1,197 @@ +// Create indexes on all collections + +/* +This script is responsible for initializing the replica set, creating collections, adding indexes and TTLs +*/ +console.log('Running create_indexes.js') + +const cv_manager_db = process.env.MONGO_DB_NAME +const cv_manager_user = process.env.MONGO_CV_MANAGER_DB_USER +const cv_manager_pass = process.env.MONGO_CV_MANAGER_DB_PASS + +const ttlInDays = process.env.MONGO_COLLECTION_TTL // TTL in days +const expire_seconds = ttlInDays * 24 * 60 * 60 +const retry_milliseconds = 5000 + +console.log('CV MANAGER DB Name: ' + cv_manager_db) + +try { + console.log('Initializing replica set...') + + var config = { + _id: 'rs0', + version: 1, + members: [ + { + _id: 0, + host: 'mongo:27017', + priority: 2, + }, + ], + } + rs.initiate(config, { force: true }) + rs.status() +} catch (e) { + rs.status().ok +} + +// name -> collection name +// ttlField -> field to perform ttl on +// timeField -> field to index for time queries + +const collections = [ + { name: 'OdeBsmJson', ttlField: 'recordGeneratedAt', timeField: 'metadata.odeReceivedAt' }, + { name: 'OdeRawEncodedBSMJson', ttlField: 'recordGeneratedAt', timeField: 'none' }, + + { name: 'OdeMapJson', ttlField: 'recordGeneratedAt', timeField: 'metadata.odeReceivedAt' }, + { name: 'OdeRawEncodedMAPJson', ttlField: 'recordGeneratedAt', timeField: 'none' }, + + { name: 'OdeSpatJson', ttlField: 'recordGeneratedAt', timeField: 'metadata.odeReceivedAt' }, + { name: 'OdeRawEncodedSPATJson', ttlField: 'recordGeneratedAt', timeField: 'none' }, + + { name: 'OdeTimJson', ttlField: 'recordGeneratedAt', timeField: 'metadata.odeReceivedAt' }, + { name: 'OdeRawEncodedTIMJson', ttlField: 'recordGeneratedAt', timeField: 'none' }, + + { name: 'OdePsmJson', ttlField: 'recordGeneratedAt', timeField: 'metadata.odeReceivedAt' }, + { name: 'OdeRawEncodedPsmJson', ttlField: 'recordGeneratedAt', timeField: 'none' }, +] + +// Function to check if the replica set is ready +function isReplicaSetReady() { + let status + try { + status = rs.status() + } catch (error) { + console.error('Error getting replica set status: ' + error) + return false + } + + // Check if the replica set has a primary + if (!status.hasOwnProperty('myState') || status.myState !== 1) { + console.log('Replica set is not ready yet') + return false + } + + console.log('Replica set is ready') + return true +} + +try { + // Wait for the replica set to be ready + while (!isReplicaSetReady()) { + sleep(retry_milliseconds) + } + sleep(retry_milliseconds) + // creates another user + console.log('Creating CV MANAGER user...') + admin = db.getSiblingDB('admin') + // Check if user already exists + var user = admin.getUser(cv_manager_user) + if (user == null) { + admin.createUser({ + user: cv_manager_user, + pwd: cv_manager_pass, + roles: [ + { role: 'readWrite', db: cv_manager_db }, + { role: 'readWrite', db: 'admin' }, + ], + }) + } else { + console.log('User "' + cv_manager_user + '" already exists.') + } +} catch (error) { + print('Error connecting to the MongoDB instance: ' + error) +} + +// Wait for the collections to exist in mongo before trying to create indexes on them +let missing_collection_count +const db = db.getSiblingDB(cv_manager_db) +do { + try { + missing_collection_count = 0 + const collection_names = db.getCollectionNames() + for (collection of collections) { + console.log('Creating Indexes for Collection' + collection['name']) + // Create Collection if It doesn't exist + let created = false + if (!collection_names.includes(collection.name)) { + created = createCollection(collection) + // created = true; + } else { + created = true + } + + if (created) { + if (collection.hasOwnProperty('ttlField') && collection.ttlField !== 'none') { + createTTLIndex(collection) + } + } else { + missing_collection_count++ + console.log('Collection ' + collection.name + ' does not exist yet') + } + } + if (missing_collection_count > 0) { + print( + 'Waiting on ' + + missing_collection_count + + ' collections to be created...will try again in ' + + retry_milliseconds + + ' ms' + ) + sleep(retry_milliseconds) + } + } catch (err) { + console.log('Error while setting up TTL indexes in collections') + console.log(rs.status()) + console.error(err) + sleep(retry_milliseconds) + } +} while (missing_collection_count > 0) + +console.log('Finished Creating All TTL indexes') + +function createCollection(collection) { + try { + db.createCollection(collection.name) + return true + } catch (err) { + console.log('Unable to Create Collection: ' + collection.name) + console.log(err) + return false + } +} + +// Create TTL Indexes +function createTTLIndex(collection) { + if (ttlIndexExists(collection)) { + console.log('TTL index already exists for ' + collection.name) + return + } + + const collection_name = collection.name + const timeField = collection.ttlField + + console.log('Creating TTL index for ' + collection_name + ' to remove documents after ' + expire_seconds + ' seconds') + + try { + var index_json = {} + index_json[timeField] = 1 + db[collection_name].createIndex(index_json, { expireAfterSeconds: expire_seconds }) + console.log('Created TTL index for ' + collection_name + ' using the field: ' + timeField + ' as the timestamp') + } catch (err) { + var pattern_json = {} + pattern_json[timeField] = 1 + db.runCommand({ + collMod: collection_name, + index: { + keyPattern: pattern_json, + expireAfterSeconds: expire_seconds, + }, + }) + console.log('Updated TTL index for ' + collection_name + ' using the field: ' + timeField + ' as the timestamp') + } +} + +function ttlIndexExists(collection) { + return db[collection.name].getIndexes().find((idx) => idx.hasOwnProperty('expireAfterSeconds')) !== undefined +} diff --git a/mongo_scripts/setup_mongo.sh b/mongo_scripts/setup_mongo.sh new file mode 100644 index 000000000..d8ae35a0a --- /dev/null +++ b/mongo_scripts/setup_mongo.sh @@ -0,0 +1,11 @@ +#!/bin/bash + +until mongosh --host mongo:27017 --eval 'quit(db.runCommand({ ping: 1 }).ok ? 0 : 2)' &>/dev/null; do + sleep 1 +done + +echo "MongoDB is up and running!" + +cd / + +mongosh -u $MONGO_ADMIN_DB_USER -p $MONGO_ADMIN_DB_PASS --authenticationDatabase admin --host mongo:27017 /create_indexes.js \ No newline at end of file diff --git a/sample.env b/sample.env index 299459235..da960e05a 100644 --- a/sample.env +++ b/sample.env @@ -92,4 +92,18 @@ GCP_PROJECT= # Levels are "DEBUG", "INFO", "WARNING", and "ERROR" API_LOGGING_LEVEL="INFO" # Supported log levels are "ALL", "DEBUG", "ERROR", "FATAL", "INFO", "OFF", "TRACE" and "WARN" -KC_LOGGING_LEVEL="INFO" \ No newline at end of file +KC_LOGGING_LEVEL="INFO" + +# Necessary Variables if using docker-compose-mongo.yml +MONGO_ADMIN_DB_USER=root +MONGO_ADMIN_DB_PASS=password +MONGO_CV_MANAGER_DB_USER=root +MONGO_CV_MANAGER_DB_PASS=password + +MONGO_IP=${DOCKER_HOST_IP} +MONGO_DB_USER=root +# This is the default password for the MongoDB container - MUST BE CHANGED FOR DEPLOYMENTS +MONGO_DB_PASS=password +MONGO_PORT=27017 +MONGO_DB_URI=mongodb://${MONGO_DB_USER}:${MONGO_DB_PASS}@${MONGO_IP}:${MONGO_PORT}/ +MONGO_COLLECTION_TTL=7 # days \ No newline at end of file From 67ec16d76470ccd94444fd5e536a4331991e0b21 Mon Sep 17 00:00:00 2001 From: Marc Wodahl Date: Fri, 17 May 2024 10:55:19 -0600 Subject: [PATCH 020/189] Update mongo-setup image --- docker-compose-mongo.yml | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/docker-compose-mongo.yml b/docker-compose-mongo.yml index 0f50df4b1..7a3b255e6 100644 --- a/docker-compose-mongo.yml +++ b/docker-compose-mongo.yml @@ -32,8 +32,7 @@ services: start_period: 30s mongo-setup: - image: arm64v8/mongo:7 - platform: linux/arm64/v8 + image: mongo:7 container_name: mongo_setup depends_on: - mongo From cfc9f7c74fe38acd657aa49479693fe854b349b0 Mon Sep 17 00:00:00 2001 From: jacob6838 Date: Tue, 21 May 2024 13:14:00 -0600 Subject: [PATCH 021/189] Updating errors in type definitions --- webapp/src/components/map/map-component.tsx | 72 +++++++++---------- .../AllowedConcurrentPermissive.d.ts | 5 +- .../Intersection/Lane.d.ts | 5 +- .../NotificationSource.d.ts | 5 +- .../jpo-conflictmonitor/VehicleEvent.d.ts | 5 +- .../assessments/Assessment.d.ts | 7 +- .../jpo-conflictmonitor/bsm/BsmEvent.d.ts | 9 ++- .../bsm/BsmIntersectionKey.d.ts | 9 ++- .../config/ConfigData.d.ts | 9 ++- .../config/IntersectionConfig.d.ts | 8 +-- .../events/ConnectionOfTravelEvent.d.ts | 4 +- .../jpo-conflictmonitor/events/Event.d.ts | 16 ++--- .../IntersectionReferenceAlignmentEvent.d.ts | 4 +- .../events/LaneDirectionOfTravelEvent.d.ts | 4 +- .../events/SignalGroupAlignmentEvent.d.ts | 4 +- .../events/SignalStateConflictEvent.d.ts | 4 +- .../events/SignalStateEvent.d.ts | 4 +- .../events/SignalStateStopEvent.d.ts | 4 +- .../events/StopLinePassageEvent.d.ts | 8 +-- .../events/StopLineStopEvent.d.ts | 6 +- .../events/TimeChangeDetailsEvent.d.ts | 8 +-- .../events/app_health/KafkaStreamsEvent.d.ts | 7 +- .../KafkaStreamsStateChangeEvent.d.ts | 9 ++- .../broadcast_rate/BroadcastRateEvent.d.ts | 7 +- .../events/minimum_data/MinimumDataEvent.d.ts | 5 +- .../spat/SpatTimeChangeDetail.d.ts | 5 +- 26 files changed, 110 insertions(+), 123 deletions(-) diff --git a/webapp/src/components/map/map-component.tsx b/webapp/src/components/map/map-component.tsx index d805f9129..0dc3677a9 100644 --- a/webapp/src/components/map/map-component.tsx +++ b/webapp/src/components/map/map-component.tsx @@ -358,17 +358,6 @@ const MapTab = (props: MAP_PROPS) => { > - - - { > - - - { > - - - - - {/* + > + + + {/* */} + + + + + + + {selectedFeature && ( dispatch(clearSelectedFeature())} /> )} diff --git a/webapp/src/models/jpo-conflictmonitor/AllowedConcurrentPermissive.d.ts b/webapp/src/models/jpo-conflictmonitor/AllowedConcurrentPermissive.d.ts index 0153e5106..d5c171bf8 100644 --- a/webapp/src/models/jpo-conflictmonitor/AllowedConcurrentPermissive.d.ts +++ b/webapp/src/models/jpo-conflictmonitor/AllowedConcurrentPermissive.d.ts @@ -1,4 +1,3 @@ - type AllowedConcurrentPermissive = { intersectionID: number roadRegulatorID: number @@ -6,5 +5,5 @@ type AllowedConcurrentPermissive = { secondIngressLane: number firstEgressLane: number secondEgressLane: number - allowConcurrent: booleanean -} \ No newline at end of file + allowConcurrent: boolean +} diff --git a/webapp/src/models/jpo-conflictmonitor/Intersection/Lane.d.ts b/webapp/src/models/jpo-conflictmonitor/Intersection/Lane.d.ts index bd6876014..8c2822a55 100644 --- a/webapp/src/models/jpo-conflictmonitor/Intersection/Lane.d.ts +++ b/webapp/src/models/jpo-conflictmonitor/Intersection/Lane.d.ts @@ -1,9 +1,8 @@ - type Lane = { id: number points: number[][] - ingress: booleanean + ingress: boolean geometryFactory: GeometryFactory laneWidthCm: number region: number -} \ No newline at end of file +} diff --git a/webapp/src/models/jpo-conflictmonitor/NotificationSource.d.ts b/webapp/src/models/jpo-conflictmonitor/NotificationSource.d.ts index 9ae058873..b4f42f31e 100644 --- a/webapp/src/models/jpo-conflictmonitor/NotificationSource.d.ts +++ b/webapp/src/models/jpo-conflictmonitor/NotificationSource.d.ts @@ -1,4 +1,3 @@ - type NotificationSource = { - getNotificationSourceString: str -} \ No newline at end of file + getNotificationSourceString: string +} diff --git a/webapp/src/models/jpo-conflictmonitor/VehicleEvent.d.ts b/webapp/src/models/jpo-conflictmonitor/VehicleEvent.d.ts index 5d78568b4..df506988f 100644 --- a/webapp/src/models/jpo-conflictmonitor/VehicleEvent.d.ts +++ b/webapp/src/models/jpo-conflictmonitor/VehicleEvent.d.ts @@ -1,7 +1,6 @@ - type VehicleEvent = { bsms: BsmAggregator spats: SpatAggregator intersection: Intersection - source: str -} \ No newline at end of file + source: string +} diff --git a/webapp/src/models/jpo-conflictmonitor/assessments/Assessment.d.ts b/webapp/src/models/jpo-conflictmonitor/assessments/Assessment.d.ts index ba4f189b0..b89b0bf49 100644 --- a/webapp/src/models/jpo-conflictmonitor/assessments/Assessment.d.ts +++ b/webapp/src/models/jpo-conflictmonitor/assessments/Assessment.d.ts @@ -1,8 +1,7 @@ - type Assessment = { assessmentGeneratedAt: number - assessmentType: str + assessmentType: string intersectionID: number roadRegulatorID: number - source: str -} \ No newline at end of file + source: string +} diff --git a/webapp/src/models/jpo-conflictmonitor/bsm/BsmEvent.d.ts b/webapp/src/models/jpo-conflictmonitor/bsm/BsmEvent.d.ts index fb592ce9e..b4ba6f509 100644 --- a/webapp/src/models/jpo-conflictmonitor/bsm/BsmEvent.d.ts +++ b/webapp/src/models/jpo-conflictmonitor/bsm/BsmEvent.d.ts @@ -1,11 +1,10 @@ - type BsmEvent = { startingBsm: OdeBsmData endingBsm: OdeBsmData startingBsmTimestamp: number endingBsmTimestamp: number - wktPath: str - wktMapBoundingBox: str - inMapBoundingBox: booleanean + wktPath: string + wktMapBoundingBox: string + inMapBoundingBox: boolean wallClockTimestamp: number -} \ No newline at end of file +} diff --git a/webapp/src/models/jpo-conflictmonitor/bsm/BsmIntersectionKey.d.ts b/webapp/src/models/jpo-conflictmonitor/bsm/BsmIntersectionKey.d.ts index 986bc41a7..b8837a09a 100644 --- a/webapp/src/models/jpo-conflictmonitor/bsm/BsmIntersectionKey.d.ts +++ b/webapp/src/models/jpo-conflictmonitor/bsm/BsmIntersectionKey.d.ts @@ -1,6 +1,5 @@ - type BsmIntersectionKey = { - logger: Logger - rsuId: str - bsmId: str -} \ No newline at end of file + logger: any + rsuId: string + bsmId: string +} diff --git a/webapp/src/models/jpo-conflictmonitor/config/ConfigData.d.ts b/webapp/src/models/jpo-conflictmonitor/config/ConfigData.d.ts index 6d256dcf5..d69c8d961 100644 --- a/webapp/src/models/jpo-conflictmonitor/config/ConfigData.d.ts +++ b/webapp/src/models/jpo-conflictmonitor/config/ConfigData.d.ts @@ -1,8 +1,7 @@ - type ConfigData = { - key: str - category: str + key: string + category: string units: UnitsEnum - description: str + description: string updateType: UpdateType -} \ No newline at end of file +} diff --git a/webapp/src/models/jpo-conflictmonitor/config/IntersectionConfig.d.ts b/webapp/src/models/jpo-conflictmonitor/config/IntersectionConfig.d.ts index a5ff9750a..ab98e9920 100644 --- a/webapp/src/models/jpo-conflictmonitor/config/IntersectionConfig.d.ts +++ b/webapp/src/models/jpo-conflictmonitor/config/IntersectionConfig.d.ts @@ -1,5 +1,5 @@ type IntersectionConfig = Config & { - intersectionID: number; - roadRegulatorID?: number; - rsuID: str; -}; + intersectionID: number + roadRegulatorID?: number + rsuID: string +} diff --git a/webapp/src/models/jpo-conflictmonitor/events/ConnectionOfTravelEvent.d.ts b/webapp/src/models/jpo-conflictmonitor/events/ConnectionOfTravelEvent.d.ts index 9b8a8799d..a60882abe 100644 --- a/webapp/src/models/jpo-conflictmonitor/events/ConnectionOfTravelEvent.d.ts +++ b/webapp/src/models/jpo-conflictmonitor/events/ConnectionOfTravelEvent.d.ts @@ -3,6 +3,6 @@ type ConnectionOfTravelEvent = MessageMonitor.Event & { timestamp: number ingressLaneID: number egressLaneID: number - source: str + source: string connectionID: number -} \ No newline at end of file +} diff --git a/webapp/src/models/jpo-conflictmonitor/events/Event.d.ts b/webapp/src/models/jpo-conflictmonitor/events/Event.d.ts index d2f01089a..bac7a6238 100644 --- a/webapp/src/models/jpo-conflictmonitor/events/Event.d.ts +++ b/webapp/src/models/jpo-conflictmonitor/events/Event.d.ts @@ -1,10 +1,10 @@ declare namespace MessageMonitor { -type Event = { - logger: Logger - eventGeneratedAt: number - eventType: str - intersectionID: number - roadRegulatorID: number - source: str + type Event = { + logger: any + eventGeneratedAt: number + eventType: string + intersectionID: number + roadRegulatorID: number + source: string + } } -} \ No newline at end of file diff --git a/webapp/src/models/jpo-conflictmonitor/events/IntersectionReferenceAlignmentEvent.d.ts b/webapp/src/models/jpo-conflictmonitor/events/IntersectionReferenceAlignmentEvent.d.ts index 43651756c..bfde5b9a6 100644 --- a/webapp/src/models/jpo-conflictmonitor/events/IntersectionReferenceAlignmentEvent.d.ts +++ b/webapp/src/models/jpo-conflictmonitor/events/IntersectionReferenceAlignmentEvent.d.ts @@ -1,7 +1,7 @@ /// type IntersectionReferenceAlignmentEvent = MessageMonitor.Event & { - source: str + source: string timestamp: number spatRegulatorIntersectionIds: Set mapRegulatorIntersectionIds: Set -} \ No newline at end of file +} diff --git a/webapp/src/models/jpo-conflictmonitor/events/LaneDirectionOfTravelEvent.d.ts b/webapp/src/models/jpo-conflictmonitor/events/LaneDirectionOfTravelEvent.d.ts index 75ed40c32..5d7c9316e 100644 --- a/webapp/src/models/jpo-conflictmonitor/events/LaneDirectionOfTravelEvent.d.ts +++ b/webapp/src/models/jpo-conflictmonitor/events/LaneDirectionOfTravelEvent.d.ts @@ -11,5 +11,5 @@ type LaneDirectionOfTravelEvent = MessageMonitor.Event & { medianVehicleHeading: number medianDistanceFromCenterline: number aggregateBSMCount: number - source: str -} \ No newline at end of file + source: string +} diff --git a/webapp/src/models/jpo-conflictmonitor/events/SignalGroupAlignmentEvent.d.ts b/webapp/src/models/jpo-conflictmonitor/events/SignalGroupAlignmentEvent.d.ts index 77b64c9bb..9715d98e2 100644 --- a/webapp/src/models/jpo-conflictmonitor/events/SignalGroupAlignmentEvent.d.ts +++ b/webapp/src/models/jpo-conflictmonitor/events/SignalGroupAlignmentEvent.d.ts @@ -1,7 +1,7 @@ /// type SignalGroupAlignmentEvent = MessageMonitor.Event & { - source: str + source: string timestamp: number spatSignalGroupIds: Set mapSignalGroupIds: Set -} \ No newline at end of file +} diff --git a/webapp/src/models/jpo-conflictmonitor/events/SignalStateConflictEvent.d.ts b/webapp/src/models/jpo-conflictmonitor/events/SignalStateConflictEvent.d.ts index 09b0427a2..cd26c98bc 100644 --- a/webapp/src/models/jpo-conflictmonitor/events/SignalStateConflictEvent.d.ts +++ b/webapp/src/models/jpo-conflictmonitor/events/SignalStateConflictEvent.d.ts @@ -6,5 +6,5 @@ type SignalStateConflictEvent = MessageMonitor.Event & { firstConflictingSignalState: J2735MovementPhaseState secondConflictingSignalGroup: number secondConflictingSignalState: J2735MovementPhaseState - source: str -} \ No newline at end of file + source: string +} diff --git a/webapp/src/models/jpo-conflictmonitor/events/SignalStateEvent.d.ts b/webapp/src/models/jpo-conflictmonitor/events/SignalStateEvent.d.ts index 3423bc413..26ae9d50f 100644 --- a/webapp/src/models/jpo-conflictmonitor/events/SignalStateEvent.d.ts +++ b/webapp/src/models/jpo-conflictmonitor/events/SignalStateEvent.d.ts @@ -5,10 +5,10 @@ type SignalStateEvent = MessageMonitor.Event & { egressLane: number connectionID: number eventState: J2735MovementPhaseState - vehicleID: str + vehicleID: string latitude: number longitude: number heading: number speed: number signalGroup: number -} \ No newline at end of file +} diff --git a/webapp/src/models/jpo-conflictmonitor/events/SignalStateStopEvent.d.ts b/webapp/src/models/jpo-conflictmonitor/events/SignalStateStopEvent.d.ts index eb40cdd7e..0eb15efdd 100644 --- a/webapp/src/models/jpo-conflictmonitor/events/SignalStateStopEvent.d.ts +++ b/webapp/src/models/jpo-conflictmonitor/events/SignalStateStopEvent.d.ts @@ -5,9 +5,9 @@ type SignalStateStopEvent = MessageMonitor.Event & { egressLane: number connectionID: number eventState: J2735MovementPhaseState - vehicleID: str + vehicleID: string latitude: number longitude: number heading: number speed: number -} \ No newline at end of file +} diff --git a/webapp/src/models/jpo-conflictmonitor/events/StopLinePassageEvent.d.ts b/webapp/src/models/jpo-conflictmonitor/events/StopLinePassageEvent.d.ts index 6c72a49bc..c3eb5f360 100644 --- a/webapp/src/models/jpo-conflictmonitor/events/StopLinePassageEvent.d.ts +++ b/webapp/src/models/jpo-conflictmonitor/events/StopLinePassageEvent.d.ts @@ -1,15 +1,15 @@ /// -type StopLinePassageEvent = MessageMonitor.Event & { - source: str +type StopLinePassageEvent = MessageMonitor.Event & { + source: string timestamp: number ingressLane: number egressLane: number connectionID: number eventState: J2735MovementPhaseState - vehicleID: str + vehicleID: string latitude: number longitude: number heading: number speed: number signalGroup: number -} \ No newline at end of file +} diff --git a/webapp/src/models/jpo-conflictmonitor/events/StopLineStopEvent.d.ts b/webapp/src/models/jpo-conflictmonitor/events/StopLineStopEvent.d.ts index 9b55254a6..72559bc01 100644 --- a/webapp/src/models/jpo-conflictmonitor/events/StopLineStopEvent.d.ts +++ b/webapp/src/models/jpo-conflictmonitor/events/StopLineStopEvent.d.ts @@ -1,6 +1,6 @@ /// type StopLineStopEvent = MessageMonitor.Event & { - source: str + source: string ingressLane: number egressLane: number connectionID: number @@ -8,7 +8,7 @@ type StopLineStopEvent = MessageMonitor.Event & { initialTimestamp: number finalEventState: J2735MovementPhaseState finalTimestamp: number - vehicleID: str + vehicleID: string latitude: number longitude: number heading: number @@ -16,4 +16,4 @@ type StopLineStopEvent = MessageMonitor.Event & { timeStoppedDuringRed: number timeStoppedDuringYellow: number timeStoppedDuringGreen: number -} \ No newline at end of file +} diff --git a/webapp/src/models/jpo-conflictmonitor/events/TimeChangeDetailsEvent.d.ts b/webapp/src/models/jpo-conflictmonitor/events/TimeChangeDetailsEvent.d.ts index 9b63af0f6..adce55066 100644 --- a/webapp/src/models/jpo-conflictmonitor/events/TimeChangeDetailsEvent.d.ts +++ b/webapp/src/models/jpo-conflictmonitor/events/TimeChangeDetailsEvent.d.ts @@ -3,13 +3,13 @@ type TimeChangeDetailsEvent = MessageMonitor.Event & { signalGroup: number firstSpatTimestamp: number secondSpatTimestamp: number - firstTimeMarkType: str - secondTimeMarkType: str + firstTimeMarkType: string + secondTimeMarkType: string firstConflictingTimemark: number secondConflictingTimemark: number firstState: J2735MovementPhaseState secondState: J2735MovementPhaseState firstConflictingUtcTimestamp: number secondConflictingUtcTimestamp: number - source: str -} \ No newline at end of file + source: string +} diff --git a/webapp/src/models/jpo-conflictmonitor/events/app_health/KafkaStreamsEvent.d.ts b/webapp/src/models/jpo-conflictmonitor/events/app_health/KafkaStreamsEvent.d.ts index 1612a0543..13643fb5e 100644 --- a/webapp/src/models/jpo-conflictmonitor/events/app_health/KafkaStreamsEvent.d.ts +++ b/webapp/src/models/jpo-conflictmonitor/events/app_health/KafkaStreamsEvent.d.ts @@ -1,5 +1,4 @@ - type KafkaStreamsEvent = { - appId: str - topology: str -} \ No newline at end of file + appId: string + topology: string +} diff --git a/webapp/src/models/jpo-conflictmonitor/events/app_health/KafkaStreamsStateChangeEvent.d.ts b/webapp/src/models/jpo-conflictmonitor/events/app_health/KafkaStreamsStateChangeEvent.d.ts index c4d5e3e1f..4dfc64f70 100644 --- a/webapp/src/models/jpo-conflictmonitor/events/app_health/KafkaStreamsStateChangeEvent.d.ts +++ b/webapp/src/models/jpo-conflictmonitor/events/app_health/KafkaStreamsStateChangeEvent.d.ts @@ -1,5 +1,4 @@ - -type KafkaStreamsStateChangeEvent = KafkaStreamsEvent & { - newState: str - oldState: str -} \ No newline at end of file +type KafkaStreamsStateChangeEvent = KafkaStreamsEvent & { + newState: string + oldState: string +} diff --git a/webapp/src/models/jpo-conflictmonitor/events/broadcast_rate/BroadcastRateEvent.d.ts b/webapp/src/models/jpo-conflictmonitor/events/broadcast_rate/BroadcastRateEvent.d.ts index 62d88e371..6a5c45c27 100644 --- a/webapp/src/models/jpo-conflictmonitor/events/broadcast_rate/BroadcastRateEvent.d.ts +++ b/webapp/src/models/jpo-conflictmonitor/events/broadcast_rate/BroadcastRateEvent.d.ts @@ -1,7 +1,6 @@ - type BroadcastRateEvent = { - topicName: str - source: str + topicName: string + source: string timePeriod: ProcessingTimePeriod numberOfMessages: number -} \ No newline at end of file +} diff --git a/webapp/src/models/jpo-conflictmonitor/events/minimum_data/MinimumDataEvent.d.ts b/webapp/src/models/jpo-conflictmonitor/events/minimum_data/MinimumDataEvent.d.ts index 2971e0ab9..043fe7fe4 100644 --- a/webapp/src/models/jpo-conflictmonitor/events/minimum_data/MinimumDataEvent.d.ts +++ b/webapp/src/models/jpo-conflictmonitor/events/minimum_data/MinimumDataEvent.d.ts @@ -1,6 +1,5 @@ - type MinimumDataEvent = { - source: str + source: string timePeriod: ProcessingTimePeriod missingDataElements: String[] -} \ No newline at end of file +} diff --git a/webapp/src/models/jpo-conflictmonitor/spat/SpatTimeChangeDetail.d.ts b/webapp/src/models/jpo-conflictmonitor/spat/SpatTimeChangeDetail.d.ts index a918051d1..165b6439d 100644 --- a/webapp/src/models/jpo-conflictmonitor/spat/SpatTimeChangeDetail.d.ts +++ b/webapp/src/models/jpo-conflictmonitor/spat/SpatTimeChangeDetail.d.ts @@ -1,8 +1,7 @@ - type SpatTimeChangeDetail = { - originIP: str + originIP: string region: number intersectionID: number timestamp: number states: SpatTimeChangeDetailState[] -} \ No newline at end of file +} From 0eff4c69a9e93469af5f063fd49f58251c6c7646 Mon Sep 17 00:00:00 2001 From: jacob6838 Date: Tue, 21 May 2024 14:50:39 -0600 Subject: [PATCH 022/189] Merging recent conflictvisualizer changes --- webapp/src/components/map/map-component.tsx | 125 ++++------ .../components/map/map-layer-style-slice.ts | 4 +- webapp/src/components/map/map-slice.tsx | 219 +++++++++++++----- webapp/src/components/map/utilities/colors.ts | 89 ++++++- .../components/map/utilities/message-utils.ts | 120 +++++----- 5 files changed, 358 insertions(+), 199 deletions(-) diff --git a/webapp/src/components/map/map-component.tsx b/webapp/src/components/map/map-component.tsx index 0dc3677a9..6d872bc3a 100644 --- a/webapp/src/components/map/map-component.tsx +++ b/webapp/src/components/map/map-component.tsx @@ -42,6 +42,8 @@ import { selectHoveredFeature, selectLaneLabelsVisible, selectLiveDataActive, + selectLiveDataRestart, + selectLiveDataRestartTimeoutId, selectLoadInitialDataTimeoutId, selectMapData, selectMapSignalGroups, @@ -169,6 +171,8 @@ const MapTab = (props: MAP_PROPS) => { const loadInitialDataTimeoutId = useSelector(selectLoadInitialDataTimeoutId) const liveDataActive = useSelector(selectLiveDataActive) const playbackModeActive = useSelector(selectPlaybackModeActive) + const liveDataRestartTimeoutId = useSelector(selectLiveDataRestartTimeoutId) + const liveDataRestart = useSelector(selectLiveDataRestart) const mapRef = React.useRef(null) const [bsmTrailLength, setBsmTrailLength] = useState(5) @@ -234,13 +238,9 @@ const MapTab = (props: MAP_PROPS) => { return } if (loadInitialDataTimeoutId) { - console.log("Clearing 'Load Initial Data' timeout") clearTimeout(loadInitialDataTimeoutId) } - const timeoutId = setTimeout(() => { - console.log('Loading Initial Data') - dispatch(pullInitialData()) - }, 500) + const timeoutId = setTimeout(() => dispatch(pullInitialData()), 500) dispatch(setLoadInitialdataTimeoutId(timeoutId)) }, [queryParams]) @@ -268,6 +268,7 @@ const MapTab = (props: MAP_PROPS) => { }) ) if (bsmTrailLength > 15) setBsmTrailLength(5) + setRawData({}) } else { console.error( 'Did not attempt to update notifications. Access token:', @@ -284,6 +285,24 @@ const MapTab = (props: MAP_PROPS) => { } }, [liveDataActive]) + useEffect(() => { + console.log('Live Data Restart:', liveDataRestart, liveDataActive) + if (liveDataRestart != -1 && liveDataRestart < 5 && liveDataActive) { + if (authToken && props.roadRegulatorId && props.intersectionId) { + dispatch( + initializeLiveStreaming({ + token: authToken, + roadRegulatorId: props.roadRegulatorId, + intersectionId: props.intersectionId, + numRestarts: liveDataRestart, + }) + ) + } + } else { + dispatch(cleanUpLiveStreaming()) + } + }, [liveDataRestart]) + return ( @@ -347,27 +366,16 @@ const MapTab = (props: MAP_PROPS) => { onMouseEnter={(e) => dispatch(onMapMouseEnter({ features: e.features, lngLat: e.lngLat }))} onMouseLeave={(e) => dispatch(onMapMouseLeave())} > - + @@ -375,68 +383,35 @@ const MapTab = (props: MAP_PROPS) => { - - - - - - - {/* - - */} - - - - - - + + + + + + + + + + + + {selectedFeature && ( dispatch(clearSelectedFeature())} /> diff --git a/webapp/src/components/map/map-layer-style-slice.ts b/webapp/src/components/map/map-layer-style-slice.ts index 7555471e4..a59cfbf59 100644 --- a/webapp/src/components/map/map-layer-style-slice.ts +++ b/webapp/src/components/map/map-layer-style-slice.ts @@ -181,7 +181,7 @@ const signalStateLayer: SymbolLayer = { 'PRE_MOVEMENT', 'traffic-light-icon-yellow-red-1', 'PERMISSIVE_MOVEMENT_ALLOWED', - 'traffic-light-icon-yellow-1', + 'traffic-light-icon-green-1', 'PROTECTED_MOVEMENT_ALLOWED', 'traffic-light-icon-green-1', 'PERMISSIVE_CLEARANCE', @@ -230,7 +230,7 @@ const mapLegendColors: MAP_LEGEND_COLORS = { STOP_THEN_PROCEED: '/icons/traffic-light-icon-red-flashing.svg', STOP_AND_REMAIN: '/icons/traffic-light-icon-red-1.svg', PRE_MOVEMENT: '/icons/traffic-light-icon-yellow-red-1.svg', - PERMISSIVE_MOVEMENT_ALLOWED: '/icons/traffic-light-icon-yellow-1.svg', + PERMISSIVE_MOVEMENT_ALLOWED: '/icons/traffic-light-icon-green-1.svg', PROTECTED_MOVEMENT_ALLOWED: '/icons/traffic-light-icon-green-1.svg', PERMISSIVE_CLEARANCE: '/icons/traffic-light-icon-yellow-1.svg', PROTECTED_CLEARANCE: '/icons/traffic-light-icon-yellow-1.svg', diff --git a/webapp/src/components/map/map-slice.tsx b/webapp/src/components/map/map-slice.tsx index 51a2021e9..e8a5fd671 100644 --- a/webapp/src/components/map/map-slice.tsx +++ b/webapp/src/components/map/map-slice.tsx @@ -102,6 +102,24 @@ interface MinimalClient { disconnect: (disconnectCallback: () => void) => void } +const getTimestamp = (dt: any): number => { + try { + const dtFromString = Date.parse(dt as any as string) + if (isNaN(dtFromString)) { + if (dt > 1000000000000) { + return dt // already in milliseconds + } else { + return dt * 1000 + } + } else { + return dtFromString + } + } catch (e) { + console.error('Failed to parse timestamp from value: ' + dt, e) + return 0 + } +} + const initialState = { layersVisible: { mapMessage: false, @@ -179,6 +197,8 @@ const initialState = { features: [], } as BsmFeatureCollection, bsmTrailLength: 20, + liveDataRestart: -1, + liveDataRestartTimeoutId: undefined as NodeJS.Timeout | undefined, } const getNewSliderTimeValue = (startDate: Date, sliderValue: number, timeWindowSeconds: number) => { @@ -234,11 +254,7 @@ export const pullInitialData = createAsyncThunk( .sort((a, b) => a.utcTimeStamp - b.utcTimeStamp) .map((spat) => ({ ...spat, - utcTimeStamp: isNaN(Date.parse(spat.utcTimeStamp as any as string)) - ? spat.utcTimeStamp > 1000000000000 - ? spat.utcTimeStamp - : spat.utcTimeStamp * 1000 - : Date.parse(spat.utcTimeStamp as any as string), + utcTimeStamp: getTimestamp(spat.utcTimeStamp), })) dispatch(getBsmDailyCounts()) @@ -301,30 +317,14 @@ export const pullInitialData = createAsyncThunk( } }, { - condition: (_, { getState }) => { - console.log( - 'PULL INITIAL DATA Condition', - selectToken(getState() as RootState) != undefined, - selectQueryParams(getState() as RootState).intersectionId != undefined, - selectQueryParams(getState() as RootState).roadRegulatorId != undefined, - selectSourceData(getState() as RootState) != undefined, - selectLoadOnNull(getState() as RootState) == true, - selectToken(getState() as RootState) != undefined && - selectQueryParams(getState() as RootState).intersectionId != undefined && - selectQueryParams(getState() as RootState).roadRegulatorId != undefined && - (selectSourceData(getState() as RootState) != undefined || selectLoadOnNull(getState() as RootState) == true) - ) - return ( - selectToken(getState() as RootState) != undefined && - selectQueryParams(getState() as RootState).intersectionId != undefined && - selectQueryParams(getState() as RootState).roadRegulatorId != undefined && - (selectSourceData(getState() as RootState) != undefined || selectLoadOnNull(getState() as RootState) == true) - ) - }, + condition: (_, { getState }) => + selectToken(getState() as RootState) != undefined && + selectQueryParams(getState() as RootState).intersectionId != undefined && + selectQueryParams(getState() as RootState).roadRegulatorId != undefined && + (selectSourceData(getState() as RootState) != undefined || selectLoadOnNull(getState() as RootState) == true), } ) -// TODO: Merge with cimms export const renderEntireMap = createAsyncThunk( 'intersectionMap/renderEntireMap', async ( @@ -455,7 +455,7 @@ export const renderIterative_Map = createAsyncThunk( const start = Date.now() const OLDEST_DATA_TO_KEEP = queryParams.eventDate.getTime() - queryParams.startDate.getTime() // milliseconds - const currTimestamp = Date.parse(newMapData.at(-1)!.properties.odeReceivedAt) / 1000 + const currTimestamp = getTimestamp(newMapData.at(-1)!.properties.odeReceivedAt) / 1000 let oldIndex = 0 for (let i = 0; i < currentMapData.length; i++) { if ((currentMapData[i].properties.odeReceivedAt as unknown as number) < currTimestamp - OLDEST_DATA_TO_KEEP) { @@ -511,8 +511,8 @@ export const renderIterative_Spat = createAsyncThunk( async (newSpatData: ProcessedSpat[], { getState, dispatch }) => { const currentState = getState() as RootState const queryParams = selectQueryParams(currentState) - const currentSpatSignalGroups: SpatSignalGroups = selectSpatSignalGroups(currentState) - const currentProcessedSpatData: ProcessedSpat[] = selectCurrentSpatData(currentState) + const currentSpatSignalGroups: SpatSignalGroups = selectSpatSignalGroups(currentState) ?? {} + const currentProcessedSpatData: ProcessedSpat[] = selectCurrentSpatData(currentState) ?? [] const start = Date.now() const OLDEST_DATA_TO_KEEP = queryParams.eventDate.getTime() - queryParams.startDate.getTime() // milliseconds @@ -524,13 +524,9 @@ export const renderIterative_Spat = createAsyncThunk( // 2024-01-09T00:24:28.354Z newSpatData = newSpatData.map((spat) => ({ ...spat, - utcTimeStamp: isNaN(Date.parse(spat.utcTimeStamp as any as string)) - ? spat.utcTimeStamp > 1000000000000 - ? spat.utcTimeStamp - : spat.utcTimeStamp * 1000 - : Date.parse(spat.utcTimeStamp as any as string), + utcTimeStamp: getTimestamp(spat.utcTimeStamp), })) - const currTimestamp = Date.parse(newSpatData.at(-1)!.utcTimeStamp as any as string) + const currTimestamp = getTimestamp(newSpatData.at(-1)!.utcTimeStamp) let oldIndex = 0 const currentSpatSignalGroupsArr = Object.keys(currentSpatSignalGroups).map((key) => ({ @@ -710,11 +706,22 @@ export const getSurroundingNotifications = createAsyncThunk( export const initializeLiveStreaming = createAsyncThunk( 'intersectionMap/initializeLiveStreaming', - async (args: { token: string; roadRegulatorId: number; intersectionId: number }, { getState, dispatch }) => { - const { token, roadRegulatorId, intersectionId } = args + async ( + args: { token: string; roadRegulatorId: number; intersectionId: number; numRestarts?: number }, + { getState, dispatch } + ) => { + const { token, roadRegulatorId, intersectionId, numRestarts = 0 } = args // Connect to WebSocket when component mounts + const liveDataActive = selectLiveDataActive(getState() as RootState) + const wsClient = selectWsClient(getState() as RootState) + dispatch(onTimeQueryChanged({ eventTime: new Date(), timeBefore: 10, timeAfter: 0, timeWindowSeconds: 2 })) - dispatch(setRawData({})) + dispatch(resetMapView()) + + if (!liveDataActive) { + console.debug('Not initializing live streaming because liveDataActive is false') + return + } let protocols = ['v10.stomp', 'v11.stomp'] protocols.push(token) @@ -723,7 +730,9 @@ export const initializeLiveStreaming = createAsyncThunk( // Stomp Client Documentation: https://stomp-js.github.io/stomp-websocket/codo/extra/docs-src/Usage.md.html let client = Stomp.client(url, protocols) - client.debug = () => {} + client.debug = (e) => { + console.debug('STOMP Debug: ' + e) + } // Topics are in the format /live/{roadRegulatorID}/{intersectionID}/{spat,map,bsm} let spatTopic = `/live/${roadRegulatorId}/${intersectionId}/spat` @@ -732,6 +741,7 @@ export const initializeLiveStreaming = createAsyncThunk( let spatTime = Date.now() let mapTime = Date.now() let bsmTime = Date.now() + let connectionStartTime = Date.now() client.connect( { // "username": "test", @@ -764,9 +774,76 @@ export const initializeLiveStreaming = createAsyncThunk( }) }, (error) => { - console.error('ERROR connecting to live data Websockets', error) + console.error('Live Streaming ERROR connecting to live data Websocket: ' + error) } ) + + client.onDisconnect = (frame) => { + console.debug( + 'Live Streaming Disconnected from STOMP endpoint: ' + + frame + + ' (numRestarts: ' + + numRestarts + + ', wsClient: ' + + wsClient + + ')' + ) + if (numRestarts < 5 && liveDataActive) { + let numRestartsLocal = numRestarts + if (Date.now() - connectionStartTime > 10000) { + numRestartsLocal = 0 + } + console.debug('Attempting to reconnect to STOMP endpoint (numRestarts: ' + numRestartsLocal + ')') + dispatch( + setLiveDataRestartTimeoutId( + setTimeout(() => { + dispatch(setLiveDataRestart(numRestartsLocal + 1)) + }, numRestartsLocal * 2000) + ) + ) + } else { + cleanUpLiveStreaming() + } + } + + client.onStompError = (frame) => { + // TODO: Consider restarting connection on error + console.error('Live Streaming STOMP ERROR', frame) + } + + client.onWebSocketClose = (frame) => { + console.error( + 'Live Streaming STOMP WebSocket Close: ' + + frame + + ' (numRestarts: ' + + numRestarts + + ', wsClient: ' + + wsClient + + ')' + ) + if (numRestarts < 5 && liveDataActive) { + let numRestartsLocal = numRestarts + if (Date.now() - connectionStartTime > 10000) { + numRestartsLocal = 0 + } + console.debug('Attempting to reconnect to STOMP endpoint (numRestarts: ' + numRestartsLocal + ')') + dispatch( + setLiveDataRestartTimeoutId( + setTimeout(() => { + dispatch(setLiveDataRestart(numRestartsLocal + 1)) + }, numRestartsLocal * 2000) + ) + ) + } else { + dispatch(cleanUpLiveStreaming()) + } + } + + client.onWebSocketError = (frame) => { + // TODO: Consider restarting connection on error + console.error('Live Streaming STOMP WebSocket Error', frame) + } + return client } ) @@ -1163,6 +1240,12 @@ export const intersectionMapSlice = createSlice({ }) state.value.timeWindowSeconds = 60 } + if (state.value.liveDataRestartTimeoutId) { + clearTimeout(state.value.liveDataRestartTimeoutId) + state.value.liveDataRestartTimeoutId = undefined + } + state.value.liveDataActive = false + state.value.liveDataRestart = -1 state.value.wsClient = undefined }, setLoadInitialdataTimeoutId: (state, action: PayloadAction) => { @@ -1216,6 +1299,32 @@ export const intersectionMapSlice = createSlice({ togglePlaybackModeActive: (state) => { state.value.playbackModeActive = !state.value.playbackModeActive }, + resetMapView: (state) => { + state.value.mapSignalGroups = undefined + state.value.signalStateData = undefined + state.value.spatSignalGroups = undefined + state.value.currentSignalGroups = undefined + state.value.connectingLanes = undefined + state.value.surroundingEvents = [] + state.value.filteredSurroundingEvents = [] + state.value.surroundingNotifications = [] + state.value.filteredSurroundingNotifications = [] + state.value.bsmData = { type: 'FeatureCollection', features: [] } + state.value.currentBsmData = { type: 'FeatureCollection', features: [] } + state.value.mapSpatTimes = { mapTime: 0, spatTime: 0 } + state.value.rawData = {} + state.value.sliderValue = 0 + state.value.playbackModeActive = false + state.value.currentSpatData = [] + // state.value.currentProcessedSpatData = []; + state.value.currentBsmData = { type: 'FeatureCollection', features: [] } + }, + setLiveDataRestartTimeoutId: (state, action) => { + state.value.liveDataRestartTimeoutId = action.payload + }, + setLiveDataRestart: (state, action) => { + state.value.liveDataRestart = action.payload + }, }, extraReducers: (builder) => { builder @@ -1248,12 +1357,7 @@ export const intersectionMapSlice = createSlice({ longitude: action.payload.mapData.properties.refPoint.longitude, zoom: 19, } - state.value.connectingLanes = { - ...action.payload.connectingLanes, - features: action.payload.connectingLanes.features.map((v) => { - return { ...v } - }), - } + state.value.connectingLanes = action.payload.connectingLanes state.value.spatSignalGroups = action.payload.spatSignalGroups state.value.mapSignalGroups = action.payload.mapSignalGroups state.value.mapSpatTimes = { ...state.value.mapSpatTimes, mapTime: action.payload.mapTime } @@ -1281,12 +1385,7 @@ export const intersectionMapSlice = createSlice({ longitude: action.payload.mapData.properties.refPoint.longitude, zoom: 19, } - state.value.connectingLanes = { - ...action.payload.connectingLanes, - features: action.payload.connectingLanes.features.map((v) => { - return { ...v } - }), - } + state.value.connectingLanes = action.payload.connectingLanes state.value.spatSignalGroups = action.payload.spatSignalGroups state.value.mapSignalGroups = action.payload.mapSignalGroups state.value.mapSpatTimes = { ...state.value.mapSpatTimes, mapTime: action.payload.mapTime } @@ -1324,12 +1423,7 @@ export const intersectionMapSlice = createSlice({ longitude: action.payload.mapData.properties.refPoint.longitude, zoom: 19, } - state.value.connectingLanes = { - ...action.payload.connectingLanes, - features: action.payload.connectingLanes.features.map((v) => { - return { ...v } - }), - } + state.value.connectingLanes = action.payload.connectingLanes state.value.mapData = action.payload.mapData state.value.mapSignalGroups = action.payload.mapSignalGroups state.value.mapSpatTimes = { ...state.value.mapSpatTimes, mapTime: action.payload.mapTime } @@ -1373,7 +1467,7 @@ export const intersectionMapSlice = createSlice({ state.value.filteredSurroundingNotifications = action.payload.filteredSurroundingNotifications } ) - .addCase(initializeLiveStreaming.fulfilled, (state, action: PayloadAction) => { + .addCase(initializeLiveStreaming.fulfilled, (state, action: PayloadAction) => { state.value.wsClient = action.payload }) .addCase(getBsmDailyCounts.fulfilled, (state, action: PayloadAction) => { @@ -1434,13 +1528,15 @@ export const selectShowPopupOnHover = (state: RootState) => state.intersectionMa export const selectImportedMessageData = (state: RootState) => state.intersectionMap.value.importedMessageData export const selectCursor = (state: RootState) => state.intersectionMap.value.cursor export const selectLoadInitialDataTimeoutId = (state: RootState) => state.intersectionMap.value.loadInitialDataTimeoutId -// export const selectWsClient = (state: RootState) => state.intersectionMap.value.wsClient; +export const selectWsClient = (state: RootState) => state.intersectionMap.value.wsClient export const selectLiveDataActive = (state: RootState) => state.intersectionMap.value.liveDataActive export const selectCurrentMapData = (state: RootState) => state.intersectionMap.value.currentMapData export const selectCurrentSpatData = (state: RootState) => state.intersectionMap.value.currentSpatData export const selectCurrentBsmData = (state: RootState) => state.intersectionMap.value.currentBsmData export const selectSliderTimeValue = (state: RootState) => state.intersectionMap.value.sliderTimeValue export const selectBsmTrailLength = (state: RootState) => state.intersectionMap.value.bsmTrailLength +export const selectLiveDataRestartTimeoutId = (state: RootState) => state.intersectionMap.value.liveDataRestartTimeoutId +export const selectLiveDataRestart = (state: RootState) => state.intersectionMap.value.liveDataRestart export const { setSurroundingEvents, @@ -1469,6 +1565,9 @@ export const { setMapProps, setSourceApi, togglePlaybackModeActive, + resetMapView, + setLiveDataRestartTimeoutId, + setLiveDataRestart, } = intersectionMapSlice.actions export default intersectionMapSlice.reducer diff --git a/webapp/src/components/map/utilities/colors.ts b/webapp/src/components/map/utilities/colors.ts index a3ee1e210..bbbbba065 100644 --- a/webapp/src/components/map/utilities/colors.ts +++ b/webapp/src/components/map/utilities/colors.ts @@ -1,14 +1,95 @@ import { sha256 } from 'js-sha256' +import { hsl, rgb } from 'color-convert' +import * as Color from 'color' + +const DISALLOWED_COLORS = new Set( + Array.from( + new Set([ + Color('#797979').hsl().color[0], + Color('#3a3a3a').hsl().color[0], + Color('#c00000').hsl().color[0], + Color('#267700').hsl().color[0], + Color('#e6b000').hsl().color[0], + Color('#ffffff').hsl().color[0], + Color('#000000').hsl().color[0], + Color('#d40000').hsl().color[0], + Color('#eb34e8').hsl().color[0], + Color('#0004ff').hsl().color[0], + ]) // Extract the hue + ).sort((a, b) => a - b) // ascending order +) +console.log('Color dictionary DISALLOWED_COLORS:', DISALLOWED_COLORS) + +// pre-allocate color space around disallowed colors +const OUTPUT_COLOR_SPACE_LENGTH = 360 +const COLOR_SPACE_AVOID_WIDTH = 7 // on each side +const COLOR_SPACE_INCREMENT = 0.1 +const COLOR_SPACE_MAP = new Map() // map from generic 0 -> 360 color space, to new shrunken color space + +// Calculate the total avoidance space +let totalAvoidanceSpace = 0 +for (let i = 0; i < OUTPUT_COLOR_SPACE_LENGTH; i += COLOR_SPACE_INCREMENT) { + for (const disallowedHue of DISALLOWED_COLORS) { + if (Math.abs(disallowedHue - i) < COLOR_SPACE_AVOID_WIDTH) { + totalAvoidanceSpace += COLOR_SPACE_INCREMENT + } + } +} + +const OUTPUT_COLOR_SPACE_LENGTH_BEFORE_SCALING = 360 - totalAvoidanceSpace + +console.log('Color dictionary Total avoidance space:', totalAvoidanceSpace) + +// Generate the new color space +let prevColor = 0 +for (let i = 0; i < OUTPUT_COLOR_SPACE_LENGTH; i += COLOR_SPACE_INCREMENT) { + let newColor = + prevColor + (COLOR_SPACE_INCREMENT * OUTPUT_COLOR_SPACE_LENGTH_BEFORE_SCALING) / OUTPUT_COLOR_SPACE_LENGTH + for (const disallowedHue of DISALLOWED_COLORS.values()) { + // console.log(disallowedHue, newColor, Math.abs(disallowedHue - newColor) < COLOR_SPACE_AVOID_WIDTH); + if (Math.abs(disallowedHue - newColor) < COLOR_SPACE_AVOID_WIDTH) { + newColor = disallowedHue + COLOR_SPACE_AVOID_WIDTH + } + } + // console.log("Color dictionary New color:", i, Math.round(i * 10) / 10, newColor, prevColor, newColor - prevColor); + COLOR_SPACE_MAP.set(Math.round(i * 10) / 10, newColor) + prevColor = newColor +} + +const start = 355 +const end = 360 +const step = 0.1 + +const numbers: number[] = [] +for (let i = start; i <= end; i += step) { + numbers.push(Math.round(i * 10) / 10) +} + export function generateColorDictionary(inputSet: Set): { [key: string]: string } { const keysArray = Array.from(inputSet).sort() // Sort the keysArray const colorDictionary: { [key: string]: string } = {} keysArray.forEach((key) => { - const hash = sha256(key) // Generate a hash for each key - const color = `#${hash.slice(0, 6)}` // Use the first 6 characters of the hash as the color - colorDictionary[key] = color + const hash = sha256(key) // Generate a hash for each key using sha256 + let hue = (parseInt(hash.slice(0, 2), 16) * 360) / 256 // Use the first 2 characters of the hash as the hue + + // Map the hue to the shrunken color space + const keyInMap = Math.round(hue * 10) / 10 + console.log('Color dictionary Key:', hue, keyInMap, COLOR_SPACE_MAP.get(keyInMap), DISALLOWED_COLORS) + hue = COLOR_SPACE_MAP.get(keyInMap) || hue + + const color = Color.hsl(hue, 100, 50) // Use the hue to generate a color + console.log('Color dictionary Color:', hue, 100, 65, color, color.hex()) + colorDictionary[key] = color.hex() }) + // console.log( + // "Color dictionary: ", + // inputSet, + // colorDictionary, + // numbers.map((n) => ({ k: n, v: COLOR_SPACE_MAP.get(n) })) + // ); + return colorDictionary } @@ -21,3 +102,5 @@ export function generateMapboxStyleExpression(colors: { [key: string]: string }) layerStyle.push('#000000') // other return layerStyle } + +// console.log(generateColorDictionary(new Set(["0139C942", "13906CFE", "37BDF36C", "5C6FA267"]))); diff --git a/webapp/src/components/map/utilities/message-utils.ts b/webapp/src/components/map/utilities/message-utils.ts index 27a10dc5b..08dd042ec 100644 --- a/webapp/src/components/map/utilities/message-utils.ts +++ b/webapp/src/components/map/utilities/message-utils.ts @@ -187,7 +187,6 @@ export const parseBsmToGeojson = (bsmData: OdeBsmData[]): BsmFeatureCollection = }), } } - export const addConnections = ( connectingLanes: ConnectingLanesFeatureCollection, signalGroups: SpatSignalGroup[], @@ -199,74 +198,77 @@ export const addConnections = ( //for each connecting lane, fetch its ingress and egress lanes connectingLanes = { ...connectingLanes, - features: connectingLanes.features?.map((connectionFeature: ConnectingLanesFeature) => { - var ingressLaneId = connectionFeature.properties.ingressLaneId - var egressLaneId = connectionFeature.properties.egressLaneId - var ingressLane = mapFeatures.features.find((feature) => feature.id === ingressLaneId) - var egressLane = mapFeatures.features.find((feature) => feature.id === egressLaneId) - - if (ingressLane && egressLane) { - var ingressCoords = ingressLane.geometry.coordinates - var egressCoords = egressLane.geometry.coordinates - - var ingressBearing = turf.bearing(ingressCoords[1], ingressCoords[0]) - var egressBearing = turf.bearing(egressCoords[1], egressCoords[0]) + features: connectingLanes.features + ?.map((connectionFeature: ConnectingLanesFeature) => { + var ingressLaneId = connectionFeature.properties.ingressLaneId + var egressLaneId = connectionFeature.properties.egressLaneId + var ingressLane = mapFeatures.features.find((feature) => feature.id === ingressLaneId) + var egressLane = mapFeatures.features.find((feature) => feature.id === egressLaneId) - //project the ingress/egress lanes through the intersection to the edge of the bbox - var ingressLine = turf.lineString([ - ingressCoords[0], - turf.destination(ingressCoords[0], 0.05, ingressBearing).geometry.coordinates, - ]) - var egressLine = turf.lineString([ - egressCoords[0], - turf.destination(egressCoords[0], 0.05, egressBearing).geometry.coordinates, - ]) - var clippedIngress = turf.bboxClip(ingressLine, bbox) - var clippedEgress = turf.bboxClip(egressLine, bbox) + if (ingressLane && egressLane) { + var ingressCoords = ingressLane.geometry.coordinates + var egressCoords = egressLane.geometry.coordinates - //find the intersection point of the projected lanes, if it exists - var intersect = turf.lineIntersect(clippedIngress.geometry, clippedEgress.geometry) + var ingressBearing = turf.bearing(ingressCoords[1], ingressCoords[0]) + var egressBearing = turf.bearing(egressCoords[1], egressCoords[0]) - //if the lanes intersect within the intersection, this is a ~90 degree turn and we add 1 more point to round the curve - if (intersect.features.length > 0) { - var intersectPoint = intersect.features[0].geometry.coordinates - //the intersection would overshoot the curve, so curveMidpoint is a weighted average the intersection and connectingLanes edges - var curveMidpoint = turf.centroid( - turf.points([ingressCoords[0], egressCoords[0], intersectPoint, intersectPoint, intersectPoint]) - ) - - var connectingLaneLine = turf.lineString([ + //project the ingress/egress lanes through the intersection to the edge of the bbox + var ingressLine = turf.lineString([ ingressCoords[0], - curveMidpoint.geometry.coordinates, + turf.destination(ingressCoords[0], 0.05, ingressBearing).geometry.coordinates, + ]) + var egressLine = turf.lineString([ egressCoords[0], + turf.destination(egressCoords[0], 0.05, egressBearing).geometry.coordinates, ]) - var curve = turf.bezierSpline(connectingLaneLine) - connectionFeature = { ...connectionFeature, geometry: curve.geometry } - } + var clippedIngress = turf.bboxClip(ingressLine, bbox) + var clippedEgress = turf.bboxClip(egressLine, bbox) - //If the ingress and egress lanes are going in generally opposite directions and didn't intersect, use the U-turn calculations - else if (Math.abs(ingressBearing - egressBearing) < 45) { - //this formula was found experimentally to give a round curve and allow parallel curving lanes to not intersect - var leadupLength = Math.min(turf.distance(ingressCoords[0], egressCoords[0]) * -7 + 0.045, -0.02) + //find the intersection point of the projected lanes, if it exists + var intersect = turf.lineIntersect(clippedIngress.geometry, clippedEgress.geometry) - var normalizedIngressPoint = turf.destination(ingressCoords[0], leadupLength, ingressBearing) - var normalizedEgressPoint = turf.destination(egressCoords[0], leadupLength, egressBearing) - var connectingLaneLine = turf.lineString([ - normalizedIngressPoint.geometry.coordinates, - ingressCoords[0], - egressCoords[0], - normalizedEgressPoint.geometry.coordinates, - ]) + //if the lanes intersect within the intersection, this is a ~90 degree turn and we add 1 more point to round the curve + if (intersect.features.length > 0) { + var intersectPoint = intersect.features[0].geometry.coordinates + //the intersection would overshoot the curve, so curveMidpoint is a weighted average the intersection and connectingLanes edges + var curveMidpoint = turf.centroid( + turf.points([ingressCoords[0], egressCoords[0], intersectPoint, intersectPoint, intersectPoint]) + ) - var rawCurve = turf.bezierSpline(connectingLaneLine) - //slice the curve back to remove the redundant ends - var curve = turf.lineSlice(ingressCoords[0], egressCoords[0], rawCurve) - connectionFeature = { ...connectionFeature, geometry: curve.geometry } + var connectingLaneLine = turf.lineString([ + ingressCoords[0], + curveMidpoint.geometry.coordinates, + egressCoords[0], + ]) + var curve = turf.bezierSpline(connectingLaneLine) + connectionFeature = { ...connectionFeature, geometry: curve.geometry } + } + + //If the ingress and egress lanes are going in generally opposite directions and didn't intersect, use the U-turn calculations + else if (Math.abs(ingressBearing - egressBearing) < 45) { + //this formula was found experimentally to give a round curve and allow parallel curving lanes to not intersect + var leadupLength = Math.min(turf.distance(ingressCoords[0], egressCoords[0]) * -7 + 0.045, -0.02) + + var normalizedIngressPoint = turf.destination(ingressCoords[0], leadupLength, ingressBearing) + var normalizedEgressPoint = turf.destination(egressCoords[0], leadupLength, egressBearing) + var connectingLaneLine = turf.lineString([ + normalizedIngressPoint.geometry.coordinates, + ingressCoords[0], + egressCoords[0], + normalizedEgressPoint.geometry.coordinates, + ]) + + var rawCurve = turf.bezierSpline(connectingLaneLine) + //slice the curve back to remove the redundant ends + var curve = turf.lineSlice(ingressCoords[0], egressCoords[0], rawCurve) + connectionFeature = { ...connectionFeature, geometry: curve.geometry } + } + //anything else is mostly straight and doesn't require a bezier curve + return connectionFeature } - //anything else is mostly straight and doesn't require a bezier curve - return connectionFeature - } - }), + return null + }) + .filter((feature) => feature !== null) as ConnectingLanesFeature[], } return { From cb333b5fec961fa43cf0e0feedca55bab0126fac Mon Sep 17 00:00:00 2001 From: jacob6838 Date: Tue, 21 May 2024 15:53:09 -0600 Subject: [PATCH 023/189] Fixing cviz bsm colors error --- webapp/package-lock.json | 51 +++++++++++++++++++ webapp/package.json | 1 + webapp/src/components/map/utilities/colors.ts | 4 +- webapp/yarn.lock | 36 +++++++++++-- 4 files changed, 86 insertions(+), 6 deletions(-) diff --git a/webapp/package-lock.json b/webapp/package-lock.json index b457edc32..e11ec3cf4 100644 --- a/webapp/package-lock.json +++ b/webapp/package-lock.json @@ -28,6 +28,7 @@ "@types/react-tabs": "^5.0.5", "abort-controller": "^3.0.0", "axios": "^1.6.5", + "color": "^4.2.3", "dayjs": "^1.11.7", "env-cmd": "^10.1.0", "fbjs": "^3.0.4", @@ -10435,6 +10436,18 @@ "node": ">=0.10.0" } }, + "node_modules/color": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/color/-/color-4.2.3.tgz", + "integrity": "sha512-1rXeuUUiGGrykh+CeBdu5Ie7OJwinCgQY0bc7GCRxy5xVHy+moaqkpL/jqQq0MtQOeYcrqEz4abc5f0KtU7W4A==", + "dependencies": { + "color-convert": "^2.0.1", + "color-string": "^1.9.0" + }, + "engines": { + "node": ">=12.5.0" + } + }, "node_modules/color-convert": { "version": "1.9.3", "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", @@ -10448,6 +10461,31 @@ "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=" }, + "node_modules/color-string": { + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/color-string/-/color-string-1.9.1.tgz", + "integrity": "sha512-shrVawQFojnZv6xM40anx4CkoDP+fZsw/ZerEMsW/pyzsRbElpsL/DBVW7q3ExxwusdNXI3lXpuhEZkzs8p5Eg==", + "dependencies": { + "color-name": "^1.0.0", + "simple-swizzle": "^0.2.2" + } + }, + "node_modules/color/node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/color/node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==" + }, "node_modules/colord": { "version": "2.9.3", "resolved": "https://registry.npmjs.org/colord/-/colord-2.9.3.tgz", @@ -26330,6 +26368,19 @@ "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz", "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==" }, + "node_modules/simple-swizzle": { + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/simple-swizzle/-/simple-swizzle-0.2.2.tgz", + "integrity": "sha512-JA//kQgZtbuY83m+xT+tXJkmJncGMTFT+C+g2h2R9uxkYIrE2yy9sgmcLhCnw57/WSD+Eh3J97FPEDFnbXnDUg==", + "dependencies": { + "is-arrayish": "^0.3.1" + } + }, + "node_modules/simple-swizzle/node_modules/is-arrayish": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.3.2.tgz", + "integrity": "sha512-eVRqCvVlZbuw3GrM63ovNSNAeA1K16kaR/LRY/92w0zxQ5/1YzwblUX652i4Xs9RwAGjW9d9y6X88t8OaAJfWQ==" + }, "node_modules/sisteransi": { "version": "1.0.5", "resolved": "https://registry.npmjs.org/sisteransi/-/sisteransi-1.0.5.tgz", diff --git a/webapp/package.json b/webapp/package.json index 337afb654..d488192a9 100644 --- a/webapp/package.json +++ b/webapp/package.json @@ -23,6 +23,7 @@ "@types/react-tabs": "^5.0.5", "abort-controller": "^3.0.0", "axios": "^1.6.5", + "color": "^4.2.3", "dayjs": "^1.11.7", "env-cmd": "^10.1.0", "fbjs": "^3.0.4", diff --git a/webapp/src/components/map/utilities/colors.ts b/webapp/src/components/map/utilities/colors.ts index bbbbba065..a2561458a 100644 --- a/webapp/src/components/map/utilities/colors.ts +++ b/webapp/src/components/map/utilities/colors.ts @@ -29,7 +29,7 @@ const COLOR_SPACE_MAP = new Map() // map from generic 0 -> 360 c // Calculate the total avoidance space let totalAvoidanceSpace = 0 for (let i = 0; i < OUTPUT_COLOR_SPACE_LENGTH; i += COLOR_SPACE_INCREMENT) { - for (const disallowedHue of DISALLOWED_COLORS) { + for (const disallowedHue of Array.from(DISALLOWED_COLORS)) { if (Math.abs(disallowedHue - i) < COLOR_SPACE_AVOID_WIDTH) { totalAvoidanceSpace += COLOR_SPACE_INCREMENT } @@ -45,7 +45,7 @@ let prevColor = 0 for (let i = 0; i < OUTPUT_COLOR_SPACE_LENGTH; i += COLOR_SPACE_INCREMENT) { let newColor = prevColor + (COLOR_SPACE_INCREMENT * OUTPUT_COLOR_SPACE_LENGTH_BEFORE_SCALING) / OUTPUT_COLOR_SPACE_LENGTH - for (const disallowedHue of DISALLOWED_COLORS.values()) { + for (const disallowedHue of Array.from(DISALLOWED_COLORS.values())) { // console.log(disallowedHue, newColor, Math.abs(disallowedHue - newColor) < COLOR_SPACE_AVOID_WIDTH); if (Math.abs(disallowedHue - newColor) < COLOR_SPACE_AVOID_WIDTH) { newColor = disallowedHue + COLOR_SPACE_AVOID_WIDTH diff --git a/webapp/yarn.lock b/webapp/yarn.lock index 6d77b650a..4e842b3c8 100644 --- a/webapp/yarn.lock +++ b/webapp/yarn.lock @@ -5644,6 +5644,11 @@ color-convert@^2.0.1: dependencies: color-name "~1.1.4" +color-name@^1.0.0, color-name@1.1.3: + version "1.1.3" + resolved "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz" + integrity sha1-p9BVi9icQveV3UIyj3QIMcpTvCU= + color-name@^1.1.4: version "1.1.4" resolved "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz" @@ -5654,10 +5659,21 @@ color-name@~1.1.4: resolved "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz" integrity sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA== -color-name@1.1.3: - version "1.1.3" - resolved "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz" - integrity sha1-p9BVi9icQveV3UIyj3QIMcpTvCU= +color-string@^1.9.0: + version "1.9.1" + resolved "https://registry.npmjs.org/color-string/-/color-string-1.9.1.tgz" + integrity sha512-shrVawQFojnZv6xM40anx4CkoDP+fZsw/ZerEMsW/pyzsRbElpsL/DBVW7q3ExxwusdNXI3lXpuhEZkzs8p5Eg== + dependencies: + color-name "^1.0.0" + simple-swizzle "^0.2.2" + +color@^4.2.3: + version "4.2.3" + resolved "https://registry.npmjs.org/color/-/color-4.2.3.tgz" + integrity sha512-1rXeuUUiGGrykh+CeBdu5Ie7OJwinCgQY0bc7GCRxy5xVHy+moaqkpL/jqQq0MtQOeYcrqEz4abc5f0KtU7W4A== + dependencies: + color-convert "^2.0.1" + color-string "^1.9.0" colord@^2.9.1: version "2.9.3" @@ -8387,6 +8403,11 @@ is-arrayish@^0.2.1: resolved "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz" integrity sha1-d8mYQFJ6qOyxqLppe4BkWnqSap0= +is-arrayish@^0.3.1: + version "0.3.2" + resolved "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.3.2.tgz" + integrity sha512-eVRqCvVlZbuw3GrM63ovNSNAeA1K16kaR/LRY/92w0zxQ5/1YzwblUX652i4Xs9RwAGjW9d9y6X88t8OaAJfWQ== + is-async-function@^2.0.0: version "2.0.0" resolved "https://registry.npmjs.org/is-async-function/-/is-async-function-2.0.0.tgz" @@ -13005,6 +13026,13 @@ signal-exit@^4.0.1: resolved "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz" integrity sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw== +simple-swizzle@^0.2.2: + version "0.2.2" + resolved "https://registry.npmjs.org/simple-swizzle/-/simple-swizzle-0.2.2.tgz" + integrity sha512-JA//kQgZtbuY83m+xT+tXJkmJncGMTFT+C+g2h2R9uxkYIrE2yy9sgmcLhCnw57/WSD+Eh3J97FPEDFnbXnDUg== + dependencies: + is-arrayish "^0.3.1" + sisteransi@^1.0.5: version "1.0.5" resolved "https://registry.npmjs.org/sisteransi/-/sisteransi-1.0.5.tgz" From 75feb3272e5d79f5d20fc9ec1f351c47e367c1fe Mon Sep 17 00:00:00 2001 From: jacob6838 Date: Wed, 22 May 2024 12:24:01 -0600 Subject: [PATCH 024/189] Tweaking cimms popup display --- webapp/src/components/map/control-panel.tsx | 4 +- webapp/src/components/map/custom-table.tsx | 8 +- webapp/src/components/map/map-component.tsx | 43 ++++++++--- webapp/src/components/map/map-slice.tsx | 57 +++++++------- webapp/src/components/map/popup.tsx | 84 ++++++++++----------- 5 files changed, 110 insertions(+), 86 deletions(-) diff --git a/webapp/src/components/map/control-panel.tsx b/webapp/src/components/map/control-panel.tsx index c7ac25ab3..11a7fbe71 100644 --- a/webapp/src/components/map/control-panel.tsx +++ b/webapp/src/components/map/control-panel.tsx @@ -418,12 +418,12 @@ function ControlPanel() { var jsZip = new JSZip() const messageData: { mapData: ProcessedMap[] - bsmData: OdeBsmData[] + bsmData: BsmFeatureCollection spatData: ProcessedSpat[] notificationData: any } = { mapData: [], - bsmData: [], + bsmData: { type: 'FeatureCollection', features: [] }, spatData: [], notificationData: undefined, } diff --git a/webapp/src/components/map/custom-table.tsx b/webapp/src/components/map/custom-table.tsx index 55c4b214b..c98c937f9 100644 --- a/webapp/src/components/map/custom-table.tsx +++ b/webapp/src/components/map/custom-table.tsx @@ -26,12 +26,14 @@ export const CustomTable = (props: CustomTableProps) => { let cellKey = 0 return ( - +
{headers.map((head) => ( - {head} + + {head} + ))} @@ -39,7 +41,7 @@ export const CustomTable = (props: CustomTableProps) => { {data.map((row) => ( {row.map((cell) => ( - + <>{cell} ))} diff --git a/webapp/src/components/map/map-component.tsx b/webapp/src/components/map/map-component.tsx index 6d872bc3a..265809ce9 100644 --- a/webapp/src/components/map/map-component.tsx +++ b/webapp/src/components/map/map-component.tsx @@ -366,16 +366,19 @@ const MapTab = (props: MAP_PROPS) => { onMouseEnter={(e) => dispatch(onMapMouseEnter({ features: e.features, lngLat: e.lngLat }))} onMouseLeave={(e) => dispatch(onMapMouseLeave())} > - + @@ -383,32 +386,48 @@ const MapTab = (props: MAP_PROPS) => { - + - + - + diff --git a/webapp/src/components/map/map-slice.tsx b/webapp/src/components/map/map-slice.tsx index e8a5fd671..fb19055ad 100644 --- a/webapp/src/components/map/map-slice.tsx +++ b/webapp/src/components/map/map-slice.tsx @@ -37,13 +37,13 @@ import EnvironmentVars from '../../EnvironmentVars' import { downloadAllData } from './utilities/file-utilities' export type MAP_LAYERS = - | 'mapMessage' - | 'mapMessageLabels' - | 'connectingLanes' - | 'connectingLanesLabels' - | 'invalidLaneCollection' + | 'map-message' + | 'map-message-labels' + | 'connecting-lanes' + | 'connecting-lanes-labels' + | 'invalid-lane-collection' | 'bsm' - | 'signalStates' + | 'signal-states' export type MAP_QUERY_PARAMS = { startDate: Date @@ -56,7 +56,7 @@ export type MAP_QUERY_PARAMS = { export type IMPORTED_MAP_MESSAGE_DATA = { mapData: ProcessedMap[] - bsmData: OdeBsmData[] + bsmData: BsmFeatureCollection spatData: ProcessedSpat[] notificationData: any } @@ -122,15 +122,15 @@ const getTimestamp = (dt: any): number => { const initialState = { layersVisible: { - mapMessage: false, - mapMessageLabels: false, - connectingLanes: false, - connectingLanesLabels: false, - invalidLaneCollection: false, + 'map-message': false, + 'map-message-labels': false, + 'connecting-lanes': false, + 'connecting-lanes-labels': false, + 'invalid-lane-collection': false, bsm: false, - signalStates: false, + 'signal-states': false, } as Record, - allInteractiveLayerIds: ['mapMessage', 'connectingLanes', 'signalStates', 'bsm'] as MAP_LAYERS[], + allInteractiveLayerIds: ['map-message', 'connecting-lanes', 'signal-states', 'bsm'] as MAP_LAYERS[], queryParams: { startDate: new Date(Date.now() - 1000 * 60 * 1), endDate: new Date(Date.now() + 1000 * 60 * 1), @@ -219,7 +219,7 @@ export const pullInitialData = createAsyncThunk( console.debug('Pulling Initial Data') let rawMap: ProcessedMap[] = [] let rawSpat: ProcessedSpat[] = [] - let rawBsm: OdeBsmData[] = [] + let rawBsm: BsmFeatureCollection = { type: 'FeatureCollection', features: [] } if (!importedMessageData) { // ######################### Retrieve MAP Data ######################### const rawMapPromise = MessageMonitorApi.getMapMessages({ @@ -262,21 +262,23 @@ export const pullInitialData = createAsyncThunk( dispatch(getSurroundingNotifications()) } else { rawMap = importedMessageData.mapData - rawSpat = importedMessageData.spatData.sort((a, b) => a.utcTimeStamp - b.utcTimeStamp) + rawSpat = [...importedMessageData.spatData].sort((a, b) => a.utcTimeStamp - b.utcTimeStamp) rawBsm = importedMessageData.bsmData } if (!rawMap || rawMap.length == 0) { console.info('NO MAP MESSAGES WITHIN TIME') - // return; + return } const latestMapMessage: ProcessedMap = rawMap.at(-1)! if (latestMapMessage != null) { - setViewState({ - latitude: latestMapMessage?.properties.refPoint.latitude, - longitude: latestMapMessage?.properties.refPoint.longitude, - zoom: 19, - }) + dispatch( + setViewState({ + latitude: latestMapMessage?.properties.refPoint.latitude, + longitude: latestMapMessage?.properties.refPoint.longitude, + zoom: 19, + }) + ) } const mapCoordinates: OdePosition3D = latestMapMessage?.properties.refPoint @@ -285,6 +287,7 @@ export const pullInitialData = createAsyncThunk( const spatSignalGroupsLocal = parseSpatSignalGroups(rawSpat) // ######################### BSMs ######################### + let bsmGeojson = rawBsm if (!importedMessageData) { const rawBsmPromise = MessageMonitorApi.getBsmMessages({ token: authToken, @@ -300,12 +303,12 @@ export const pullInitialData = createAsyncThunk( success: `Successfully got BSM Data`, error: `Failed to get BSM data. Please see console`, }) - rawBsm = await rawBsmPromise + const rawBsmData = await rawBsmPromise + rawBsm = parseBsmToGeojson(rawBsmData) } - let bsmGeojson = parseBsmToGeojson(rawBsm) bsmGeojson = { ...bsmGeojson, - features: [...bsmGeojson.features.sort((a, b) => b.properties.odeReceivedAt - a.properties.odeReceivedAt)], + features: [...[...bsmGeojson.features].sort((a, b) => b.properties.odeReceivedAt - a.properties.odeReceivedAt)], } dispatch(renderEntireMap({ currentMapData: rawMap, currentSpatData: rawSpat, currentBsmData: bsmGeojson })) return { @@ -1102,7 +1105,7 @@ export const intersectionMapSlice = createSlice({ state, action: PayloadAction<{ mapData: ProcessedMap[] - bsmData: OdeBsmData[] + bsmData: BsmFeatureCollection spatData: ProcessedSpat[] notificationData: any }> @@ -1199,7 +1202,7 @@ export const intersectionMapSlice = createSlice({ }> ) => { const features = action.payload.mapRef.current.queryRenderedFeatures(action.payload.event.point, { - layers: state.value.allInteractiveLayerIds, + // layers: state.value.allInteractiveLayerIds, }) const feature = features?.[0] if (feature && state.value.allInteractiveLayerIds.includes(feature.layer.id)) { diff --git a/webapp/src/components/map/popup.tsx b/webapp/src/components/map/popup.tsx index 77b9c495a..96ceede2d 100644 --- a/webapp/src/components/map/popup.tsx +++ b/webapp/src/components/map/popup.tsx @@ -1,80 +1,80 @@ -import React, { useState, useEffect } from "react"; -import Map, { Source, Layer, Popup } from "react-map-gl"; +import React, { useState, useEffect } from 'react' +import Map, { Source, Layer, Popup } from 'react-map-gl' -import { Container, Col } from "reactstrap"; +import { Container, Col } from 'reactstrap' -import { Paper, Box, Typography } from "@mui/material"; -import { CustomTable } from "./custom-table"; +import { Paper, Box, Typography } from '@mui/material' +import { CustomTable } from './custom-table' export const getSelectedLayerPopupContent = (feature: any) => { switch (feature?.layer?.id) { - case "bsm": - let bsm = feature.properties; + case 'bsm': + let bsm = feature.properties return ( BSM - ); - case "mapMessage": - let map = feature.properties; - let connectedObjs: any[] = []; - JSON.parse(map?.connectsTo ?? "[]")?.forEach((connectsTo) => { - connectedObjs.push(["Connected Lane", connectsTo.connectingLane.lane]); - connectedObjs.push(["Signal Group", connectsTo.signalGroup]); - connectedObjs.push(["Connection ID", connectsTo.connectionID]); - }); + ) + case 'map-message': + let map = feature.properties + let connectedObjs: any[] = [] + JSON.parse(map?.connectsTo ?? '[]')?.forEach((connectsTo) => { + connectedObjs.push(['Connected Lane', connectsTo.connectingLane.lane]) + connectedObjs.push(['Signal Group', connectsTo.signalGroup]) + connectedObjs.push(['Connection ID', connectsTo.connectionID]) + }) return ( MAP Lane - + - ); + ) - case "connectingLanes": + case 'connecting-lanes': return ( Connecting Lane - ); + ) - case "signalStates": + case 'signal-states': return ( Signal State - ); + ) default: { - return {JSON.stringify(feature)}; + return {JSON.stringify(feature)} } } - return No Data; -}; + return No Data +} export const CustomPopup = (props) => { return ( @@ -84,10 +84,10 @@ export const CustomPopup = (props) => { anchor="bottom" onClose={props.onClose} onOpen={() => {}} - maxWidth={"500px"} + maxWidth={'500px'} closeOnClick={false} > {getSelectedLayerPopupContent(props.selectedFeature.feature)} - ); -}; + ) +} From d653c7de49530e82e00d747c9eb7844da71726e1 Mon Sep 17 00:00:00 2001 From: jacob6838 Date: Wed, 22 May 2024 17:13:50 -0600 Subject: [PATCH 025/189] Fixing PR comments, removing comments --- .vscode/settings.json | 3 +- webapp/src/apis/assessments-api.ts | 1 - webapp/src/apis/configuration-param-api.ts | 3 - webapp/src/apis/events-api.ts | 4 -- webapp/src/apis/keycloak-api.ts | 2 +- webapp/src/apis/rsu-api.test.ts | 1 - webapp/src/apis/user-management-api.ts | 1 - webapp/src/components/map/control-panel.tsx | 10 +-- webapp/src/components/map/map-legend.tsx | 33 ++-------- webapp/src/components/map/map-slice.tsx | 63 ++++++++----------- .../components/map/utilities/message-utils.ts | 11 ++-- .../models/MessageMonitorNotification.d.ts | 12 ---- webapp/src/models/README.md | 26 ++++++++ webapp/src/models/imports/ProcessedMap.d.ts | 7 --- webapp/src/models/imports/README.md | 2 + .../config/ConfigDataClass.d.ts | 4 -- 16 files changed, 67 insertions(+), 116 deletions(-) delete mode 100644 webapp/src/models/MessageMonitorNotification.d.ts create mode 100644 webapp/src/models/README.md delete mode 100644 webapp/src/models/jpo-conflictmonitor/config/ConfigDataClass.d.ts diff --git a/.vscode/settings.json b/.vscode/settings.json index ac1ec05f1..49d64d468 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -22,5 +22,6 @@ "[python]": { "editor.defaultFormatter": "ms-python.black-formatter", "editor.formatOnSave": true - } + }, + "cSpell.words": ["conflictvisualizer", "cviz", "millis"] } diff --git a/webapp/src/apis/assessments-api.ts b/webapp/src/apis/assessments-api.ts index 5c76919fd..c0697e475 100644 --- a/webapp/src/apis/assessments-api.ts +++ b/webapp/src/apis/assessments-api.ts @@ -1,4 +1,3 @@ -import { number } from 'prop-types' import { authApiHelper } from './api-helper-cviz' class AssessmentsApi { diff --git a/webapp/src/apis/configuration-param-api.ts b/webapp/src/apis/configuration-param-api.ts index eb7ba7d1c..e2c0cd241 100644 --- a/webapp/src/apis/configuration-param-api.ts +++ b/webapp/src/apis/configuration-param-api.ts @@ -24,7 +24,6 @@ import { authApiHelper } from './api-helper-cviz' class ConfigParamsApi { async getGeneralParameters(token: string): Promise { - // return ConfigParamsGeneral; try { var response = await authApiHelper.invokeApi({ path: '/config/default/all', @@ -43,7 +42,6 @@ class ConfigParamsApi { intersectionId: number, roadRegulatorId: number ): Promise { - // return configParamsIntersection; try { var response = await authApiHelper.invokeApi({ path: '/config/intersection/unique', @@ -104,7 +102,6 @@ class ConfigParamsApi { token: token, queryParams: { intersection_id: intersectionId.toString(), road_regulator_id: roadRegulatorId.toString() }, toastOnFailure: false, - // failureMessage: `Failed to Retrieve Configuration Parameter ${key}`, }) ) .filter((c) => c.key === key && c.intersectionID !== null && c.intersectionID !== 0 && c.intersectionID !== -1) diff --git a/webapp/src/apis/events-api.ts b/webapp/src/apis/events-api.ts index 17f974ac5..106f8251d 100644 --- a/webapp/src/apis/events-api.ts +++ b/webapp/src/apis/events-api.ts @@ -9,7 +9,6 @@ const EVENT_TYPES: Item[] = [ { label: 'ConnectionOfTravelEvent', value: 'connection_of_travel' }, { label: 'IntersectionReferenceAlignmentEvent', value: 'intersection_reference_alignment' }, { label: 'LaneDirectionOfTravelEvent', value: 'lane_direction_of_travel' }, - // { label: "ProcessingTimePeriod", value: "processing_time_period" }, { label: 'SignalGroupAlignmentEvent', value: 'signal_group_alignment' }, { label: 'SignalStateConflictEvent', value: 'signal_state_conflict' }, { label: 'SignalStateEvent', value: 'signal_state' }, @@ -38,7 +37,6 @@ class EventsApi { end_time_utc_millis: endTime.getTime().toString(), latest: latest.toString(), } - // if (roadRegulatorId) queryParams["road_regulator_id"] = roadRegulatorId; const response = await authApiHelper.invokeApi({ path: `/events/${eventType}`, @@ -62,7 +60,6 @@ class EventsApi { start_time_utc_millis: startTime.getTime().toString(), end_time_utc_millis: endTime.getTime().toString(), } - // if (roadRegulatorId) queryParams["road_regulator_id"] = roadRegulatorId; const events: MessageMonitor.Event[] = [] for (const eventTypeObj of EVENT_TYPES) { @@ -91,7 +88,6 @@ class EventsApi { end_time_utc_millis: endTime.getTime().toString(), test: test.toString(), } - // if (roadRegulatorId) queryParams["road_regulator_id"] = roadRegulatorId; const response = await authApiHelper.invokeApi({ path: `/events/bsm_events_by_minute`, diff --git a/webapp/src/apis/keycloak-api.ts b/webapp/src/apis/keycloak-api.ts index 7ce92f281..307025617 100644 --- a/webapp/src/apis/keycloak-api.ts +++ b/webapp/src/apis/keycloak-api.ts @@ -176,7 +176,7 @@ class KeycloakApi { basePath: KEYCLOAK_AUTH_ENDPOINT, method: 'POST', token: token, - body: `client_id=conflictvisualizer-gui&client_secret=O2HDhrF14XJE9FhTTYX6iEdgBdSy9E6d&refresh_token=${refresh_token}`, + body: `client_id=${EnvironmentVars.KEYCLOAK_REALM}&refresh_token=${refresh_token}`, headers: { 'Content-Type': 'application/x-www-form-urlencoded', }, diff --git a/webapp/src/apis/rsu-api.test.ts b/webapp/src/apis/rsu-api.test.ts index 4b3609ad5..7583f4f8a 100644 --- a/webapp/src/apis/rsu-api.test.ts +++ b/webapp/src/apis/rsu-api.test.ts @@ -1,6 +1,5 @@ import RsuApi from './rsu-api' import EnvironmentVars from '../EnvironmentVars' -import ApiHelper from './api-helper' beforeEach(() => { fetchMock.mockClear() diff --git a/webapp/src/apis/user-management-api.ts b/webapp/src/apis/user-management-api.ts index 51e264311..d7f7b85c5 100644 --- a/webapp/src/apis/user-management-api.ts +++ b/webapp/src/apis/user-management-api.ts @@ -1,4 +1,3 @@ -import { User } from '../icons/user' import { authApiHelper } from './api-helper-cviz' class UserManagementApi { diff --git a/webapp/src/components/map/control-panel.tsx b/webapp/src/components/map/control-panel.tsx index 11a7fbe71..ce959cff1 100644 --- a/webapp/src/components/map/control-panel.tsx +++ b/webapp/src/components/map/control-panel.tsx @@ -70,15 +70,7 @@ import playIcon from '../../icons/play.png' import { BarChart, XAxis, Bar, ResponsiveContainer, Tooltip } from 'recharts' const Accordion = styled((props: AccordionProps) => )( - ({ theme }) => ({ - //border: `1px solid ${theme.palette.divider}`, - // '&:not(:last-child)': { - // borderBottom: 0, - // }, - // '&:before': { - // display: 'none', - // }, - }) + ({ theme }) => ({}) ) const AccordionSummary = styled((props: AccordionSummaryProps) => ( diff --git a/webapp/src/components/map/map-legend.tsx b/webapp/src/components/map/map-legend.tsx index 908679553..8fc98ddc2 100644 --- a/webapp/src/components/map/map-legend.tsx +++ b/webapp/src/components/map/map-legend.tsx @@ -26,35 +26,8 @@ import trafficLightIconGreen1 from '../../icons/map/traffic-light-icon-green-1.s import { selectMapLegendColors } from './map-layer-style-slice' import { useSelector } from 'react-redux' -// "DARK": "/icons/traffic-light-icon-unknown", -// "STOP_THEN_PROCEED": "/icons/traffic-light-icon-red-flashing", -// "STOP_AND_REMAIN": "/icons/traffic-light-icon-red-1", -// "PRE_MOVEMENT": "/icons/traffic-light-icon-yellow-red-1", -// "PERMISSIVE_MOVEMENT_ALLOWED": "/icons/traffic-light-icon-yellow-1", -// "PROTECTED_MOVEMENT_ALLOWED": "/icons/traffic-light-icon-green-1", -// "PERMISSIVE_CLEARANCE": "/icons/traffic-light-icon-yellow-1", -// "PROTECTED_CLEARANCE": "/icons/traffic-light-icon-yellow-1", -// "CAUTION_CONFLICTING_TRAFFIC": "/icons/traffic-light-icon-yellow-1", - -const signalHeadIconsObj = { - 'traffic-light-icon-unknown': trafficLightIconUnknown, - 'traffic-light-icon-red-flashing': trafficLightIconRedFlashing, - 'traffic-light-icon-red-1': trafficLightIconRed1, - 'traffic-light-icon-yellow-red-1': trafficLightIconYellowRed1, - 'traffic-light-icon-yellow-1': trafficLightIconYellow1, - 'traffic-light-icon-green-1': trafficLightIconGreen1, -} - const Accordion = styled((props: AccordionProps) => )( - ({ theme }) => ({ - //border: `1px solid ${theme.palette.divider}`, - // '&:not(:last-child)': { - // borderBottom: 0, - // }, - // '&:before': { - // display: 'none', - // }, - }) + ({ theme }) => ({}) ) const AccordionSummary = styled((props: AccordionSummaryProps) => ( @@ -103,7 +76,9 @@ export const MapLegend = () => { const travelConnectionColorsList: JSX.Element[] = [] for (const [key, value] of Object.entries(travelConnectionColors)) { - const heightFactor = 20 / 2 / value[1].reduce((partialSum, a) => partialSum + a, 0) // 20 is the height of the colored bar, 2 is the number of sets of colors + const barHeight = 20 + const numColorSets = 2 + const heightFactor = barHeight / numColorSets / value[1].reduce((partialSum, a) => partialSum + a, 0) const heightColored = value[1][0] * heightFactor const heightWhite = (value[1].length == 1 ? 0 : value[1][1]) * heightFactor travelConnectionColorsList.push( diff --git a/webapp/src/components/map/map-slice.tsx b/webapp/src/components/map/map-slice.tsx index fb19055ad..21f7ce2b3 100644 --- a/webapp/src/components/map/map-slice.tsx +++ b/webapp/src/components/map/map-slice.tsx @@ -1,7 +1,7 @@ -import { AnyAction, createAsyncThunk, createSlice, PayloadAction, ThunkDispatch } from '@reduxjs/toolkit' +import { createAsyncThunk, createSlice, PayloadAction } from '@reduxjs/toolkit' import { RootState } from '../../store' import { selectToken } from '../../generalSlices/userSlice' -import { Client, CompatClient, IMessage, Stomp } from '@stomp/stompjs' +import { CompatClient, IMessage, Stomp } from '@stomp/stompjs' import MessageMonitorApi from '../../apis/mm-api' import EventsApi from '../../apis/events-api' import NotificationApi from '../../apis/notification-api' @@ -16,23 +16,8 @@ import { generateColorDictionary, generateMapboxStyleExpression } from './utilit import { setBsmCircleColor, setBsmLegendColors } from './map-layer-style-slice' import { getTimeRange } from './utilities/map-utils' import { ViewState } from 'react-map-gl' -import JSZip from 'jszip' -import FileSaver from 'file-saver' -import { features } from 'process' -import { - getMapData, - selectMapDate, - selectMapList, - selectRsu, - selectRsuData, - selectRsuIpv4, - selectRsuMapData, - selectSelectedRsu, - selectSelectedSrm, - selectSrmSsmList, - selectSsmDisplay, -} from '../../generalSlices/rsuSlice' -import { RsuInfo } from '../../apis/rsu-api-types' +import { getMapData, selectMapList, selectRsu } from '../../generalSlices/rsuSlice' +import { RsuInfo, SsmSrmData } from '../../apis/rsu-api-types' import EnvironmentVars from '../../EnvironmentVars' import { downloadAllData } from './utilities/file-utilities' @@ -199,6 +184,9 @@ const initialState = { bsmTrailLength: 20, liveDataRestart: -1, liveDataRestartTimeoutId: undefined as NodeJS.Timeout | undefined, + srmCount: 0, + srmSsmCount: 0, + srmMsgList: [], } const getNewSliderTimeValue = (startDate: Date, sliderValue: number, timeWindowSeconds: number) => { @@ -810,7 +798,6 @@ export const initializeLiveStreaming = createAsyncThunk( } client.onStompError = (frame) => { - // TODO: Consider restarting connection on error console.error('Live Streaming STOMP ERROR', frame) } @@ -1034,24 +1021,24 @@ export const intersectionMapSlice = createSlice({ value: initialState, }, reducers: { - updateSsmSrmCounts: (state, action: PayloadAction) => { - // let localSrmCount = 0 - // let localSsmCount = 0 - // let localMsgList = [] - // // console.error('srmSsmList', state.value.srmSsmList) - // for (const elem of state.value.srmSsmList) { - // if (elem.ip === state.value.rsuIpv4) { - // localMsgList.push(elem) - // if (elem.type === 'srmTx') { - // localSrmCount += 1 - // } else { - // localSsmCount += 1 - // } - // } - // } - // state.value.srmCount = localSrmCount - // state.value.srmSsmCount = localSsmCount - // state.value.srmMsgList = localMsgList + updateSsmSrmCounts: (state, action: PayloadAction<{ srmSsmList: SsmSrmData; rsuIpv4: string }>) => { + let localSrmCount = 0 + let localSsmCount = 0 + let localMsgList = [] + // console.error('srmSsmList', state.value.srmSsmList) + for (const elem of action.payload.srmSsmList) { + if (elem.ip === action.payload.rsuIpv4) { + localMsgList.push(elem) + if (elem.type === 'srmTx') { + localSrmCount += 1 + } else { + localSsmCount += 1 + } + } + } + state.value.srmCount = localSrmCount + state.value.srmSsmCount = localSsmCount + state.value.srmMsgList = localMsgList }, setSurroundingEvents: (state, action: PayloadAction) => { state.value.surroundingEvents = action.payload diff --git a/webapp/src/components/map/utilities/message-utils.ts b/webapp/src/components/map/utilities/message-utils.ts index 08dd042ec..e17e5cb71 100644 --- a/webapp/src/components/map/utilities/message-utils.ts +++ b/webapp/src/components/map/utilities/message-utils.ts @@ -49,6 +49,7 @@ export const createMarkerForNotification = ( switch (notification.notificationType) { case 'ConnectionOfTravelNotification': // TODO: Re-add once more notification data is available + console.warn('ConnectionOfTravelNotification type does not have a graphical display yet') // const connTravelNotification = notification as ConnectionOfTravelNotification; // const connTravelAssessmentGroups = connTravelNotification.assessment.connectionOfTravelAssessmentGroups; // connTravelAssessmentGroups?.forEach((assessmentGroup) => { @@ -78,7 +79,7 @@ export const createMarkerForNotification = ( // }); break case 'IntersectionReferenceAlignmentNotification': - // No markers for this notification + console.warn('IntersectionReferenceAlignmentNotification type does not have a graphical display yet') break case 'LaneDirectionOfTravelNotification': const laneDirTravelNotification = notification as LaneDirectionOfTravelNotification @@ -107,7 +108,7 @@ export const createMarkerForNotification = ( }) break case 'SignalGroupAlignmentNotification': - // No markers for this notification + console.warn('SignalGroupAlignmentNotification type does not have a graphical display yet') break case 'SignalStateConflictNotification': const sigStateConflictNotification = notification as SignalStateConflictNotification @@ -126,13 +127,13 @@ export const createMarkerForNotification = ( markerCollection.features.push(sigStateConflictMarker) break case 'TimeChangeDetailsNotification': - // No markers for this notification + console.warn('TimeChangeDetailsNotification type does not have a graphical display yet') break case 'KafkaStreamsAnomalyNotification': - // No markers for this notification + console.warn('KafkaStreamsAnomalyNotification type does not have a graphical display yet') break case 'BroadcastRateNotification': - // No markers for this notification + console.warn('BroadcastRateNotification type does not have a graphical display yet') break } return markerCollection diff --git a/webapp/src/models/MessageMonitorNotification.d.ts b/webapp/src/models/MessageMonitorNotification.d.ts deleted file mode 100644 index 66cfe8edc..000000000 --- a/webapp/src/models/MessageMonitorNotification.d.ts +++ /dev/null @@ -1,12 +0,0 @@ -type MessageMonitorNotification = - | IntersectionReferenceAlignmentNotification - | SignalGroupAlignmentNotification - // | SpatMinimumDataNotification // - // | MapMinimumDataNotification // - | SpatBroadcastRateNotification - | MapBroadcastRateNotification - // | StopLineStopNotification // - // | LaneDirectionOfTravelAssessmentNotification - | ConnectionOfTravelNotification // - | SignalStateConflictNotification - | TimeChangeDetailsNotification diff --git a/webapp/src/models/README.md b/webapp/src/models/README.md new file mode 100644 index 000000000..a50315cad --- /dev/null +++ b/webapp/src/models/README.md @@ -0,0 +1,26 @@ +# Models + +This directory contains type definitions for the types used across this webapp. + +## Generated Classes + +The convert_java_to_ts.py script is used for generating typescript type definition files from java classes. This script utilizes the jpo-conflictmonitor submodule in the api directory. It then searches through that directory, utilizes regular expressions to identify data classes, and pulls out the properties. Each Java class gets converted to a .d.ts file in the jpo-conflictmonitor directory. + +This script is usually accurate, but can leave some errors. These are best fixed by manually going through any files that fail to compile and resolving the issue. + +To re-generate the classes from the latest Java file updates, simply remove the jpo-conflictmonitor directory and run the python script: + +```sh +rm -r ./jpo-conflictmonitor +python convert_java_to_ts.py +``` + +## get_required_imports.py + +The get_required_imports.py script is used to generate a required_types.json file, which contains links to all required external dependencies. These should be manually downloaded and run through the convert_java_to_ts.py script, or manually converted to .d.ts files. + +To regenerate the imports JSON file: + +```sh +python get_required_imports.py +``` diff --git a/webapp/src/models/imports/ProcessedMap.d.ts b/webapp/src/models/imports/ProcessedMap.d.ts index 2776549b5..19598bb41 100644 --- a/webapp/src/models/imports/ProcessedMap.d.ts +++ b/webapp/src/models/imports/ProcessedMap.d.ts @@ -24,13 +24,6 @@ type MapSharedProperties = { type MapSource = 'RSU' | 'V2X' | 'MMITSS' | 'unknown' -// enum MapSource { -// RSU, -// V2X, -// MMITSS, -// unknown, -// }; - type MapFeatureCollection = { type: 'FeatureCollection' features: MapFeature[] diff --git a/webapp/src/models/imports/README.md b/webapp/src/models/imports/README.md index 17d5c0d55..7fcfd291e 100644 --- a/webapp/src/models/imports/README.md +++ b/webapp/src/models/imports/README.md @@ -1 +1,3 @@ # Manually Generated External Imports + +These files were either too complicated to be converted by the convert_java_to_ts.py script, or too deep in the submodule tree. These files were created by manually going through the Java classes and creating .d.ts typescript files diff --git a/webapp/src/models/jpo-conflictmonitor/config/ConfigDataClass.d.ts b/webapp/src/models/jpo-conflictmonitor/config/ConfigDataClass.d.ts deleted file mode 100644 index 01857c35b..000000000 --- a/webapp/src/models/jpo-conflictmonitor/config/ConfigDataClass.d.ts +++ /dev/null @@ -1,4 +0,0 @@ - -type ConfigDataClass = { - -} \ No newline at end of file From b36ea6725db2b4355f732717a107fb21f4cceb72 Mon Sep 17 00:00:00 2001 From: jacob6838 Date: Wed, 22 May 2024 17:24:27 -0600 Subject: [PATCH 026/189] Removing unnecessary images, updating documentation --- webapp/README.md | 2 +- .../public/icons/traffic-light-icon-green.svg | 93 ------- .../public/icons/traffic-light-icon-red.svg | 93 ------- ...ffic-light-icon-stop-then-proceed-high.svg | 95 -------- ...affic-light-icon-stop-then-proceed-low.svg | 180 -------------- .../icons/traffic-light-icon-yellow-red.svg | 93 ------- .../icons/traffic-light-icon-yellow.svg | 93 ------- webapp/src/apis/configuration-param-api.ts | 22 -- webapp/src/components/map/map-legend.tsx | 6 - .../icons/map/traffic-light-icon-green-1.svg | 93 ------- .../icons/map/traffic-light-icon-green.svg | 93 ------- ...raffic-light-icon-proceed-with-caution.svg | 180 -------------- ...traffic-light-icon-protected-clearance.svg | 128 ---------- ...-light-icon-protected-movement-allowed.svg | 129 ---------- .../icons/map/traffic-light-icon-red-1.svg | 93 ------- .../map/traffic-light-icon-red-flashing.svg | 152 ------------ .../src/icons/map/traffic-light-icon-red.svg | 93 ------- ...ffic-light-icon-stop-then-proceed-high.svg | 95 -------- ...affic-light-icon-stop-then-proceed-low.svg | 180 -------------- .../icons/map/traffic-light-icon-unknown.svg | 93 ------- .../icons/map/traffic-light-icon-yellow-1.svg | 1 - .../map/traffic-light-icon-yellow-red-1.svg | 93 ------- .../traffic-light-icon-yellow-red-green.svg | 227 ------------------ .../map/traffic-light-icon-yellow-red.svg | 93 ------- .../icons/map/traffic-light-icon-yellow.svg | 93 ------- 25 files changed, 1 insertion(+), 2512 deletions(-) delete mode 100644 webapp/public/icons/traffic-light-icon-green.svg delete mode 100644 webapp/public/icons/traffic-light-icon-red.svg delete mode 100644 webapp/public/icons/traffic-light-icon-stop-then-proceed-high.svg delete mode 100644 webapp/public/icons/traffic-light-icon-stop-then-proceed-low.svg delete mode 100644 webapp/public/icons/traffic-light-icon-yellow-red.svg delete mode 100644 webapp/public/icons/traffic-light-icon-yellow.svg delete mode 100644 webapp/src/icons/map/traffic-light-icon-green-1.svg delete mode 100644 webapp/src/icons/map/traffic-light-icon-green.svg delete mode 100644 webapp/src/icons/map/traffic-light-icon-proceed-with-caution.svg delete mode 100644 webapp/src/icons/map/traffic-light-icon-protected-clearance.svg delete mode 100644 webapp/src/icons/map/traffic-light-icon-protected-movement-allowed.svg delete mode 100644 webapp/src/icons/map/traffic-light-icon-red-1.svg delete mode 100644 webapp/src/icons/map/traffic-light-icon-red-flashing.svg delete mode 100644 webapp/src/icons/map/traffic-light-icon-red.svg delete mode 100644 webapp/src/icons/map/traffic-light-icon-stop-then-proceed-high.svg delete mode 100644 webapp/src/icons/map/traffic-light-icon-stop-then-proceed-low.svg delete mode 100644 webapp/src/icons/map/traffic-light-icon-unknown.svg delete mode 100644 webapp/src/icons/map/traffic-light-icon-yellow-1.svg delete mode 100644 webapp/src/icons/map/traffic-light-icon-yellow-red-1.svg delete mode 100644 webapp/src/icons/map/traffic-light-icon-yellow-red-green.svg delete mode 100644 webapp/src/icons/map/traffic-light-icon-yellow-red.svg delete mode 100644 webapp/src/icons/map/traffic-light-icon-yellow.svg diff --git a/webapp/README.md b/webapp/README.md index 2f8d266a9..23a1d3224 100644 --- a/webapp/README.md +++ b/webapp/README.md @@ -118,7 +118,7 @@ The usdot [jpo-conflictvisualizer](https://github.com/usdot-jpo-ode/jpo-conflict ### Changes Made -These changes were tested running locally in docker. These changes require a jpo-conflictvisualizer api to be running, and to be connected to the cvmanager keycloak server (and cvmanager keycloak realm). This API also requires the jpo-conflictmonitor and jpo-geojsonconverter to be running, so that there is data available. Once +These changes were tested running locally in docker. These changes require a jpo-conflictvisualizer api to be running, and to be connected to the cvmanager keycloak server (and cvmanager keycloak realm). This API also requires the jpo-conflictmonitor and jpo-geojsonconverter to be running, so that there is data available. Once the jpo-conflictmonitor, jpo-geojsonconverter, and jpo-ode, then a jpo-conflictvisualizer api should be deployed, which should be modified to authenticate with the cvmanager keycloak realm (see the conflictvisualizer-map-page branch), and the port should be specified in the environment file (REACT_APP_CVIZ_API_SERVER_URL). Once all of these components are deployed, then the cvmanager webapp can be run! ## Unit Testing diff --git a/webapp/public/icons/traffic-light-icon-green.svg b/webapp/public/icons/traffic-light-icon-green.svg deleted file mode 100644 index 0afebded0..000000000 --- a/webapp/public/icons/traffic-light-icon-green.svg +++ /dev/null @@ -1,93 +0,0 @@ - - - - - - - - - - - - - - - - diff --git a/webapp/public/icons/traffic-light-icon-red.svg b/webapp/public/icons/traffic-light-icon-red.svg deleted file mode 100644 index 5a3baf721..000000000 --- a/webapp/public/icons/traffic-light-icon-red.svg +++ /dev/null @@ -1,93 +0,0 @@ - - - - - - - - - - - - - - - - diff --git a/webapp/public/icons/traffic-light-icon-stop-then-proceed-high.svg b/webapp/public/icons/traffic-light-icon-stop-then-proceed-high.svg deleted file mode 100644 index 3a71b58fc..000000000 --- a/webapp/public/icons/traffic-light-icon-stop-then-proceed-high.svg +++ /dev/null @@ -1,95 +0,0 @@ - - - - - - - - - - - - - - - - diff --git a/webapp/public/icons/traffic-light-icon-stop-then-proceed-low.svg b/webapp/public/icons/traffic-light-icon-stop-then-proceed-low.svg deleted file mode 100644 index 4d3de7857..000000000 --- a/webapp/public/icons/traffic-light-icon-stop-then-proceed-low.svg +++ /dev/null @@ -1,180 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/webapp/public/icons/traffic-light-icon-yellow-red.svg b/webapp/public/icons/traffic-light-icon-yellow-red.svg deleted file mode 100644 index 2295902c7..000000000 --- a/webapp/public/icons/traffic-light-icon-yellow-red.svg +++ /dev/null @@ -1,93 +0,0 @@ - - - - - - - - - - - - - - - - diff --git a/webapp/public/icons/traffic-light-icon-yellow.svg b/webapp/public/icons/traffic-light-icon-yellow.svg deleted file mode 100644 index b3e03adb3..000000000 --- a/webapp/public/icons/traffic-light-icon-yellow.svg +++ /dev/null @@ -1,93 +0,0 @@ - - - - - - - - - - - - - - - - diff --git a/webapp/src/apis/configuration-param-api.ts b/webapp/src/apis/configuration-param-api.ts index e2c0cd241..5bb801bb3 100644 --- a/webapp/src/apis/configuration-param-api.ts +++ b/webapp/src/apis/configuration-param-api.ts @@ -1,27 +1,5 @@ import { authApiHelper } from './api-helper-cviz' -// const CONFIGURATION_PARAMETERS = [ -// /config/intersection/signal_state/red_light_running_minimum_speed -// /config/intersection/signal_state/minimum_red_light_percentage_threshold -// /config/intersection/signal_state/minimum_number_of_events -// /config/intersection/signal_state/maximum_distance_from_stopbar -// /config/intersection/signal_state/look_back_period -// /config/intersection/signal_state/heading_tolerance -// /config/intersection/lane_direction_of_travel/minimum_speed_threshold -// /config/intersection/lane_direction_of_travel/minimum_number_of_points -// /config/intersection/lane_direction_of_travel/minimum_number_of_events -// /config/intersection/lane_direction_of_travel/look_back_period -// /config/intersection/lane_direction_of_travel/heading_tolerance -// /config/intersection/general/v2x_message_processing_frequency -// /config/intersection/general/spat_minimum_10_second_reception -// /config/intersection/general/spat_maximum_10_second_reception -// /config/intersection/general/message_storage_period -// /config/intersection/general/map_minimum_10_second_reception -// /config/intersection/general/map_maximum_10_second_reception -// /config/intersection/connection_of_travel/minimum_number_of_events -// /config/intersection/connection_of_travel/look_back_period -// ] - class ConfigParamsApi { async getGeneralParameters(token: string): Promise { try { diff --git a/webapp/src/components/map/map-legend.tsx b/webapp/src/components/map/map-legend.tsx index 8fc98ddc2..5ef04301b 100644 --- a/webapp/src/components/map/map-legend.tsx +++ b/webapp/src/components/map/map-legend.tsx @@ -17,12 +17,6 @@ import MuiAccordionSummary, { AccordionSummaryProps } from '@mui/material/Accord import MuiAccordionDetails from '@mui/material/AccordionDetails' import ArrowForwardIosSharpIcon from '@mui/icons-material/ArrowForwardIosSharp' import { styled } from '@mui/material/styles' -import trafficLightIconUnknown from '../../icons/map/traffic-light-icon-unknown.svg' -import trafficLightIconRedFlashing from '../../icons/map/traffic-light-icon-red-flashing.svg' -import trafficLightIconRed1 from '../../icons/map/traffic-light-icon-red-1.svg' -import trafficLightIconYellowRed1 from '../../icons/map/traffic-light-icon-yellow-red-1.svg' -import trafficLightIconYellow1 from '../../icons/map/traffic-light-icon-yellow-1.svg' -import trafficLightIconGreen1 from '../../icons/map/traffic-light-icon-green-1.svg' import { selectMapLegendColors } from './map-layer-style-slice' import { useSelector } from 'react-redux' diff --git a/webapp/src/icons/map/traffic-light-icon-green-1.svg b/webapp/src/icons/map/traffic-light-icon-green-1.svg deleted file mode 100644 index dbdf15986..000000000 --- a/webapp/src/icons/map/traffic-light-icon-green-1.svg +++ /dev/null @@ -1,93 +0,0 @@ - - - - - - - - - - - - - - - - diff --git a/webapp/src/icons/map/traffic-light-icon-green.svg b/webapp/src/icons/map/traffic-light-icon-green.svg deleted file mode 100644 index 457fb9968..000000000 --- a/webapp/src/icons/map/traffic-light-icon-green.svg +++ /dev/null @@ -1,93 +0,0 @@ - - - - - - - - - - - - - - - - diff --git a/webapp/src/icons/map/traffic-light-icon-proceed-with-caution.svg b/webapp/src/icons/map/traffic-light-icon-proceed-with-caution.svg deleted file mode 100644 index 6f6866474..000000000 --- a/webapp/src/icons/map/traffic-light-icon-proceed-with-caution.svg +++ /dev/null @@ -1,180 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/webapp/src/icons/map/traffic-light-icon-protected-clearance.svg b/webapp/src/icons/map/traffic-light-icon-protected-clearance.svg deleted file mode 100644 index e167d28ad..000000000 --- a/webapp/src/icons/map/traffic-light-icon-protected-clearance.svg +++ /dev/null @@ -1,128 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - diff --git a/webapp/src/icons/map/traffic-light-icon-protected-movement-allowed.svg b/webapp/src/icons/map/traffic-light-icon-protected-movement-allowed.svg deleted file mode 100644 index 46e943f0d..000000000 --- a/webapp/src/icons/map/traffic-light-icon-protected-movement-allowed.svg +++ /dev/null @@ -1,129 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - diff --git a/webapp/src/icons/map/traffic-light-icon-red-1.svg b/webapp/src/icons/map/traffic-light-icon-red-1.svg deleted file mode 100644 index e132f11e4..000000000 --- a/webapp/src/icons/map/traffic-light-icon-red-1.svg +++ /dev/null @@ -1,93 +0,0 @@ - - - - - - - - - - - - - - - - diff --git a/webapp/src/icons/map/traffic-light-icon-red-flashing.svg b/webapp/src/icons/map/traffic-light-icon-red-flashing.svg deleted file mode 100644 index cedf9c813..000000000 --- a/webapp/src/icons/map/traffic-light-icon-red-flashing.svg +++ /dev/null @@ -1,152 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/webapp/src/icons/map/traffic-light-icon-red.svg b/webapp/src/icons/map/traffic-light-icon-red.svg deleted file mode 100644 index 05f27c199..000000000 --- a/webapp/src/icons/map/traffic-light-icon-red.svg +++ /dev/null @@ -1,93 +0,0 @@ - - - - - - - - - - - - - - - - diff --git a/webapp/src/icons/map/traffic-light-icon-stop-then-proceed-high.svg b/webapp/src/icons/map/traffic-light-icon-stop-then-proceed-high.svg deleted file mode 100644 index 1e591a3e9..000000000 --- a/webapp/src/icons/map/traffic-light-icon-stop-then-proceed-high.svg +++ /dev/null @@ -1,95 +0,0 @@ - - - - - - - - - - - - - - - - diff --git a/webapp/src/icons/map/traffic-light-icon-stop-then-proceed-low.svg b/webapp/src/icons/map/traffic-light-icon-stop-then-proceed-low.svg deleted file mode 100644 index 5f991d6d5..000000000 --- a/webapp/src/icons/map/traffic-light-icon-stop-then-proceed-low.svg +++ /dev/null @@ -1,180 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/webapp/src/icons/map/traffic-light-icon-unknown.svg b/webapp/src/icons/map/traffic-light-icon-unknown.svg deleted file mode 100644 index b185903d1..000000000 --- a/webapp/src/icons/map/traffic-light-icon-unknown.svg +++ /dev/null @@ -1,93 +0,0 @@ - - - - - - - - - - - - - - - - diff --git a/webapp/src/icons/map/traffic-light-icon-yellow-1.svg b/webapp/src/icons/map/traffic-light-icon-yellow-1.svg deleted file mode 100644 index 80d4a1c3d..000000000 --- a/webapp/src/icons/map/traffic-light-icon-yellow-1.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/webapp/src/icons/map/traffic-light-icon-yellow-red-1.svg b/webapp/src/icons/map/traffic-light-icon-yellow-red-1.svg deleted file mode 100644 index 866ca53f0..000000000 --- a/webapp/src/icons/map/traffic-light-icon-yellow-red-1.svg +++ /dev/null @@ -1,93 +0,0 @@ - - - - - - - - - - - - - - - - diff --git a/webapp/src/icons/map/traffic-light-icon-yellow-red-green.svg b/webapp/src/icons/map/traffic-light-icon-yellow-red-green.svg deleted file mode 100644 index 445f7adac..000000000 --- a/webapp/src/icons/map/traffic-light-icon-yellow-red-green.svg +++ /dev/null @@ -1,227 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/webapp/src/icons/map/traffic-light-icon-yellow-red.svg b/webapp/src/icons/map/traffic-light-icon-yellow-red.svg deleted file mode 100644 index 8f05c4bfc..000000000 --- a/webapp/src/icons/map/traffic-light-icon-yellow-red.svg +++ /dev/null @@ -1,93 +0,0 @@ - - - - - - - - - - - - - - - - diff --git a/webapp/src/icons/map/traffic-light-icon-yellow.svg b/webapp/src/icons/map/traffic-light-icon-yellow.svg deleted file mode 100644 index d80f3ee41..000000000 --- a/webapp/src/icons/map/traffic-light-icon-yellow.svg +++ /dev/null @@ -1,93 +0,0 @@ - - - - - - - - - - - - - - - - From 63366da5f0e3167ce5a8d08657bd02f8b6eef6c9 Mon Sep 17 00:00:00 2001 From: jacob6838 Date: Wed, 22 May 2024 18:54:46 -0600 Subject: [PATCH 027/189] Removing updateGeoMsg comments and prints --- webapp/src/generalSlices/rsuSlice.ts | 8 -------- 1 file changed, 8 deletions(-) diff --git a/webapp/src/generalSlices/rsuSlice.ts b/webapp/src/generalSlices/rsuSlice.ts index a1545bed5..7064a73fa 100644 --- a/webapp/src/generalSlices/rsuSlice.ts +++ b/webapp/src/generalSlices/rsuSlice.ts @@ -337,7 +337,6 @@ export const rsuSlice = createSlice({ state.value.addGeoMsgPoint = !state.value.addGeoMsgPoint }, updateGeoMsgPoints: (state, action: PayloadAction) => { - console.debug('updateGeoMsgPoints') state.value.geoMsgCoordinates = action.payload }, updateGeoMsgDate: (state, action: PayloadAction<{ type: 'start' | 'end'; date: string }>) => { @@ -351,7 +350,6 @@ export const rsuSlice = createSlice({ state.value.countsMsgType = action.payload }, changeGeoMsgType: (state, action: PayloadAction) => { - console.debug('changeGeoMsgType', action.payload) state.value.geoMsgType = action.payload }, setGeoMsgFilter: (state, action: PayloadAction) => { @@ -478,15 +476,10 @@ export const rsuSlice = createSlice({ state.value.messageLoading = false }) .addCase(updateGeoMsgData.pending, (state) => { - console.debug('updateGeoMsgData pending') state.loading = true state.value.addGeoMsgPoint = false - // Removed 1 day limitation for new mongo deployment - // state.value.geoMsgDateError = - // new Date(state.value.geoMsgEnd).getTime() - new Date(state.value.geoMsgStart).getTime() > 86400000 }) .addCase(updateGeoMsgData.fulfilled, (state, action) => { - console.debug('updateGeoMsgData fulfilled') state.value.geoMsgData = action.payload.body state.loading = false state.value.geoMsgFilter = true @@ -494,7 +487,6 @@ export const rsuSlice = createSlice({ state.value.geoMsgFilterOffset = 0 }) .addCase(updateGeoMsgData.rejected, (state) => { - console.debug('updateGeoMsgData rejected') state.loading = false }) .addCase(getMapData.pending, (state) => { From 0d2a17fbcb8bdaf094e32c743db184b360b23f77 Mon Sep 17 00:00:00 2001 From: jacob6838 Date: Wed, 22 May 2024 19:24:29 -0600 Subject: [PATCH 028/189] Removing rsu_ip from intersection selection menu --- webapp/src/components/map/control-panel.tsx | 96 +---------- webapp/src/components/map/map-component.tsx | 8 - webapp/src/components/map/map-slice.tsx | 167 ++++++-------------- 3 files changed, 58 insertions(+), 213 deletions(-) diff --git a/webapp/src/components/map/control-panel.tsx b/webapp/src/components/map/control-panel.tsx index ce959cff1..ba2b71682 100644 --- a/webapp/src/components/map/control-panel.tsx +++ b/webapp/src/components/map/control-panel.tsx @@ -28,8 +28,6 @@ import { AnyAction, ThunkDispatch } from '@reduxjs/toolkit' import { RootState } from '../../store' import { useDispatch, useSelector } from 'react-redux' import { - MAP_PROPS, - MAP_PROPS_SOURCE_API, downloadMapData, handleImportedMapMessageData, onTimeQueryChanged, @@ -37,13 +35,11 @@ import { selectBsmTrailLength, selectPlaybackModeActive, selectSliderTimeValue, - selectSourceApi, setBsmTrailLength, setLaneLabelsVisible, setShowPopupOnHover, setSigGroupLabelsVisible, setSliderValue, - setSourceApi, toggleLiveDataActive, togglePlaybackModeActive, } from './map-slice' @@ -64,7 +60,7 @@ import { setSelectedIntersection, selectSelectedIntersectionId, } from '../../generalSlices/intersectionSlice' -import { selectRsu, selectRsuData, selectSelectedRsu } from '../../generalSlices/rsuSlice' +import { selectRsuData, selectSelectedRsu } from '../../generalSlices/rsuSlice' import pauseIcon from '../../icons/pause.png' import playIcon from '../../icons/play.png' import { BarChart, XAxis, Bar, ResponsiveContainer, Tooltip } from 'recharts' @@ -91,42 +87,6 @@ const AccordionSummary = styled((props: AccordionSummaryProps) => ( const AccordionDetails = styled(MuiAccordionDetails)(({ theme }) => ({})) -interface ControlPanelProps { - sx: SxProps | undefined - timeQueryParams: { - startDate: Date - endDate: Date - eventDate: Date - timeWindowSeconds: number - } - onTimeQueryChanged: (eventTime?: Date, timeBefore?: number, timeAfter?: number, timeWindowSeconds?: number) => void - handleImportedMessageData: (messageData: any) => void - sliderValue: number - setSlider: (event: Event, value: number | number[], activeThumb: number) => void - max: number - sliderTimeValue: { - start: Date - end: Date - } - mapSpatTimes: { - mapTime: number - spatTime: number - } - downloadAllData: () => void - signalStateLayer: any - setSignalStateLayer: React.Dispatch> - laneLabelsVisible: boolean - setLaneLabelsVisible: React.Dispatch> - sigGroupLabelsVisible: boolean - setSigGroupLabelsVisible: React.Dispatch> - showPopupOnHover: boolean - setShowPopupOnHover: React.Dispatch> - liveDataActive: boolean - setLiveDataActive: React.Dispatch> - bsmTrailLength: number - setBsmTrailLength: React.Dispatch> -} - function ControlPanel() { const dispatch: ThunkDispatch = useDispatch() @@ -142,11 +102,8 @@ function ControlPanel() { const liveDataActive = useSelector(selectLiveDataActive) const sliderTimeValue = useSelector(selectSliderTimeValue) const bsmTrailLength = useSelector(selectBsmTrailLength) - const dataSourceApi = useSelector(selectSourceApi) const selectedIntersectionId = useSelector(selectSelectedIntersectionId) const intersectionsList = useSelector(selectIntersections) - const rsuData = useSelector(selectRsuData) - const selectedRsu = useSelector(selectSelectedRsu) const bsmEventsByMinute = useSelector(selectBsmEventsByMinute) const playbackModeActive = useSelector(selectPlaybackModeActive) @@ -450,26 +407,16 @@ function ControlPanel() { > - Data Sources + + Time Query + {liveDataActive && } + - - Data Source - - - {dataSourceApi === 'conflictvisualizer' && ( + - Intersection + Intersection ID - )} - {dataSourceApi === 'cvmanager' && ( - - RSU IP - - - )} - - - - - - - Time Query - {liveDataActive && } - - - - - { const { currentMapData, currentSpatData, currentBsmData } = args const currentState = getState() as RootState - const sourceApi = selectSourceApi(currentState) - if (sourceApi == 'conflictvisualizer') { - const queryParams = selectQueryParams(currentState) - const sourceData = selectSourceData(currentState) - const sourceDataType = selectSourceDataType(currentState) + const queryParams = selectQueryParams(currentState) + const sourceData = selectSourceData(currentState) + const sourceDataType = selectSourceDataType(currentState) - // ######################### MAP Data ######################### - const latestMapMessage: ProcessedMap = currentMapData.at(-1) - if (latestMapMessage != null) { - setViewState({ - latitude: latestMapMessage?.properties.refPoint.latitude, - longitude: latestMapMessage?.properties.refPoint.longitude, - zoom: 19, - }) - } + // ######################### MAP Data ######################### + const latestMapMessage: ProcessedMap = currentMapData.at(-1) + if (latestMapMessage != null) { + setViewState({ + latitude: latestMapMessage?.properties.refPoint.latitude, + longitude: latestMapMessage?.properties.refPoint.longitude, + zoom: 19, + }) + } - // ######################### SPAT Signal Groups ######################### - const mapSignalGroupsLocal = parseMapSignalGroups(latestMapMessage) - - const spatSignalGroupsLocal = parseSpatSignalGroups(currentSpatData) - - const uniqueIds = new Set(currentBsmData.features.map((bsm) => bsm.properties?.id)) - // generate equally spaced unique colors for each uniqueId - const colors = generateColorDictionary(uniqueIds) - dispatch(setBsmLegendColors(colors)) - // add color to each feature - const bsmLayerStyle = generateMapboxStyleExpression(colors) - dispatch(setBsmCircleColor(bsmLayerStyle)) - - // ######################### Message Data ######################### - const rawData = {} - rawData['map'] = currentMapData - rawData['spat'] = currentSpatData - rawData['bsm'] = currentBsmData - if (sourceDataType == 'notification') { - rawData['notification'] = sourceData as MessageMonitor.Notification - } else if (sourceDataType == 'event') { - rawData['event'] = sourceData as MessageMonitor.Event - } else if (sourceDataType == 'assessment') { - rawData['assessment'] = sourceData as Assessment - } - return { - connectingLanes: latestMapMessage.connectingLanesFeatureCollection, - mapData: latestMapMessage, - mapTime: latestMapMessage.properties.odeReceivedAt as unknown as number, - mapSignalGroups: mapSignalGroupsLocal, - spatSignalGroups: spatSignalGroupsLocal, - bsmData: currentBsmData, - rawData: rawData, - sliderValue: Math.min( - getTimeRange(queryParams.startDate, queryParams.eventDate ?? new Date()), - getTimeRange(queryParams.startDate, queryParams.endDate) - ), - } - } else if (sourceApi == 'cvmanager') { - const queryParams = selectQueryParams(currentState) - const sourceData = selectSourceData(currentState) - const sourceDataType = selectSourceDataType(currentState) - - // ######################### MAP Data ######################### - const latestMapMessage: ProcessedMap = currentMapData.at(-1)! - - // ######################### SPAT Signal Groups ######################### - const mapSignalGroupsLocal = parseMapSignalGroups(latestMapMessage) - - const spatSignalGroupsLocal = parseSpatSignalGroups(currentSpatData) - - const uniqueIds = new Set(currentBsmData.features.map((bsm) => bsm.properties?.id)) - // generate equally spaced unique colors for each uniqueId - const colors = generateColorDictionary(uniqueIds) - dispatch(setBsmLegendColors(colors)) - // add color to each feature - const bsmLayerStyle = generateMapboxStyleExpression(colors) - dispatch(setBsmCircleColor(bsmLayerStyle)) - - // ######################### Message Data ######################### - const rawData = {} - rawData['map'] = currentMapData - rawData['spat'] = currentSpatData - rawData['bsm'] = currentBsmData - if (sourceDataType == 'rsu_ip') { - const mapList = selectMapList(currentState) - const rsu = sourceData as RsuInfo['rsuList'][0] - if (rsu != null && mapList.includes(rsu.properties.ipv4_address)) { - dispatch(getMapData()) - dispatch(selectRsu(rsu)) - } - } - return { - connectingLanes: latestMapMessage.connectingLanesFeatureCollection, - mapData: latestMapMessage, - mapTime: latestMapMessage.properties.odeReceivedAt as unknown as number, - mapSignalGroups: mapSignalGroupsLocal, - spatSignalGroups: spatSignalGroupsLocal, - bsmData: currentBsmData, - rawData: rawData, - sliderValue: Math.min( - getTimeRange(queryParams.startDate, queryParams.eventDate ?? new Date()), - getTimeRange(queryParams.startDate, queryParams.endDate) - ), - } + // ######################### SPAT Signal Groups ######################### + const mapSignalGroupsLocal = parseMapSignalGroups(latestMapMessage) + + const spatSignalGroupsLocal = parseSpatSignalGroups(currentSpatData) + + const uniqueIds = new Set(currentBsmData.features.map((bsm) => bsm.properties?.id)) + // generate equally spaced unique colors for each uniqueId + const colors = generateColorDictionary(uniqueIds) + dispatch(setBsmLegendColors(colors)) + // add color to each feature + const bsmLayerStyle = generateMapboxStyleExpression(colors) + dispatch(setBsmCircleColor(bsmLayerStyle)) + + // ######################### Message Data ######################### + const rawData = {} + rawData['map'] = currentMapData + rawData['spat'] = currentSpatData + rawData['bsm'] = currentBsmData + if (sourceDataType == 'notification') { + rawData['notification'] = sourceData as MessageMonitor.Notification + } else if (sourceDataType == 'event') { + rawData['event'] = sourceData as MessageMonitor.Event + } else if (sourceDataType == 'assessment') { + rawData['assessment'] = sourceData as Assessment + } + return { + connectingLanes: latestMapMessage.connectingLanesFeatureCollection, + mapData: latestMapMessage, + mapTime: latestMapMessage.properties.odeReceivedAt as unknown as number, + mapSignalGroups: mapSignalGroupsLocal, + spatSignalGroups: spatSignalGroupsLocal, + bsmData: currentBsmData, + rawData: rawData, + sliderValue: Math.min( + getTimeRange(queryParams.startDate, queryParams.eventDate ?? new Date()), + getTimeRange(queryParams.startDate, queryParams.endDate) + ), } }, { @@ -1280,9 +1220,6 @@ export const intersectionMapSlice = createSlice({ state.value.roadRegulatorId = action.payload.roadRegulatorId state.value.loadOnNull = action.payload.loadOnNull }, - setSourceApi: (state, action: PayloadAction) => { - state.value.sourceApi = action.payload - }, setCurrentSpatData: (state, action: PayloadAction) => { state.value.currentSpatData = action.payload }, @@ -1482,7 +1419,6 @@ export const selectLoading = (state: RootState) => state.intersectionMap.loading export const selectLayersVisible = (state: RootState) => state.intersectionMap.value.layersVisible export const selectAllInteractiveLayerIds = (state: RootState) => state.intersectionMap.value.allInteractiveLayerIds export const selectQueryParams = (state: RootState) => state.intersectionMap.value.queryParams -export const selectSourceApi = (state: RootState) => state.intersectionMap.value.sourceApi export const selectSourceData = (state: RootState) => state.intersectionMap.value.sourceData export const selectSourceDataType = (state: RootState) => state.intersectionMap.value.sourceDataType export const selectIntersectionId = (state: RootState) => state.intersectionMap.value.intersectionId @@ -1553,7 +1489,6 @@ export const { setBsmTrailLength, setRawData, setMapProps, - setSourceApi, togglePlaybackModeActive, resetMapView, setLiveDataRestartTimeoutId, From 89bae4d16f464dcb629b43bad8af99691d10da3f Mon Sep 17 00:00:00 2001 From: jacob6838 Date: Wed, 22 May 2024 21:25:18 -0600 Subject: [PATCH 029/189] Adding SSM/SRM menu to intersection map --- .../{pages => components}/css/RsuMapView.css | 92 ++++++------------- webapp/src/components/map/control-panel.tsx | 2 +- webapp/src/components/map/map-component.tsx | 33 ++++++- webapp/src/components/map/map-slice.tsx | 34 ++++++- webapp/src/components/map/side-panel.tsx | 52 ++++++++++- webapp/src/pages/IntersectionMapView.tsx | 1 - webapp/src/pages/Map.tsx | 24 ----- 7 files changed, 144 insertions(+), 94 deletions(-) rename webapp/src/{pages => components}/css/RsuMapView.css (69%) diff --git a/webapp/src/pages/css/RsuMapView.css b/webapp/src/components/css/RsuMapView.css similarity index 69% rename from webapp/src/pages/css/RsuMapView.css rename to webapp/src/components/css/RsuMapView.css index 17d2166b8..0cd98077e 100644 --- a/webapp/src/pages/css/RsuMapView.css +++ b/webapp/src/components/css/RsuMapView.css @@ -9,46 +9,8 @@ padding-left: 30px; padding-right: 30px; font-size: 1em; - background: white; + /* background: white; */ border-radius: 10px; - color: black; -} - -.backButton { - height: fit-content; - width: fit-content; - position: absolute; - left: 50px; - top: 160px; - padding-top: 10px; - padding-bottom: 10px; - padding-left: 30px; - padding-right: 30px; - font-size: 1em; - background: #d16d15; - border-radius: 20px; - border-color: white; - cursor: pointer; - font-weight: bold; - color: white; -} - -.srmSsmToggle { - height: fit-content; - width: fit-content; - position: absolute; - left: 50px; - top: 220px; - padding-top: 10px; - padding-bottom: 10px; - padding-left: 30px; - padding-right: 30px; - font-size: 1em; - background: #d16d15; - border-radius: 20px; - border-color: white; - cursor: pointer; - font-weight: bold; color: white; } @@ -69,19 +31,17 @@ } .ssmSrmContainer { - height: calc(100vh - 200px); - width: 500px; - position: absolute; + width: '100%'; top: 220px; left: 0px; - background: white; - overflow-y: scroll; + /* background: white; */ + overflow-y: auto; text-align: center; } #ssmsrmDataHeader { font-family: Arial, Helvetica, sans-serif; - color: #0e2052; + color: #fff; font-weight: bolder; margin: 15px; margin-top: 25px; @@ -92,8 +52,8 @@ justify-content: space-evenly; align-items: center; text-align: center; - padding: 10px; - border-bottom: 1px #0e2052 solid; + padding: 4px; + border-bottom: 1px #fff solid; } #countsContainer { @@ -108,23 +68,31 @@ #countsHeader { font-family: Arial, Helvetica, sans-serif; - color: #0e2052; + color: #fff; font-weight: bolder; margin: 10px; } #countsData { font-family: Arial, Helvetica, sans-serif; - color: #0e2052; + color: #fff; font-weight: bold; width: 90px; padding: 5px; margin-bottom: 20px; } +#ssmTimeHeader { + font-family: Arial, Helvetica, sans-serif; + color: #fff; + font-weight: bold; + width: 100px; + margin-left: 20px; +} + #requestHeader { font-family: Arial, Helvetica, sans-serif; - color: #0e2052; + color: #fff; font-weight: bold; width: 100px; margin-left: 20px; @@ -132,7 +100,7 @@ #roleHeader { font-family: Arial, Helvetica, sans-serif; - color: #0e2052; + color: #fff; font-weight: bold; width: 90px; margin-left: 40px; @@ -140,7 +108,7 @@ #statusHeader { font-family: Arial, Helvetica, sans-serif; - color: #0e2052; + color: #fff; font-weight: bold; width: 90px; padding-left: 100px; @@ -148,7 +116,7 @@ #ssmSrmHeader { font-family: Arial, Helvetica, sans-serif; - color: #0e2052; + color: #fff; font-weight: bold; width: 100px; } @@ -157,7 +125,7 @@ height: fit-content; width: 180px; position: absolute; - background-color: white; + /* background-color: white; */ border-radius: 10px; right: 50px; top: 225px; @@ -178,7 +146,7 @@ width: 20px; background-color: rgb(50, 205, 50); margin: 8px 5px; - border: 0.5px solid black; + border: 0.5px solid white; border-radius: 10px; } @@ -199,36 +167,36 @@ height: 20px; width: 20px; margin: 8px 5px; - border: 0.5px solid black; + border: 0.5px solid white; border-radius: 10px; } #ssmKey { display: inline-block; - background-color: #fff1d3; + background-color: #6b5b3c; height: 20px; width: 20px; margin: 8px 5px; - border: 0.5px solid black; + border: 0.5px solid white; border-radius: 10px; } #srmKey { display: inline-block; - background-color: #bdceff; + background-color: #3d465f; height: 20px; width: 20px; margin: 8px 5px; - border: 0.5px solid black; + border: 0.5px solid white; border-radius: 10px; } #srmKeyMap { display: inline-block; - background-color: #0e2052; + background-color: #0e2972; height: 20px; width: 20px; margin: 8px 5px; - border: 0.5px solid black; + border: 0.5px solid white; border-radius: 10px; } diff --git a/webapp/src/components/map/control-panel.tsx b/webapp/src/components/map/control-panel.tsx index ba2b71682..02ffefc5a 100644 --- a/webapp/src/components/map/control-panel.tsx +++ b/webapp/src/components/map/control-panel.tsx @@ -31,6 +31,7 @@ import { downloadMapData, handleImportedMapMessageData, onTimeQueryChanged, + renderEntireMap, selectBsmEventsByMinute, selectBsmTrailLength, selectPlaybackModeActive, @@ -60,7 +61,6 @@ import { setSelectedIntersection, selectSelectedIntersectionId, } from '../../generalSlices/intersectionSlice' -import { selectRsuData, selectSelectedRsu } from '../../generalSlices/rsuSlice' import pauseIcon from '../../icons/pause.png' import playIcon from '../../icons/play.png' import { BarChart, XAxis, Bar, ResponsiveContainer, Tooltip } from 'recharts' diff --git a/webapp/src/components/map/map-component.tsx b/webapp/src/components/map/map-component.tsx index c68e8925f..55f379069 100644 --- a/webapp/src/components/map/map-component.tsx +++ b/webapp/src/components/map/map-component.tsx @@ -18,6 +18,7 @@ import { selectMapMessageLayerStyle, selectMarkerLayerStyle, selectSignalStateLayerStyle, + selectSrmLayerStyle, } from './map-layer-style-slice' import { MAP_PROPS, @@ -31,6 +32,7 @@ import { onMapMouseLeave, onMapMouseMove, pullInitialData, + renderRsuData, selectAllInteractiveLayerIds, selectBsmData, selectConnectingLanes, @@ -71,7 +73,7 @@ import { addConnections, createMarkerForNotification } from './utilities/message import { AnyAction, ThunkDispatch } from '@reduxjs/toolkit' import { RootState } from '../../store' import { MapLegend } from './map-legend' -import { RsuInfo } from '../../apis/rsu-api-types' +import { selectSelectedSrm } from '../../generalSlices/rsuSlice' const generateQueryParams = (source: MAP_PROPS['sourceData'], sourceDataType: MAP_PROPS['sourceDataType']) => { const startOffset = 1000 * 60 * 1 @@ -135,9 +137,12 @@ const MapTab = (props: MAP_PROPS) => { const connectingLanesLayerStyle = useSelector(selectConnectingLanesLayerStyle) const connectingLanesLabelsLayerStyle = useSelector(selectConnectingLanesLabelsLayerStyle) const markerLayerStyle = useSelector(selectMarkerLayerStyle) + const srmLayerStyle = useSelector(selectSrmLayerStyle) const bsmLayerStyle = useSelector(selectBsmLayerStyle) const signalStateLayerStyle = useSelector(selectSignalStateLayerStyle) + const selectedSrm = useSelector(selectSelectedSrm) + const allInteractiveLayerIds = useSelector(selectAllInteractiveLayerIds) const queryParams = useSelector(selectQueryParams) const mapData = useSelector(selectMapData) @@ -375,6 +380,32 @@ const MapTab = (props: MAP_PROPS) => { > + { + return { + type: 'Feature', + geometry: { + type: 'Point', + coordinates: [srm.long, srm.lat], + }, + properties: { + requestId: srm.requestId, + requestedId: srm.requestedId, + status: srm.status, + time: srm.time, + role: srm.role, + }, + } + }), + } as GeoJSON.FeatureCollection + } + > + + { + const currentState = getState() as RootState + const rsuMapData = selectRsuMapData(currentState) + console.log('rsuMapData', rsuMapData) + + dispatch(resetMapView()) + + dispatch( + renderEntireMap({ + currentMapData: [rsuMapData as unknown as ProcessedMap], + currentSpatData: [], + currentBsmData: { type: 'FeatureCollection', features: [] }, + }) + ) + + return + }, + { + condition: (_, { getState }) => + selectToken(getState() as RootState) != undefined && + selectQueryParams(getState() as RootState).intersectionId != undefined && + selectQueryParams(getState() as RootState).roadRegulatorId != undefined, + } +) + export const intersectionMapSlice = createSlice({ name: 'intersectionMap', initialState: { @@ -1463,6 +1490,9 @@ export const selectSliderTimeValue = (state: RootState) => state.intersectionMap export const selectBsmTrailLength = (state: RootState) => state.intersectionMap.value.bsmTrailLength export const selectLiveDataRestartTimeoutId = (state: RootState) => state.intersectionMap.value.liveDataRestartTimeoutId export const selectLiveDataRestart = (state: RootState) => state.intersectionMap.value.liveDataRestart +export const selectSrmCount = (state: RootState) => state.intersectionMap.value.srmCount +export const selectSrmSsmCount = (state: RootState) => state.intersectionMap.value.srmSsmCount +export const selectSrmMsgList = (state: RootState) => state.intersectionMap.value.srmMsgList export const { setSurroundingEvents, diff --git a/webapp/src/components/map/side-panel.tsx b/webapp/src/components/map/side-panel.tsx index 120366aed..b61da05a2 100644 --- a/webapp/src/components/map/side-panel.tsx +++ b/webapp/src/components/map/side-panel.tsx @@ -11,8 +11,15 @@ import { styled } from '@mui/material/styles' import { CustomTable } from './custom-table' import { format } from 'date-fns' import { ExpandableTable } from './expandable-table' -import { MAP_PROPS } from './map-slice' +import { MAP_PROPS, selectSrmCount, selectSrmMsgList, selectSrmSsmCount } from './map-slice' import { RsuInfo } from '../../apis/rsu-api-types' +import SsmSrmItem from '../SsmSrmItem' +import { setSelectedSrm, toggleSsmSrmDisplay } from '../../generalSlices/rsuSlice' +import { AnyAction, ThunkDispatch } from '@reduxjs/toolkit' +import { useDispatch, useSelector } from 'react-redux' +import { RootState } from '../../store' +import { selectSelectedIntersection } from '../../generalSlices/intersectionSlice' +import '../css/RsuMapView.css' const Accordion = styled((props: AccordionProps) => )( ({ theme }) => ({ @@ -57,6 +64,13 @@ interface SidePanelProps { export const SidePanel = (props: SidePanelProps) => { const { laneInfo, signalGroups, bsms, events, notifications, sourceData, sourceDataType } = props + const dispatch: ThunkDispatch = useDispatch() + + const srmCount = useSelector(selectSrmCount) + const srmSsmCount = useSelector(selectSrmSsmCount) + const srmMsgList = useSelector(selectSrmMsgList) + const selectedIntersection = useSelector(selectSelectedIntersection) + const [open, setOpen] = useState(false) const getDataTable = (sourceData: MAP_PROPS['sourceData'], sourceDataType: MAP_PROPS['sourceDataType']) => { @@ -69,8 +83,6 @@ export const SidePanel = (props: SidePanelProps) => { return No Data //getNotificationTableFromAssessment(sourceData as Assessment); case 'timestamp': return {format((sourceData as { timestamp: number }).timestamp, 'MM/dd/yyyy HH:mm:ss')} //getNotificationTableFromAssessment(sourceData as Assessment); - case 'rsu_ip': - return getRsuInfoTable(sourceData as RsuInfo['rsuList'][0]) default: return No Data } @@ -140,6 +152,30 @@ export const SidePanel = (props: SidePanelProps) => { ) } + const getSsmSrmTable = (msgList, rsuIpv4: string | undefined, ssmCount: number, srmCount: number) => { + if (rsuIpv4 == undefined) return
No RSU IP Found
+ return ( +
+

SSM / SRM Data For {rsuIpv4}

+
+

Time

+

Request Id

+

Role

+

Status

+

Display

+
+ {msgList.map((index) => ( + dispatch(setSelectedSrm())} /> + ))} +

Total Counts

+
+

SSM: {ssmCount}

+

SRM: {srmCount}

+
+
+ ) + } + return (
{ + + + Ssm Srm Data + + + + {getSsmSrmTable(srmMsgList, selectedIntersection?.rsuIP, srmSsmCount, srmCount)} + + + {sourceDataType && ( diff --git a/webapp/src/pages/IntersectionMapView.tsx b/webapp/src/pages/IntersectionMapView.tsx index 0e606f2d2..8188b6099 100644 --- a/webapp/src/pages/IntersectionMapView.tsx +++ b/webapp/src/pages/IntersectionMapView.tsx @@ -34,7 +34,6 @@ function IntersectionMapView() { sourceDataType={undefined} intersectionId={intersectionId} roadRegulatorId={roadRegulatorId} - sourceApi={'conflictvisualizer'} loadOnNull={true} /> diff --git a/webapp/src/pages/Map.tsx b/webapp/src/pages/Map.tsx index eb4bbe2f0..31f8d7cdc 100644 --- a/webapp/src/pages/Map.tsx +++ b/webapp/src/pages/Map.tsx @@ -14,7 +14,6 @@ import Select from 'react-select' import { DropdownList } from 'react-widgets' import { selectRsuOnlineStatus, - selectMapList, selectRsuData, selectRsuCounts, selectIssScmsStatusData, @@ -37,7 +36,6 @@ import { selectRsu, toggleMapDisplay, getIssScmsStatus, - getMapData, getRsuLastOnline, toggleGeoMsgPointSelect, clearGeoMsg, @@ -106,7 +104,6 @@ function MapPage(props: MapPageProps) { const rsuData = useSelector(selectRsuData) const rsuCounts = useSelector(selectRsuCounts) const selectedRsu = useSelector(selectSelectedRsu) - const mapList = useSelector(selectMapList) const countsMsgType = useSelector(selectMsgType) const issScmsStatusData = useSelector(selectIssScmsStatusData) const rsuOnlineStatus = useSelector(selectRsuOnlineStatus) @@ -469,14 +466,6 @@ function MapPage(props: MapPageProps) { setWzdxMarkers(getAllMarkers(wzdxData)) }, [dispatch, wzdxData]) - const setMapDisplayRsu = async () => { - let display = !displayMap - if (display === true) { - dispatch(getMapData()) - } - dispatch(toggleMapDisplay()) - } - function break_line(val: string) { var arr = [] var remainingData = '' @@ -740,19 +729,6 @@ function MapPage(props: MapPageProps) { )}
)} - {activeLayers.includes('rsu-layer') && selectedRsu !== null && mapList.includes(rsuIpv4) ? ( - - ) : null} {activeLayers.includes('rsu-layer') ? (

Filter RSUs

From 8348e681d5d9e6053db033abeffab97ac347ba9f Mon Sep 17 00:00:00 2001 From: jacob6838 Date: Sun, 26 May 2024 11:36:18 -0600 Subject: [PATCH 030/189] Adding conflictmonitor release-compose --- .vscode/settings.json | 30 +- release-compose.example.env | 410 +++++++++++++++++++ release-compose.yml | 302 ++++++++++++++ services/conflictmonitor-release-compose.yml | 158 +++++++ 4 files changed, 899 insertions(+), 1 deletion(-) create mode 100644 release-compose.example.env create mode 100644 release-compose.yml create mode 100644 services/conflictmonitor-release-compose.yml diff --git a/.vscode/settings.json b/.vscode/settings.json index 49d64d468..710be3c5f 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -23,5 +23,33 @@ "editor.defaultFormatter": "ms-python.black-formatter", "editor.formatOnSave": true }, - "cSpell.words": ["conflictvisualizer", "cviz", "millis"] + "cSpell.words": [ + "BOOTSTRAPSERVERS", + "cdot", + "cimms", + "conflictmonitor", + "conflictvisualizer", + "cviz", + "cvmanager", + "CVPEP", + "geojsonconverter", + "healthcheck", + "inet", + "INITDB", + "keyfile", + "LOGLEVEL", + "MESSAGETYPE", + "millis", + "mongosh", + "pgdb", + "postgis", + "pytz", + "SASL", + "SNMP", + "svcs", + "TABLENAME", + "usdotjpoode", + "wydot", + "Zabbix" + ] } diff --git a/release-compose.example.env b/release-compose.example.env new file mode 100644 index 000000000..1e10f0b04 --- /dev/null +++ b/release-compose.example.env @@ -0,0 +1,410 @@ +# Environment variable file for the JPO CV Manager API deployment through docker-compose +DOCKER_HOST_IP= +# Note if debugging, this webapp host IP should be set to the IP of the host machine running the webapp (localhost) +WEBAPP_HOST_IP=${DOCKER_HOST_IP} +# Note if using WEBAPP_DOMAIN for the docker-compose-webapp-deployment.yml file you will need to include http:// or https:// +WEBAPP_DOMAIN=cvmanager.local.com +WEBAPP_CM_DOMAIN=cimms.local.com +KC_HOST_IP=${DOCKER_HOST_IP} + +# Firmware Manager connectivity in the format 'http://endpoint:port' +FIRMWARE_MANAGER_ENDPOINT=http://${DOCKER_HOST_IP}:8089 + +# Allowed CORS domain for accessing the CV Manager API from (set to the web application hostname) +# Make sure to include http:// or https:// +# If using docker then this value should be set to: http://${WEBAPP_HOST_IP}:3000 +# If running the webapp using npm then set it to: http://localhost:3000 +# Leave as * to allow all domains access +CORS_DOMAIN = * + +# PostgreSQL Database connection information +# this value may need to follow with the webapp host if debugging the applications +PG_DB_HOST=${DOCKER_HOST_IP}:5432 +PG_DB_NAME=postgres +PG_DB_USER=postgres +# If the PG_DB_PASS variable has special characters, make sure to wrap it in single quotes +PG_DB_PASS= + +# If connecting to PGDB over websocket: +INSTANCE_CONNECTION_NAME= + +# Keycloak authentication credentials +KEYCLOAK_ADMIN=admin +KEYCLOAK_ADMIN_PASSWORD= + +# Keycloak Parameters - to generate secret key use a password generator such as: https://www.avast.com/en-us/random-password-generator#pc and set the length to 32 +KEYCLOAK_REALM=cvmanager +KEYCLOAK_API_CLIENT_ID=cvmanager-api +KEYCLOAK_API_CLIENT_SECRET_KEY= +KEYCLOAK_CM_API_CLIENT_SECRET_KEY= +KEYCLOAK_LOGIN_THEME_NAME=sample_theme +# Note if using KEYCLOAK_DOMAIN for the docker-compose-webapp-deployment.yml file you will need to include http:// or https:// +KEYCLOAK_DOMAIN=cvmanager.auth.com + + +# GCP OAuth2.0 client ID for SSO authentication in keycloak - if not specified the google SSO will not be functional +GOOGLE_CLIENT_ID= +GOOGLE_CLIENT_SECRET= + +# If "BIGQUERY", set the location of the GCP service account key attached as a volume +GOOGLE_APPLICATION_CREDENTIALS='./resources/google/sample_gcp_service_account.json' + +# If "MONGODB", set the MongoDB variables +MONGO_DB_URI= +MONGO_DB_NAME="ode" + +# Set these variables if using either "MONGODB" or "BIGQUERY" +# COUNTS_MSG_TYPES: Comma separated list of message types. +# COUNTS_MSG_TYPES must be set for the counts menu to correctly populate when building an image for deployment +COUNTS_MSG_TYPES='BSM,SSM,SPAT,SRM,MAP' +VIEWER_MSG_TYPES='BSM' +GEO_DB_NAME='V2XGeoJson' +SSM_DB_NAME= +SRM_DB_NAME= + +# Specifies the maximum number of V2x messages returned from the geo_query_geo_data_mongo method before filtering occurs +MAX_GEO_QUERY_RECORDS= + +# WZDx API key and endpoint for pulling WZDx data into the CV Manager +WZDX_API_KEY= +WZDX_ENDPOINT= + +# Contact Support Menu Email Configuration +CSM_EMAIL_TO_SEND_FROM= +CSM_EMAIL_APP_USERNAME= +CSM_EMAIL_APP_PASSWORD= +CSM_EMAILS_TO_SEND_TO= +CSM_TARGET_SMTP_SERVER_ADDRESS= +CSM_TARGET_SMTP_SERVER_PORT= + +# Python timezone for the CV Manager (You can list pytz timezones with the command 'pytz.all_timezones') +TIMEZONE="US/Mountain" + +# Mapbox token for map rendering in the webapp +MAPBOX_TOKEN= +# DOT_NAME must be set for the DOT name to correctly populate when building an image for deployment +DOT_NAME="CDOT" +MAPBOX_INIT_LATITUDE="39.7392" +MAPBOX_INIT_LONGITUDE="-104.9903" +MAPBOX_INIT_ZOOM="10" + +GCP_PROJECT_ID = '' + +# --------------------------------------------------------------------- + +# Count Metric Addon: +ENABLE_EMAILER = 'True' + +# If ENABLE_EMAILER is 'True', set the following environment variables +DEPLOYMENT_TITLE = 'JPO-ODE' + +# SMTP REQUIRED VARIABLES +SMTP_SERVER_IP = '' +SMTP_USERNAME = '' +SMTP_PASSWORD = '' +SMTP_EMAIL = '' +# Multiple emails can be delimited by a ',' +SMTP_EMAIL_RECIPIENTS = 'test1@gmail.com,test2@gmail.com' + +# If ENABLE_EMAILER is 'False', set the following environment variables + +COUNT_MESSAGE_TYPES = 'bsm' +ODE_KAFKA_BROKERS = {DOCKER_HOST_IP}:9092 + +# EITHER "MONGODB" or "BIGQUERY" +COUNT_DESTINATION_DB = 'MONGODB' + +# MONGODB REQUIRED VARIABLES +INPUT_COUNTS_MONGO_COLLECTION_NAME = '' +OUTPUT_COUNTS_MONGO_COLLECTION_NAME = '' + +# BIGQUERY REQUIRED VARIABLES +KAFKA_BIGQUERY_TABLENAME = '' +# --------------------------------------------------------------------- + +# Firmware Manager Addon: +BLOB_STORAGE_PROVIDER=GCP +BLOB_STORAGE_BUCKET= +GCP_PROJECT= +# --------------------------------------------------------------------- + +# Geo-spatial message query Addon: +GEO_INPUT_COLLECTIONS='OdeBsmJson,OdePsmJson' +# TTL duration in days: +GEO_TTL_DURATION=90 +# --------------------------------------------------------------------- + +# ISS Health Check Addon +ISS_API_KEY= +ISS_API_KEY_NAME= +ISS_PROJECT_ID= +ISS_SCMS_TOKEN_REST_ENDPOINT= +ISS_SCMS_VEHICLE_REST_ENDPOINT= +# --------------------------------------------------------------------- + +# RSU Status Addon: + +# Services that can be toggled on or off +# 'True' or 'False' are the only legal values + +# Toggles monitoring of RSU online status +RSU_PING=True + +# Fetches ping data from Zabbix - alternatively the service will ping the RSUs on its own +# Only used when RSU_PING is 'True' +ZABBIX=False + +# Fetches SNMP configuration data for all RSUs +RSU_SNMP_FETCH=True + +# Zabbix endpoint and API authentication +# Only used when ZABBIX is 'True' +ZABBIX_ENDPOINT= +ZABBIX_USER= +ZABBIX_PASSWORD= + +# Customize the period at which the purger will determine a ping log is too old and will be deleted +# Number of hours +STALE_PERIOD=24 +# --------------------------------------------------------------------- + +# Levels are "DEBUG", "INFO", "WARNING", and "ERROR" +API_LOGGING_LEVEL="INFO" +FIRMWARE_MANAGER_LOGGING_LEVEL="INFO" +GEO_LOGGING_LEVEL="INFO" +ISS_LOGGING_LEVEL="INFO" +RSU_STATUS_LOGGING_LEVEL="INFO" +COUNTS_LOGGING_LEVEL="INFO" +# Supported log levels are "ALL", "DEBUG", "ERROR", "FATAL", "INFO", "OFF", "TRACE" and "WARN" +KC_LOGGING_LEVEL="INFO" + + +################################### +# JPO ConflictMonitor Docker Environment File # +################################### +# WARNING! The contents of this file may be sensitive. Take care not to add to source control. +# +# Instructions: +# Rename this file from `sample.env` to `.env` and Docker will automatically pick up the variables. +# +# Description: +# This file aggregates all the variables used in docker-compose.yml for ease of use. Variables are +# prefixed with the format DESTINATION_MESSAGETYPE_ to provide guidance. Some variables are filled +# out with commonly used values. +################################### + +#################### +# General Properties + +# (Required) The IP address of Docker host machine which can be found by running "ifconfig" +# Hint: look for "inet addr:" within "eth0" or "en0" for OSX +# For Local deployments DB_HOST_IP and KAFKA_BROKER_IP should be set to DOCKER_HOST_IP, for remote deployments, these can be set independently. +DOCKER_HOST_IP= +DB_HOST_IP= +KAFKA_BROKER_IP= +KAFKA_CONNECT_IP=localhost +# Set to "CONFLUENT" if broker is a Confluent Cloud broker +KAFKA_TYPE= + +# Set to actual Confluent Cloud access key and secret values for SASL authentication +CONFLUENT_KEY= +CONFLUENT_SECRET= + +# The Username and passwords to use for accessing mongoDB. +MONGO_INITDB_ROOT_USERNAME=root +MONGO_INITDB_ROOT_PASSWORD=root +CM_MONGO_CONNECTOR_USERNAME=connector +CM_MONGO_CONNECTOR_PASSWORD=connector +CM_MONGO_API_USERNAME=api +CM_MONGO_API_PASSWORD=api +CM_MONGO_USER_USERNAME=user +CM_MONGO_USER_PASSWORD=user + + + +################################### +# JPO GeoJsonConverter Docker Environment File # +################################### +# WARNING! The contents of this file may be sensitive. Take care not to add to source control. +# +# Instructions: +# Rename this file from `sample.env` to `.env` and Docker will automatically pick up the variables. +# +# Description: +# This file aggregates all the variables used in docker-compose.yml for ease of use. Variables are +# prefixed with the format DESTINATION_MESSAGETYPE_ to provide guidance. Some variables are filled +# out with commonly used values. +################################### + +#################### +# General Properties + +# (Required) The IP address of Docker host machine which can be found by running "ifconfig" +# Hint: look for "inet addr:" within "eth0" or "en0" for OSX +DOCKER_HOST_IP= + +#################### +# Confluent Cloud Properties (for Confluent Cloud support) + +# Set to "CONFLUENT" if broker is a Confluent Cloud broker +KAFKA_TYPE= + +# Set to actual Confluent Cloud access key and secret values for SASL authentication +CONFLUENT_KEY= +CONFLUENT_SECRET= + +# Feature geometry output mode +# Options are GEOJSON_ONLY or WKT, defaults to GEOJSON_ONLY if nothing is specified +# WKT mode will still generate geoJSON output +GEOMETRY_OUTPUT_MODE= + + +################################### +# JPO ODE Docker Environment File # +################################### +# WARNING! The contents of this file may be sensitive. Take care not to add to source control. +# +# Instructions: +# Rename this file from `sample.env` to `.env` and Docker will automatically pick up the variables. +# +# Description: +# This file aggregates all the variables used in docker-compose.yml for ease of use. Variables are +# prefixed with the format DESTINATION_MESSAGETYPE_ to provide guidance. Some variables are filled +# out with commonly used values. +# +# Variable usage guide: +# _ACCESS_KEY_ID : The Amazon Web Services (AWS) bucket access key ID. +# _SECRET_ACCESS_KEY : The AWS bucket secret access key. +# _BUCKET_NAME : The name of the destination AWS bucket. +# _DEPOSIT_KEY : The filename prefix appended to files deposited to the destination bucket (may be a file path). +# _TOPIC : The name of the Kafka topic to which this depositor will listen. +# _DESTINATION : (Optional) Depositors will default to Firehose depositing. Uncomment this to override to S3 deposit. +################################### + +#################### +# General Properties + +# (Required) The IP address of Docker host machine which can be found by running "ifconfig" +# Hint: look for "inet addr:" within "eth0" or "en0" for OSX +DOCKER_HOST_IP= + +# (Required) The full path of a directory on the host machine to be shared with docker containers. +# Hint: usually the path to the `jpo-ode` directory. +DOCKER_SHARED_VOLUME= +DOCKER_SHARED_VOLUME_WINDOWS= + +# (Required if values are not sent in REST request JSON messages) +# RSU SNMP username and password +ODE_RSU_USERNAME= +ODE_RSU_PASSWORD= + +######################### +# Kafka and Confluent Cloud Properties + +# The IP address of Docker host machine which can be found by running "ifconfig" +KAFKA_TYPE= + +# Confluent Cloud API access credentials +CONFLUENT_KEY= +CONFLUENT_SECRET= + +######################### +# S3 Depositor Properties + + +CVPEP_BSM_BUCKET_NAME= +CVPEP_BSM_DEPOSIT_KEY=wydot-bsm- +CVPEP_BSM_TOPIC=topic.OdeBsmJson +CVPEP_BSM_K_AWS_ACCESS_KEY_ID=AccessKeyId +CVPEP_BSM_K_AWS_SECRET_ACCESS_SECRET=SecretAccessKey +CVPEP_BSM_K_AWS_SESSION_TOKEN=SessionToken +CVPEP_BSM_K_AWS_EXPIRATION=Expiration +CVPEP_BSM_API_ENDPOINT= +CVPEP_BSM_HEADER_ACCEPT=application/json +CVPEP_BSM_HEADER_X_API_KEY= +CVPEP_BSM_GROUP=group_cvpep_bsm + +RDE_BSM_BUCKET_NAME= +RDE_BSM_DEPOSIT_KEY=wydot-filtered-bsm- +RDE_BSM_TOPIC=topic.FilteredOdeBsmJson +RDE_BSM_K_AWS_ACCESS_KEY_ID=AccessKeyId +RDE_BSM_K_AWS_SECRET_ACCESS_SECRET=SecretAccessKey +RDE_BSM_K_AWS_SESSION_TOKEN=SessionToken +RDE_BSM_K_AWS_EXPIRATION=Expiration +RDE_BSM_API_ENDPOINT= +RDE_BSM_HEADER_ACCEPT=application/json +RDE_BSM_HEADER_X_API_KEY= +RDE_BSM_GROUP=group_rde_bsm + +CVPEP_TIM_BUCKET_NAME= +CVPEP_TIM_DEPOSIT_KEY=wydot-tim- +CVPEP_TIM_TOPIC=topic.OdeTimJson +CVPEP_TIM_K_AWS_ACCESS_KEY_ID=AccessKeyId +CVPEP_TIM_K_AWS_SECRET_ACCESS_SECRET=SecretAccessKey +CVPEP_TIM_K_AWS_SESSION_TOKEN=SessionToken +CVPEP_TIM_K_AWS_EXPIRATION=Expiration +CVPEP_TIM_API_ENDPOINT= +CVPEP_TIM_HEADER_ACCEPT=application/json +CVPEP_TIM_HEADER_X_API_KEY= +CVPEP_TIM_GROUP=group_cvpep_tim + +RDE_TIM_BUCKET_NAME= +RDE_TIM_DEPOSIT_KEY=wydot-filtered-tim- +RDE_TIM_TOPIC=topic.FilteredOdeTimJson +RDE_TIM_K_AWS_ACCESS_KEY_ID=AccessKeyId +RDE_TIM_K_AWS_SECRET_ACCESS_SECRET=SecretAccessKey +RDE_TIM_K_AWS_SESSION_TOKEN=SessionToken +RDE_TIM_K_AWS_EXPIRATION=Expiration +RDE_TIM_API_ENDPOINT= +RDE_TIM_HEADER_ACCEPT=application/json +RDE_TIM_HEADER_X_API_KEY= +RDE_TIM_GROUP=group_rde_tim + +######################### +# SDW Depositor Properties + +# Uncomment and set this variable to `true` if you want to use the DDS depositor. +# Otherwise, leave it commented out or set to `false` if you want to use the SDW Depositor Module +#ODE_DEPOSIT_SDW_MESSAGES_OVER_WEBSOCKET=false + +# Note: you only need to set one of the following pairs of username/pw combinations +# The first pair is used for the SDW depositor submodule +# The second pair is used for the built-in DDS depositor + +## Required if using SDX depositor module (REST interface) +SDW_API_KEY= + +## Optional overrides +#SDW_DESTINATION_URL= +#SDW_GROUP_ID= +#SDW_KAFKA_PORT= +#SDW_SUBSCRIPTION_TOPIC=topic.SDWDepositorInput + +## Required if using built-in DDS deposit (websocket interface) +#ODE_DDS_CAS_USERNAME= +#ODE_DDS_CAS_PASSWORD= + +#jpo-security-svcs module properties +SEC_CRYPTO_SERVICE_BASE_URI= +ODE_SECURITY_SVCS_SIGNATURE_URI= + +# These are just the file names expected to be found in the ODE directory +PPM_BSM_CONFIG_FILE=ppmBsm.properties +PPM_TIM_CONFIG_FILE=ppmTim.properties +PPM_MAP_FILE=I_80.edges + +# PPM Logging +PPM_BSM_LOG_TO_CONSOLE=true +PPM_BSM_LOG_TO_FILE=false +PPM_BSM_LOG_LEVEL=INFO +PPM_TIM_LOG_TO_FILE=false +PPM_TIM_LOG_TO_CONSOLE=true +PPM_TIM_LOG_LEVEL=INFO + +# ACM Logging +AEM_LOG_TO_CONSOLE=true +AEM_LOG_TO_FILE=false +AEM_LOG_LEVEL=INFO +ADM_LOG_TO_FILE=false +ADM_LOG_TO_CONSOLE=true +ADM_LOG_LEVEL=INFO diff --git a/release-compose.yml b/release-compose.yml new file mode 100644 index 000000000..be4408c84 --- /dev/null +++ b/release-compose.yml @@ -0,0 +1,302 @@ +version: '3.9' +services: + cvmanager_api: + build: + context: services + dockerfile: Dockerfile.api + image: jpo_cvmanager_api:latest + restart: always + extra_hosts: + ${WEBAPP_DOMAIN}: ${WEBAPP_HOST_IP} + ${KEYCLOAK_DOMAIN}: ${KC_HOST_IP} + ports: + - '8081:5000' + environment: + PG_DB_HOST: ${PG_DB_HOST} + PG_DB_USER: ${PG_DB_USER} + PG_DB_PASS: ${PG_DB_PASS} + PG_DB_NAME: postgres + INSTANCE_CONNECTION_NAME: ${INSTANCE_CONNECTION_NAME} + + MONGO_DB_URI: ${MONGO_DB_URI} + MONGO_DB_NAME: ${MONGO_DB_NAME} + + COUNTS_MSG_TYPES: ${COUNTS_MSG_TYPES} + GOOGLE_APPLICATION_CREDENTIALS: '/google/gcp_credentials.json' + + GEO_DB_NAME: ${GEO_DB_NAME} + SSM_DB_NAME: ${SSM_DB_NAME} + SRM_DB_NAME: ${SRM_DB_NAME} + + MAX_GEO_QUERY_RECORDS: ${MAX_GEO_QUERY_RECORDS} + + FIRMWARE_MANAGER_ENDPOINT: ${FIRMWARE_MANAGER_ENDPOINT} + + WZDX_API_KEY: ${WZDX_API_KEY} + WZDX_ENDPOINT: ${WZDX_ENDPOINT} + + CORS_DOMAIN: ${CORS_DOMAIN} + KEYCLOAK_ENDPOINT: http://${KEYCLOAK_DOMAIN}:8084/ + KEYCLOAK_REALM: ${KEYCLOAK_REALM} + KEYCLOAK_API_CLIENT_ID: ${KEYCLOAK_API_CLIENT_ID} + KEYCLOAK_API_CLIENT_SECRET_KEY: ${KEYCLOAK_API_CLIENT_SECRET_KEY} + + CSM_EMAIL_TO_SEND_FROM: ${CSM_EMAIL_TO_SEND_FROM} + CSM_EMAIL_APP_USERNAME: ${CSM_EMAIL_APP_USERNAME} + CSM_EMAIL_APP_PASSWORD: ${CSM_EMAIL_APP_PASSWORD} + CSM_EMAILS_TO_SEND_TO: ${CSM_EMAILS_TO_SEND_TO} + CSM_TARGET_SMTP_SERVER_ADDRESS: ${CSM_TARGET_SMTP_SERVER_ADDRESS} + CSM_TARGET_SMTP_SERVER_PORT: ${CSM_TARGET_SMTP_SERVER_PORT} + + TIMEZONE: ${TIMEZONE} + LOGGING_LEVEL: ${API_LOGGING_LEVEL} + volumes: + - ${GOOGLE_APPLICATION_CREDENTIALS}:/google/gcp_credentials.json + logging: + options: + max-size: '10m' + max-file: '5' + + cvmanager_webapp: + build: + context: webapp + dockerfile: Dockerfile + args: + API_URI: http://${WEBAPP_DOMAIN}:8081 + MAPBOX_TOKEN: ${MAPBOX_TOKEN} + KEYCLOAK_HOST_URL: http://${KEYCLOAK_DOMAIN}:8084/ + COUNT_MESSAGE_TYPES: ${COUNTS_MSG_TYPES} + VIEWER_MESSAGE_TYPES: ${VIEWER_MSG_TYPES} + DOT_NAME: ${DOT_NAME} + MAPBOX_INIT_LATITUDE: ${MAPBOX_INIT_LATITUDE} + MAPBOX_INIT_LONGITUDE: ${MAPBOX_INIT_LONGITUDE} + MAPBOX_INIT_ZOOM: ${MAPBOX_INIT_ZOOM} + CVIZ_MAPBOX_TOKEN: ${CVIZ_MAPBOX_TOKEN} + CVIZ_MAPBOX_STYLE_URL: ${CVIZ_MAPBOX_STYLE_URL} + CVIZ_API_SERVER_URL: ${CVIZ_API_SERVER_URL} + CVIZ_API_WS_URL: ${CVIZ_API_WS_URL} + image: jpo_cvmanager_webapp:latest + restart: always + depends_on: + cvmanager_keycloak: + condition: service_healthy + extra_hosts: + ${WEBAPP_DOMAIN}: ${WEBAPP_HOST_IP} + ${KEYCLOAK_DOMAIN}: ${KC_HOST_IP} + ports: + - '80:80' + logging: + options: + max-size: '10m' + + cvmanager_postgres: + image: postgis/postgis:15-master + restart: always + ports: + - '5432:5432' + environment: + POSTGRES_USER: ${PG_DB_USER} + POSTGRES_PASSWORD: ${PG_DB_PASS} + volumes: + - pgdb:/var/lib/postgresql/data + - ./resources/sql_scripts:/docker-entrypoint-initdb.d + logging: + options: + max-size: '10m' + + cvmanager_keycloak: + build: + context: ./resources/keycloak + dockerfile: Dockerfile + args: + KEYCLOAK_LOGIN_THEME_NAME: ${KEYCLOAK_LOGIN_THEME_NAME}.jar + image: jpo_cvmanager_keycloak:latest + restart: always + depends_on: + - cvmanager_postgres + extra_hosts: + ${WEBAPP_DOMAIN}: ${WEBAPP_HOST_IP} + ${KEYCLOAK_DOMAIN}: ${KC_HOST_IP} + ports: + - '8084:8080' + environment: + KEYCLOAK_ADMIN: ${KEYCLOAK_ADMIN} + KEYCLOAK_ADMIN_PASSWORD: ${KEYCLOAK_ADMIN_PASSWORD} + WEBAPP_ORIGIN: http://${WEBAPP_DOMAIN} + WEBAPP_CM_ORIGIN: http://${WEBAPP_CM_DOMAIN} + KC_HEALTH_ENABLED: true + KC_DB: postgres + KC_DB_URL: jdbc:postgresql://${PG_DB_HOST}/postgres?currentSchema=keycloak + KC_DB_USERNAME: ${PG_DB_USER} + KC_DB_PASSWORD: ${PG_DB_PASS} + KC_HOSTNAME: ${KEYCLOAK_DOMAIN} + KEYCLOAK_API_CLIENT_SECRET_KEY: ${KEYCLOAK_API_CLIENT_SECRET_KEY} + KEYCLOAK_CM_API_CLIENT_SECRET_KEY: ${KEYCLOAK_CM_API_CLIENT_SECRET_KEY} + GOOGLE_CLIENT_ID: ${GOOGLE_CLIENT_ID} + GOOGLE_CLIENT_SECRET: ${GOOGLE_CLIENT_SECRET} + command: + - start-dev + - --log-level=${KC_LOGGING_LEVEL} + - --import-realm + - --spi-theme-welcome-theme=custom-welcome + logging: + options: + max-size: '10m' + + conflictmonitor: + image: usdotjpoode/jpo-conflictmonitor:release_1.2.0 + restart: always + ports: + - '8082:8082' + environment: + DOCKER_HOST_IP: ${DOCKER_HOST_IP} + KAFKA_BROKER_IP: ${KAFKA_BROKER_IP} + DB_HOST_IP: ${DB_HOST_IP} + healthcheck: + test: ['CMD', 'java', '-version'] + interval: 10s + timeout: 10s + retries: 20 + logging: + options: + max-size: '10m' + max-file: '5' + deploy: + resources: + limits: + memory: 3G + + conflictvisualizer-api: + image: us-central1-docker.pkg.dev/cdot-oim-cv-dev/jpo-conflictvisualizer-api-cvmanager/jpo-conflictvisualizer-api-cvmanager:v1.0.0 + build: + context: api + dockerfile: Dockerfile + ports: + - '8089:8081' + restart: always + environment: + AUTH_SERVER_URL: ${AUTH_SERVER_URL} + KEYCLOAK_ADMIN: ${KEYCLOAK_ADMIN} + KEYCLOAK_ADMIN_PASSWORD: ${KEYCLOAK_ADMIN_PASSWORD} + DB_HOST_IP: ${DB_HOST_IP} + DB_HOST_PORT: ${DB_HOST_PORT} + SPRING_KAFKA_BOOTSTRAPSERVERS: ${KAFKA_BROKER_IP}:${KAFKA_BROKER_PORT} + CM_SERVER_URL: ${CM_SERVER_URL} + load: 'false' + logging: + options: + max-size: '10m' + max-file: '5' + depends_on: + cvmanager_keycloak: + condition: service_healthy + + mongodb_container: + image: mongo:6 + container_name: jpo-conflictmonitor-mongodb-container + restart: always + environment: + - MONGO_REPLICA_SET_NAME=rs0 + - DB_HOST_IP=${DB_HOST_IP} + - MONGO_INITDB_ROOT_USERNAME=${MONGO_INITDB_ROOT_USERNAME} + - MONGO_INITDB_ROOT_PASSWORD=${MONGO_INITDB_ROOT_PASSWORD} + - CM_MONGO_CONNECTOR_USERNAME=${CM_MONGO_CONNECTOR_USERNAME} + - CM_MONGO_CONNECTOR_PASSWORD=${CM_MONGO_CONNECTOR_PASSWORD} + - CM_MONGO_API_USERNAME=${CM_MONGO_API_USERNAME} + - CM_MONGO_API_PASSWORD=${CM_MONGO_API_PASSWORD} + - CM_MONGO_USER_USERNAME=${CM_MONGO_USER_USERNAME} + - CM_MONGO_USER_PASSWORD=${CM_MONGO_USER_PASSWORD} + ports: + - '27017:27017' + volumes: + - mongodb_data_container:/data/db + - ./docker/mongo/keyfile.txt:/data/keyfile.txt + - ./docker/mongo/a_init_replicas.js:/docker-entrypoint-initdb.d/a_init_replicas.js + - ./docker/mongo/b_create_indexes.js:/docker-entrypoint-initdb.d/b_create_indexes.js + healthcheck: + test: | + test $$(mongosh --username ${MONGO_INITDB_ROOT_USERNAME} --password ${MONGO_INITDB_ROOT_PASSWORD} --quiet --eval "try { rs.initiate({ _id: 'rs0', members: [{ _id: 0, host: '${DB_HOST_IP}' }] }).ok } catch (_) { rs.status().ok }") -eq 1 + interval: 10s + start_period: 30s + entrypoint: + - bash + - -c + - | + chmod 400 /data/keyfile.txt + chown 999:999 /data/keyfile.txt + exec docker-entrypoint.sh $$@ + + command: ['mongod', '--replSet', 'rs0', '--bind_ip_all', '--keyFile', '/data/keyfile.txt'] + logging: + options: + max-size: '10m' + max-file: '5' + deploy: + resources: + limits: + memory: 3G + + connect: + image: cp-kafka-connect:6.1.9 + build: + context: ./docker/connect + dockerfile: Dockerfile + container_name: jpo-conflictmonitor-kafka-connect + restart: always + ports: + - '8083:8083' + depends_on: + mongodb_container: + condition: service_healthy + environment: + DOCKER_HOST_IP: ${DOCKER_HOST_IP} + DB_HOST_IP: ${DB_HOST_IP} + CONNECT_BOOTSTRAP_SERVERS: ${KAFKA_BROKER_IP}:9092 + CONNECT_REST_ADVERTISED_HOST_NAME: connect + CONNECT_REST_PORT: 8083 + CONNECT_GROUP_ID: compose-connect-group + CONNECT_CONFIG_STORAGE_TOPIC: CmConnectConfigs + CONNECT_CONFIG_STORAGE_REPLICATION_FACTOR: 1 + CONNECT_CONFIG_STORAGE_CLEANUP_POLICY: compact + CONNECT_OFFSET_FLUSH_INTERVAL_MS: 10000 + CONNECT_OFFSET_STORAGE_TOPIC: CmConnectOffsets + CONNECT_OFFSET_STORAGE_REPLICATION_FACTOR: 1 + CONNECT_OFFSET_STORAGE_CLEANUP_POLICY: compact + CONNECT_STATUS_STORAGE_TOPIC: CmConnectStatus + CONNECT_STATUS_STORAGE_CLEANUP_POLICY: compact + CONNECT_STATUS_STORAGE_REPLICATION_FACTOR: 1 + CONNECT_KEY_CONVERTER: 'org.apache.kafka.connect.json.JsonConverter' + CONNECT_VALUE_CONVERTER: 'org.apache.kafka.connect.json.JsonConverter' + CONNECT_INTERNAL_KEY_CONVERTER: 'org.apache.kafka.connect.json.JsonConverter' + CONNECT_INTERNAL_VALUE_CONVERTER: 'org.apache.kafka.connect.json.JsonConverter' + CONNECT_LOG4J_ROOT_LOGLEVEL: 'ERROR' + CONNECT_LOG4J_LOGGERS: 'org.apache.kafka.connect.runtime.rest=ERROR,org.reflections=ERROR,com.mongodb.kafka=ERROR' + CONNECT_PLUGIN_PATH: /usr/share/confluent-hub-components + CONNECT_ZOOKEEPER_CONNECT: 'zookeeper:2181' + CM_MONGO_CONNECTOR_USERNAME: ${CM_MONGO_CONNECTOR_USERNAME} + CM_MONGO_CONNECTOR_PASSWORD: ${CM_MONGO_CONNECTOR_PASSWORD} + logging: + options: + max-size: '10m' + max-file: '5' + command: + - bash + - -c + - | + /etc/confluent/docker/run & + echo "Waiting for Kafka Connect to start listening on kafka-connect ❳" + while [ $$(curl -s -o /dev/null -w %{http_code} http://${KAFKA_CONNECT_IP}:8083/connectors) -eq 000 ] ; do + echo -e $$(date) " Kafka Connect listener HTTP state: " $$(curl -s -o /dev/null -w %{http_code} http://${KAFKA_CONNECT_IP}:8083/connectors) " (waiting for 200)" + sleep 5 + done + sleep 10 + echo -e "\n--\n+> Creating Kafka Connect MongoDB sink" + bash /scripts/connect_start.sh "mongodb://${CM_MONGO_CONNECTOR_USERNAME}:${CM_MONGO_CONNECTOR_USERNAME}@${DOCKER_HOST_IP}:27017/?authMechanism=DEFAULT&authSource=ConflictMonitor&replicaSet=rs0" + sleep infinity + deploy: + resources: + limits: + memory: 3G + +volumes: + mongodb_data_container: diff --git a/services/conflictmonitor-release-compose.yml b/services/conflictmonitor-release-compose.yml new file mode 100644 index 000000000..4100c6d5f --- /dev/null +++ b/services/conflictmonitor-release-compose.yml @@ -0,0 +1,158 @@ +version: '3.9' +services: + conflictmonitor: + image: usdotjpoode/jpo-conflictmonitor:release_1.2.0 + restart: always + ports: + - '8082:8082' + environment: + DOCKER_HOST_IP: ${DOCKER_HOST_IP} + KAFKA_BROKER_IP: ${KAFKA_BROKER_IP} + DB_HOST_IP: ${DB_HOST_IP} + healthcheck: + test: ['CMD', 'java', '-version'] + interval: 10s + timeout: 10s + retries: 20 + logging: + options: + max-size: '10m' + max-file: '5' + deploy: + resources: + limits: + memory: 3G + + conflictvisualizer-api: + image: us-central1-docker.pkg.dev/cdot-oim-cv-dev/jpo-conflictvisualizer-api-cvmanager/jpo-conflictvisualizer-api-cvmanager:v1.0.0 + build: + context: api + dockerfile: Dockerfile + ports: + - '8089:8081' + restart: always + environment: + AUTH_SERVER_URL: ${AUTH_SERVER_URL} + KEYCLOAK_ADMIN: ${KEYCLOAK_ADMIN} + KEYCLOAK_ADMIN_PASSWORD: ${KEYCLOAK_ADMIN_PASSWORD} + DB_HOST_IP: ${DB_HOST_IP} + DB_HOST_PORT: ${DB_HOST_PORT} + SPRING_KAFKA_BOOTSTRAPSERVERS: ${KAFKA_BROKER_IP}:${KAFKA_BROKER_PORT} + CM_SERVER_URL: ${CM_SERVER_URL} + load: 'false' + #KAFKA_BROKER_IP: ${KAFKA_BROKER_IP} + #KAFKA_BROKER_PORT: ${KAFKA_BROKER_PORT} + logging: + options: + max-size: '10m' + max-file: '5' + + mongodb_container: + image: mongo:6 + container_name: jpo-conflictmonitor-mongodb-container + restart: always + environment: + - MONGO_REPLICA_SET_NAME=rs0 + - DB_HOST_IP=${DB_HOST_IP} + - MONGO_INITDB_ROOT_USERNAME=${MONGO_INITDB_ROOT_USERNAME} + - MONGO_INITDB_ROOT_PASSWORD=${MONGO_INITDB_ROOT_PASSWORD} + - CM_MONGO_CONNECTOR_USERNAME=${CM_MONGO_CONNECTOR_USERNAME} + - CM_MONGO_CONNECTOR_PASSWORD=${CM_MONGO_CONNECTOR_PASSWORD} + - CM_MONGO_API_USERNAME=${CM_MONGO_API_USERNAME} + - CM_MONGO_API_PASSWORD=${CM_MONGO_API_PASSWORD} + - CM_MONGO_USER_USERNAME=${CM_MONGO_USER_USERNAME} + - CM_MONGO_USER_PASSWORD=${CM_MONGO_USER_PASSWORD} + ports: + - '27017:27017' + volumes: + - mongodb_data_container:/data/db + - ./docker/mongo/keyfile.txt:/data/keyfile.txt + - ./docker/mongo/a_init_replicas.js:/docker-entrypoint-initdb.d/a_init_replicas.js + - ./docker/mongo/b_create_indexes.js:/docker-entrypoint-initdb.d/b_create_indexes.js + healthcheck: + test: | + test $$(mongosh --username ${MONGO_INITDB_ROOT_USERNAME} --password ${MONGO_INITDB_ROOT_PASSWORD} --quiet --eval "try { rs.initiate({ _id: 'rs0', members: [{ _id: 0, host: '${DB_HOST_IP}' }] }).ok } catch (_) { rs.status().ok }") -eq 1 + interval: 10s + start_period: 30s + entrypoint: + - bash + - -c + - | + chmod 400 /data/keyfile.txt + chown 999:999 /data/keyfile.txt + exec docker-entrypoint.sh $$@ + + command: ['mongod', '--replSet', 'rs0', '--bind_ip_all', '--keyFile', '/data/keyfile.txt'] + logging: + options: + max-size: '10m' + max-file: '5' + deploy: + resources: + limits: + memory: 3G + + connect: + image: cp-kafka-connect:6.1.9 + build: + context: ./docker/connect + dockerfile: Dockerfile + container_name: jpo-conflictmonitor-kafka-connect + restart: always + ports: + - '8083:8083' + depends_on: + mongodb_container: + condition: service_healthy + environment: + DOCKER_HOST_IP: ${DOCKER_HOST_IP} + DB_HOST_IP: ${DB_HOST_IP} + CONNECT_BOOTSTRAP_SERVERS: ${KAFKA_BROKER_IP}:9092 + CONNECT_REST_ADVERTISED_HOST_NAME: connect + CONNECT_REST_PORT: 8083 + CONNECT_GROUP_ID: compose-connect-group + CONNECT_CONFIG_STORAGE_TOPIC: CmConnectConfigs + CONNECT_CONFIG_STORAGE_REPLICATION_FACTOR: 1 + CONNECT_CONFIG_STORAGE_CLEANUP_POLICY: compact + CONNECT_OFFSET_FLUSH_INTERVAL_MS: 10000 + CONNECT_OFFSET_STORAGE_TOPIC: CmConnectOffsets + CONNECT_OFFSET_STORAGE_REPLICATION_FACTOR: 1 + CONNECT_OFFSET_STORAGE_CLEANUP_POLICY: compact + CONNECT_STATUS_STORAGE_TOPIC: CmConnectStatus + CONNECT_STATUS_STORAGE_CLEANUP_POLICY: compact + CONNECT_STATUS_STORAGE_REPLICATION_FACTOR: 1 + CONNECT_KEY_CONVERTER: 'org.apache.kafka.connect.json.JsonConverter' + CONNECT_VALUE_CONVERTER: 'org.apache.kafka.connect.json.JsonConverter' + CONNECT_INTERNAL_KEY_CONVERTER: 'org.apache.kafka.connect.json.JsonConverter' + CONNECT_INTERNAL_VALUE_CONVERTER: 'org.apache.kafka.connect.json.JsonConverter' + CONNECT_LOG4J_ROOT_LOGLEVEL: 'ERROR' + CONNECT_LOG4J_LOGGERS: 'org.apache.kafka.connect.runtime.rest=ERROR,org.reflections=ERROR,com.mongodb.kafka=ERROR' + CONNECT_PLUGIN_PATH: /usr/share/confluent-hub-components + CONNECT_ZOOKEEPER_CONNECT: 'zookeeper:2181' + CM_MONGO_CONNECTOR_USERNAME: ${CM_MONGO_CONNECTOR_USERNAME} + CM_MONGO_CONNECTOR_PASSWORD: ${CM_MONGO_CONNECTOR_PASSWORD} + logging: + options: + max-size: '10m' + max-file: '5' + command: + - bash + - -c + - | + /etc/confluent/docker/run & + echo "Waiting for Kafka Connect to start listening on kafka-connect ❳" + while [ $$(curl -s -o /dev/null -w %{http_code} http://${KAFKA_CONNECT_IP}:8083/connectors) -eq 000 ] ; do + echo -e $$(date) " Kafka Connect listener HTTP state: " $$(curl -s -o /dev/null -w %{http_code} http://${KAFKA_CONNECT_IP}:8083/connectors) " (waiting for 200)" + sleep 5 + done + sleep 10 + echo -e "\n--\n+> Creating Kafka Connect MongoDB sink" + bash /scripts/connect_start.sh "mongodb://${CM_MONGO_CONNECTOR_USERNAME}:${CM_MONGO_CONNECTOR_USERNAME}@${DOCKER_HOST_IP}:27017/?authMechanism=DEFAULT&authSource=ConflictMonitor&replicaSet=rs0" + sleep infinity + deploy: + resources: + limits: + memory: 3G + +volumes: + mongodb_data_container: From e93e680fb2d13b9020f22bca6e9d88403751259e Mon Sep 17 00:00:00 2001 From: jacob6838 Date: Sun, 26 May 2024 13:24:54 -0600 Subject: [PATCH 031/189] Working CIMMS integration with release-compose --- README.md | 19 ++ docker/README.md | 3 + docker/connect/Dockerfile | 6 + docker/connect/connect_start.sh | 269 +++++++++++++++++++++++++ docker/mongo/Dockerfile | 5 + docker/mongo/a_init_replicas.js | 33 +++ docker/mongo/b_create_indexes.js | 332 +++++++++++++++++++++++++++++++ docker/mongo/keyfile.txt | 1 + docker/mongo/mongo_initialize.sh | 12 ++ release-compose.example.env | 32 +++ release-compose.yml | 5 +- 11 files changed, 714 insertions(+), 3 deletions(-) create mode 100644 docker/README.md create mode 100644 docker/connect/Dockerfile create mode 100644 docker/connect/connect_start.sh create mode 100644 docker/mongo/Dockerfile create mode 100644 docker/mongo/a_init_replicas.js create mode 100644 docker/mongo/b_create_indexes.js create mode 100644 docker/mongo/keyfile.txt create mode 100644 docker/mongo/mongo_initialize.sh diff --git a/README.md b/README.md index 56425ad5f..90b54bab5 100644 --- a/README.md +++ b/README.md @@ -48,6 +48,25 @@ The JPO CV Manager was originally developed for the Google Cloud Platform and a - Keycloak is used for the CV Manager webapp's authentication. - The Keycloak pod requires a `realm.json` file in the folder: `./resources/keycloak/` to startup with the proper configurations. It also requires a login theme that can be modified and generated using the [keycloakify](https://github.com/keycloakify/keycloakify) forked repository in resources/keycloak/keycloakify. The theme will be automatically generated when using the docker image provided but can also be built using instructions found in the keycloakify folder. +### ConflictMontior and ConflictVisualizer API + +- The CV Manager webapp has been integrated with the ConflictVisualizer tool to allow users to view data directly from a jpo-conflictmonitor instance. This integration currently requires an additional jpo-conflictvisualizer api to be deployed alongside the jpo-cvmanager api. This allows the webapp to make authenticated requests to the jpo-conflictvisualizer api to retrieve the conflict monitor data. +- All of the components required for the ConflictVisualizer integration are available in the usdot-jpo-ode GitHub repositories: + - [jpo-conflictvisualizer (api)](https://github.com/usdot-jpo-ode/jpo-conflictvisualizer/tree/cvmgr-cimms-integration/api) + - [jpo-conflictmonitor](https://github.com/usdot-jpo-ode/jpo-conflictmonitor) + - [jpo-geojsonconverter](https://github.com/usdot-jpo-ode/jpo-geojsonconverter) + - [jpo-ode](https://github.com/usdot-jpo-ode/jpo-ode) + +#### Running the ConflictMonitor and ConflictVisualizer API + +Use the release-compose.yml to pull images for the jpo-conflictviualizer-api-cvmanager, jpo-conflictmonitor, jpo-geojsonconverter, and jpo-ode. +The release-compose.example.env file contains the necessary environment variables for the jpo-conflictvisualizer-api-cvmanager. Copy the release-compose.example.env file to your .env and fill in the necessary environment variables. +For more information on these services and environment variables, please visit the usdot-jpo-ode repositories listed above. + +``` +docker compose -f release-compose.yml up -d +``` + ## Getting Started The following steps are intended to help get a new user up and running the JPO CV Manager in their own environment. diff --git a/docker/README.md b/docker/README.md new file mode 100644 index 000000000..f75a49748 --- /dev/null +++ b/docker/README.md @@ -0,0 +1,3 @@ +# jpo-conflictmonitor Connect/Mongo Configuration Files + +This directory contains the configuration files for the Connect/Mongo service for the jpo-conflictmonitor project. diff --git a/docker/connect/Dockerfile b/docker/connect/Dockerfile new file mode 100644 index 000000000..e2cc9c69e --- /dev/null +++ b/docker/connect/Dockerfile @@ -0,0 +1,6 @@ +FROM confluentinc/cp-kafka-connect:6.1.9 + +COPY connect_start.sh /scripts/connect_start.sh + +RUN confluent-hub install --no-prompt mongodb/kafka-connect-mongodb:1.8.0 +RUN confluent-hub install --no-prompt confluentinc/connect-transforms:1.4.3 \ No newline at end of file diff --git a/docker/connect/connect_start.sh b/docker/connect/connect_start.sh new file mode 100644 index 000000000..03428ed1a --- /dev/null +++ b/docker/connect/connect_start.sh @@ -0,0 +1,269 @@ +# bin/bash +DB_HOST_IP = $1 + +echo "------------------------------------------" +echo "Kafka connector creation started." +echo "Provided Docker Host IP: $DOCKER_HOST_IP" +echo "------------------------------------------" + + + + + + +# Record BSM JSON Data +declare -A OdeRawEncodedBSMJson=([name]="topic.OdeRawEncodedBSMJson" [collection]="OdeRawEncodedBSMJson" + [convert_timestamp]=false [timefield]="" [use_key]=false [key]="" [add_timestamp]=true) +declare -A OdeBsmJson=([name]="topic.OdeBsmJson" [collection]="OdeBsmJson" + [convert_timestamp]=false [timefield]="" [use_key]=false [key]="" [add_timestamp]=true) + +# Record Map Data +declare -A OdeMapJson=([name]="topic.DeduplicatedOdeMapJson" [collection]="OdeMapJson" + [convert_timestamp]=false [timefield]="" [use_key]=false [key]="" [add_timestamp]=true) +declare -A ProcessedMap=([name]="topic.DeduplicatedProcessedMap" [collection]="ProcessedMap" + [convert_timestamp]=false [timefield]="" [use_key]=false [key]="" [add_timestamp]=true) +declare -A OdeRawEncodedMAPJson=([name]="topic.OdeRawEncodedMAPJson" [collection]="OdeRawEncodedMAPJson" + [convert_timestamp]=false [timefield]="" [use_key]=false [key]="" [add_timestamp]=true) + +# Record Spat Data +declare -A OdeRawEncodedSPATJson=([name]="topic.OdeRawEncodedSPATJson" [collection]="OdeRawEncodedSPATJson" + [convert_timestamp]=false [timefield]="" [use_key]=false [key]="" [add_timestamp]=true) +declare -A OdeSpatJson=([name]="topic.OdeSpatJson" [collection]="OdeSpatJson" + [convert_timestamp]=false [timefield]="" [use_key]=false [key]="" [add_timestamp]=true) +declare -A ProcessedSpat=([name]="topic.ProcessedSpat" [collection]="ProcessedSpat" + [convert_timestamp]=false [timefield]="" [use_key]=false [key]="" [add_timestamp]=true) +declare -A OdeSpatRxJson=([name]="topic.OdeSpatRxJson" [collection]="OdeSpatRxJson" + [convert_timestamp]=false [timefield]="" [use_key]=false [key]="" [add_timestamp]=true) + +# Record Driver Alert Data +declare -A OdeDriverAlertJson=([name]="topic.OdeDriverAlertJson" [collection]="OdeDriverAlertJson" + [convert_timestamp]=false [timefield]="" [use_key]=false [key]="" [add_timestamp]=true) + +# Record SRM Data +declare -A OdeSrmJson=([name]="topic.OdeSrmJson" [collection]="OdeSrmJson" + [convert_timestamp]=false [timefield]="" [use_key]=false [key]="" [add_timestamp]=true) +declare -A OdeRawEncodedSRMJson=([name]="topic.OdeRawEncodedSRMJson" [collection]="OdeRawEncodedSRMJson" + [convert_timestamp]=false [timefield]="" [use_key]=false [key]="" [add_timestamp]=true) + +# Record SSM Data +declare -A OdeSsmJson=([name]="topic.OdeSsmJson" [collection]="OdeSsmJson" + [convert_timestamp]=false [timefield]="" [use_key]=false [key]="" [add_timestamp]=true) +declare -A OdeRawEncodedSSMJson=([name]="topic.OdeRawEncodedSSMJson" [collection]="OdeRawEncodedSSMJson" + [convert_timestamp]=false [timefield]="" [use_key]=false [key]="" [add_timestamp]=true) + + +# Record TIM JSON Data +declare -A OdeTimJson=([name]="topic.DeduplicatedOdeTimJson" [collection]="OdeTimJson" + [convert_timestamp]=false [timefield]="" [use_key]=false [key]="" [add_timestamp]=true) +declare -A OdeTimBroadcastJson=([name]="topic.OdeTimBroadcastJson" [collection]="OdeTimBroadcastJson" + [convert_timestamp]=false [timefield]="" [use_key]=false [key]="" [add_timestamp]=true) +declare -A OdeTIMCertExpirationTimeJson=([name]="topic.OdeTIMCertExpirationTimeJson" [collection]="OdeTIMCertExpirationTimeJson" + [convert_timestamp]=false [timefield]="" [use_key]=false [key]="" [add_timestamp]=true) +declare -A OdeRawEncodedTIMJson=([name]="topic.OdeRawEncodedTIMJson" [collection]="OdeRawEncodedTIMJson" + [convert_timestamp]=false [timefield]="" [use_key]=false [key]="" [add_timestamp]=true) + +############################################################################################### + +# Record Events +declare -A CmStopLinePassageEvent=([name]="topic.CmStopLinePassageEvent" [collection]="CmStopLinePassageEvent" + [convert_timestamp]=true [timefield]="eventGeneratedAt" [use_key]=false [key]="" [add_timestamp]=false) +declare -A CmStopLineStopEvent=([name]="topic.CmStopLineStopEvent" [collection]="CmStopLineStopEvent" + [convert_timestamp]=true [timefield]="eventGeneratedAt" [use_key]=false [key]="" [add_timestamp]=false) +declare -A CmSignalStateConflictEvents=([name]="topic.CmSignalStateConflictEvents" [collection]="CmSignalStateConflictEvents" + [convert_timestamp]=true [timefield]="eventGeneratedAt" [use_key]=false [key]="" [add_timestamp]=false) +declare -A CmIntersectionReferenceAlignmentEvents=([name]="topic.CmIntersectionReferenceAlignmentEvents" [collection]="CmIntersectionReferenceAlignmentEvents" + [convert_timestamp]=true [timefield]="eventGeneratedAt" [use_key]=false [key]="" [add_timestamp]=false) +declare -A CmSignalGroupAlignmentEvents=([name]="topic.CmSignalGroupAlignmentEvents" [collection]="CmSignalGroupAlignmentEvents" + [convert_timestamp]=true [timefield]="eventGeneratedAt" [use_key]=false [key]="" [add_timestamp]=false) +declare -A CmConnectionOfTravelEvent=([name]="topic.CmConnectionOfTravelEvent" [collection]="CmConnectionOfTravelEvent" + [convert_timestamp]=true [timefield]="eventGeneratedAt" [use_key]=false [key]="" [add_timestamp]=false) +declare -A CmLaneDirectionOfTravelEvent=([name]="topic.CmLaneDirectionOfTravelEvent" [collection]="CmLaneDirectionOfTravelEvent" + [convert_timestamp]=true [timefield]="eventGeneratedAt" [use_key]=false [key]="" [add_timestamp]=false) +declare -A CmSpatTimeChangeDetailsEvent=([name]="topic.CmSpatTimeChangeDetailsEvent" [collection]="CmSpatTimeChangeDetailsEvent" + [convert_timestamp]=true [timefield]="eventGeneratedAt" [use_key]=false [key]="" [add_timestamp]=false) +declare -A CmSpatMinimumDataEvents=([name]="topic.CmSpatMinimumDataEvents" [collection]="CmSpatMinimumDataEvents" + [convert_timestamp]=true [timefield]="eventGeneratedAt" [use_key]=false [key]="" [add_timestamp]=false) +declare -A CmMapBroadcastRateEvents=([name]="topic.CmMapBroadcastRateEvents" [collection]="CmMapBroadcastRateEvents" + [convert_timestamp]=true [timefield]="eventGeneratedAt" [use_key]=false [key]="" [add_timestamp]=false) +declare -A CmMapMinimumDataEvents=([name]="topic.CmMapMinimumDataEvents" [collection]="CmMapMinimumDataEvents" + [convert_timestamp]=true [timefield]="eventGeneratedAt" [use_key]=false [key]="" [add_timestamp]=false) +declare -A CmSpatBroadcastRateEvents=([name]="topic.CmSpatBroadcastRateEvents" [collection]="CmSpatBroadcastRateEvents" + [convert_timestamp]=true [timefield]="eventGeneratedAt" [use_key]=false [key]="" [add_timestamp]=false) + +# Record BSM Events +declare -A CmBsmEvents=([name]="topic.CmBsmEvents" [collection]="CmBsmEvents" + [convert_timestamp]=false [timefield]="" [use_key]=false [key]="" [add_timestamp]=true) + + +# Record Assessments +declare -A CmLaneDirectionOfTravelAssessment=([name]="topic.CmLaneDirectionOfTravelAssessment" [collection]="CmLaneDirectionOfTravelAssessment" + [convert_timestamp]=true [timefield]="assessmentGeneratedAt" [use_key]=false [key]="" [add_timestamp]=false) +declare -A CmConnectionOfTravelAssessment=([name]="topic.CmConnectionOfTravelAssessment" [collection]="CmConnectionOfTravelAssessment" + [convert_timestamp]=true [timefield]="assessmentGeneratedAt" [use_key]=false [key]="" [add_timestamp]=false) +declare -A CmSignalStateEventAssessment=([name]="topic.CmSignalStateEventAssessment" [collection]="CmSignalStateEventAssessment" + [convert_timestamp]=true [timefield]="assessmentGeneratedAt" [use_key]=false [key]="" [add_timestamp]=false) +declare -A CmStopLineStopAssessment=([name]="topic.CmStopLineStopAssessment" [collection]="CmStopLineStopAssessment" + [convert_timestamp]=true [timefield]="assessmentGeneratedAt" [use_key]=false [key]="" [add_timestamp]=false) + + +# Record Notifications +declare -A CmSpatTimeChangeDetailsNotification=([name]="topic.CmSpatTimeChangeDetailsNotification" [collection]="CmSpatTimeChangeDetailsNotification" + [convert_timestamp]=true [timefield]="notificationGeneratedAt" [use_key]=false [key]="" [add_timestamp]=false) +declare -A CmLaneDirectionOfTravelNotification=([name]="topic.CmLaneDirectionOfTravelNotification" [collection]="CmLaneDirectionOfTravelNotification" + [convert_timestamp]=true [timefield]="notificationGeneratedAt" [use_key]=false [key]="" [add_timestamp]=false) +declare -A CmConnectionOfTravelNotification=([name]="topic.CmConnectionOfTravelNotification" [collection]="CmConnectionOfTravelNotification" + [convert_timestamp]=true [timefield]="notificationGeneratedAt" [use_key]=false [key]="" [add_timestamp]=false) +declare -A CmAppHealthNotifications=([name]="topic.CmAppHealthNotifications" [collection]="CmAppHealthNotifications" + [convert_timestamp]=true [timefield]="notificationGeneratedAt" [use_key]=false [key]="" [add_timestamp]=false) +declare -A CmSignalStateConflictNotification=([name]="topic.CmSignalStateConflictNotification" [collection]="CmSignalStateConflictNotification" + [convert_timestamp]=true [timefield]="notificationGeneratedAt" [use_key]=false [key]="" [add_timestamp]=false) +declare -A CmSignalGroupAlignmentNotification=([name]="topic.CmSignalGroupAlignmentNotification" [collection]="CmSignalGroupAlignmentNotification" + [convert_timestamp]=true [timefield]="notificationGeneratedAt" [use_key]=false [key]="" [add_timestamp]=false) +declare -A CmNotification=([name]="topic.CmNotification" [collection]="CmNotification" + [convert_timestamp]=true [timefield]="notificationGeneratedAt" [use_key]=true [key]="key" [add_timestamp]=false) +declare -A CmStopLineStopNotification=([name]="topic.CmStopLineStopNotification" [collection]="CmStopLineStopNotification" + [convert_timestamp]=true [timefield]="notificationGeneratedAt" [use_key]=true [key]="key" [add_timestamp]=false) +declare -A CmStopLinePassageNotification=([name]="topic.CmStopLinePassageNotification" [collection]="CmStopLinePassageNotification" + [convert_timestamp]=true [timefield]="notificationGeneratedAt" [use_key]=true [key]="key" [add_timestamp]=false) + +function createSink() { + local -n topic=$1 + echo "Creating sink connector for:" + for val in "${topic[@]}"; do echo $val; done + + local name=${topic[name]} + local collection=${topic[collection]} + local timefield=${topic[timefield]} + local convert_timestamp=${topic[convert_timestamp]} + local use_key=${topic[use_key]} + local key=${topic[key]} + local add_timestamp=${topic[add_timestamp]} + + echo "name=$name" + echo "collection=$collection" + echo "timefield=$timefield" + echo "convert_timestamp=$convert_timestamp" + echo "add_timestamp=$add_timestamp" + + local connectConfig=' { + "group.id":"connector-consumer", + "connector.class":"com.mongodb.kafka.connect.MongoSinkConnector", + "tasks.max":3, + "topics":"'$name'", + "connection.uri":"mongodb://'$CM_MONGO_CONNECTOR_USERNAME':'$CM_MONGO_CONNECTOR_PASSWORD'@'$DB_HOST_IP':27017/database?authSource=ConflictMonitor", + "database":"ConflictMonitor", + "collection":"'$collection'", + "key.converter":"org.apache.kafka.connect.storage.StringConverter", + "key.converter.schemas.enable":false, + "value.converter":"org.apache.kafka.connect.json.JsonConverter", + "value.converter.schemas.enable":false, + "errors.tolerance": "all", + "mongo.errors.tolerance": "all", + "errors.deadletterqueue.topic.name": "", + "errors.log.enable": false, + "errors.log.include.messages": false, + "errors.deadletterqueue.topic.replication.factor": 0' + + #"errors.deadletterqueue.context.headers.enable": true, + #"errors.log.enable": false, + #"errors.log.include.messages": false, + #"errors.deadletterqueue.topic.replication.factor": 1' + #"errors.deadletterqueue.topic.name": "dlq.'$collection'.sink", + + + if [ "$convert_timestamp" == true ] + then + local connectConfig=''$connectConfig', + "transforms": "TimestampConverter", + "transforms.TimestampConverter.field": "'$timefield'", + "transforms.TimestampConverter.type": "org.apache.kafka.connect.transforms.TimestampConverter$Value", + "transforms.TimestampConverter.target.type": "Timestamp"' + fi + + if [ "$add_timestamp" == true ] + then + local connectConfig=''$connectConfig', + "transforms": "AddTimestamp,AddedTimestampConverter", + "transforms.AddTimestamp.type": "org.apache.kafka.connect.transforms.InsertField$Value", + "transforms.AddTimestamp.timestamp.field": "recordGeneratedAt", + "transforms.AddedTimestampConverter.field": "recordGeneratedAt", + "transforms.AddedTimestampConverter.type": "org.apache.kafka.connect.transforms.TimestampConverter$Value", + "transforms.AddedTimestampConverter.target.type": "Timestamp"' + fi + + if [ "$use_key" == true ] + then + local connectConfig=''$connectConfig', + "document.id.strategy": "com.mongodb.kafka.connect.sink.processor.id.strategy.PartialValueStrategy", + "document.id.strategy.partial.value.projection.list": "'$key'", + "document.id.strategy.partial.value.projection.type": "AllowList", + "document.id.strategy.overwrite.existing": true' + fi + + local connectConfig=''$connectConfig' }' + + echo " Creating connector with Config : $connectConfig" + + curl -X PUT http://localhost:8083/connectors/MongoSink.${name}/config -H "Content-Type: application/json" -d "$connectConfig" +} + +createSink OdeRawEncodedBSMJson +createSink OdeBsmJson + +createSink OdeMapJson +createSink ProcessedMap +createSink OdeRawEncodedMAPJson + +createSink OdeRawEncodedSPATJson +createSink OdeSpatJson +createSink ProcessedSpat +createSink OdeSpatRxJson + +createSink OdeDriverAlertJson + +createSink OdeSrmJson +createSink OdeRawEncodedSRMJson + +createSink OdeSsmJson +createSink OdeRawEncodedSSMJson + +createSink OdeTimJson +createSink OdeTimBroadcastJson +createSink OdeTIMCertExpirationTimeJson +createSink OdeRawEncodedTIMJson + +createSink CmStopLinePassageEvent +createSink CmStopLineStopEvent +createSink CmSignalStateConflictEvents +createSink CmIntersectionReferenceAlignmentEvents +createSink CmSignalGroupAlignmentEvents +createSink CmConnectionOfTravelEvent +createSink CmLaneDirectionOfTravelEvent +createSink CmSignalStateEvent +createSink CmSpatTimeChangeDetailsEvent +createSink CmSpatMinimumDataEvents +createSink CmMapBroadcastRateEvents +createSink CmMapMinimumDataEvents +createSink CmSpatBroadcastRateEvents + +createSink CmBsmEvents + +createSink CmLaneDirectionOfTravelAssessment +createSink CmConnectionOfTravelAssessment +createSink CmSignalStateEventAssessment +createSink CmStopLineStopAssessment + +createSink CmSpatTimeChangeDetailsNotification +createSink CmLaneDirectionOfTravelNotification +createSink CmConnectionOfTravelNotification +createSink CmAppHealthNotifications +createSink CmSignalStateConflictNotification +createSink CmSignalGroupAlignmentNotification +createSink CmNotification +createSink CmStopLineStopNotification +createSink CmStopLinePassageNotification + + +echo "----------------------------------" +echo "Kafka connector creation complete!" +echo "----------------------------------" diff --git a/docker/mongo/Dockerfile b/docker/mongo/Dockerfile new file mode 100644 index 000000000..edf4f7172 --- /dev/null +++ b/docker/mongo/Dockerfile @@ -0,0 +1,5 @@ +FROM mongo:6 + +COPY a_init_replicas.js /docker-entrypoint-initdb.d/a_init_replicas.js +COPY b_create_indexes.js /docker-entrypoint-initdb.d/b_create_indexes.js + diff --git a/docker/mongo/a_init_replicas.js b/docker/mongo/a_init_replicas.js new file mode 100644 index 000000000..957837701 --- /dev/null +++ b/docker/mongo/a_init_replicas.js @@ -0,0 +1,33 @@ + +/* + +This script is the first of two scripts responsible for setting up mongoDB on initial boot. +These scripts should be copied to the /docker-entrypoint-initdb.d/ directory within the docker image; +the docker image will then execute these scripts automatically when the database is first created. +This script and is partner are prefixed with the letters a and b respectively to ensure they are run +in the proper order when copied into the mongoDB docker image. + +Since the conflict monitor uses a replica set in its mongoDB configuration. Initializing the replica set +and configuring the collections are separated from one another. The purpose of this to force a reconnect +to the database. This in turn allows the new connection to be connected to the primary replica which is +required for creating indexes on all the collections. This script is only responsible for creating the +replica set. Almost all other configuration should go in b_create_indexes.js + +Documentation on how the mongoDB docker image runs startup scripts can be found here +https://hub.docker.com/_/mongo/ + +*/ + + + +console.log("Initializing Replicas"); +try{ + db_status = rs.status(); +} catch(err){ + console.log("Initializing New DB"); + try{ + rs.initiate({ _id: 'rs0', members: [{ _id: 0, host: 'localhost:27017' }] }).ok + }catch(err){ + console.log("Unable to Initialize DB"); + } +} diff --git a/docker/mongo/b_create_indexes.js b/docker/mongo/b_create_indexes.js new file mode 100644 index 000000000..3cb50feae --- /dev/null +++ b/docker/mongo/b_create_indexes.js @@ -0,0 +1,332 @@ +// Create indexes on all collections + +/* +This is the second script responsible for configuring mongoDB automatically on startup. +This script is responsible for creating users, creating collections, adding indexes, and configuring TTLs +For more information see the header in a_init_replicas.js +*/ + +console.log(""); +console.log("Running create_indexes.js"); + + +// Setup Username and Password Definitions +const CM_MONGO_ROOT_USERNAME = process.env.CM_MONGO_ROOT_USERNAME; +const CM_MONGO_ROOT_PASSWORD = process.env.CM_MONGO_ROOT_PASSWORD; + +const CM_MONGO_CONNECTOR_USERNAME = process.env.CM_MONGO_CONNECTOR_USERNAME; +const CM_MONGO_CONNECTOR_PASSWORD = process.env.CM_MONGO_CONNECTOR_PASSWORD; + +const CM_MONGO_API_USERNAME = process.env.CM_MONGO_API_USERNAME; +const CM_MONGO_API_PASSWORD = process.env.CM_MONGO_API_PASSWORD; + +const CM_MONGO_USER_USERNAME = process.env.CM_MONGO_USER_USERNAME; +const CM_MONGO_USER_PASSWORD = process.env.CM_MONGO_USER_PASSWORD; + +const CM_DATABASE_NAME = process.env.CM_DATABASE_NAME || "ConflictMonitor"; + +const expireSeconds = 5184000; // 2 months +const retryMilliseconds = 10000; + + +const users = [ + // {username: CM_MONGO_ROOT_USERNAME, password: CM_MONGO_ROOT_PASSWORD, roles: "root", database: "admin" }, + {username: CM_MONGO_CONNECTOR_USERNAME, password: CM_MONGO_CONNECTOR_PASSWORD, roles: "readWrite", database: CM_DATABASE_NAME}, + {username: CM_MONGO_API_USERNAME, password: CM_MONGO_API_PASSWORD, roles: "readWrite", database: CM_DATABASE_NAME}, + {username: CM_MONGO_USER_USERNAME, password: CM_MONGO_USER_PASSWORD, roles: "read", database: CM_DATABASE_NAME}, +]; + + +// name -> collection name +// ttlField -> field to perform ttl on +// timeField -> field to index for time queries +// intersectionField -> field containing intersection id for id queries +const collections = [ + + // ODE Json data + {name: "OdeDriverAlertJson", ttlField: "recordGeneratedAt", "timeField": "metadata.odeReceivedAt", intersectionField: null, rsuIP:"metadata.originIp"}, + {name: "OdeBsmJson", ttlField: "recordGeneratedAt", "timeField": "metadata.odeReceivedAt", intersectionField: null, rsuIP:"metadata.originIp"}, + {name: "OdeMapJson", ttlField: "recordGeneratedAt", "timeField": "metadata.odeReceivedAt", intersectionField: null, rsuIP:"metadata.originIp"}, + {name: "OdeSpatJson", ttlField: "recordGeneratedAt", "timeField": "metadata.odeReceivedAt", intersectionField: null, rsuIP:"metadata.originIp"}, + {name: "OdeSpatRxJson", ttlField: "recordGeneratedAt", "timeField": "metadata.odeReceivedAt", intersectionField: null, rsuIP:"metadata.originIp"}, + {name: "OdeSrmJson", ttlField: "recordGeneratedAt", "timeField": "metadata.odeReceivedAt", intersectionField: null, rsuIP:"metadata.originIp"}, + {name: "OdeSsmJson", ttlField: "recordGeneratedAt", "timeField": "metadata.odeReceivedAt", intersectionField: null, rsuIP:"metadata.originIp"}, + {name: "OdeTimJson", ttlField: "recordGeneratedAt", "timeField": "metadata.odeReceivedAt", intersectionField: null, rsuIP:"metadata.originIp"}, + {name: "OdeTimBroadcastJson", ttlField: "recordGeneratedAt", "timeField": "metadata.odeReceivedAt", intersectionField: null, rsuIP:"metadata.originIp"}, + {name: "OdeTIMCertExpirationTimeJson", ttlField: "recordGeneratedAt", "timeField": "metadata.odeReceivedAt", intersectionField: null, rsuIP:"metadata.originIp"}, + + // Ode Raw ASN + {name: "OdeRawEncodedBSMJson", ttlField: "recordGeneratedAt", "timeField": "BsmMessageContent.metadata.utctimestamp", intersectionField: null, rsuIP:"BsmMessageContent.metadata.originRsu"}, + {name: "OdeRawEncodedMAPJson", ttlField: "recordGeneratedAt", "timeField": "MapMessageContent.metadata.utctimestamp", intersectionField: null, rsuIP:"MapMessageContent.metadata.originRsu"}, + {name: "OdeRawEncodedSPATJson", ttlField: "recordGeneratedAt", "timeField": "SpatMessageContent.metadata.utctimestamp", intersectionField: null, rsuIP:"SpatMessageContent.metadata.originRsu"}, + {name: "OdeRawEncodedSRMJson", ttlField: "recordGeneratedAt", "timeField": "SrmMessageContent.metadata.utctimestamp", intersectionField: null, rsuIP:"SpatMessageContent.metadata.originRsu"}, + {name: "OdeRawEncodedSSMJson", ttlField: "recordGeneratedAt", "timeField": "SsmMessageContent.metadata.utctimestamp", intersectionField: null, rsuIP:"SpatMessageContent.metadata.originRsu"}, + {name: "OdeRawEncodedTIMJson", ttlField: "recordGeneratedAt", "timeField": "TimMessageContent.metadata.utctimestamp", intersectionField: null, rsuIP:"SpatMessageContent.metadata.originRsu"}, + + // GeoJson Converter Data + {name: "ProcessedMap", ttlField: "recordGeneratedAt", timeField: "properties.timeStamp", intersectionField: "properties.intersectionId"}, + {name: "ProcessedSpat", ttlField: "recordGeneratedAt", timeField: "odeReceivedAt", intersectionField: "intersectionId"}, + + // Conflict Monitor Events + { name: "CmStopLineStopEvent", ttlField: "eventGeneratedAt", timeField: "eventGeneratedAt", intersectionField: "intersectionID" }, + { name: "CmStopLinePassageEvent", ttlField: "eventGeneratedAt", timeField: "eventGeneratedAt", intersectionField: "intersectionID" }, + { name: "CmIntersectionReferenceAlignmentEvents", ttlField: "eventGeneratedAt", timeField: "eventGeneratedAt", intersectionField: "intersectionID" }, + { name: "CmSignalGroupAlignmentEvents", ttlField: "eventGeneratedAt", timeField: "eventGeneratedAt", intersectionField: "intersectionID" }, + { name: "CmConnectionOfTravelEvent", ttlField: "eventGeneratedAt", timeField: "eventGeneratedAt", intersectionField: "intersectionID" }, + { name: "CmSignalStateConflictEvents", ttlField: "eventGeneratedAt", timeField: "eventGeneratedAt", intersectionField: "intersectionID" }, + { name: "CmLaneDirectionOfTravelEvent", ttlField: "eventGeneratedAt", timeField: "eventGeneratedAt", intersectionField: "intersectionID" }, + { name: "CmSpatTimeChangeDetailsEvent", ttlField: "eventGeneratedAt", timeField: "eventGeneratedAt", intersectionField: "intersectionID" }, + { name: "CmSpatMinimumDataEvents", ttlField: "eventGeneratedAt", timeField: "eventGeneratedAt", intersectionField: "intersectionID" }, + { name: "CmMapBroadcastRateEvents", ttlField: "eventGeneratedAt", timeField: "eventGeneratedAt", intersectionField: "intersectionID" }, + { name: "CmMapMinimumDataEvents", ttlField: "eventGeneratedAt", timeField: "eventGeneratedAt", intersectionField: "intersectionID" }, + { name: "CmSpatBroadcastRateEvents", ttlField: "eventGeneratedAt", timeField: "eventGeneratedAt", intersectionField: "intersectionID" }, + { name: "CmBsmEvents", ttlField: "recordGeneratedAt", timeField: "recordGeneratedAt", intersectionField: "intersectionID" }, + + // Conflict Monitor Assessments + { name: "CmLaneDirectionOfTravelAssessment", ttlField: "assessmentGeneratedAt", timeField: "assessmentGeneratedAt", intersectionField: "intersectionID" }, + { name: "CmConnectionOfTravelAssessment", ttlField: "assessmentGeneratedAt", timeField: "assessmentGeneratedAt", intersectionField: "intersectionID" }, + { name: "CmSignalStateEventAssessment", ttlField: "assessmentGeneratedAt", timeField: "assessmentGeneratedAt", intersectionField: "intersectionID" }, + { name: "CmStopLineStopAssessment", ttlField: "assessmentGeneratedAt", timeField: "assessmentGeneratedAt", intersectionField: "intersectionID" }, + + // Conflict Monitor Notifications + { name: "CmSpatTimeChangeDetailsNotification", ttlField: "notificationGeneratedAt", timeField: "notificationGeneratedAt", intersectionField: "intersectionID" }, + { name: "CmLaneDirectionOfTravelNotification", ttlField: "notificationGeneratedAt", timeField: "notificationGeneratedAt", intersectionField: "intersectionID" }, + { name: "CmConnectionOfTravelNotification", ttlField: "notificationGeneratedAt", timeField: "notificationGeneratedAt", intersectionField: "intersectionID" }, + { name: "CmAppHealthNotifications", ttlField: "notificationGeneratedAt", timeField: "notificationGeneratedAt", intersectionField: "intersectionID" }, + { name: "CmSignalStateConflictNotification", ttlField: "notificationGeneratedAt", timeField: "notificationGeneratedAt", intersectionField: "intersectionID" }, + { name: "CmSignalGroupAlignmentNotification", ttlField: "notificationGeneratedAt", timeField: "notificationGeneratedAt", intersectionField: "intersectionID" }, + { name: "CmStopLinePassageNotification", ttlField: "notificationGeneratedAt", timeField: "notificationGeneratedAt", intersectionField: "intersectionID" }, + { name: "CmStopLineStopNotification", ttlField: "notificationGeneratedAt", timeField: "notificationGeneratedAt", intersectionField: "intersectionID" }, + { name: "CmNotification", ttlField: "notificationGeneratedAt", timeField: "notificationGeneratedAt", intersectionField: "intersectionID" } +]; + +try{ + db.getMongo().setReadPref("primaryPreferred"); + db = db.getSiblingDB("ConflictMonitor"); + db.getMongo().setReadPref("primaryPreferred"); + var isMaster = db.isMaster(); + if (isMaster.primary) { + console.log("Connected to the primary replica set member."); + } else { + console.log("Not connected to the primary replica set member. Current node: " + isMaster.host); + } +} +catch(err){ + console.log("Could not switch DB to Sibling DB"); + console.log(err); +} + +// Create Users in Database +for(user of users){ + createUser(user); +} + +// Wait for the collections to exist in mongo before trying to create indexes on them +let missing_collection_count; +do { + try { + missing_collection_count = 0; + const collectionNames = db.getCollectionNames(); + for (collection of collections) { + // Create Collection if it doesn't exist + let created = false; + if(!collectionNames.includes(collection.name)){ + created = createCollection(collection); + // created = true; + }else{ + created = true; + } + + if(created){ + createTTLIndex(collection); + createTimeIntersectionIndex(collection); + createTimeRsuIpIndex(collection); + createTimeIndex(collection); + }else{ + missing_collection_count++; + console.log("Collection " + collection.name + " does not exist yet"); + } + } + if (missing_collection_count > 0) { + console.log("Waiting on " + missing_collection_count + " collections to be created...will try again in " + retryMilliseconds + " ms"); + sleep(retryMilliseconds); + } + } catch (err) { + console.log("Error while setting up TTL indexs in collections"); + console.log(rs.status()); + console.error(err); + sleep(retryMilliseconds); + } +} while (missing_collection_count > 0); + +console.log("Finished Creating All TTL indexes"); + +function createUser(user){ + try{ + console.log("Creating User: " + user.username + " with Permissions: " + user.roles); + db.createUser( + { + user: user.username, + pwd: user.password, + roles: [ + { role: user.roles, db: user.database }, + ] + }); + + }catch (err){ + console.log(err); + console.log("Unable to Create User. Perhaps the User already exists."); + } +} + +function createCollection(collection){ + try { + db.createCollection(collection.name); + return true; + } catch (err) { + console.log("Unable to Create Collection: " + collection.name); + console.log(err); + return false; + } +} + +// Create TTL Indexes +function createTTLIndex(collection) { + try{ + if(collection.hasOwnProperty("ttlField") && collection.ttlField != null){ + const ttlField = collection.ttlField; + const collectionName = collection.name; + + let indexJson = {}; + indexJson[ttlField] = 1; + + if (ttlIndexExists(collection)) { + db.runCommand({ + "collMod": collectionName, + "index": { + keyPattern: indexJson, + expireAfterSeconds: expireSeconds + } + }); + console.log("Updated TTL index for " + collectionName + " using the field: " + ttlField + " as the timestamp"); + }else{ + db[collectionName].createIndex(indexJson, + {expireAfterSeconds: expireSeconds} + ); + console.log("Created TTL index for " + collectionName + " using the field: " + ttlField + " as the timestamp"); + } + } + } catch(err){ + console.log("Failed to Create or Update index for " + collectionName + "using the field: " + ttlField + " as the timestamp"); + } +} + +function createTimeIndex(collection){ + if(timeIndexExists(collection)){ + // Skip if Index already Exists + return; + } + + if(collection.hasOwnProperty("timeField") && collection.timeField != null){ + const collectionName = collection.name; + const timeField = collection.timeField; + console.log("Creating Time Index for " + collectionName); + + var indexJson = {}; + indexJson[timeField] = -1; + + try { + db[collectionName].createIndex(indexJson); + console.log("Created Time Intersection index for " + collectionName + " using the field: " + timeField + " as the timestamp"); + } catch (err) { + db.runCommand({ + "collMod": collectionName, + "index": { + keyPattern: indexJson + } + }); + console.log("Updated Time index for " + collectionName + " using the field: " + timeField + " as the timestamp"); + } + } +} + +function createTimeRsuIpIndex(){ + if(timeRsuIpIndexExists(collection)){ + // Skip if Index already Exists + return; + } + + if(collection.hasOwnProperty("timeField") && collection.timeField != null && collection.hasOwnProperty("rsuIP") && collection.rsuIP != null){ + const collectionName = collection.name; + const timeField = collection.timeField; + const rsuIP = collection.rsuIP; + console.log("Creating Time rsuIP Index for " + collectionName); + + var indexJson = {}; + indexJson[rsuIP] = -1; + indexJson[timeField] = -1; + + + try { + db[collectionName].createIndex(indexJson); + console.log("Created Time rsuIP Intersection index for " + collectionName + " using the field: " + timeField + " as the timestamp and : " + rsuIP+" as the rsuIP"); + } catch (err) { + db.runCommand({ + "collMod": collectionName, + "index": { + keyPattern: indexJson + } + }); + console.log("Updated Time rsuIP index for " + collectionName + " using the field: " + timeField + " as the timestamp and : " + rsuIP+" as the rsuIP"); + } + } +} + + +function createTimeIntersectionIndex(collection){ + if(timeIntersectionIndexExists(collection)){ + // Skip if Index already Exists + return; + } + + if(collection.hasOwnProperty("timeField") && collection.timeField != null && collection.hasOwnProperty("intersectionField") && collection.intersectionField != null){ + const collectionName = collection.name; + const timeField = collection.timeField; + const intersectionField = collection.intersectionField; + console.log("Creating time intersection index for " + collectionName); + + var indexJson = {}; + indexJson[intersectionField] = -1; + indexJson[timeField] = -1; + + + try { + db[collectionName].createIndex(indexJson); + console.log("Created time intersection index for " + collectionName + " using the field: " + timeField + " as the timestamp and : " + intersectionField + " as the rsuIP"); + } catch (err) { + db.runCommand({ + "collMod": collectionName, + "index": { + keyPattern: indexJson + } + }); + console.log("Updated time intersection index for " + collectionName + " using the field: " + timeField + " as the timestamp and : " + intersectionField + " as the rsuIP"); + } + } +} + +function ttlIndexExists(collection) { + return db[collection.name].getIndexes().find((idx) => idx.hasOwnProperty("expireAfterSeconds")) !== undefined; +} + +function timeIntersectionIndexExists(collection){ + return db[collection.name].getIndexes().find((idx) => idx.name == collection.intersectionField + "_-1_" + collection.timeField + "_-1") !== undefined; +} + +function timeRsuIpIndexExists(collection){ + return db[collection.name].getIndexes().find((idx) => idx.name == collection.rsuIP + "_-1_" + collection.timeField + "_-1") !== undefined; +} + +function timeIndexExists(collection){ + return db[collection.name].getIndexes().find((idx) => idx.name == collection.timeField + "_-1") !== undefined; +} diff --git a/docker/mongo/keyfile.txt b/docker/mongo/keyfile.txt new file mode 100644 index 000000000..a32a4347a --- /dev/null +++ b/docker/mongo/keyfile.txt @@ -0,0 +1 @@ +1234567890 diff --git a/docker/mongo/mongo_initialize.sh b/docker/mongo/mongo_initialize.sh new file mode 100644 index 000000000..9745b3ae4 --- /dev/null +++ b/docker/mongo/mongo_initialize.sh @@ -0,0 +1,12 @@ +#!/bin/bash + +# echo "Running mongo_initialize.sh" + +# # connect_string="$1" +# # echo "connect_string = ${connect_string}" +# # cd /scripts +# # ls + +# mongosh mongodb://${DB_HOST_IP}:27017/ConflictMonitor?replicaSet=rs0 create_indexes.txt + +# # $SHELL diff --git a/release-compose.example.env b/release-compose.example.env index 1e10f0b04..4b432a12e 100644 --- a/release-compose.example.env +++ b/release-compose.example.env @@ -408,3 +408,35 @@ AEM_LOG_LEVEL=INFO ADM_LOG_TO_FILE=false ADM_LOG_TO_CONSOLE=true ADM_LOG_LEVEL=INFO + +################################################################## +### jpo-conflictvisualizer cvmanager api environment variables ### +################################################################## +# Set the HOST IP of where the containers are running +KAFKA_BROKER_IP= +DOCKER_HOST_IP= +KAFKA_BROKER_PORT=9092 + +# Set to the directory where the source code is. Generally the location of this file. +DOCKER_HOST_DIR= + +KEYCLOAK_REALM= +MAPBOX_TOKEN= +MAPBOX_STYLE_URL= + +KEYCLOAK_ADMIN= +KEYCLOAK_ADMIN_PASSWORD= + +# May be different from docker host ip for production deployments. Change as appropriate. +GUI_SERVER_URL=http://${DOCKER_HOST_IP}:3000 +AUTH_SERVER_URL=http://${DOCKER_HOST_IP}:8084 +API_SERVER_URL=http://${DOCKER_HOST_IP}:8081 +CM_SERVER_URL=http://${DOCKER_HOST_IP}:8082 +API_WS_URL=ws://${DOCKER_HOST_IP}:8081 + +DB_HOST_IP=${DOCKER_HOST_IP} +DB_HOST_PORT=27017 + + +CM_MONGO_API_PASSWORD=api +CM_MONGO_API_USERNAME=api diff --git a/release-compose.yml b/release-compose.yml index be4408c84..613305b26 100644 --- a/release-compose.yml +++ b/release-compose.yml @@ -168,9 +168,6 @@ services: conflictvisualizer-api: image: us-central1-docker.pkg.dev/cdot-oim-cv-dev/jpo-conflictvisualizer-api-cvmanager/jpo-conflictvisualizer-api-cvmanager:v1.0.0 - build: - context: api - dockerfile: Dockerfile ports: - '8089:8081' restart: always @@ -300,3 +297,5 @@ services: volumes: mongodb_data_container: + pgdb: + driver: local From 55919226c538a16f969e928de6c59dc5b4496bc7 Mon Sep 17 00:00:00 2001 From: jacob6838 Date: Sun, 26 May 2024 13:25:26 -0600 Subject: [PATCH 032/189] Fixing formatting on map view --- webapp/src/components/map/map-component.tsx | 2 +- webapp/src/pages/IntersectionMapView.tsx | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/webapp/src/components/map/map-component.tsx b/webapp/src/components/map/map-component.tsx index 55f379069..c43acdbe4 100644 --- a/webapp/src/components/map/map-component.tsx +++ b/webapp/src/components/map/map-component.tsx @@ -301,7 +301,7 @@ const MapTab = (props: MAP_PROPS) => { }, [liveDataRestart]) return ( - +
Date: Wed, 29 May 2024 15:54:45 -0600 Subject: [PATCH 033/189] Updating docker-compose with cm kafka and ode --- docker-compose-no-cm.yml | 148 +++++++ docker-compose.yml | 263 +++++++++++ release-compose.example.env | 442 ------------------- release-compose.yml | 301 ------------- sample.env | 166 ++++++- services/conflictmonitor-release-compose.yml | 158 ------- 6 files changed, 566 insertions(+), 912 deletions(-) create mode 100644 docker-compose-no-cm.yml delete mode 100644 release-compose.example.env delete mode 100644 release-compose.yml delete mode 100644 services/conflictmonitor-release-compose.yml diff --git a/docker-compose-no-cm.yml b/docker-compose-no-cm.yml new file mode 100644 index 000000000..43752a3f8 --- /dev/null +++ b/docker-compose-no-cm.yml @@ -0,0 +1,148 @@ +version: '3.9' +services: + cvmanager_api: + build: + context: services + dockerfile: Dockerfile.api + image: jpo_cvmanager_api:latest + restart: always + extra_hosts: + ${WEBAPP_DOMAIN}: ${WEBAPP_HOST_IP} + ${KEYCLOAK_DOMAIN}: ${KC_HOST_IP} + ports: + - '8081:5000' + environment: + PG_DB_HOST: ${PG_DB_HOST} + PG_DB_USER: ${PG_DB_USER} + PG_DB_PASS: ${PG_DB_PASS} + PG_DB_NAME: postgres + INSTANCE_CONNECTION_NAME: ${INSTANCE_CONNECTION_NAME} + + MONGO_DB_URI: ${MONGO_DB_URI} + MONGO_DB_NAME: ${MONGO_DB_NAME} + + COUNTS_MSG_TYPES: ${COUNTS_MSG_TYPES} + GOOGLE_APPLICATION_CREDENTIALS: '/google/gcp_credentials.json' + + GEO_DB_NAME: ${GEO_DB_NAME} + SSM_DB_NAME: ${SSM_DB_NAME} + SRM_DB_NAME: ${SRM_DB_NAME} + + MAX_GEO_QUERY_RECORDS: ${MAX_GEO_QUERY_RECORDS} + + FIRMWARE_MANAGER_ENDPOINT: ${FIRMWARE_MANAGER_ENDPOINT} + + WZDX_API_KEY: ${WZDX_API_KEY} + WZDX_ENDPOINT: ${WZDX_ENDPOINT} + + CORS_DOMAIN: ${CORS_DOMAIN} + KEYCLOAK_ENDPOINT: http://${KEYCLOAK_DOMAIN}:8084/ + KEYCLOAK_REALM: ${KEYCLOAK_REALM} + KEYCLOAK_API_CLIENT_ID: ${KEYCLOAK_API_CLIENT_ID} + KEYCLOAK_API_CLIENT_SECRET_KEY: ${KEYCLOAK_API_CLIENT_SECRET_KEY} + + CSM_EMAIL_TO_SEND_FROM: ${CSM_EMAIL_TO_SEND_FROM} + CSM_EMAIL_APP_USERNAME: ${CSM_EMAIL_APP_USERNAME} + CSM_EMAIL_APP_PASSWORD: ${CSM_EMAIL_APP_PASSWORD} + CSM_EMAILS_TO_SEND_TO: ${CSM_EMAILS_TO_SEND_TO} + CSM_TARGET_SMTP_SERVER_ADDRESS: ${CSM_TARGET_SMTP_SERVER_ADDRESS} + CSM_TARGET_SMTP_SERVER_PORT: ${CSM_TARGET_SMTP_SERVER_PORT} + + TIMEZONE: ${TIMEZONE} + LOGGING_LEVEL: ${API_LOGGING_LEVEL} + volumes: + - ${GOOGLE_APPLICATION_CREDENTIALS}:/google/gcp_credentials.json + logging: + options: + max-size: '10m' + max-file: '5' + + cvmanager_webapp: + build: + context: webapp + dockerfile: Dockerfile + args: + API_URI: http://${WEBAPP_DOMAIN}:8081 + MAPBOX_TOKEN: ${MAPBOX_TOKEN} + KEYCLOAK_HOST_URL: http://${KEYCLOAK_DOMAIN}:8084/ + COUNT_MESSAGE_TYPES: ${COUNTS_MSG_TYPES} + VIEWER_MESSAGE_TYPES: ${VIEWER_MSG_TYPES} + DOT_NAME: ${DOT_NAME} + MAPBOX_INIT_LATITUDE: ${MAPBOX_INIT_LATITUDE} + MAPBOX_INIT_LONGITUDE: ${MAPBOX_INIT_LONGITUDE} + MAPBOX_INIT_ZOOM: ${MAPBOX_INIT_ZOOM} + CVIZ_MAPBOX_TOKEN: ${CVIZ_MAPBOX_TOKEN} + CVIZ_MAPBOX_STYLE_URL: ${CVIZ_MAPBOX_STYLE_URL} + CVIZ_API_SERVER_URL: ${CVIZ_API_SERVER_URL} + CVIZ_API_WS_URL: ${CVIZ_API_WS_URL} + image: jpo_cvmanager_webapp:latest + restart: always + depends_on: + cvmanager_keycloak: + condition: service_healthy + extra_hosts: + ${WEBAPP_DOMAIN}: ${WEBAPP_HOST_IP} + ${KEYCLOAK_DOMAIN}: ${KC_HOST_IP} + ports: + - '80:80' + logging: + options: + max-size: '10m' + + cvmanager_postgres: + image: postgis/postgis:15-master + restart: always + ports: + - '5432:5432' + environment: + POSTGRES_USER: ${PG_DB_USER} + POSTGRES_PASSWORD: ${PG_DB_PASS} + volumes: + - pgdb:/var/lib/postgresql/data + - ./resources/sql_scripts:/docker-entrypoint-initdb.d + logging: + options: + max-size: '10m' + + cvmanager_keycloak: + build: + context: ./resources/keycloak + dockerfile: Dockerfile + args: + KEYCLOAK_LOGIN_THEME_NAME: ${KEYCLOAK_LOGIN_THEME_NAME}.jar + image: jpo_cvmanager_keycloak:latest + restart: always + depends_on: + - cvmanager_postgres + extra_hosts: + ${WEBAPP_DOMAIN}: ${WEBAPP_HOST_IP} + ${KEYCLOAK_DOMAIN}: ${KC_HOST_IP} + ports: + - '8084:8080' + environment: + KEYCLOAK_ADMIN: ${KEYCLOAK_ADMIN} + KEYCLOAK_ADMIN_PASSWORD: ${KEYCLOAK_ADMIN_PASSWORD} + WEBAPP_ORIGIN: http://${WEBAPP_DOMAIN} + WEBAPP_CM_ORIGIN: http://${WEBAPP_CM_DOMAIN} + KC_HEALTH_ENABLED: true + KC_DB: postgres + KC_DB_URL: jdbc:postgresql://${PG_DB_HOST}/postgres?currentSchema=keycloak + KC_DB_USERNAME: ${PG_DB_USER} + KC_DB_PASSWORD: ${PG_DB_PASS} + KC_HOSTNAME: ${KEYCLOAK_DOMAIN} + KEYCLOAK_API_CLIENT_SECRET_KEY: ${KEYCLOAK_API_CLIENT_SECRET_KEY} + KEYCLOAK_CM_API_CLIENT_SECRET_KEY: ${KEYCLOAK_CM_API_CLIENT_SECRET_KEY} + GOOGLE_CLIENT_ID: ${GOOGLE_CLIENT_ID} + GOOGLE_CLIENT_SECRET: ${GOOGLE_CLIENT_SECRET} + command: + - start-dev + - --log-level=${KC_LOGGING_LEVEL} + - --import-realm + - --spi-theme-welcome-theme=custom-welcome + logging: + options: + max-size: '10m' + +volumes: + pgdb: + driver: local diff --git a/docker-compose.yml b/docker-compose.yml index 42d4ec0b5..d931ac9de 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -143,6 +143,269 @@ services: options: max-size: '10m' + zookeeper: + image: wurstmeister/zookeeper + ports: + - '2181:2181' + logging: + options: + max-size: '10m' + max-file: '5' + + kafka: + image: wurstmeister/kafka + ports: + - '9092:9092' + environment: + DOCKER_HOST_IP: ${DOCKER_HOST_IP} + ZK: ${DOCKER_HOST_IP}:2181 + KAFKA_ADVERTISED_HOST_NAME: ${DOCKER_HOST_IP} + KAFKA_ZOOKEEPER_CONNECT: zookeeper:2181 + KAFKA_AUTO_CREATE_TOPICS_ENABLE: 'true' + KAFKA_CREATE_TOPICS: 'topic.OdeBsmPojo:1:1,topic.OdeSpatTxPojo:1:1,topic.OdeSpatPojo:1:1,topic.OdeSpatJson:1:1,topic.FilteredOdeSpatJson:1:1,topic.OdeSpatRxJson:1:1,topic.OdeSpatRxPojo:1:1,topic.OdeBsmJson:1:1,topic.FilteredOdeBsmJson:1:1,topic.OdeTimJson:1:1,topic.OdeTimBroadcastJson:1:1,topic.J2735TimBroadcastJson:1:1,topic.OdeDriverAlertJson:1:1,topic.Asn1DecoderInput:1:1,topic.Asn1DecoderOutput:1:1,topic.Asn1EncoderInput:1:1,topic.Asn1EncoderOutput:1:1,topic.SDWDepositorInput:1:1,topic.OdeTIMCertExpirationTimeJson:1:1,topic.OdeRawEncodedBSMJson:1:1,topic.OdeRawEncodedSPATJson:1:1,topic.OdeRawEncodedTIMJson:1:1,topic.OdeRawEncodedMAPJson:1:1,topic.OdeMapTxPojo:1:1,topic.OdeMapJson:1:1,topic.OdeRawEncodedSSMJson:1:1,topic.OdeSsmPojo:1:1,topic.OdeSsmJson:1:1,topic.OdeRawEncodedSRMJson:1:1,topic.OdeSrmTxPojo:1:1,topic.OdeSrmJson:1:1,topic.OdeRawEncodedPSMJson:1:1,topic.OdePsmTxPojo:1:1,topic.OdePsmJson:1:1' + KAFKA_DELETE_TOPIC_ENABLED: 'true' + KAFKA_CLEANUP_POLICY: 'delete' # delete old logs + KAFKA_LOG_RETENTION_HOURS: 2 + KAFKA_GROUP_INITIAL_REBALANCE_DELAY_MS: 3000 + KAFKA_RETENTION_MS: 7200000 # delete old logs after 2 hours + KAFKA_SEGMENT_MS: + 7200000 # roll segment logs every 2 hours. + # This configuration controls the period of time after + # which Kafka will force the log to roll even if the segment + # file isn't full to ensure that retention can delete or compact old data. + depends_on: + - zookeeper + volumes: + - ${DOCKER_SHARED_VOLUME}/var/run/docker.sock:/var/run/docker.sock + logging: + options: + max-size: '10m' + max-file: '5' + + ode: + image: usdotjpoode/jpo-ode:develop + ports: + - '8080:8080' + - '9090:9090' + - '46753:46753/udp' + - '46800:46800/udp' + - '47900:47900/udp' + - '44900:44900/udp' + - '44910:44910/udp' + - '44920:44920/udp' + - '44930:44930/udp' + - '44940:44940/udp' + - '5555:5555/udp' + - '6666:6666/udp' + environment: + DOCKER_HOST_IP: ${DOCKER_HOST_IP} + ZK: ${DOCKER_HOST_IP}:2181 + ODE_SECURITY_SVCS_SIGNATURE_URI: ${ODE_SECURITY_SVCS_SIGNATURE_URI} + ODE_RSU_USERNAME: ${ODE_RSU_USERNAME} + ODE_RSU_PASSWORD: ${ODE_RSU_PASSWORD} + depends_on: + - kafka + volumes: + - ${DOCKER_SHARED_VOLUME}:/jpo-ode + - ${DOCKER_SHARED_VOLUME}/uploads:/home/uploads + logging: + options: + max-size: '10m' + max-file: '5' + + geojsonconverter: + image: usdotjpoode/geojsonconverter:develop + environment: + DOCKER_HOST_IP: ${DOCKER_HOST_IP} + geometry.output.mode: ${GEOMETRY_OUTPUT_MODE} + spring.kafka.bootstrap-servers: ${DOCKER_HOST_IP}:9092 + logging: + options: + max-size: '10m' + max-file: '5' + depends_on: + - ode + + conflictmonitor: + image: usdotjpoode/jpo-conflictmonitor:develop + restart: always + ports: + - '8082:8082' + environment: + DOCKER_HOST_IP: ${DOCKER_HOST_IP} + KAFKA_BROKER_IP: ${KAFKA_BROKER_IP} + DB_HOST_IP: ${DB_HOST_IP} + spring.kafka.bootstrap-servers: ${KAFKA_BROKER_IP}:9092 + healthcheck: + test: ['CMD', 'java', '-version'] + interval: 10s + timeout: 10s + retries: 20 + logging: + options: + max-size: '10m' + max-file: '5' + deploy: + resources: + limits: + memory: 3G + + conflictvisualizer-api: + image: us-central1-docker.pkg.dev/cdot-oim-cv-dev/jpo-conflictvisualizer-api-cvmanager/jpo-conflictvisualizer-api-cvmanager:v1.0.0 + ports: + - '8089:8081' + restart: always + environment: + AUTH_SERVER_URL: ${AUTH_SERVER_URL} + KEYCLOAK_ADMIN: ${KEYCLOAK_ADMIN} + KEYCLOAK_ADMIN_PASSWORD: ${KEYCLOAK_ADMIN_PASSWORD} + DB_HOST_IP: ${DB_HOST_IP} + DB_HOST_PORT: ${DB_HOST_PORT} + SPRING_KAFKA_BOOTSTRAPSERVERS: ${KAFKA_BROKER_IP}:${KAFKA_BROKER_PORT} + CM_SERVER_URL: ${CM_SERVER_URL} + load: 'false' + logging: + options: + max-size: '10m' + max-file: '5' + depends_on: + cvmanager_keycloak: + condition: service_healthy + mongodb_container: + condition: service_healthy + + deduplicator: + build: + context: /mnt/c/Users/rando/Github/jpo-conflictmonitor + dockerfile: Dedup_Dockerfile + image: jpo-deduplicator:latest + restart: always + environment: + DOCKER_HOST_IP: ${DOCKER_HOST_IP} + KAFKA_BROKER_IP: ${KAFKA_BROKER_IP} + spring.kafka.bootstrap-servers: ${KAFKA_BROKER_IP}:9092 + healthcheck: + test: ['CMD', 'java', '-version'] + interval: 10s + timeout: 10s + retries: 20 + logging: + options: + max-size: '10m' + max-file: '5' + deploy: + resources: + limits: + memory: 3G + + mongodb_container: + image: mongo:6 + container_name: jpo-conflictmonitor-mongodb-container + restart: always + environment: + - MONGO_REPLICA_SET_NAME=rs0 + - DB_HOST_IP=${DB_HOST_IP} + - MONGO_INITDB_ROOT_USERNAME=${MONGO_INITDB_ROOT_USERNAME} + - MONGO_INITDB_ROOT_PASSWORD=${MONGO_INITDB_ROOT_PASSWORD} + - CM_MONGO_CONNECTOR_USERNAME=${CM_MONGO_CONNECTOR_USERNAME} + - CM_MONGO_CONNECTOR_PASSWORD=${CM_MONGO_CONNECTOR_PASSWORD} + - CM_MONGO_API_USERNAME=${CM_MONGO_API_USERNAME} + - CM_MONGO_API_PASSWORD=${CM_MONGO_API_PASSWORD} + - CM_MONGO_USER_USERNAME=${CM_MONGO_USER_USERNAME} + - CM_MONGO_USER_PASSWORD=${CM_MONGO_USER_PASSWORD} + ports: + - '27017:27017' + volumes: + - mongodb_data_container:/data/db + - ./docker/mongo/a_init_replicas.js:/docker-entrypoint-initdb.d/a_init_replicas.js + - ./docker/mongo/b_create_indexes.js:/docker-entrypoint-initdb.d/b_create_indexes.js + healthcheck: + test: | + test $$(mongosh --username ${MONGO_INITDB_ROOT_USERNAME} --password ${MONGO_INITDB_ROOT_PASSWORD} --quiet --eval "try { rs.initiate({ _id: 'rs0', members: [{ _id: 0, host: '${DB_HOST_IP}' }] }).ok } catch (_) { rs.status().ok }") -eq 1 + interval: 10s + start_period: 30s + entrypoint: + - bash + - -c + - | + openssl rand -base64 741 > /data/keyfile.txt + chmod 600 /data/keyfile.txt + chown 999:999 /data/keyfile.txt + exec docker-entrypoint.sh $$@ + + command: ['mongod', '--replSet', 'rs0', '--bind_ip_all', '--keyFile', '/data/keyfile.txt'] + logging: + options: + max-size: '10m' + max-file: '5' + deploy: + resources: + limits: + memory: 3G + + connect: + image: cp-kafka-connect:6.1.9 + build: + context: ./docker/connect + dockerfile: Dockerfile + container_name: jpo-conflictmonitor-kafka-connect + restart: always + ports: + - '8083:8083' + depends_on: + mongodb_container: + condition: service_healthy + environment: + DOCKER_HOST_IP: ${DOCKER_HOST_IP} + DB_HOST_IP: ${DB_HOST_IP} + CONNECT_BOOTSTRAP_SERVERS: ${KAFKA_BROKER_IP}:9092 + CONNECT_REST_ADVERTISED_HOST_NAME: connect + CONNECT_REST_PORT: 8083 + CONNECT_GROUP_ID: compose-connect-group + CONNECT_CONFIG_STORAGE_TOPIC: CmConnectConfigs + CONNECT_CONFIG_STORAGE_REPLICATION_FACTOR: 1 + CONNECT_CONFIG_STORAGE_CLEANUP_POLICY: compact + CONNECT_OFFSET_FLUSH_INTERVAL_MS: 10000 + CONNECT_OFFSET_STORAGE_TOPIC: CmConnectOffsets + CONNECT_OFFSET_STORAGE_REPLICATION_FACTOR: 1 + CONNECT_OFFSET_STORAGE_CLEANUP_POLICY: compact + CONNECT_STATUS_STORAGE_TOPIC: CmConnectStatus + CONNECT_STATUS_STORAGE_CLEANUP_POLICY: compact + CONNECT_STATUS_STORAGE_REPLICATION_FACTOR: 1 + CONNECT_KEY_CONVERTER: 'org.apache.kafka.connect.json.JsonConverter' + CONNECT_VALUE_CONVERTER: 'org.apache.kafka.connect.json.JsonConverter' + CONNECT_INTERNAL_KEY_CONVERTER: 'org.apache.kafka.connect.json.JsonConverter' + CONNECT_INTERNAL_VALUE_CONVERTER: 'org.apache.kafka.connect.json.JsonConverter' + CONNECT_LOG4J_ROOT_LOGLEVEL: 'ERROR' + CONNECT_LOG4J_LOGGERS: 'org.apache.kafka.connect.runtime.rest=ERROR,org.reflections=ERROR,com.mongodb.kafka=ERROR' + CONNECT_PLUGIN_PATH: /usr/share/confluent-hub-components + CONNECT_ZOOKEEPER_CONNECT: 'zookeeper:2181' + CM_MONGO_CONNECTOR_USERNAME: ${CM_MONGO_CONNECTOR_USERNAME} + CM_MONGO_CONNECTOR_PASSWORD: ${CM_MONGO_CONNECTOR_PASSWORD} + logging: + options: + max-size: '10m' + max-file: '5' + command: + - bash + - -c + - | + /etc/confluent/docker/run & + echo "Waiting for Kafka Connect to start listening on kafka-connect ❳" + while [ $$(curl -s -o /dev/null -w %{http_code} http://${KAFKA_CONNECT_IP}:8083/connectors) -eq 000 ] ; do + echo -e $$(date) " Kafka Connect listener HTTP state: " $$(curl -s -o /dev/null -w %{http_code} http://${KAFKA_CONNECT_IP}:8083/connectors) " (waiting for 200)" + sleep 5 + done + sleep 10 + echo -e "\n--\n+> Creating Kafka Connect MongoDB sink" + bash /scripts/connect_start.sh "mongodb://${CM_MONGO_CONNECTOR_USERNAME}:${CM_MONGO_CONNECTOR_USERNAME}@${DOCKER_HOST_IP}:27017/?authMechanism=DEFAULT&authSource=ConflictMonitor&replicaSet=rs0" + sleep infinity + deploy: + resources: + limits: + memory: 3G + volumes: + mongodb_data_container: pgdb: driver: local diff --git a/release-compose.example.env b/release-compose.example.env deleted file mode 100644 index 4b432a12e..000000000 --- a/release-compose.example.env +++ /dev/null @@ -1,442 +0,0 @@ -# Environment variable file for the JPO CV Manager API deployment through docker-compose -DOCKER_HOST_IP= -# Note if debugging, this webapp host IP should be set to the IP of the host machine running the webapp (localhost) -WEBAPP_HOST_IP=${DOCKER_HOST_IP} -# Note if using WEBAPP_DOMAIN for the docker-compose-webapp-deployment.yml file you will need to include http:// or https:// -WEBAPP_DOMAIN=cvmanager.local.com -WEBAPP_CM_DOMAIN=cimms.local.com -KC_HOST_IP=${DOCKER_HOST_IP} - -# Firmware Manager connectivity in the format 'http://endpoint:port' -FIRMWARE_MANAGER_ENDPOINT=http://${DOCKER_HOST_IP}:8089 - -# Allowed CORS domain for accessing the CV Manager API from (set to the web application hostname) -# Make sure to include http:// or https:// -# If using docker then this value should be set to: http://${WEBAPP_HOST_IP}:3000 -# If running the webapp using npm then set it to: http://localhost:3000 -# Leave as * to allow all domains access -CORS_DOMAIN = * - -# PostgreSQL Database connection information -# this value may need to follow with the webapp host if debugging the applications -PG_DB_HOST=${DOCKER_HOST_IP}:5432 -PG_DB_NAME=postgres -PG_DB_USER=postgres -# If the PG_DB_PASS variable has special characters, make sure to wrap it in single quotes -PG_DB_PASS= - -# If connecting to PGDB over websocket: -INSTANCE_CONNECTION_NAME= - -# Keycloak authentication credentials -KEYCLOAK_ADMIN=admin -KEYCLOAK_ADMIN_PASSWORD= - -# Keycloak Parameters - to generate secret key use a password generator such as: https://www.avast.com/en-us/random-password-generator#pc and set the length to 32 -KEYCLOAK_REALM=cvmanager -KEYCLOAK_API_CLIENT_ID=cvmanager-api -KEYCLOAK_API_CLIENT_SECRET_KEY= -KEYCLOAK_CM_API_CLIENT_SECRET_KEY= -KEYCLOAK_LOGIN_THEME_NAME=sample_theme -# Note if using KEYCLOAK_DOMAIN for the docker-compose-webapp-deployment.yml file you will need to include http:// or https:// -KEYCLOAK_DOMAIN=cvmanager.auth.com - - -# GCP OAuth2.0 client ID for SSO authentication in keycloak - if not specified the google SSO will not be functional -GOOGLE_CLIENT_ID= -GOOGLE_CLIENT_SECRET= - -# If "BIGQUERY", set the location of the GCP service account key attached as a volume -GOOGLE_APPLICATION_CREDENTIALS='./resources/google/sample_gcp_service_account.json' - -# If "MONGODB", set the MongoDB variables -MONGO_DB_URI= -MONGO_DB_NAME="ode" - -# Set these variables if using either "MONGODB" or "BIGQUERY" -# COUNTS_MSG_TYPES: Comma separated list of message types. -# COUNTS_MSG_TYPES must be set for the counts menu to correctly populate when building an image for deployment -COUNTS_MSG_TYPES='BSM,SSM,SPAT,SRM,MAP' -VIEWER_MSG_TYPES='BSM' -GEO_DB_NAME='V2XGeoJson' -SSM_DB_NAME= -SRM_DB_NAME= - -# Specifies the maximum number of V2x messages returned from the geo_query_geo_data_mongo method before filtering occurs -MAX_GEO_QUERY_RECORDS= - -# WZDx API key and endpoint for pulling WZDx data into the CV Manager -WZDX_API_KEY= -WZDX_ENDPOINT= - -# Contact Support Menu Email Configuration -CSM_EMAIL_TO_SEND_FROM= -CSM_EMAIL_APP_USERNAME= -CSM_EMAIL_APP_PASSWORD= -CSM_EMAILS_TO_SEND_TO= -CSM_TARGET_SMTP_SERVER_ADDRESS= -CSM_TARGET_SMTP_SERVER_PORT= - -# Python timezone for the CV Manager (You can list pytz timezones with the command 'pytz.all_timezones') -TIMEZONE="US/Mountain" - -# Mapbox token for map rendering in the webapp -MAPBOX_TOKEN= -# DOT_NAME must be set for the DOT name to correctly populate when building an image for deployment -DOT_NAME="CDOT" -MAPBOX_INIT_LATITUDE="39.7392" -MAPBOX_INIT_LONGITUDE="-104.9903" -MAPBOX_INIT_ZOOM="10" - -GCP_PROJECT_ID = '' - -# --------------------------------------------------------------------- - -# Count Metric Addon: -ENABLE_EMAILER = 'True' - -# If ENABLE_EMAILER is 'True', set the following environment variables -DEPLOYMENT_TITLE = 'JPO-ODE' - -# SMTP REQUIRED VARIABLES -SMTP_SERVER_IP = '' -SMTP_USERNAME = '' -SMTP_PASSWORD = '' -SMTP_EMAIL = '' -# Multiple emails can be delimited by a ',' -SMTP_EMAIL_RECIPIENTS = 'test1@gmail.com,test2@gmail.com' - -# If ENABLE_EMAILER is 'False', set the following environment variables - -COUNT_MESSAGE_TYPES = 'bsm' -ODE_KAFKA_BROKERS = {DOCKER_HOST_IP}:9092 - -# EITHER "MONGODB" or "BIGQUERY" -COUNT_DESTINATION_DB = 'MONGODB' - -# MONGODB REQUIRED VARIABLES -INPUT_COUNTS_MONGO_COLLECTION_NAME = '' -OUTPUT_COUNTS_MONGO_COLLECTION_NAME = '' - -# BIGQUERY REQUIRED VARIABLES -KAFKA_BIGQUERY_TABLENAME = '' -# --------------------------------------------------------------------- - -# Firmware Manager Addon: -BLOB_STORAGE_PROVIDER=GCP -BLOB_STORAGE_BUCKET= -GCP_PROJECT= -# --------------------------------------------------------------------- - -# Geo-spatial message query Addon: -GEO_INPUT_COLLECTIONS='OdeBsmJson,OdePsmJson' -# TTL duration in days: -GEO_TTL_DURATION=90 -# --------------------------------------------------------------------- - -# ISS Health Check Addon -ISS_API_KEY= -ISS_API_KEY_NAME= -ISS_PROJECT_ID= -ISS_SCMS_TOKEN_REST_ENDPOINT= -ISS_SCMS_VEHICLE_REST_ENDPOINT= -# --------------------------------------------------------------------- - -# RSU Status Addon: - -# Services that can be toggled on or off -# 'True' or 'False' are the only legal values - -# Toggles monitoring of RSU online status -RSU_PING=True - -# Fetches ping data from Zabbix - alternatively the service will ping the RSUs on its own -# Only used when RSU_PING is 'True' -ZABBIX=False - -# Fetches SNMP configuration data for all RSUs -RSU_SNMP_FETCH=True - -# Zabbix endpoint and API authentication -# Only used when ZABBIX is 'True' -ZABBIX_ENDPOINT= -ZABBIX_USER= -ZABBIX_PASSWORD= - -# Customize the period at which the purger will determine a ping log is too old and will be deleted -# Number of hours -STALE_PERIOD=24 -# --------------------------------------------------------------------- - -# Levels are "DEBUG", "INFO", "WARNING", and "ERROR" -API_LOGGING_LEVEL="INFO" -FIRMWARE_MANAGER_LOGGING_LEVEL="INFO" -GEO_LOGGING_LEVEL="INFO" -ISS_LOGGING_LEVEL="INFO" -RSU_STATUS_LOGGING_LEVEL="INFO" -COUNTS_LOGGING_LEVEL="INFO" -# Supported log levels are "ALL", "DEBUG", "ERROR", "FATAL", "INFO", "OFF", "TRACE" and "WARN" -KC_LOGGING_LEVEL="INFO" - - -################################### -# JPO ConflictMonitor Docker Environment File # -################################### -# WARNING! The contents of this file may be sensitive. Take care not to add to source control. -# -# Instructions: -# Rename this file from `sample.env` to `.env` and Docker will automatically pick up the variables. -# -# Description: -# This file aggregates all the variables used in docker-compose.yml for ease of use. Variables are -# prefixed with the format DESTINATION_MESSAGETYPE_ to provide guidance. Some variables are filled -# out with commonly used values. -################################### - -#################### -# General Properties - -# (Required) The IP address of Docker host machine which can be found by running "ifconfig" -# Hint: look for "inet addr:" within "eth0" or "en0" for OSX -# For Local deployments DB_HOST_IP and KAFKA_BROKER_IP should be set to DOCKER_HOST_IP, for remote deployments, these can be set independently. -DOCKER_HOST_IP= -DB_HOST_IP= -KAFKA_BROKER_IP= -KAFKA_CONNECT_IP=localhost -# Set to "CONFLUENT" if broker is a Confluent Cloud broker -KAFKA_TYPE= - -# Set to actual Confluent Cloud access key and secret values for SASL authentication -CONFLUENT_KEY= -CONFLUENT_SECRET= - -# The Username and passwords to use for accessing mongoDB. -MONGO_INITDB_ROOT_USERNAME=root -MONGO_INITDB_ROOT_PASSWORD=root -CM_MONGO_CONNECTOR_USERNAME=connector -CM_MONGO_CONNECTOR_PASSWORD=connector -CM_MONGO_API_USERNAME=api -CM_MONGO_API_PASSWORD=api -CM_MONGO_USER_USERNAME=user -CM_MONGO_USER_PASSWORD=user - - - -################################### -# JPO GeoJsonConverter Docker Environment File # -################################### -# WARNING! The contents of this file may be sensitive. Take care not to add to source control. -# -# Instructions: -# Rename this file from `sample.env` to `.env` and Docker will automatically pick up the variables. -# -# Description: -# This file aggregates all the variables used in docker-compose.yml for ease of use. Variables are -# prefixed with the format DESTINATION_MESSAGETYPE_ to provide guidance. Some variables are filled -# out with commonly used values. -################################### - -#################### -# General Properties - -# (Required) The IP address of Docker host machine which can be found by running "ifconfig" -# Hint: look for "inet addr:" within "eth0" or "en0" for OSX -DOCKER_HOST_IP= - -#################### -# Confluent Cloud Properties (for Confluent Cloud support) - -# Set to "CONFLUENT" if broker is a Confluent Cloud broker -KAFKA_TYPE= - -# Set to actual Confluent Cloud access key and secret values for SASL authentication -CONFLUENT_KEY= -CONFLUENT_SECRET= - -# Feature geometry output mode -# Options are GEOJSON_ONLY or WKT, defaults to GEOJSON_ONLY if nothing is specified -# WKT mode will still generate geoJSON output -GEOMETRY_OUTPUT_MODE= - - -################################### -# JPO ODE Docker Environment File # -################################### -# WARNING! The contents of this file may be sensitive. Take care not to add to source control. -# -# Instructions: -# Rename this file from `sample.env` to `.env` and Docker will automatically pick up the variables. -# -# Description: -# This file aggregates all the variables used in docker-compose.yml for ease of use. Variables are -# prefixed with the format DESTINATION_MESSAGETYPE_ to provide guidance. Some variables are filled -# out with commonly used values. -# -# Variable usage guide: -# _ACCESS_KEY_ID : The Amazon Web Services (AWS) bucket access key ID. -# _SECRET_ACCESS_KEY : The AWS bucket secret access key. -# _BUCKET_NAME : The name of the destination AWS bucket. -# _DEPOSIT_KEY : The filename prefix appended to files deposited to the destination bucket (may be a file path). -# _TOPIC : The name of the Kafka topic to which this depositor will listen. -# _DESTINATION : (Optional) Depositors will default to Firehose depositing. Uncomment this to override to S3 deposit. -################################### - -#################### -# General Properties - -# (Required) The IP address of Docker host machine which can be found by running "ifconfig" -# Hint: look for "inet addr:" within "eth0" or "en0" for OSX -DOCKER_HOST_IP= - -# (Required) The full path of a directory on the host machine to be shared with docker containers. -# Hint: usually the path to the `jpo-ode` directory. -DOCKER_SHARED_VOLUME= -DOCKER_SHARED_VOLUME_WINDOWS= - -# (Required if values are not sent in REST request JSON messages) -# RSU SNMP username and password -ODE_RSU_USERNAME= -ODE_RSU_PASSWORD= - -######################### -# Kafka and Confluent Cloud Properties - -# The IP address of Docker host machine which can be found by running "ifconfig" -KAFKA_TYPE= - -# Confluent Cloud API access credentials -CONFLUENT_KEY= -CONFLUENT_SECRET= - -######################### -# S3 Depositor Properties - - -CVPEP_BSM_BUCKET_NAME= -CVPEP_BSM_DEPOSIT_KEY=wydot-bsm- -CVPEP_BSM_TOPIC=topic.OdeBsmJson -CVPEP_BSM_K_AWS_ACCESS_KEY_ID=AccessKeyId -CVPEP_BSM_K_AWS_SECRET_ACCESS_SECRET=SecretAccessKey -CVPEP_BSM_K_AWS_SESSION_TOKEN=SessionToken -CVPEP_BSM_K_AWS_EXPIRATION=Expiration -CVPEP_BSM_API_ENDPOINT= -CVPEP_BSM_HEADER_ACCEPT=application/json -CVPEP_BSM_HEADER_X_API_KEY= -CVPEP_BSM_GROUP=group_cvpep_bsm - -RDE_BSM_BUCKET_NAME= -RDE_BSM_DEPOSIT_KEY=wydot-filtered-bsm- -RDE_BSM_TOPIC=topic.FilteredOdeBsmJson -RDE_BSM_K_AWS_ACCESS_KEY_ID=AccessKeyId -RDE_BSM_K_AWS_SECRET_ACCESS_SECRET=SecretAccessKey -RDE_BSM_K_AWS_SESSION_TOKEN=SessionToken -RDE_BSM_K_AWS_EXPIRATION=Expiration -RDE_BSM_API_ENDPOINT= -RDE_BSM_HEADER_ACCEPT=application/json -RDE_BSM_HEADER_X_API_KEY= -RDE_BSM_GROUP=group_rde_bsm - -CVPEP_TIM_BUCKET_NAME= -CVPEP_TIM_DEPOSIT_KEY=wydot-tim- -CVPEP_TIM_TOPIC=topic.OdeTimJson -CVPEP_TIM_K_AWS_ACCESS_KEY_ID=AccessKeyId -CVPEP_TIM_K_AWS_SECRET_ACCESS_SECRET=SecretAccessKey -CVPEP_TIM_K_AWS_SESSION_TOKEN=SessionToken -CVPEP_TIM_K_AWS_EXPIRATION=Expiration -CVPEP_TIM_API_ENDPOINT= -CVPEP_TIM_HEADER_ACCEPT=application/json -CVPEP_TIM_HEADER_X_API_KEY= -CVPEP_TIM_GROUP=group_cvpep_tim - -RDE_TIM_BUCKET_NAME= -RDE_TIM_DEPOSIT_KEY=wydot-filtered-tim- -RDE_TIM_TOPIC=topic.FilteredOdeTimJson -RDE_TIM_K_AWS_ACCESS_KEY_ID=AccessKeyId -RDE_TIM_K_AWS_SECRET_ACCESS_SECRET=SecretAccessKey -RDE_TIM_K_AWS_SESSION_TOKEN=SessionToken -RDE_TIM_K_AWS_EXPIRATION=Expiration -RDE_TIM_API_ENDPOINT= -RDE_TIM_HEADER_ACCEPT=application/json -RDE_TIM_HEADER_X_API_KEY= -RDE_TIM_GROUP=group_rde_tim - -######################### -# SDW Depositor Properties - -# Uncomment and set this variable to `true` if you want to use the DDS depositor. -# Otherwise, leave it commented out or set to `false` if you want to use the SDW Depositor Module -#ODE_DEPOSIT_SDW_MESSAGES_OVER_WEBSOCKET=false - -# Note: you only need to set one of the following pairs of username/pw combinations -# The first pair is used for the SDW depositor submodule -# The second pair is used for the built-in DDS depositor - -## Required if using SDX depositor module (REST interface) -SDW_API_KEY= - -## Optional overrides -#SDW_DESTINATION_URL= -#SDW_GROUP_ID= -#SDW_KAFKA_PORT= -#SDW_SUBSCRIPTION_TOPIC=topic.SDWDepositorInput - -## Required if using built-in DDS deposit (websocket interface) -#ODE_DDS_CAS_USERNAME= -#ODE_DDS_CAS_PASSWORD= - -#jpo-security-svcs module properties -SEC_CRYPTO_SERVICE_BASE_URI= -ODE_SECURITY_SVCS_SIGNATURE_URI= - -# These are just the file names expected to be found in the ODE directory -PPM_BSM_CONFIG_FILE=ppmBsm.properties -PPM_TIM_CONFIG_FILE=ppmTim.properties -PPM_MAP_FILE=I_80.edges - -# PPM Logging -PPM_BSM_LOG_TO_CONSOLE=true -PPM_BSM_LOG_TO_FILE=false -PPM_BSM_LOG_LEVEL=INFO -PPM_TIM_LOG_TO_FILE=false -PPM_TIM_LOG_TO_CONSOLE=true -PPM_TIM_LOG_LEVEL=INFO - -# ACM Logging -AEM_LOG_TO_CONSOLE=true -AEM_LOG_TO_FILE=false -AEM_LOG_LEVEL=INFO -ADM_LOG_TO_FILE=false -ADM_LOG_TO_CONSOLE=true -ADM_LOG_LEVEL=INFO - -################################################################## -### jpo-conflictvisualizer cvmanager api environment variables ### -################################################################## -# Set the HOST IP of where the containers are running -KAFKA_BROKER_IP= -DOCKER_HOST_IP= -KAFKA_BROKER_PORT=9092 - -# Set to the directory where the source code is. Generally the location of this file. -DOCKER_HOST_DIR= - -KEYCLOAK_REALM= -MAPBOX_TOKEN= -MAPBOX_STYLE_URL= - -KEYCLOAK_ADMIN= -KEYCLOAK_ADMIN_PASSWORD= - -# May be different from docker host ip for production deployments. Change as appropriate. -GUI_SERVER_URL=http://${DOCKER_HOST_IP}:3000 -AUTH_SERVER_URL=http://${DOCKER_HOST_IP}:8084 -API_SERVER_URL=http://${DOCKER_HOST_IP}:8081 -CM_SERVER_URL=http://${DOCKER_HOST_IP}:8082 -API_WS_URL=ws://${DOCKER_HOST_IP}:8081 - -DB_HOST_IP=${DOCKER_HOST_IP} -DB_HOST_PORT=27017 - - -CM_MONGO_API_PASSWORD=api -CM_MONGO_API_USERNAME=api diff --git a/release-compose.yml b/release-compose.yml deleted file mode 100644 index 613305b26..000000000 --- a/release-compose.yml +++ /dev/null @@ -1,301 +0,0 @@ -version: '3.9' -services: - cvmanager_api: - build: - context: services - dockerfile: Dockerfile.api - image: jpo_cvmanager_api:latest - restart: always - extra_hosts: - ${WEBAPP_DOMAIN}: ${WEBAPP_HOST_IP} - ${KEYCLOAK_DOMAIN}: ${KC_HOST_IP} - ports: - - '8081:5000' - environment: - PG_DB_HOST: ${PG_DB_HOST} - PG_DB_USER: ${PG_DB_USER} - PG_DB_PASS: ${PG_DB_PASS} - PG_DB_NAME: postgres - INSTANCE_CONNECTION_NAME: ${INSTANCE_CONNECTION_NAME} - - MONGO_DB_URI: ${MONGO_DB_URI} - MONGO_DB_NAME: ${MONGO_DB_NAME} - - COUNTS_MSG_TYPES: ${COUNTS_MSG_TYPES} - GOOGLE_APPLICATION_CREDENTIALS: '/google/gcp_credentials.json' - - GEO_DB_NAME: ${GEO_DB_NAME} - SSM_DB_NAME: ${SSM_DB_NAME} - SRM_DB_NAME: ${SRM_DB_NAME} - - MAX_GEO_QUERY_RECORDS: ${MAX_GEO_QUERY_RECORDS} - - FIRMWARE_MANAGER_ENDPOINT: ${FIRMWARE_MANAGER_ENDPOINT} - - WZDX_API_KEY: ${WZDX_API_KEY} - WZDX_ENDPOINT: ${WZDX_ENDPOINT} - - CORS_DOMAIN: ${CORS_DOMAIN} - KEYCLOAK_ENDPOINT: http://${KEYCLOAK_DOMAIN}:8084/ - KEYCLOAK_REALM: ${KEYCLOAK_REALM} - KEYCLOAK_API_CLIENT_ID: ${KEYCLOAK_API_CLIENT_ID} - KEYCLOAK_API_CLIENT_SECRET_KEY: ${KEYCLOAK_API_CLIENT_SECRET_KEY} - - CSM_EMAIL_TO_SEND_FROM: ${CSM_EMAIL_TO_SEND_FROM} - CSM_EMAIL_APP_USERNAME: ${CSM_EMAIL_APP_USERNAME} - CSM_EMAIL_APP_PASSWORD: ${CSM_EMAIL_APP_PASSWORD} - CSM_EMAILS_TO_SEND_TO: ${CSM_EMAILS_TO_SEND_TO} - CSM_TARGET_SMTP_SERVER_ADDRESS: ${CSM_TARGET_SMTP_SERVER_ADDRESS} - CSM_TARGET_SMTP_SERVER_PORT: ${CSM_TARGET_SMTP_SERVER_PORT} - - TIMEZONE: ${TIMEZONE} - LOGGING_LEVEL: ${API_LOGGING_LEVEL} - volumes: - - ${GOOGLE_APPLICATION_CREDENTIALS}:/google/gcp_credentials.json - logging: - options: - max-size: '10m' - max-file: '5' - - cvmanager_webapp: - build: - context: webapp - dockerfile: Dockerfile - args: - API_URI: http://${WEBAPP_DOMAIN}:8081 - MAPBOX_TOKEN: ${MAPBOX_TOKEN} - KEYCLOAK_HOST_URL: http://${KEYCLOAK_DOMAIN}:8084/ - COUNT_MESSAGE_TYPES: ${COUNTS_MSG_TYPES} - VIEWER_MESSAGE_TYPES: ${VIEWER_MSG_TYPES} - DOT_NAME: ${DOT_NAME} - MAPBOX_INIT_LATITUDE: ${MAPBOX_INIT_LATITUDE} - MAPBOX_INIT_LONGITUDE: ${MAPBOX_INIT_LONGITUDE} - MAPBOX_INIT_ZOOM: ${MAPBOX_INIT_ZOOM} - CVIZ_MAPBOX_TOKEN: ${CVIZ_MAPBOX_TOKEN} - CVIZ_MAPBOX_STYLE_URL: ${CVIZ_MAPBOX_STYLE_URL} - CVIZ_API_SERVER_URL: ${CVIZ_API_SERVER_URL} - CVIZ_API_WS_URL: ${CVIZ_API_WS_URL} - image: jpo_cvmanager_webapp:latest - restart: always - depends_on: - cvmanager_keycloak: - condition: service_healthy - extra_hosts: - ${WEBAPP_DOMAIN}: ${WEBAPP_HOST_IP} - ${KEYCLOAK_DOMAIN}: ${KC_HOST_IP} - ports: - - '80:80' - logging: - options: - max-size: '10m' - - cvmanager_postgres: - image: postgis/postgis:15-master - restart: always - ports: - - '5432:5432' - environment: - POSTGRES_USER: ${PG_DB_USER} - POSTGRES_PASSWORD: ${PG_DB_PASS} - volumes: - - pgdb:/var/lib/postgresql/data - - ./resources/sql_scripts:/docker-entrypoint-initdb.d - logging: - options: - max-size: '10m' - - cvmanager_keycloak: - build: - context: ./resources/keycloak - dockerfile: Dockerfile - args: - KEYCLOAK_LOGIN_THEME_NAME: ${KEYCLOAK_LOGIN_THEME_NAME}.jar - image: jpo_cvmanager_keycloak:latest - restart: always - depends_on: - - cvmanager_postgres - extra_hosts: - ${WEBAPP_DOMAIN}: ${WEBAPP_HOST_IP} - ${KEYCLOAK_DOMAIN}: ${KC_HOST_IP} - ports: - - '8084:8080' - environment: - KEYCLOAK_ADMIN: ${KEYCLOAK_ADMIN} - KEYCLOAK_ADMIN_PASSWORD: ${KEYCLOAK_ADMIN_PASSWORD} - WEBAPP_ORIGIN: http://${WEBAPP_DOMAIN} - WEBAPP_CM_ORIGIN: http://${WEBAPP_CM_DOMAIN} - KC_HEALTH_ENABLED: true - KC_DB: postgres - KC_DB_URL: jdbc:postgresql://${PG_DB_HOST}/postgres?currentSchema=keycloak - KC_DB_USERNAME: ${PG_DB_USER} - KC_DB_PASSWORD: ${PG_DB_PASS} - KC_HOSTNAME: ${KEYCLOAK_DOMAIN} - KEYCLOAK_API_CLIENT_SECRET_KEY: ${KEYCLOAK_API_CLIENT_SECRET_KEY} - KEYCLOAK_CM_API_CLIENT_SECRET_KEY: ${KEYCLOAK_CM_API_CLIENT_SECRET_KEY} - GOOGLE_CLIENT_ID: ${GOOGLE_CLIENT_ID} - GOOGLE_CLIENT_SECRET: ${GOOGLE_CLIENT_SECRET} - command: - - start-dev - - --log-level=${KC_LOGGING_LEVEL} - - --import-realm - - --spi-theme-welcome-theme=custom-welcome - logging: - options: - max-size: '10m' - - conflictmonitor: - image: usdotjpoode/jpo-conflictmonitor:release_1.2.0 - restart: always - ports: - - '8082:8082' - environment: - DOCKER_HOST_IP: ${DOCKER_HOST_IP} - KAFKA_BROKER_IP: ${KAFKA_BROKER_IP} - DB_HOST_IP: ${DB_HOST_IP} - healthcheck: - test: ['CMD', 'java', '-version'] - interval: 10s - timeout: 10s - retries: 20 - logging: - options: - max-size: '10m' - max-file: '5' - deploy: - resources: - limits: - memory: 3G - - conflictvisualizer-api: - image: us-central1-docker.pkg.dev/cdot-oim-cv-dev/jpo-conflictvisualizer-api-cvmanager/jpo-conflictvisualizer-api-cvmanager:v1.0.0 - ports: - - '8089:8081' - restart: always - environment: - AUTH_SERVER_URL: ${AUTH_SERVER_URL} - KEYCLOAK_ADMIN: ${KEYCLOAK_ADMIN} - KEYCLOAK_ADMIN_PASSWORD: ${KEYCLOAK_ADMIN_PASSWORD} - DB_HOST_IP: ${DB_HOST_IP} - DB_HOST_PORT: ${DB_HOST_PORT} - SPRING_KAFKA_BOOTSTRAPSERVERS: ${KAFKA_BROKER_IP}:${KAFKA_BROKER_PORT} - CM_SERVER_URL: ${CM_SERVER_URL} - load: 'false' - logging: - options: - max-size: '10m' - max-file: '5' - depends_on: - cvmanager_keycloak: - condition: service_healthy - - mongodb_container: - image: mongo:6 - container_name: jpo-conflictmonitor-mongodb-container - restart: always - environment: - - MONGO_REPLICA_SET_NAME=rs0 - - DB_HOST_IP=${DB_HOST_IP} - - MONGO_INITDB_ROOT_USERNAME=${MONGO_INITDB_ROOT_USERNAME} - - MONGO_INITDB_ROOT_PASSWORD=${MONGO_INITDB_ROOT_PASSWORD} - - CM_MONGO_CONNECTOR_USERNAME=${CM_MONGO_CONNECTOR_USERNAME} - - CM_MONGO_CONNECTOR_PASSWORD=${CM_MONGO_CONNECTOR_PASSWORD} - - CM_MONGO_API_USERNAME=${CM_MONGO_API_USERNAME} - - CM_MONGO_API_PASSWORD=${CM_MONGO_API_PASSWORD} - - CM_MONGO_USER_USERNAME=${CM_MONGO_USER_USERNAME} - - CM_MONGO_USER_PASSWORD=${CM_MONGO_USER_PASSWORD} - ports: - - '27017:27017' - volumes: - - mongodb_data_container:/data/db - - ./docker/mongo/keyfile.txt:/data/keyfile.txt - - ./docker/mongo/a_init_replicas.js:/docker-entrypoint-initdb.d/a_init_replicas.js - - ./docker/mongo/b_create_indexes.js:/docker-entrypoint-initdb.d/b_create_indexes.js - healthcheck: - test: | - test $$(mongosh --username ${MONGO_INITDB_ROOT_USERNAME} --password ${MONGO_INITDB_ROOT_PASSWORD} --quiet --eval "try { rs.initiate({ _id: 'rs0', members: [{ _id: 0, host: '${DB_HOST_IP}' }] }).ok } catch (_) { rs.status().ok }") -eq 1 - interval: 10s - start_period: 30s - entrypoint: - - bash - - -c - - | - chmod 400 /data/keyfile.txt - chown 999:999 /data/keyfile.txt - exec docker-entrypoint.sh $$@ - - command: ['mongod', '--replSet', 'rs0', '--bind_ip_all', '--keyFile', '/data/keyfile.txt'] - logging: - options: - max-size: '10m' - max-file: '5' - deploy: - resources: - limits: - memory: 3G - - connect: - image: cp-kafka-connect:6.1.9 - build: - context: ./docker/connect - dockerfile: Dockerfile - container_name: jpo-conflictmonitor-kafka-connect - restart: always - ports: - - '8083:8083' - depends_on: - mongodb_container: - condition: service_healthy - environment: - DOCKER_HOST_IP: ${DOCKER_HOST_IP} - DB_HOST_IP: ${DB_HOST_IP} - CONNECT_BOOTSTRAP_SERVERS: ${KAFKA_BROKER_IP}:9092 - CONNECT_REST_ADVERTISED_HOST_NAME: connect - CONNECT_REST_PORT: 8083 - CONNECT_GROUP_ID: compose-connect-group - CONNECT_CONFIG_STORAGE_TOPIC: CmConnectConfigs - CONNECT_CONFIG_STORAGE_REPLICATION_FACTOR: 1 - CONNECT_CONFIG_STORAGE_CLEANUP_POLICY: compact - CONNECT_OFFSET_FLUSH_INTERVAL_MS: 10000 - CONNECT_OFFSET_STORAGE_TOPIC: CmConnectOffsets - CONNECT_OFFSET_STORAGE_REPLICATION_FACTOR: 1 - CONNECT_OFFSET_STORAGE_CLEANUP_POLICY: compact - CONNECT_STATUS_STORAGE_TOPIC: CmConnectStatus - CONNECT_STATUS_STORAGE_CLEANUP_POLICY: compact - CONNECT_STATUS_STORAGE_REPLICATION_FACTOR: 1 - CONNECT_KEY_CONVERTER: 'org.apache.kafka.connect.json.JsonConverter' - CONNECT_VALUE_CONVERTER: 'org.apache.kafka.connect.json.JsonConverter' - CONNECT_INTERNAL_KEY_CONVERTER: 'org.apache.kafka.connect.json.JsonConverter' - CONNECT_INTERNAL_VALUE_CONVERTER: 'org.apache.kafka.connect.json.JsonConverter' - CONNECT_LOG4J_ROOT_LOGLEVEL: 'ERROR' - CONNECT_LOG4J_LOGGERS: 'org.apache.kafka.connect.runtime.rest=ERROR,org.reflections=ERROR,com.mongodb.kafka=ERROR' - CONNECT_PLUGIN_PATH: /usr/share/confluent-hub-components - CONNECT_ZOOKEEPER_CONNECT: 'zookeeper:2181' - CM_MONGO_CONNECTOR_USERNAME: ${CM_MONGO_CONNECTOR_USERNAME} - CM_MONGO_CONNECTOR_PASSWORD: ${CM_MONGO_CONNECTOR_PASSWORD} - logging: - options: - max-size: '10m' - max-file: '5' - command: - - bash - - -c - - | - /etc/confluent/docker/run & - echo "Waiting for Kafka Connect to start listening on kafka-connect ❳" - while [ $$(curl -s -o /dev/null -w %{http_code} http://${KAFKA_CONNECT_IP}:8083/connectors) -eq 000 ] ; do - echo -e $$(date) " Kafka Connect listener HTTP state: " $$(curl -s -o /dev/null -w %{http_code} http://${KAFKA_CONNECT_IP}:8083/connectors) " (waiting for 200)" - sleep 5 - done - sleep 10 - echo -e "\n--\n+> Creating Kafka Connect MongoDB sink" - bash /scripts/connect_start.sh "mongodb://${CM_MONGO_CONNECTOR_USERNAME}:${CM_MONGO_CONNECTOR_USERNAME}@${DOCKER_HOST_IP}:27017/?authMechanism=DEFAULT&authSource=ConflictMonitor&replicaSet=rs0" - sleep infinity - deploy: - resources: - limits: - memory: 3G - -volumes: - mongodb_data_container: - pgdb: - driver: local diff --git a/sample.env b/sample.env index 854a6f0c6..9a7a9d2aa 100644 --- a/sample.env +++ b/sample.env @@ -18,7 +18,7 @@ FIRMWARE_MANAGER_ENDPOINT=http://${DOCKER_HOST_IP}:8089 CORS_DOMAIN = * # PostgreSQL Database connection information -# this value may need to folow with the webapp host if debugging the applications +# this value may need to follow with the webapp host if debugging the applications PG_DB_HOST=${DOCKER_HOST_IP}:5432 PG_DB_NAME=postgres PG_DB_USER=postgres @@ -28,7 +28,7 @@ PG_DB_PASS= # If connecting to PGDB over websocket: INSTANCE_CONNECTION_NAME= -# Keycloak authentication crendentials +# Keycloak authentication credentials KEYCLOAK_ADMIN=admin KEYCLOAK_ADMIN_PASSWORD= @@ -54,7 +54,7 @@ MONGO_DB_URI= MONGO_DB_NAME="ode" # Set these variables if using either "MONGODB" or "BIGQUERY" -# COUNTS_MSG_TYPES: Comma seperated list of message types. +# COUNTS_MSG_TYPES: Comma separated list of message types. # COUNTS_MSG_TYPES must be set for the counts menu to correctly populate when building an image for deployment COUNTS_MSG_TYPES='BSM,SSM,SPAT,SRM,MAP' VIEWER_MSG_TYPES='BSM' @@ -77,13 +77,6 @@ CSM_EMAILS_TO_SEND_TO= CSM_TARGET_SMTP_SERVER_ADDRESS= CSM_TARGET_SMTP_SERVER_PORT= -# Email configuration for sending firmware-manager failure emails -SMTP_EMAIL= -SMTP_USERNAME= -SMTP_PASSWORD= -FW_EMAIL_RECIPIENTS= -SMTP_SERVER_IP= - # Python timezone for the CV Manager (You can list pytz timezones with the command 'pytz.all_timezones') TIMEZONE="US/Mountain" @@ -183,4 +176,155 @@ ISS_LOGGING_LEVEL="INFO" RSU_STATUS_LOGGING_LEVEL="INFO" COUNTS_LOGGING_LEVEL="INFO" # Supported log levels are "ALL", "DEBUG", "ERROR", "FATAL", "INFO", "OFF", "TRACE" and "WARN" -KC_LOGGING_LEVEL="INFO" \ No newline at end of file +KC_LOGGING_LEVEL="INFO" + + +################################### +# JPO ConflictMonitor Docker Environment File # +################################### +# WARNING! The contents of this file may be sensitive. Take care not to add to source control. +# +# Instructions: +# Rename this file from `sample.env` to `.env` and Docker will automatically pick up the variables. +# +# Description: +# This file aggregates all the variables used in docker-compose.yml for ease of use. Variables are +# prefixed with the format DESTINATION_MESSAGETYPE_ to provide guidance. Some variables are filled +# out with commonly used values. +################################### + +#################### +# General Properties + +# (Required) The IP address of Docker host machine which can be found by running "ifconfig" +# Hint: look for "inet addr:" within "eth0" or "en0" for OSX +# For Local deployments DB_HOST_IP and KAFKA_BROKER_IP should be set to DOCKER_HOST_IP, for remote deployments, these can be set independently. +DOCKER_HOST_IP= +DB_HOST_IP= +KAFKA_BROKER_IP= +KAFKA_CONNECT_IP=localhost +# Set to "CONFLUENT" if broker is a Confluent Cloud broker +KAFKA_TYPE= + +# Set to actual Confluent Cloud access key and secret values for SASL authentication +CONFLUENT_KEY= +CONFLUENT_SECRET= + +# The Username and passwords to use for accessing mongoDB. +MONGO_INITDB_ROOT_USERNAME=root +MONGO_INITDB_ROOT_PASSWORD=root +CM_MONGO_CONNECTOR_USERNAME=connector +CM_MONGO_CONNECTOR_PASSWORD=connector +CM_MONGO_API_USERNAME=api +CM_MONGO_API_PASSWORD=api +CM_MONGO_USER_USERNAME=user +CM_MONGO_USER_PASSWORD=user + + + +################################### +# JPO GeoJsonConverter Docker Environment File # +################################### +# WARNING! The contents of this file may be sensitive. Take care not to add to source control. +# +# Instructions: +# Rename this file from `sample.env` to `.env` and Docker will automatically pick up the variables. +# +# Description: +# This file aggregates all the variables used in docker-compose.yml for ease of use. Variables are +# prefixed with the format DESTINATION_MESSAGETYPE_ to provide guidance. Some variables are filled +# out with commonly used values. +################################### + +#################### +# General Properties + +# (Required) The IP address of Docker host machine which can be found by running "ifconfig" +# Hint: look for "inet addr:" within "eth0" or "en0" for OSX +DOCKER_HOST_IP= + +#################### +# Confluent Cloud Properties (for Confluent Cloud support) + +# Set to "CONFLUENT" if broker is a Confluent Cloud broker +KAFKA_TYPE= + +# Set to actual Confluent Cloud access key and secret values for SASL authentication +CONFLUENT_KEY= +CONFLUENT_SECRET= + +# Feature geometry output mode +# Options are GEOJSON_ONLY or WKT, defaults to GEOJSON_ONLY if nothing is specified +# WKT mode will still generate geoJSON output +GEOMETRY_OUTPUT_MODE= + + +################################### +# JPO ODE Docker Environment File # +################################### +# WARNING! The contents of this file may be sensitive. Take care not to add to source control. +# +# Instructions: +# Rename this file from `sample.env` to `.env` and Docker will automatically pick up the variables. +# +# Description: +# This file aggregates all the variables used in docker-compose.yml for ease of use. Variables are +# prefixed with the format DESTINATION_MESSAGETYPE_ to provide guidance. Some variables are filled +# out with commonly used values. +# +# Variable usage guide: +# _ACCESS_KEY_ID : The Amazon Web Services (AWS) bucket access key ID. +# _SECRET_ACCESS_KEY : The AWS bucket secret access key. +# _BUCKET_NAME : The name of the destination AWS bucket. +# _DEPOSIT_KEY : The filename prefix appended to files deposited to the destination bucket (may be a file path). +# _TOPIC : The name of the Kafka topic to which this depositor will listen. +# _DESTINATION : (Optional) Depositors will default to Firehose depositing. Uncomment this to override to S3 deposit. +################################### + +#################### +# General Properties + +# (Required) The IP address of Docker host machine which can be found by running "ifconfig" +# Hint: look for "inet addr:" within "eth0" or "en0" for OSX +DOCKER_HOST_IP= + +# (Required) The full path of a directory on the host machine to be shared with docker containers. +# Hint: usually the path to the `jpo-ode` directory. +DOCKER_SHARED_VOLUME= +DOCKER_SHARED_VOLUME_WINDOWS= + +# (Required if values are not sent in REST request JSON messages) +# RSU SNMP username and password +ODE_RSU_USERNAME= +ODE_RSU_PASSWORD= + +######################### +# Kafka and Confluent Cloud Properties + +# The IP address of Docker host machine which can be found by running "ifconfig" +KAFKA_TYPE= + +# Confluent Cloud API access credentials +CONFLUENT_KEY= +CONFLUENT_SECRET= + +################################################################## +### jpo-conflictvisualizer cvmanager api environment variables ### +################################################################## +# Set the HOST IP of where the containers are running +KAFKA_BROKER_IP= +DOCKER_HOST_IP= +KAFKA_BROKER_PORT=9092 + +KEYCLOAK_ADMIN= +KEYCLOAK_ADMIN_PASSWORD= + +# May be different from docker host ip for production deployments. Change as appropriate. +CM_SERVER_URL=http://${DOCKER_HOST_IP}:8082 + +DB_HOST_IP=${DOCKER_HOST_IP} +DB_HOST_PORT=27017 + + +CM_MONGO_API_PASSWORD=api +CM_MONGO_API_USERNAME=api diff --git a/services/conflictmonitor-release-compose.yml b/services/conflictmonitor-release-compose.yml deleted file mode 100644 index 4100c6d5f..000000000 --- a/services/conflictmonitor-release-compose.yml +++ /dev/null @@ -1,158 +0,0 @@ -version: '3.9' -services: - conflictmonitor: - image: usdotjpoode/jpo-conflictmonitor:release_1.2.0 - restart: always - ports: - - '8082:8082' - environment: - DOCKER_HOST_IP: ${DOCKER_HOST_IP} - KAFKA_BROKER_IP: ${KAFKA_BROKER_IP} - DB_HOST_IP: ${DB_HOST_IP} - healthcheck: - test: ['CMD', 'java', '-version'] - interval: 10s - timeout: 10s - retries: 20 - logging: - options: - max-size: '10m' - max-file: '5' - deploy: - resources: - limits: - memory: 3G - - conflictvisualizer-api: - image: us-central1-docker.pkg.dev/cdot-oim-cv-dev/jpo-conflictvisualizer-api-cvmanager/jpo-conflictvisualizer-api-cvmanager:v1.0.0 - build: - context: api - dockerfile: Dockerfile - ports: - - '8089:8081' - restart: always - environment: - AUTH_SERVER_URL: ${AUTH_SERVER_URL} - KEYCLOAK_ADMIN: ${KEYCLOAK_ADMIN} - KEYCLOAK_ADMIN_PASSWORD: ${KEYCLOAK_ADMIN_PASSWORD} - DB_HOST_IP: ${DB_HOST_IP} - DB_HOST_PORT: ${DB_HOST_PORT} - SPRING_KAFKA_BOOTSTRAPSERVERS: ${KAFKA_BROKER_IP}:${KAFKA_BROKER_PORT} - CM_SERVER_URL: ${CM_SERVER_URL} - load: 'false' - #KAFKA_BROKER_IP: ${KAFKA_BROKER_IP} - #KAFKA_BROKER_PORT: ${KAFKA_BROKER_PORT} - logging: - options: - max-size: '10m' - max-file: '5' - - mongodb_container: - image: mongo:6 - container_name: jpo-conflictmonitor-mongodb-container - restart: always - environment: - - MONGO_REPLICA_SET_NAME=rs0 - - DB_HOST_IP=${DB_HOST_IP} - - MONGO_INITDB_ROOT_USERNAME=${MONGO_INITDB_ROOT_USERNAME} - - MONGO_INITDB_ROOT_PASSWORD=${MONGO_INITDB_ROOT_PASSWORD} - - CM_MONGO_CONNECTOR_USERNAME=${CM_MONGO_CONNECTOR_USERNAME} - - CM_MONGO_CONNECTOR_PASSWORD=${CM_MONGO_CONNECTOR_PASSWORD} - - CM_MONGO_API_USERNAME=${CM_MONGO_API_USERNAME} - - CM_MONGO_API_PASSWORD=${CM_MONGO_API_PASSWORD} - - CM_MONGO_USER_USERNAME=${CM_MONGO_USER_USERNAME} - - CM_MONGO_USER_PASSWORD=${CM_MONGO_USER_PASSWORD} - ports: - - '27017:27017' - volumes: - - mongodb_data_container:/data/db - - ./docker/mongo/keyfile.txt:/data/keyfile.txt - - ./docker/mongo/a_init_replicas.js:/docker-entrypoint-initdb.d/a_init_replicas.js - - ./docker/mongo/b_create_indexes.js:/docker-entrypoint-initdb.d/b_create_indexes.js - healthcheck: - test: | - test $$(mongosh --username ${MONGO_INITDB_ROOT_USERNAME} --password ${MONGO_INITDB_ROOT_PASSWORD} --quiet --eval "try { rs.initiate({ _id: 'rs0', members: [{ _id: 0, host: '${DB_HOST_IP}' }] }).ok } catch (_) { rs.status().ok }") -eq 1 - interval: 10s - start_period: 30s - entrypoint: - - bash - - -c - - | - chmod 400 /data/keyfile.txt - chown 999:999 /data/keyfile.txt - exec docker-entrypoint.sh $$@ - - command: ['mongod', '--replSet', 'rs0', '--bind_ip_all', '--keyFile', '/data/keyfile.txt'] - logging: - options: - max-size: '10m' - max-file: '5' - deploy: - resources: - limits: - memory: 3G - - connect: - image: cp-kafka-connect:6.1.9 - build: - context: ./docker/connect - dockerfile: Dockerfile - container_name: jpo-conflictmonitor-kafka-connect - restart: always - ports: - - '8083:8083' - depends_on: - mongodb_container: - condition: service_healthy - environment: - DOCKER_HOST_IP: ${DOCKER_HOST_IP} - DB_HOST_IP: ${DB_HOST_IP} - CONNECT_BOOTSTRAP_SERVERS: ${KAFKA_BROKER_IP}:9092 - CONNECT_REST_ADVERTISED_HOST_NAME: connect - CONNECT_REST_PORT: 8083 - CONNECT_GROUP_ID: compose-connect-group - CONNECT_CONFIG_STORAGE_TOPIC: CmConnectConfigs - CONNECT_CONFIG_STORAGE_REPLICATION_FACTOR: 1 - CONNECT_CONFIG_STORAGE_CLEANUP_POLICY: compact - CONNECT_OFFSET_FLUSH_INTERVAL_MS: 10000 - CONNECT_OFFSET_STORAGE_TOPIC: CmConnectOffsets - CONNECT_OFFSET_STORAGE_REPLICATION_FACTOR: 1 - CONNECT_OFFSET_STORAGE_CLEANUP_POLICY: compact - CONNECT_STATUS_STORAGE_TOPIC: CmConnectStatus - CONNECT_STATUS_STORAGE_CLEANUP_POLICY: compact - CONNECT_STATUS_STORAGE_REPLICATION_FACTOR: 1 - CONNECT_KEY_CONVERTER: 'org.apache.kafka.connect.json.JsonConverter' - CONNECT_VALUE_CONVERTER: 'org.apache.kafka.connect.json.JsonConverter' - CONNECT_INTERNAL_KEY_CONVERTER: 'org.apache.kafka.connect.json.JsonConverter' - CONNECT_INTERNAL_VALUE_CONVERTER: 'org.apache.kafka.connect.json.JsonConverter' - CONNECT_LOG4J_ROOT_LOGLEVEL: 'ERROR' - CONNECT_LOG4J_LOGGERS: 'org.apache.kafka.connect.runtime.rest=ERROR,org.reflections=ERROR,com.mongodb.kafka=ERROR' - CONNECT_PLUGIN_PATH: /usr/share/confluent-hub-components - CONNECT_ZOOKEEPER_CONNECT: 'zookeeper:2181' - CM_MONGO_CONNECTOR_USERNAME: ${CM_MONGO_CONNECTOR_USERNAME} - CM_MONGO_CONNECTOR_PASSWORD: ${CM_MONGO_CONNECTOR_PASSWORD} - logging: - options: - max-size: '10m' - max-file: '5' - command: - - bash - - -c - - | - /etc/confluent/docker/run & - echo "Waiting for Kafka Connect to start listening on kafka-connect ❳" - while [ $$(curl -s -o /dev/null -w %{http_code} http://${KAFKA_CONNECT_IP}:8083/connectors) -eq 000 ] ; do - echo -e $$(date) " Kafka Connect listener HTTP state: " $$(curl -s -o /dev/null -w %{http_code} http://${KAFKA_CONNECT_IP}:8083/connectors) " (waiting for 200)" - sleep 5 - done - sleep 10 - echo -e "\n--\n+> Creating Kafka Connect MongoDB sink" - bash /scripts/connect_start.sh "mongodb://${CM_MONGO_CONNECTOR_USERNAME}:${CM_MONGO_CONNECTOR_USERNAME}@${DOCKER_HOST_IP}:27017/?authMechanism=DEFAULT&authSource=ConflictMonitor&replicaSet=rs0" - sleep infinity - deploy: - resources: - limits: - memory: 3G - -volumes: - mongodb_data_container: From b1ca32df67a402ab85d2d7830fa9d56088913e14 Mon Sep 17 00:00:00 2001 From: jacob6838 Date: Wed, 29 May 2024 16:10:35 -0600 Subject: [PATCH 034/189] Moving conflictmonitor mongo/connect components to a sub-folder --- README.md | 4 ++++ conflictmonitor/README.md | 7 +++++++ {docker => conflictmonitor/docker}/connect/Dockerfile | 0 .../docker}/connect/connect_start.sh | 0 {docker => conflictmonitor/docker}/mongo/Dockerfile | 0 .../docker}/mongo/a_init_replicas.js | 0 .../docker}/mongo/b_create_indexes.js | 0 {docker => conflictmonitor/docker}/mongo/keyfile.txt | 0 .../docker}/mongo/mongo_initialize.sh | 0 docker-compose.yml | 6 +++--- docker/README.md | 3 --- 11 files changed, 14 insertions(+), 6 deletions(-) create mode 100644 conflictmonitor/README.md rename {docker => conflictmonitor/docker}/connect/Dockerfile (100%) rename {docker => conflictmonitor/docker}/connect/connect_start.sh (100%) rename {docker => conflictmonitor/docker}/mongo/Dockerfile (100%) rename {docker => conflictmonitor/docker}/mongo/a_init_replicas.js (100%) rename {docker => conflictmonitor/docker}/mongo/b_create_indexes.js (100%) rename {docker => conflictmonitor/docker}/mongo/keyfile.txt (100%) rename {docker => conflictmonitor/docker}/mongo/mongo_initialize.sh (100%) delete mode 100644 docker/README.md diff --git a/README.md b/README.md index 90b54bab5..a84433269 100644 --- a/README.md +++ b/README.md @@ -67,6 +67,10 @@ For more information on these services and environment variables, please visit t docker compose -f release-compose.yml up -d ``` +#### Generating Sample Data + +Use the test-message-sender, included in the jpo-conflictmonitor, to generate sample data, as well as pushing live data through the websocket. These components are described here: [jpo-conflictmonitor/test-message-sender](https://github.com/usdot-jpo-ode/jpo-conflictmonitor/tree/develop/test-message-sender) + ## Getting Started The following steps are intended to help get a new user up and running the JPO CV Manager in their own environment. diff --git a/conflictmonitor/README.md b/conflictmonitor/README.md new file mode 100644 index 000000000..a6e797f42 --- /dev/null +++ b/conflictmonitor/README.md @@ -0,0 +1,7 @@ +# JPO-Conflictmonitor Components + +This directory contains useful configuration files for the jpo-conflictmonitor. + +## ./docker - jpo-conflictmonitor Connect/Mongo Configuration Files + +This directory contains the configuration files for the Connect/Mongo service for the jpo-conflictmonitor project. diff --git a/docker/connect/Dockerfile b/conflictmonitor/docker/connect/Dockerfile similarity index 100% rename from docker/connect/Dockerfile rename to conflictmonitor/docker/connect/Dockerfile diff --git a/docker/connect/connect_start.sh b/conflictmonitor/docker/connect/connect_start.sh similarity index 100% rename from docker/connect/connect_start.sh rename to conflictmonitor/docker/connect/connect_start.sh diff --git a/docker/mongo/Dockerfile b/conflictmonitor/docker/mongo/Dockerfile similarity index 100% rename from docker/mongo/Dockerfile rename to conflictmonitor/docker/mongo/Dockerfile diff --git a/docker/mongo/a_init_replicas.js b/conflictmonitor/docker/mongo/a_init_replicas.js similarity index 100% rename from docker/mongo/a_init_replicas.js rename to conflictmonitor/docker/mongo/a_init_replicas.js diff --git a/docker/mongo/b_create_indexes.js b/conflictmonitor/docker/mongo/b_create_indexes.js similarity index 100% rename from docker/mongo/b_create_indexes.js rename to conflictmonitor/docker/mongo/b_create_indexes.js diff --git a/docker/mongo/keyfile.txt b/conflictmonitor/docker/mongo/keyfile.txt similarity index 100% rename from docker/mongo/keyfile.txt rename to conflictmonitor/docker/mongo/keyfile.txt diff --git a/docker/mongo/mongo_initialize.sh b/conflictmonitor/docker/mongo/mongo_initialize.sh similarity index 100% rename from docker/mongo/mongo_initialize.sh rename to conflictmonitor/docker/mongo/mongo_initialize.sh diff --git a/docker-compose.yml b/docker-compose.yml index d931ac9de..ea3ecd91c 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -317,8 +317,8 @@ services: - '27017:27017' volumes: - mongodb_data_container:/data/db - - ./docker/mongo/a_init_replicas.js:/docker-entrypoint-initdb.d/a_init_replicas.js - - ./docker/mongo/b_create_indexes.js:/docker-entrypoint-initdb.d/b_create_indexes.js + - ./conflictmonitor/docker/mongo/a_init_replicas.js:/docker-entrypoint-initdb.d/a_init_replicas.js + - ./conflictmonitor/docker/mongo/b_create_indexes.js:/docker-entrypoint-initdb.d/b_create_indexes.js healthcheck: test: | test $$(mongosh --username ${MONGO_INITDB_ROOT_USERNAME} --password ${MONGO_INITDB_ROOT_PASSWORD} --quiet --eval "try { rs.initiate({ _id: 'rs0', members: [{ _id: 0, host: '${DB_HOST_IP}' }] }).ok } catch (_) { rs.status().ok }") -eq 1 @@ -346,7 +346,7 @@ services: connect: image: cp-kafka-connect:6.1.9 build: - context: ./docker/connect + context: ./conflictmonitor/docker/connect dockerfile: Dockerfile container_name: jpo-conflictmonitor-kafka-connect restart: always diff --git a/docker/README.md b/docker/README.md deleted file mode 100644 index f75a49748..000000000 --- a/docker/README.md +++ /dev/null @@ -1,3 +0,0 @@ -# jpo-conflictmonitor Connect/Mongo Configuration Files - -This directory contains the configuration files for the Connect/Mongo service for the jpo-conflictmonitor project. From 734ce4050a4a83e03972d928abb92a6ca22e1fd1 Mon Sep 17 00:00:00 2001 From: jacob6838 Date: Thu, 30 May 2024 08:43:50 -0600 Subject: [PATCH 035/189] Updating connect/mongo conflictmonitor scripts --- conflictmonitor/docker/mongo/Dockerfile | 5 - .../docker/mongo/a_init_replicas.js | 25 +- .../docker/mongo/b_create_indexes.js | 831 ++++++++++++------ .../docker/mongo/mongo_initialize.sh | 12 - 4 files changed, 570 insertions(+), 303 deletions(-) delete mode 100644 conflictmonitor/docker/mongo/Dockerfile delete mode 100644 conflictmonitor/docker/mongo/mongo_initialize.sh diff --git a/conflictmonitor/docker/mongo/Dockerfile b/conflictmonitor/docker/mongo/Dockerfile deleted file mode 100644 index edf4f7172..000000000 --- a/conflictmonitor/docker/mongo/Dockerfile +++ /dev/null @@ -1,5 +0,0 @@ -FROM mongo:6 - -COPY a_init_replicas.js /docker-entrypoint-initdb.d/a_init_replicas.js -COPY b_create_indexes.js /docker-entrypoint-initdb.d/b_create_indexes.js - diff --git a/conflictmonitor/docker/mongo/a_init_replicas.js b/conflictmonitor/docker/mongo/a_init_replicas.js index 957837701..5c0d33106 100644 --- a/conflictmonitor/docker/mongo/a_init_replicas.js +++ b/conflictmonitor/docker/mongo/a_init_replicas.js @@ -1,10 +1,9 @@ - /* This script is the first of two scripts responsible for setting up mongoDB on initial boot. These scripts should be copied to the /docker-entrypoint-initdb.d/ directory within the docker image; the docker image will then execute these scripts automatically when the database is first created. -This script and is partner are prefixed with the letters a and b respectively to ensure they are run +This script and its partner are prefixed with the letters a and b respectively to ensure they are run in the proper order when copied into the mongoDB docker image. Since the conflict monitor uses a replica set in its mongoDB configuration. Initializing the replica set @@ -18,16 +17,14 @@ https://hub.docker.com/_/mongo/ */ - - -console.log("Initializing Replicas"); -try{ - db_status = rs.status(); -} catch(err){ - console.log("Initializing New DB"); - try{ - rs.initiate({ _id: 'rs0', members: [{ _id: 0, host: 'localhost:27017' }] }).ok - }catch(err){ - console.log("Unable to Initialize DB"); - } +console.log('Initializing Replicas') +try { + db_status = rs.status() +} catch (err) { + console.log('Initializing New DB') + try { + rs.initiate({ _id: 'rs0', members: [{ _id: 0, host: 'localhost:27017' }] }).ok + } catch (err) { + console.log('Unable to Initialize DB') + } } diff --git a/conflictmonitor/docker/mongo/b_create_indexes.js b/conflictmonitor/docker/mongo/b_create_indexes.js index 3cb50feae..5100f31c1 100644 --- a/conflictmonitor/docker/mongo/b_create_indexes.js +++ b/conflictmonitor/docker/mongo/b_create_indexes.js @@ -6,327 +6,614 @@ This script is responsible for creating users, creating collections, adding inde For more information see the header in a_init_replicas.js */ -console.log(""); -console.log("Running create_indexes.js"); - +console.log('') +console.log('Running create_indexes.js') // Setup Username and Password Definitions -const CM_MONGO_ROOT_USERNAME = process.env.CM_MONGO_ROOT_USERNAME; -const CM_MONGO_ROOT_PASSWORD = process.env.CM_MONGO_ROOT_PASSWORD; - -const CM_MONGO_CONNECTOR_USERNAME = process.env.CM_MONGO_CONNECTOR_USERNAME; -const CM_MONGO_CONNECTOR_PASSWORD = process.env.CM_MONGO_CONNECTOR_PASSWORD; +const CM_MONGO_ROOT_USERNAME = process.env.CM_MONGO_ROOT_USERNAME +const CM_MONGO_ROOT_PASSWORD = process.env.CM_MONGO_ROOT_PASSWORD -const CM_MONGO_API_USERNAME = process.env.CM_MONGO_API_USERNAME; -const CM_MONGO_API_PASSWORD = process.env.CM_MONGO_API_PASSWORD; +const CM_MONGO_CONNECTOR_USERNAME = process.env.CM_MONGO_CONNECTOR_USERNAME +const CM_MONGO_CONNECTOR_PASSWORD = process.env.CM_MONGO_CONNECTOR_PASSWORD -const CM_MONGO_USER_USERNAME = process.env.CM_MONGO_USER_USERNAME; -const CM_MONGO_USER_PASSWORD = process.env.CM_MONGO_USER_PASSWORD; +const CM_MONGO_API_USERNAME = process.env.CM_MONGO_API_USERNAME +const CM_MONGO_API_PASSWORD = process.env.CM_MONGO_API_PASSWORD -const CM_DATABASE_NAME = process.env.CM_DATABASE_NAME || "ConflictMonitor"; +const CM_MONGO_USER_USERNAME = process.env.CM_MONGO_USER_USERNAME +const CM_MONGO_USER_PASSWORD = process.env.CM_MONGO_USER_PASSWORD -const expireSeconds = 5184000; // 2 months -const retryMilliseconds = 10000; +const CM_DATABASE_NAME = process.env.CM_DATABASE_NAME || 'ConflictMonitor' +const expireSeconds = 5184000 // 2 months +const retryMilliseconds = 10000 const users = [ - // {username: CM_MONGO_ROOT_USERNAME, password: CM_MONGO_ROOT_PASSWORD, roles: "root", database: "admin" }, - {username: CM_MONGO_CONNECTOR_USERNAME, password: CM_MONGO_CONNECTOR_PASSWORD, roles: "readWrite", database: CM_DATABASE_NAME}, - {username: CM_MONGO_API_USERNAME, password: CM_MONGO_API_PASSWORD, roles: "readWrite", database: CM_DATABASE_NAME}, - {username: CM_MONGO_USER_USERNAME, password: CM_MONGO_USER_PASSWORD, roles: "read", database: CM_DATABASE_NAME}, -]; - + // {username: CM_MONGO_ROOT_USERNAME, password: CM_MONGO_ROOT_PASSWORD, roles: "root", database: "admin" }, + { + username: CM_MONGO_CONNECTOR_USERNAME, + password: CM_MONGO_CONNECTOR_PASSWORD, + roles: 'readWrite', + database: CM_DATABASE_NAME, + }, + { username: CM_MONGO_API_USERNAME, password: CM_MONGO_API_PASSWORD, roles: 'readWrite', database: CM_DATABASE_NAME }, + { username: CM_MONGO_USER_USERNAME, password: CM_MONGO_USER_PASSWORD, roles: 'read', database: CM_DATABASE_NAME }, +] // name -> collection name -// ttlField -> field to perform ttl on +// ttlField -> field to perform ttl on // timeField -> field to index for time queries // intersectionField -> field containing intersection id for id queries const collections = [ - - // ODE Json data - {name: "OdeDriverAlertJson", ttlField: "recordGeneratedAt", "timeField": "metadata.odeReceivedAt", intersectionField: null, rsuIP:"metadata.originIp"}, - {name: "OdeBsmJson", ttlField: "recordGeneratedAt", "timeField": "metadata.odeReceivedAt", intersectionField: null, rsuIP:"metadata.originIp"}, - {name: "OdeMapJson", ttlField: "recordGeneratedAt", "timeField": "metadata.odeReceivedAt", intersectionField: null, rsuIP:"metadata.originIp"}, - {name: "OdeSpatJson", ttlField: "recordGeneratedAt", "timeField": "metadata.odeReceivedAt", intersectionField: null, rsuIP:"metadata.originIp"}, - {name: "OdeSpatRxJson", ttlField: "recordGeneratedAt", "timeField": "metadata.odeReceivedAt", intersectionField: null, rsuIP:"metadata.originIp"}, - {name: "OdeSrmJson", ttlField: "recordGeneratedAt", "timeField": "metadata.odeReceivedAt", intersectionField: null, rsuIP:"metadata.originIp"}, - {name: "OdeSsmJson", ttlField: "recordGeneratedAt", "timeField": "metadata.odeReceivedAt", intersectionField: null, rsuIP:"metadata.originIp"}, - {name: "OdeTimJson", ttlField: "recordGeneratedAt", "timeField": "metadata.odeReceivedAt", intersectionField: null, rsuIP:"metadata.originIp"}, - {name: "OdeTimBroadcastJson", ttlField: "recordGeneratedAt", "timeField": "metadata.odeReceivedAt", intersectionField: null, rsuIP:"metadata.originIp"}, - {name: "OdeTIMCertExpirationTimeJson", ttlField: "recordGeneratedAt", "timeField": "metadata.odeReceivedAt", intersectionField: null, rsuIP:"metadata.originIp"}, - - // Ode Raw ASN - {name: "OdeRawEncodedBSMJson", ttlField: "recordGeneratedAt", "timeField": "BsmMessageContent.metadata.utctimestamp", intersectionField: null, rsuIP:"BsmMessageContent.metadata.originRsu"}, - {name: "OdeRawEncodedMAPJson", ttlField: "recordGeneratedAt", "timeField": "MapMessageContent.metadata.utctimestamp", intersectionField: null, rsuIP:"MapMessageContent.metadata.originRsu"}, - {name: "OdeRawEncodedSPATJson", ttlField: "recordGeneratedAt", "timeField": "SpatMessageContent.metadata.utctimestamp", intersectionField: null, rsuIP:"SpatMessageContent.metadata.originRsu"}, - {name: "OdeRawEncodedSRMJson", ttlField: "recordGeneratedAt", "timeField": "SrmMessageContent.metadata.utctimestamp", intersectionField: null, rsuIP:"SpatMessageContent.metadata.originRsu"}, - {name: "OdeRawEncodedSSMJson", ttlField: "recordGeneratedAt", "timeField": "SsmMessageContent.metadata.utctimestamp", intersectionField: null, rsuIP:"SpatMessageContent.metadata.originRsu"}, - {name: "OdeRawEncodedTIMJson", ttlField: "recordGeneratedAt", "timeField": "TimMessageContent.metadata.utctimestamp", intersectionField: null, rsuIP:"SpatMessageContent.metadata.originRsu"}, - - // GeoJson Converter Data - {name: "ProcessedMap", ttlField: "recordGeneratedAt", timeField: "properties.timeStamp", intersectionField: "properties.intersectionId"}, - {name: "ProcessedSpat", ttlField: "recordGeneratedAt", timeField: "odeReceivedAt", intersectionField: "intersectionId"}, - - // Conflict Monitor Events - { name: "CmStopLineStopEvent", ttlField: "eventGeneratedAt", timeField: "eventGeneratedAt", intersectionField: "intersectionID" }, - { name: "CmStopLinePassageEvent", ttlField: "eventGeneratedAt", timeField: "eventGeneratedAt", intersectionField: "intersectionID" }, - { name: "CmIntersectionReferenceAlignmentEvents", ttlField: "eventGeneratedAt", timeField: "eventGeneratedAt", intersectionField: "intersectionID" }, - { name: "CmSignalGroupAlignmentEvents", ttlField: "eventGeneratedAt", timeField: "eventGeneratedAt", intersectionField: "intersectionID" }, - { name: "CmConnectionOfTravelEvent", ttlField: "eventGeneratedAt", timeField: "eventGeneratedAt", intersectionField: "intersectionID" }, - { name: "CmSignalStateConflictEvents", ttlField: "eventGeneratedAt", timeField: "eventGeneratedAt", intersectionField: "intersectionID" }, - { name: "CmLaneDirectionOfTravelEvent", ttlField: "eventGeneratedAt", timeField: "eventGeneratedAt", intersectionField: "intersectionID" }, - { name: "CmSpatTimeChangeDetailsEvent", ttlField: "eventGeneratedAt", timeField: "eventGeneratedAt", intersectionField: "intersectionID" }, - { name: "CmSpatMinimumDataEvents", ttlField: "eventGeneratedAt", timeField: "eventGeneratedAt", intersectionField: "intersectionID" }, - { name: "CmMapBroadcastRateEvents", ttlField: "eventGeneratedAt", timeField: "eventGeneratedAt", intersectionField: "intersectionID" }, - { name: "CmMapMinimumDataEvents", ttlField: "eventGeneratedAt", timeField: "eventGeneratedAt", intersectionField: "intersectionID" }, - { name: "CmSpatBroadcastRateEvents", ttlField: "eventGeneratedAt", timeField: "eventGeneratedAt", intersectionField: "intersectionID" }, - { name: "CmBsmEvents", ttlField: "recordGeneratedAt", timeField: "recordGeneratedAt", intersectionField: "intersectionID" }, - - // Conflict Monitor Assessments - { name: "CmLaneDirectionOfTravelAssessment", ttlField: "assessmentGeneratedAt", timeField: "assessmentGeneratedAt", intersectionField: "intersectionID" }, - { name: "CmConnectionOfTravelAssessment", ttlField: "assessmentGeneratedAt", timeField: "assessmentGeneratedAt", intersectionField: "intersectionID" }, - { name: "CmSignalStateEventAssessment", ttlField: "assessmentGeneratedAt", timeField: "assessmentGeneratedAt", intersectionField: "intersectionID" }, - { name: "CmStopLineStopAssessment", ttlField: "assessmentGeneratedAt", timeField: "assessmentGeneratedAt", intersectionField: "intersectionID" }, - - // Conflict Monitor Notifications - { name: "CmSpatTimeChangeDetailsNotification", ttlField: "notificationGeneratedAt", timeField: "notificationGeneratedAt", intersectionField: "intersectionID" }, - { name: "CmLaneDirectionOfTravelNotification", ttlField: "notificationGeneratedAt", timeField: "notificationGeneratedAt", intersectionField: "intersectionID" }, - { name: "CmConnectionOfTravelNotification", ttlField: "notificationGeneratedAt", timeField: "notificationGeneratedAt", intersectionField: "intersectionID" }, - { name: "CmAppHealthNotifications", ttlField: "notificationGeneratedAt", timeField: "notificationGeneratedAt", intersectionField: "intersectionID" }, - { name: "CmSignalStateConflictNotification", ttlField: "notificationGeneratedAt", timeField: "notificationGeneratedAt", intersectionField: "intersectionID" }, - { name: "CmSignalGroupAlignmentNotification", ttlField: "notificationGeneratedAt", timeField: "notificationGeneratedAt", intersectionField: "intersectionID" }, - { name: "CmStopLinePassageNotification", ttlField: "notificationGeneratedAt", timeField: "notificationGeneratedAt", intersectionField: "intersectionID" }, - { name: "CmStopLineStopNotification", ttlField: "notificationGeneratedAt", timeField: "notificationGeneratedAt", intersectionField: "intersectionID" }, - { name: "CmNotification", ttlField: "notificationGeneratedAt", timeField: "notificationGeneratedAt", intersectionField: "intersectionID" } -]; - -try{ - db.getMongo().setReadPref("primaryPreferred"); - db = db.getSiblingDB("ConflictMonitor"); - db.getMongo().setReadPref("primaryPreferred"); - var isMaster = db.isMaster(); - if (isMaster.primary) { - console.log("Connected to the primary replica set member."); - } else { - console.log("Not connected to the primary replica set member. Current node: " + isMaster.host); - } -} -catch(err){ - console.log("Could not switch DB to Sibling DB"); - console.log(err); + // ODE Json data + { + name: 'OdeDriverAlertJson', + ttlField: 'recordGeneratedAt', + timeField: 'metadata.odeReceivedAt', + intersectionField: null, + rsuIP: 'metadata.originIp', + }, + { + name: 'OdeBsmJson', + ttlField: 'recordGeneratedAt', + timeField: 'metadata.odeReceivedAt', + intersectionField: null, + rsuIP: 'metadata.originIp', + }, + { + name: 'OdeMapJson', + ttlField: 'recordGeneratedAt', + timeField: 'metadata.odeReceivedAt', + intersectionField: null, + rsuIP: 'metadata.originIp', + }, + { + name: 'OdeSpatJson', + ttlField: 'recordGeneratedAt', + timeField: 'metadata.odeReceivedAt', + intersectionField: null, + rsuIP: 'metadata.originIp', + }, + { + name: 'OdeSpatRxJson', + ttlField: 'recordGeneratedAt', + timeField: 'metadata.odeReceivedAt', + intersectionField: null, + rsuIP: 'metadata.originIp', + }, + { + name: 'OdeSrmJson', + ttlField: 'recordGeneratedAt', + timeField: 'metadata.odeReceivedAt', + intersectionField: null, + rsuIP: 'metadata.originIp', + }, + { + name: 'OdeSsmJson', + ttlField: 'recordGeneratedAt', + timeField: 'metadata.odeReceivedAt', + intersectionField: null, + rsuIP: 'metadata.originIp', + }, + { + name: 'OdeTimJson', + ttlField: 'recordGeneratedAt', + timeField: 'metadata.odeReceivedAt', + intersectionField: null, + rsuIP: 'metadata.originIp', + }, + { + name: 'OdeTimBroadcastJson', + ttlField: 'recordGeneratedAt', + timeField: 'metadata.odeReceivedAt', + intersectionField: null, + rsuIP: 'metadata.originIp', + }, + { + name: 'OdeTIMCertExpirationTimeJson', + ttlField: 'recordGeneratedAt', + timeField: 'metadata.odeReceivedAt', + intersectionField: null, + rsuIP: 'metadata.originIp', + }, + + // Ode Raw ASN + { + name: 'OdeRawEncodedBSMJson', + ttlField: 'recordGeneratedAt', + timeField: 'BsmMessageContent.metadata.utctimestamp', + intersectionField: null, + rsuIP: 'BsmMessageContent.metadata.originRsu', + }, + { + name: 'OdeRawEncodedMAPJson', + ttlField: 'recordGeneratedAt', + timeField: 'MapMessageContent.metadata.utctimestamp', + intersectionField: null, + rsuIP: 'MapMessageContent.metadata.originRsu', + }, + { + name: 'OdeRawEncodedSPATJson', + ttlField: 'recordGeneratedAt', + timeField: 'SpatMessageContent.metadata.utctimestamp', + intersectionField: null, + rsuIP: 'SpatMessageContent.metadata.originRsu', + }, + { + name: 'OdeRawEncodedSRMJson', + ttlField: 'recordGeneratedAt', + timeField: 'SrmMessageContent.metadata.utctimestamp', + intersectionField: null, + rsuIP: 'SpatMessageContent.metadata.originRsu', + }, + { + name: 'OdeRawEncodedSSMJson', + ttlField: 'recordGeneratedAt', + timeField: 'SsmMessageContent.metadata.utctimestamp', + intersectionField: null, + rsuIP: 'SpatMessageContent.metadata.originRsu', + }, + { + name: 'OdeRawEncodedTIMJson', + ttlField: 'recordGeneratedAt', + timeField: 'TimMessageContent.metadata.utctimestamp', + intersectionField: null, + rsuIP: 'SpatMessageContent.metadata.originRsu', + }, + + // GeoJson Converter Data + { + name: 'ProcessedMap', + ttlField: 'recordGeneratedAt', + timeField: 'properties.timeStamp', + intersectionField: 'properties.intersectionId', + }, + { + name: 'ProcessedSpat', + ttlField: 'recordGeneratedAt', + timeField: 'odeReceivedAt', + intersectionField: 'intersectionId', + }, + + // Conflict Monitor Events + { + name: 'CmStopLineStopEvent', + ttlField: 'eventGeneratedAt', + timeField: 'eventGeneratedAt', + intersectionField: 'intersectionID', + }, + { + name: 'CmStopLinePassageEvent', + ttlField: 'eventGeneratedAt', + timeField: 'eventGeneratedAt', + intersectionField: 'intersectionID', + }, + { + name: 'CmIntersectionReferenceAlignmentEvents', + ttlField: 'eventGeneratedAt', + timeField: 'eventGeneratedAt', + intersectionField: 'intersectionID', + }, + { + name: 'CmSignalGroupAlignmentEvents', + ttlField: 'eventGeneratedAt', + timeField: 'eventGeneratedAt', + intersectionField: 'intersectionID', + }, + { + name: 'CmConnectionOfTravelEvent', + ttlField: 'eventGeneratedAt', + timeField: 'eventGeneratedAt', + intersectionField: 'intersectionID', + }, + { + name: 'CmSignalStateConflictEvents', + ttlField: 'eventGeneratedAt', + timeField: 'eventGeneratedAt', + intersectionField: 'intersectionID', + }, + { + name: 'CmLaneDirectionOfTravelEvent', + ttlField: 'eventGeneratedAt', + timeField: 'eventGeneratedAt', + intersectionField: 'intersectionID', + }, + { + name: 'CmSpatTimeChangeDetailsEvent', + ttlField: 'eventGeneratedAt', + timeField: 'eventGeneratedAt', + intersectionField: 'intersectionID', + }, + { + name: 'CmSpatMinimumDataEvents', + ttlField: 'eventGeneratedAt', + timeField: 'eventGeneratedAt', + intersectionField: 'intersectionID', + }, + { + name: 'CmMapBroadcastRateEvents', + ttlField: 'eventGeneratedAt', + timeField: 'eventGeneratedAt', + intersectionField: 'intersectionID', + }, + { + name: 'CmMapMinimumDataEvents', + ttlField: 'eventGeneratedAt', + timeField: 'eventGeneratedAt', + intersectionField: 'intersectionID', + }, + { + name: 'CmSpatBroadcastRateEvents', + ttlField: 'eventGeneratedAt', + timeField: 'eventGeneratedAt', + intersectionField: 'intersectionID', + }, + { + name: 'CmBsmEvents', + ttlField: 'recordGeneratedAt', + timeField: 'recordGeneratedAt', + intersectionField: 'intersectionID', + }, + + // Conflict Monitor Assessments + { + name: 'CmLaneDirectionOfTravelAssessment', + ttlField: 'assessmentGeneratedAt', + timeField: 'assessmentGeneratedAt', + intersectionField: 'intersectionID', + }, + { + name: 'CmConnectionOfTravelAssessment', + ttlField: 'assessmentGeneratedAt', + timeField: 'assessmentGeneratedAt', + intersectionField: 'intersectionID', + }, + { + name: 'CmSignalStateEventAssessment', + ttlField: 'assessmentGeneratedAt', + timeField: 'assessmentGeneratedAt', + intersectionField: 'intersectionID', + }, + { + name: 'CmStopLineStopAssessment', + ttlField: 'assessmentGeneratedAt', + timeField: 'assessmentGeneratedAt', + intersectionField: 'intersectionID', + }, + + // Conflict Monitor Notifications + { + name: 'CmSpatTimeChangeDetailsNotification', + ttlField: 'notificationGeneratedAt', + timeField: 'notificationGeneratedAt', + intersectionField: 'intersectionID', + }, + { + name: 'CmLaneDirectionOfTravelNotification', + ttlField: 'notificationGeneratedAt', + timeField: 'notificationGeneratedAt', + intersectionField: 'intersectionID', + }, + { + name: 'CmConnectionOfTravelNotification', + ttlField: 'notificationGeneratedAt', + timeField: 'notificationGeneratedAt', + intersectionField: 'intersectionID', + }, + { + name: 'CmAppHealthNotifications', + ttlField: 'notificationGeneratedAt', + timeField: 'notificationGeneratedAt', + intersectionField: 'intersectionID', + }, + { + name: 'CmSignalStateConflictNotification', + ttlField: 'notificationGeneratedAt', + timeField: 'notificationGeneratedAt', + intersectionField: 'intersectionID', + }, + { + name: 'CmSignalGroupAlignmentNotification', + ttlField: 'notificationGeneratedAt', + timeField: 'notificationGeneratedAt', + intersectionField: 'intersectionID', + }, + { + name: 'CmStopLinePassageNotification', + ttlField: 'notificationGeneratedAt', + timeField: 'notificationGeneratedAt', + intersectionField: 'intersectionID', + }, + { + name: 'CmStopLineStopNotification', + ttlField: 'notificationGeneratedAt', + timeField: 'notificationGeneratedAt', + intersectionField: 'intersectionID', + }, + { + name: 'CmNotification', + ttlField: 'notificationGeneratedAt', + timeField: 'notificationGeneratedAt', + intersectionField: 'intersectionID', + }, +] + +try { + db.getMongo().setReadPref('primaryPreferred') + db = db.getSiblingDB('ConflictMonitor') + db.getMongo().setReadPref('primaryPreferred') + var isMaster = db.isMaster() + if (isMaster.primary) { + console.log('Connected to the primary replica set member.') + } else { + console.log('Not connected to the primary replica set member. Current node: ' + isMaster.host) + } +} catch (err) { + console.log('Could not switch DB to Sibling DB') + console.log(err) } // Create Users in Database -for(user of users){ - createUser(user); +for (user of users) { + createUser(user) } // Wait for the collections to exist in mongo before trying to create indexes on them -let missing_collection_count; +let missing_collection_count do { - try { - missing_collection_count = 0; - const collectionNames = db.getCollectionNames(); - for (collection of collections) { - // Create Collection if it doesn't exist - let created = false; - if(!collectionNames.includes(collection.name)){ - created = createCollection(collection); - // created = true; - }else{ - created = true; - } - - if(created){ - createTTLIndex(collection); - createTimeIntersectionIndex(collection); - createTimeRsuIpIndex(collection); - createTimeIndex(collection); - }else{ - missing_collection_count++; - console.log("Collection " + collection.name + " does not exist yet"); - } - } - if (missing_collection_count > 0) { - console.log("Waiting on " + missing_collection_count + " collections to be created...will try again in " + retryMilliseconds + " ms"); - sleep(retryMilliseconds); - } - } catch (err) { - console.log("Error while setting up TTL indexs in collections"); - console.log(rs.status()); - console.error(err); - sleep(retryMilliseconds); + try { + missing_collection_count = 0 + const collectionNames = db.getCollectionNames() + for (collection of collections) { + // Create Collection if it doesn't exist + let created = false + if (!collectionNames.includes(collection.name)) { + created = createCollection(collection) + // created = true; + } else { + created = true + } + + if (created) { + createTTLIndex(collection) + createTimeIntersectionIndex(collection) + createTimeRsuIpIndex(collection) + createTimeIndex(collection) + } else { + missing_collection_count++ + console.log('Collection ' + collection.name + ' does not exist yet') + } } -} while (missing_collection_count > 0); - -console.log("Finished Creating All TTL indexes"); - -function createUser(user){ - try{ - console.log("Creating User: " + user.username + " with Permissions: " + user.roles); - db.createUser( - { - user: user.username, - pwd: user.password, - roles: [ - { role: user.roles, db: user.database }, - ] - }); - - }catch (err){ - console.log(err); - console.log("Unable to Create User. Perhaps the User already exists."); + if (missing_collection_count > 0) { + console.log( + 'Waiting on ' + + missing_collection_count + + ' collections to be created...will try again in ' + + retryMilliseconds + + ' ms' + ) + sleep(retryMilliseconds) } + } catch (err) { + console.log('Error while setting up TTL indexs in collections') + console.log(rs.status()) + console.error(err) + sleep(retryMilliseconds) + } +} while (missing_collection_count > 0) + +console.log('Finished Creating All TTL indexes') + +function createUser(user) { + try { + console.log('Creating User: ' + user.username + ' with Permissions: ' + user.roles) + db.createUser({ + user: user.username, + pwd: user.password, + roles: [{ role: user.roles, db: user.database }], + }) + } catch (err) { + console.log(err) + console.log('Unable to Create User. Perhaps the User already exists.') + } } -function createCollection(collection){ - try { - db.createCollection(collection.name); - return true; - } catch (err) { - console.log("Unable to Create Collection: " + collection.name); - console.log(err); - return false; - } +function createCollection(collection) { + try { + db.createCollection(collection.name) + return true + } catch (err) { + console.log('Unable to Create Collection: ' + collection.name) + console.log(err) + return false + } } // Create TTL Indexes function createTTLIndex(collection) { - try{ - if(collection.hasOwnProperty("ttlField") && collection.ttlField != null){ - const ttlField = collection.ttlField; - const collectionName = collection.name; - - let indexJson = {}; - indexJson[ttlField] = 1; - - if (ttlIndexExists(collection)) { - db.runCommand({ - "collMod": collectionName, - "index": { - keyPattern: indexJson, - expireAfterSeconds: expireSeconds - } - }); - console.log("Updated TTL index for " + collectionName + " using the field: " + ttlField + " as the timestamp"); - }else{ - db[collectionName].createIndex(indexJson, - {expireAfterSeconds: expireSeconds} - ); - console.log("Created TTL index for " + collectionName + " using the field: " + ttlField + " as the timestamp"); - } - } - } catch(err){ - console.log("Failed to Create or Update index for " + collectionName + "using the field: " + ttlField + " as the timestamp"); + try { + if (collection.hasOwnProperty('ttlField') && collection.ttlField != null) { + const ttlField = collection.ttlField + const collectionName = collection.name + + let indexJson = {} + indexJson[ttlField] = 1 + + if (ttlIndexExists(collection)) { + db.runCommand({ + collMod: collectionName, + index: { + keyPattern: indexJson, + expireAfterSeconds: expireSeconds, + }, + }) + console.log('Updated TTL index for ' + collectionName + ' using the field: ' + ttlField + ' as the timestamp') + } else { + db[collectionName].createIndex(indexJson, { expireAfterSeconds: expireSeconds }) + console.log('Created TTL index for ' + collectionName + ' using the field: ' + ttlField + ' as the timestamp') + } } + } catch (err) { + console.log( + 'Failed to Create or Update index for ' + collectionName + 'using the field: ' + ttlField + ' as the timestamp' + ) + } } -function createTimeIndex(collection){ - if(timeIndexExists(collection)){ - // Skip if Index already Exists - return; - } +function createTimeIndex(collection) { + if (timeIndexExists(collection)) { + // Skip if Index already Exists + return + } - if(collection.hasOwnProperty("timeField") && collection.timeField != null){ - const collectionName = collection.name; - const timeField = collection.timeField; - console.log("Creating Time Index for " + collectionName); - - var indexJson = {}; - indexJson[timeField] = -1; - - try { - db[collectionName].createIndex(indexJson); - console.log("Created Time Intersection index for " + collectionName + " using the field: " + timeField + " as the timestamp"); - } catch (err) { - db.runCommand({ - "collMod": collectionName, - "index": { - keyPattern: indexJson - } - }); - console.log("Updated Time index for " + collectionName + " using the field: " + timeField + " as the timestamp"); - } - } -} + if (collection.hasOwnProperty('timeField') && collection.timeField != null) { + const collectionName = collection.name + const timeField = collection.timeField + console.log('Creating Time Index for ' + collectionName) -function createTimeRsuIpIndex(){ - if(timeRsuIpIndexExists(collection)){ - // Skip if Index already Exists - return; - } + var indexJson = {} + indexJson[timeField] = -1 - if(collection.hasOwnProperty("timeField") && collection.timeField != null && collection.hasOwnProperty("rsuIP") && collection.rsuIP != null){ - const collectionName = collection.name; - const timeField = collection.timeField; - const rsuIP = collection.rsuIP; - console.log("Creating Time rsuIP Index for " + collectionName); - - var indexJson = {}; - indexJson[rsuIP] = -1; - indexJson[timeField] = -1; - - - try { - db[collectionName].createIndex(indexJson); - console.log("Created Time rsuIP Intersection index for " + collectionName + " using the field: " + timeField + " as the timestamp and : " + rsuIP+" as the rsuIP"); - } catch (err) { - db.runCommand({ - "collMod": collectionName, - "index": { - keyPattern: indexJson - } - }); - console.log("Updated Time rsuIP index for " + collectionName + " using the field: " + timeField + " as the timestamp and : " + rsuIP+" as the rsuIP"); - } + try { + db[collectionName].createIndex(indexJson) + console.log( + 'Created Time Intersection index for ' + collectionName + ' using the field: ' + timeField + ' as the timestamp' + ) + } catch (err) { + db.runCommand({ + collMod: collectionName, + index: { + keyPattern: indexJson, + }, + }) + console.log('Updated Time index for ' + collectionName + ' using the field: ' + timeField + ' as the timestamp') } + } } +function createTimeRsuIpIndex() { + if (timeRsuIpIndexExists(collection)) { + // Skip if Index already Exists + return + } + + if ( + collection.hasOwnProperty('timeField') && + collection.timeField != null && + collection.hasOwnProperty('rsuIP') && + collection.rsuIP != null + ) { + const collectionName = collection.name + const timeField = collection.timeField + const rsuIP = collection.rsuIP + console.log('Creating Time rsuIP Index for ' + collectionName) + + var indexJson = {} + indexJson[rsuIP] = -1 + indexJson[timeField] = -1 -function createTimeIntersectionIndex(collection){ - if(timeIntersectionIndexExists(collection)){ - // Skip if Index already Exists - return; + try { + db[collectionName].createIndex(indexJson) + console.log( + 'Created Time rsuIP Intersection index for ' + + collectionName + + ' using the field: ' + + timeField + + ' as the timestamp and : ' + + rsuIP + + ' as the rsuIP' + ) + } catch (err) { + db.runCommand({ + collMod: collectionName, + index: { + keyPattern: indexJson, + }, + }) + console.log( + 'Updated Time rsuIP index for ' + + collectionName + + ' using the field: ' + + timeField + + ' as the timestamp and : ' + + rsuIP + + ' as the rsuIP' + ) } + } +} + +function createTimeIntersectionIndex(collection) { + if (timeIntersectionIndexExists(collection)) { + // Skip if Index already Exists + return + } + + if ( + collection.hasOwnProperty('timeField') && + collection.timeField != null && + collection.hasOwnProperty('intersectionField') && + collection.intersectionField != null + ) { + const collectionName = collection.name + const timeField = collection.timeField + const intersectionField = collection.intersectionField + console.log('Creating time intersection index for ' + collectionName) + + var indexJson = {} + indexJson[intersectionField] = -1 + indexJson[timeField] = -1 - if(collection.hasOwnProperty("timeField") && collection.timeField != null && collection.hasOwnProperty("intersectionField") && collection.intersectionField != null){ - const collectionName = collection.name; - const timeField = collection.timeField; - const intersectionField = collection.intersectionField; - console.log("Creating time intersection index for " + collectionName); - - var indexJson = {}; - indexJson[intersectionField] = -1; - indexJson[timeField] = -1; - - - try { - db[collectionName].createIndex(indexJson); - console.log("Created time intersection index for " + collectionName + " using the field: " + timeField + " as the timestamp and : " + intersectionField + " as the rsuIP"); - } catch (err) { - db.runCommand({ - "collMod": collectionName, - "index": { - keyPattern: indexJson - } - }); - console.log("Updated time intersection index for " + collectionName + " using the field: " + timeField + " as the timestamp and : " + intersectionField + " as the rsuIP"); - } + try { + db[collectionName].createIndex(indexJson) + console.log( + 'Created time intersection index for ' + + collectionName + + ' using the field: ' + + timeField + + ' as the timestamp and : ' + + intersectionField + + ' as the rsuIP' + ) + } catch (err) { + db.runCommand({ + collMod: collectionName, + index: { + keyPattern: indexJson, + }, + }) + console.log( + 'Updated time intersection index for ' + + collectionName + + ' using the field: ' + + timeField + + ' as the timestamp and : ' + + intersectionField + + ' as the rsuIP' + ) } + } } function ttlIndexExists(collection) { - return db[collection.name].getIndexes().find((idx) => idx.hasOwnProperty("expireAfterSeconds")) !== undefined; + return db[collection.name].getIndexes().find((idx) => idx.hasOwnProperty('expireAfterSeconds')) !== undefined } -function timeIntersectionIndexExists(collection){ - return db[collection.name].getIndexes().find((idx) => idx.name == collection.intersectionField + "_-1_" + collection.timeField + "_-1") !== undefined; +function timeIntersectionIndexExists(collection) { + return ( + db[collection.name] + .getIndexes() + .find((idx) => idx.name == collection.intersectionField + '_-1_' + collection.timeField + '_-1') !== undefined + ) } -function timeRsuIpIndexExists(collection){ - return db[collection.name].getIndexes().find((idx) => idx.name == collection.rsuIP + "_-1_" + collection.timeField + "_-1") !== undefined; +function timeRsuIpIndexExists(collection) { + return ( + db[collection.name] + .getIndexes() + .find((idx) => idx.name == collection.rsuIP + '_-1_' + collection.timeField + '_-1') !== undefined + ) } -function timeIndexExists(collection){ - return db[collection.name].getIndexes().find((idx) => idx.name == collection.timeField + "_-1") !== undefined; +function timeIndexExists(collection) { + return db[collection.name].getIndexes().find((idx) => idx.name == collection.timeField + '_-1') !== undefined } diff --git a/conflictmonitor/docker/mongo/mongo_initialize.sh b/conflictmonitor/docker/mongo/mongo_initialize.sh deleted file mode 100644 index 9745b3ae4..000000000 --- a/conflictmonitor/docker/mongo/mongo_initialize.sh +++ /dev/null @@ -1,12 +0,0 @@ -#!/bin/bash - -# echo "Running mongo_initialize.sh" - -# # connect_string="$1" -# # echo "connect_string = ${connect_string}" -# # cd /scripts -# # ls - -# mongosh mongodb://${DB_HOST_IP}:27017/ConflictMonitor?replicaSet=rs0 create_indexes.txt - -# # $SHELL From b4bfb18673ddcaf16ae8793201beeb55cc212ff0 Mon Sep 17 00:00:00 2001 From: jacob6838 Date: Thu, 30 May 2024 11:25:49 -0600 Subject: [PATCH 036/189] Re-adding SMTP env vars to sample.env --- sample.env | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/sample.env b/sample.env index 9a7a9d2aa..66adb2236 100644 --- a/sample.env +++ b/sample.env @@ -53,6 +53,13 @@ GOOGLE_APPLICATION_CREDENTIALS='./resources/google/sample_gcp_service_account.js MONGO_DB_URI= MONGO_DB_NAME="ode" +# Email configuration for sending firmware-manager failure emails +SMTP_EMAIL= +SMTP_USERNAME= +SMTP_PASSWORD= +FW_EMAIL_RECIPIENTS= +SMTP_SERVER_IP= + # Set these variables if using either "MONGODB" or "BIGQUERY" # COUNTS_MSG_TYPES: Comma separated list of message types. # COUNTS_MSG_TYPES must be set for the counts menu to correctly populate when building an image for deployment From 6e58feeb9f5528827eacc0388d24735ce8e866e4 Mon Sep 17 00:00:00 2001 From: jacob6838 Date: Thu, 30 May 2024 11:26:36 -0600 Subject: [PATCH 037/189] Update b_create_indexes.js --- conflictmonitor/docker/mongo/b_create_indexes.js | 1 - 1 file changed, 1 deletion(-) diff --git a/conflictmonitor/docker/mongo/b_create_indexes.js b/conflictmonitor/docker/mongo/b_create_indexes.js index 5100f31c1..5132d3549 100644 --- a/conflictmonitor/docker/mongo/b_create_indexes.js +++ b/conflictmonitor/docker/mongo/b_create_indexes.js @@ -368,7 +368,6 @@ do { let created = false if (!collectionNames.includes(collection.name)) { created = createCollection(collection) - // created = true; } else { created = true } From ab51895eac713d21bd83d55c7bfa2f1660ea884d Mon Sep 17 00:00:00 2001 From: jacob6838 Date: Thu, 30 May 2024 11:27:18 -0600 Subject: [PATCH 038/189] Minor spelling updates --- .vscode/settings.json | 1 + conflictmonitor/docker/mongo/b_create_indexes.js | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/.vscode/settings.json b/.vscode/settings.json index 710be3c5f..fa72519b6 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -49,6 +49,7 @@ "svcs", "TABLENAME", "usdotjpoode", + "utctimestamp", "wydot", "Zabbix" ] diff --git a/conflictmonitor/docker/mongo/b_create_indexes.js b/conflictmonitor/docker/mongo/b_create_indexes.js index 5132d3549..2c54340ae 100644 --- a/conflictmonitor/docker/mongo/b_create_indexes.js +++ b/conflictmonitor/docker/mongo/b_create_indexes.js @@ -393,7 +393,7 @@ do { sleep(retryMilliseconds) } } catch (err) { - console.log('Error while setting up TTL indexs in collections') + console.log('Error while setting up TTL indexes in collections') console.log(rs.status()) console.error(err) sleep(retryMilliseconds) From c101194ea81c49c256f3ce49a063d4ac1376ec9c Mon Sep 17 00:00:00 2001 From: jacob6838 Date: Thu, 30 May 2024 13:26:55 -0600 Subject: [PATCH 039/189] Fixing unit tests --- webapp/src/components/map/utilities/colors.ts | 2 +- webapp/src/generalSlices/rsuSlice.ts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/webapp/src/components/map/utilities/colors.ts b/webapp/src/components/map/utilities/colors.ts index a2561458a..ce16dac12 100644 --- a/webapp/src/components/map/utilities/colors.ts +++ b/webapp/src/components/map/utilities/colors.ts @@ -1,6 +1,6 @@ import { sha256 } from 'js-sha256' import { hsl, rgb } from 'color-convert' -import * as Color from 'color' +import Color from 'color' const DISALLOWED_COLORS = new Set( Array.from( diff --git a/webapp/src/generalSlices/rsuSlice.ts b/webapp/src/generalSlices/rsuSlice.ts index 7064a73fa..cdda10602 100644 --- a/webapp/src/generalSlices/rsuSlice.ts +++ b/webapp/src/generalSlices/rsuSlice.ts @@ -331,7 +331,7 @@ export const rsuSlice = createSlice({ state.value.ssmDisplay = !state.value.ssmDisplay }, setSelectedSrm: (state, action: PayloadAction) => { - state.value.selectedSrm = Object.keys(action.payload).length === 0 ? [] : [action.payload] + state.value.selectedSrm = Object.keys(action.payload ?? {}).length === 0 ? [] : [action.payload] }, toggleGeoMsgPointSelect: (state) => { state.value.addGeoMsgPoint = !state.value.addGeoMsgPoint From aa9ebea75a95bbf2f5aa7f87dcd8a3db48be2bc2 Mon Sep 17 00:00:00 2001 From: Michael7371 <40476797+Michael7371@users.noreply.github.com> Date: Thu, 30 May 2024 15:57:02 -0600 Subject: [PATCH 040/189] performance updates and basic authentication --- .../images/obu_ota_server/obu_ota_server.py | 29 ++++++++++++++++--- .../obu_ota_server/test_obu_ota_server.py | 26 +++++++++++++---- services/common/gcs_utils.py | 9 ++++-- services/common/tests/test_gcs_utils.py | 6 ++-- 4 files changed, 54 insertions(+), 16 deletions(-) diff --git a/services/addons/images/obu_ota_server/obu_ota_server.py b/services/addons/images/obu_ota_server/obu_ota_server.py index 4c2842a2a..91c1e8775 100644 --- a/services/addons/images/obu_ota_server/obu_ota_server.py +++ b/services/addons/images/obu_ota_server/obu_ota_server.py @@ -1,4 +1,6 @@ -from fastapi import FastAPI, Request, Response, HTTPException +from fastapi import FastAPI, Request, Response, HTTPException, Depends +from fastapi.security import HTTPBasic, HTTPBasicCredentials +from fastapi.middleware.cors import CORSMiddleware from common import gcs_utils import commsginia_manifest import os @@ -12,11 +14,28 @@ logging.basicConfig(format="%(levelname)s:%(message)s", level=log_level) +security = HTTPBasic() + + +def authenticate_user(credentials: HTTPBasicCredentials = Depends(security)): + correct_username = os.getenv("BASIC_AUTH_USERNAME") + correct_password = os.getenv("BASIC_AUTH_PASSWORD") + if ( + credentials.username != correct_username + or credentials.password != correct_password + ): + raise HTTPException( + status_code=401, + detail="Incorrect username or password", + headers={"WWW-Authenticate": "Basic"}, + ) + return credentials.username + @app.get("/") async def read_root(request: Request): return { - "message": "obu ota server root path", + "message": "obu ota server healthcheck", "root_path": request.scope.get("root_path"), } @@ -33,7 +52,7 @@ def get_firmware_list(): return files -@app.get("/firmwares") +@app.get("/firmwares/commsignia", dependencies=[Depends(authenticate_user)]) async def get_manifest(request: Request): try: files = get_firmware_list() @@ -93,7 +112,9 @@ async def read_file(file_path, start, end): raise HTTPException(status_code=500, detail="Error reading file") -@app.get("/firmwares/{firmware_id}") +@app.get( + "/firmwares/commsignia/{firmware_id}", dependencies=[Depends(authenticate_user)] +) async def get_fw(request: Request, firmware_id: str): file_path = f"/firmwares/{firmware_id}" diff --git a/services/addons/tests/obu_ota_server/test_obu_ota_server.py b/services/addons/tests/obu_ota_server/test_obu_ota_server.py index 94baa12c3..6dd63e6f2 100644 --- a/services/addons/tests/obu_ota_server/test_obu_ota_server.py +++ b/services/addons/tests/obu_ota_server/test_obu_ota_server.py @@ -1,7 +1,7 @@ import pytest import tempfile import os -from httpx import AsyncClient +from httpx import AsyncClient, BasicAuth from fastapi import HTTPException from unittest.mock import patch @@ -38,8 +38,8 @@ def test_get_firmware_list_gcs(mock_list_gcs_blobs, mock_getenv): result = get_firmware_list() - mock_getenv.assert_called_once_with("BLOB_STORAGE_PROVIDER", "LOCAL") - mock_list_gcs_blobs.assert_called_once_with("firmwares", ".tar.sig") + # mock_getenv.assert_called_once_with("BLOB_STORAGE_PROVIDER", "LOCAL") + mock_list_gcs_blobs.assert_called_once_with("GCP", ".tar.sig") assert result == ["/firmwares/test1.tar.sig", "/firmwares/test2.tar.sig"] @@ -189,14 +189,20 @@ async def test_read_file_no_end_range(): os.remove(temp_path) +@patch.dict( + "os.environ", {"BASIC_AUTH_USERNAME": "username", "BASIC_AUTH_PASSWORD": "password"} +) @pytest.mark.anyio async def test_read_root(): async with AsyncClient(app=app, base_url="http://test") as ac: response = await ac.get("/") assert response.status_code == 200 - assert response.json() == {"message": "obu ota server root path", "root_path": ""} + assert response.json() == {"message": "obu ota server healthcheck", "root_path": ""} +@patch.dict( + "os.environ", {"BASIC_AUTH_USERNAME": "username", "BASIC_AUTH_PASSWORD": "password"} +) @pytest.mark.anyio @patch("addons.images.obu_ota_server.obu_ota_server.get_firmware_list") @patch("addons.images.obu_ota_server.obu_ota_server.commsginia_manifest.add_contents") @@ -207,11 +213,16 @@ async def test_get_manifest(mock_commsginia_manifest, mock_get_firmware_list): ] mock_commsginia_manifest.return_value = {"json": "data"} async with AsyncClient(app=app, base_url="http://test") as ac: - response = await ac.get("/firmwares") + response = await ac.get( + "/firmwares/commsignia", auth=BasicAuth("username", "password") + ) assert response.status_code == 200 assert response.json() == {"json": "data"} +@patch.dict( + "os.environ", {"BASIC_AUTH_USERNAME": "username", "BASIC_AUTH_PASSWORD": "password"} +) @pytest.mark.anyio @patch("addons.images.obu_ota_server.obu_ota_server.get_firmware") @patch("addons.images.obu_ota_server.obu_ota_server.parse_range_header") @@ -221,7 +232,10 @@ async def test_get_fw(mock_read_file, mock_parse_range_header, mock_get_firmware mock_parse_range_header.return_value = 0, 100 mock_read_file.return_value = b"Test data", 100, 100 async with AsyncClient(app=app, base_url="http://test") as ac: - response = await ac.get("/firmwares/test_firmware_id") + response = await ac.get( + "/firmwares/commsignia/test_firmware_id", + auth=BasicAuth("username", "password"), + ) assert response.status_code == 200 assert response.content == b"Test data" assert response.headers["Content-Length"] == "100" diff --git a/services/common/gcs_utils.py b/services/common/gcs_utils.py index 9c41d8930..9244566d2 100644 --- a/services/common/gcs_utils.py +++ b/services/common/gcs_utils.py @@ -33,9 +33,12 @@ def list_gcs_blobs(gcs_prefix, file_extension): if blob.name.endswith(file_extension): path = f"/firmwares/{blob.name.split(gcs_prefix)[-1]}" files.append(path) - blob.download_to_filename(path) - logging.debug(f"Downloaded blob {blob.name} to {path}") - download_count += 1 + if not os.path.exists(os.path.dirname(path)): + blob.download_to_filename(path) + logging.debug(f"Downloaded blob {blob.name} to {path}") + download_count += 1 + else: + logging.debug(f"File {path} already exists, skipping download.") else: logging.debug(f"Blob {blob.name} does not end with {file_extension}") blob_count += 1 diff --git a/services/common/tests/test_gcs_utils.py b/services/common/tests/test_gcs_utils.py index e769e2303..15fcbf105 100644 --- a/services/common/tests/test_gcs_utils.py +++ b/services/common/tests/test_gcs_utils.py @@ -54,9 +54,9 @@ def test_list_gcs_blobs(mock_storage_client): # validate mock_storage_client.assert_called_with("test-project") mock_client.list_blobs.assert_called_with( - bucket_name="test-bucket", prefix="path/to/files/" + "test-bucket", prefix="path/to/files/", delimiter="/" ) - assert result == ["file1.txt", "file3.txt"] + assert result == ["/firmwares/file1.txt", "/firmwares/file3.txt"] @patch.dict( @@ -79,6 +79,6 @@ def test_list_gcs_blobs_empty(mock_storage_client): # validate mock_storage_client.assert_called_with("test-project") mock_client.list_blobs.assert_called_with( - bucket_name="test-bucket", prefix="path/to/files/" + "test-bucket", prefix="path/to/files/", delimiter="/" ) assert result == [] From 3f407f0b2cada59bae1ddac9ba5b311c2cc4d0bd Mon Sep 17 00:00:00 2001 From: jacob6838 Date: Thu, 30 May 2024 16:03:25 -0600 Subject: [PATCH 041/189] Updating reference to jpo-deduplicator to use gcr --- docker-compose.yml | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/docker-compose.yml b/docker-compose.yml index ea3ecd91c..7637514fb 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -275,10 +275,7 @@ services: condition: service_healthy deduplicator: - build: - context: /mnt/c/Users/rando/Github/jpo-conflictmonitor - dockerfile: Dedup_Dockerfile - image: jpo-deduplicator:latest + image: docker push us-central1-docker.pkg.dev/cdot-oim-cv-dev/jpo-conflictvisualizer-api-cvmanager/jpo-deduplicator:v1.0.0 restart: always environment: DOCKER_HOST_IP: ${DOCKER_HOST_IP} From 7204c4ce5d3901c61f69b9dd67848f78ab2550a6 Mon Sep 17 00:00:00 2001 From: jacob6838 Date: Thu, 30 May 2024 16:03:42 -0600 Subject: [PATCH 042/189] Update docker-compose.yml --- docker-compose.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docker-compose.yml b/docker-compose.yml index 7637514fb..17d2bb501 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -275,7 +275,7 @@ services: condition: service_healthy deduplicator: - image: docker push us-central1-docker.pkg.dev/cdot-oim-cv-dev/jpo-conflictvisualizer-api-cvmanager/jpo-deduplicator:v1.0.0 + image: us-central1-docker.pkg.dev/cdot-oim-cv-dev/jpo-conflictvisualizer-api-cvmanager/jpo-deduplicator:v1.0.0 restart: always environment: DOCKER_HOST_IP: ${DOCKER_HOST_IP} From 783601a0841b1eac931d0725c0dc856728b24e80 Mon Sep 17 00:00:00 2001 From: Michael7371 <40476797+Michael7371@users.noreply.github.com> Date: Thu, 30 May 2024 16:35:05 -0600 Subject: [PATCH 043/189] updates to move basic auth from nginx to api --- docker-compose-obu-ota-server.yml | 8 +++---- resources/ota/nginx/nginx-plain.conf | 3 --- resources/ota/nginx/nginx-ssl.conf | 3 --- resources/ota/nginx/set_up_users.sh | 24 ------------------- sample.env | 1 + .../images/obu_ota_server/obu_ota_server.py | 4 ++-- .../addons/images/obu_ota_server/sample.env | 1 + 7 files changed, 8 insertions(+), 36 deletions(-) delete mode 100644 resources/ota/nginx/set_up_users.sh diff --git a/docker-compose-obu-ota-server.yml b/docker-compose-obu-ota-server.yml index 828305cc3..7bf7cce6b 100644 --- a/docker-compose-obu-ota-server.yml +++ b/docker-compose-obu-ota-server.yml @@ -13,6 +13,10 @@ services: SERVER_HOST: ${OBU_OTA_SERVER_HOST} LOGGING_LEVEL: ${OBU_OTA_SERVER_LOGGING_LEVEL} BLOB_STORAGE_PROVIDER: ${BLOB_STORAGE_PROVIDER} + BLOB_STORAGE_PATH: ${BLOB_STORAGE_PATH} + + OTA_USERNAME: ${OTA_USERNAME} + OTA_PASSWORD: ${OTA_PASSWORD} volumes: - ./resources/ota:/firmwares logging: @@ -30,13 +34,9 @@ services: - 443:443 environment: NGINX_ENVSUBST_OUTPUT_DIR: /etc/nginx - SERVER_HOST: ${OBU_OTA_SERVER_HOST} - OTA_USERNAME: ${OTA_USERNAME} - OTA_PASSWORD: ${OTA_PASSWORD} volumes: - ./resources/ota/nginx/nginx-${NGINX_ENCRYPTION}.conf:/etc/nginx/templates/nginx.conf.template - - ./resources/ota/nginx/set_up_users.sh:/docker-entrypoint.d/set_up_users.sh - ./resources/ota/nginx/gen_dhparam.sh:/docker-entrypoint.d/gen_dhparam.sh - ./resources/ota/nginx/ssl/${SERVER_CERT_FILE}:/etc/ssl/certs/ota_server.crt - ./resources/ota/nginx/ssl/${SERVER_KEY_FILE}:/etc/ssl/private/ota_server.key diff --git a/resources/ota/nginx/nginx-plain.conf b/resources/ota/nginx/nginx-plain.conf index 0b5d43234..d47854cc6 100644 --- a/resources/ota/nginx/nginx-plain.conf +++ b/resources/ota/nginx/nginx-plain.conf @@ -19,9 +19,6 @@ http { listen 80; server_name http://$SERVER_HOST; - auth_basic "Basic Protected Area"; - auth_basic_user_file /var/www/.htpasswd; - client_max_body_size 300M; location ~ /\.ht { diff --git a/resources/ota/nginx/nginx-ssl.conf b/resources/ota/nginx/nginx-ssl.conf index e93ba5b67..ffa10bfd5 100644 --- a/resources/ota/nginx/nginx-ssl.conf +++ b/resources/ota/nginx/nginx-ssl.conf @@ -58,9 +58,6 @@ http { ssl_certificate /etc/ssl/certs/ota_server.crt; ssl_certificate_key /etc/ssl/private/ota_server.key; - auth_basic "Basic Protected Area"; - auth_basic_user_file /var/www/.htpasswd; - client_max_body_size 300M; location ~ /\.ht { diff --git a/resources/ota/nginx/set_up_users.sh b/resources/ota/nginx/set_up_users.sh deleted file mode 100644 index e9696bd82..000000000 --- a/resources/ota/nginx/set_up_users.sh +++ /dev/null @@ -1,24 +0,0 @@ -#!/usr/bin/env bash - -function generate_user() { - htpasswd -c -b /var/www/.htpasswd "${OTA_USERNAME}" "${OTA_PASSWORD}" -} - -function main() { - mkdir -p /var/www/ - chmod 755 /var/www/ - touch /var/www/.htpasswd - generate_user -} - -if [[ -z "${OTA_USERNAME}" ]]; then - echo "Error: USERNAME environment variable is not set." - exit 1 -fi - -if [[ -z "${OTA_PASSWORD}" ]]; then - echo "Error: PASSWORD environment variable is not set." - exit 1 -fi - -main \ No newline at end of file diff --git a/sample.env b/sample.env index 299459235..f00ac8581 100644 --- a/sample.env +++ b/sample.env @@ -87,6 +87,7 @@ MAPBOX_INIT_ZOOM="10" # Firmware Manager BLOB_STORAGE_PROVIDER=GCP BLOB_STORAGE_BUCKET= +BLOB_STORAGE_PATH= GCP_PROJECT= # Levels are "DEBUG", "INFO", "WARNING", and "ERROR" diff --git a/services/addons/images/obu_ota_server/obu_ota_server.py b/services/addons/images/obu_ota_server/obu_ota_server.py index 91c1e8775..915f7ff30 100644 --- a/services/addons/images/obu_ota_server/obu_ota_server.py +++ b/services/addons/images/obu_ota_server/obu_ota_server.py @@ -18,8 +18,8 @@ def authenticate_user(credentials: HTTPBasicCredentials = Depends(security)): - correct_username = os.getenv("BASIC_AUTH_USERNAME") - correct_password = os.getenv("BASIC_AUTH_PASSWORD") + correct_username = os.getenv("OTA_USERNAME") + correct_password = os.getenv("OTA_PASSWORD") if ( credentials.username != correct_username or credentials.password != correct_password diff --git a/services/addons/images/obu_ota_server/sample.env b/services/addons/images/obu_ota_server/sample.env index 8d7817f5d..898befda8 100644 --- a/services/addons/images/obu_ota_server/sample.env +++ b/services/addons/images/obu_ota_server/sample.env @@ -10,6 +10,7 @@ BLOB_STORAGE_PROVIDER="LOCAL" # For users using GCP cloud storage BLOB_STORAGE_BUCKET="" +BLOB_STORAGE_PATH="" GCP_PROJECT="" GOOGLE_APPLICATION_CREDENTIALS="" From 7d7305a9c7c08570f2807b640ad8f4e1303aa1f5 Mon Sep 17 00:00:00 2001 From: Michael7371 <40476797+Michael7371@users.noreply.github.com> Date: Thu, 30 May 2024 16:41:03 -0600 Subject: [PATCH 044/189] updates to obu ota server k8 file --- resources/kubernetes/obu-ota-server.yaml | 92 ++++++++++++++++++------ 1 file changed, 71 insertions(+), 21 deletions(-) diff --git a/resources/kubernetes/obu-ota-server.yaml b/resources/kubernetes/obu-ota-server.yaml index 6c6b94f3b..dd9a02f84 100644 --- a/resources/kubernetes/obu-ota-server.yaml +++ b/resources/kubernetes/obu-ota-server.yaml @@ -49,10 +49,6 @@ metadata: kubernetes.io/ingress.regional-static-ip-name: 'cv-manager-ip' networking.gke.io/managed-certificates: 'obu-ota-server-managed-cert' networking.gke.io/v1beta1.FrontendConfig: 'obu-ota-server-frontend' - # basic auth config: - nginx.ingress.kubernetes.io/auth-type: basic - nginx.ingress.kubernetes.io/auth-secret: basic-auth - nginx.ingress.kubernetes.io/auth-realm: 'Authentication Required - foo' spec: defaultBackend: service: @@ -73,16 +69,34 @@ spec: port: number: 80 --- -# Limits the number of pods that are down simultaneously -apiVersion: policy/v1 -kind: PodDisruptionBudget +# Persistent Volume for Downloaded fmwr +apiVersion: v1 +kind: PersistentVolume metadata: - name: obu-ota-server-pdb + name: obu-ota-server-volume spec: - selector: - matchLabels: - app: obu-ota-server - maxUnavailable: 1 + accessModes: + - ReadWriteOnce + capacity: + storage: 10Gi + storageClassName: standard-rwo + gcePersistentDisk: + pdName: obu-ota-server-disk + fsType: ext4 +--- +# Persistent Volume Claim for Downloaded fmwr +apiVersion: v1 +kind: PersistentVolumeClaim +metadata: + name: obu-ota-server-claim +spec: + storageClassName: standard-rwo + volumeName: obu-ota-server-volume + accessModes: + - ReadWriteOnce + resources: + requests: + storage: 10Gi --- apiVersion: 'apps/v1' kind: 'Deployment' @@ -90,6 +104,8 @@ metadata: name: obu-ota-server spec: replicas: 1 + strategy: + type: Recreate selector: matchLabels: app: obu-ota-server @@ -101,18 +117,52 @@ spec: containers: - name: obu-ota-server imagePullPolicy: Always - # Replace image with an actual built jpo-cvmanager webapp image from an image repository - image: jpoode-cvmanager-webapp:tag + image: 'iss-health-check-image' resources: requests: - memory: '4Gi' - cpu: '2' - ephemeral-storage: '4Gi' + memory: 512Mi + cpu: 1 limits: - memory: '4Gi' - cpu: '2' - ephemeral-storage: '4Gi' + memory: 1Gi + cpu: 1 tty: true stdin: true ports: - - containerPort: 80 + - containerPort: 8085 + env: + - name: SERVER_HOST + value: '' + - name: LOGGING_LEVEL + value: '' + - name: BLOB_STORAGE_PROVIDER + value: '' + - name: GCP_PROJECT + value: '' + - name: BLOB_STORAGE_BUCKET + value: '' + - name: BLOB_STORAGE_PATH + value: '' + - name: GOOGLE_APPLICATION_CREDENTIALS + value: '' + - name: OTA_USERNAME + valueFrom: + secretKeyRef: + name: some-api-username-name + key: some-api-username-key + - name: OTA_PASSWORD + valueFrom: + secretKeyRef: + name: some-api-password-name + key: some-api-password-key + volumeMounts: + - name: cv-manager-service-key + mountPath: /home/secret + - name: firmware-folder + mountPath: '/firmwares/' + volumes: + - name: cv-manager-service-key + secret: + secretName: cv-manager-service-key + - name: firmware-folder + persistentVolumeClaim: + claimName: obu-ota-server-claim From c1df509f7f2ce90ad310a8cf319c96cbc2ed97d9 Mon Sep 17 00:00:00 2001 From: jacob6838 Date: Thu, 30 May 2024 17:06:14 -0600 Subject: [PATCH 045/189] Updating sample.env, removing duplicates --- sample.env | 84 +++++++++++++----------------------------------------- 1 file changed, 20 insertions(+), 64 deletions(-) diff --git a/sample.env b/sample.env index 66adb2236..3021be399 100644 --- a/sample.env +++ b/sample.env @@ -4,7 +4,6 @@ DOCKER_HOST_IP= WEBAPP_HOST_IP=${DOCKER_HOST_IP} # Note if using WEBAPP_DOMAIN for the docker-compose-webapp-deployment.yml file you will need to include http:// or https:// WEBAPP_DOMAIN=cvmanager.local.com -WEBAPP_CM_DOMAIN=cimms.local.com KC_HOST_IP=${DOCKER_HOST_IP} # Firmware Manager connectivity in the format 'http://endpoint:port' @@ -15,7 +14,7 @@ FIRMWARE_MANAGER_ENDPOINT=http://${DOCKER_HOST_IP}:8089 # If using docker then this value should be set to: http://${WEBAPP_HOST_IP}:3000 # If running the webapp using npm then set it to: http://localhost:3000 # Leave as * to allow all domains access -CORS_DOMAIN = * +CORS_DOMAIN=* # PostgreSQL Database connection information # this value may need to follow with the webapp host if debugging the applications @@ -97,6 +96,14 @@ MAPBOX_INIT_ZOOM="10" GCP_PROJECT_ID = '' +# CIMMS IntersectionMap Variables +CVIZ_MAPBOX_STYLE_URL= +CVIZ_MAPBOX_TOKEN= +CVIZ_API_SERVER_URL='http://localhost:8089' +CVIZ_API_WS_URL='ws://localhost:8089' +WEBAPP_CM_DOMAIN='cvmanager.local.com' +KEYCLOAK_CM_API_CLIENT_SECRET_KEY= + # --------------------------------------------------------------------- # Count Metric Addon: @@ -186,29 +193,16 @@ COUNTS_LOGGING_LEVEL="INFO" KC_LOGGING_LEVEL="INFO" -################################### -# JPO ConflictMonitor Docker Environment File # -################################### -# WARNING! The contents of this file may be sensitive. Take care not to add to source control. -# -# Instructions: -# Rename this file from `sample.env` to `.env` and Docker will automatically pick up the variables. -# -# Description: -# This file aggregates all the variables used in docker-compose.yml for ease of use. Variables are -# prefixed with the format DESTINATION_MESSAGETYPE_ to provide guidance. Some variables are filled -# out with commonly used values. -################################### - -#################### +########################################## +# JPO ConflictMonitor Docker Environment # +########################################## # General Properties # (Required) The IP address of Docker host machine which can be found by running "ifconfig" # Hint: look for "inet addr:" within "eth0" or "en0" for OSX # For Local deployments DB_HOST_IP and KAFKA_BROKER_IP should be set to DOCKER_HOST_IP, for remote deployments, these can be set independently. -DOCKER_HOST_IP= DB_HOST_IP= -KAFKA_BROKER_IP= +KAFKA_BROKER_IP=172.24.31.138 KAFKA_CONNECT_IP=localhost # Set to "CONFLUENT" if broker is a Confluent Cloud broker KAFKA_TYPE= @@ -229,27 +223,11 @@ CM_MONGO_USER_PASSWORD=user -################################### -# JPO GeoJsonConverter Docker Environment File # -################################### -# WARNING! The contents of this file may be sensitive. Take care not to add to source control. -# -# Instructions: -# Rename this file from `sample.env` to `.env` and Docker will automatically pick up the variables. -# -# Description: -# This file aggregates all the variables used in docker-compose.yml for ease of use. Variables are -# prefixed with the format DESTINATION_MESSAGETYPE_ to provide guidance. Some variables are filled -# out with commonly used values. -################################### - -#################### +########################################### +# JPO GeoJsonConverter Docker Environment # +########################################### # General Properties -# (Required) The IP address of Docker host machine which can be found by running "ifconfig" -# Hint: look for "inet addr:" within "eth0" or "en0" for OSX -DOCKER_HOST_IP= - #################### # Confluent Cloud Properties (for Confluent Cloud support) @@ -266,18 +244,9 @@ CONFLUENT_SECRET= GEOMETRY_OUTPUT_MODE= -################################### -# JPO ODE Docker Environment File # -################################### -# WARNING! The contents of this file may be sensitive. Take care not to add to source control. -# -# Instructions: -# Rename this file from `sample.env` to `.env` and Docker will automatically pick up the variables. -# -# Description: -# This file aggregates all the variables used in docker-compose.yml for ease of use. Variables are -# prefixed with the format DESTINATION_MESSAGETYPE_ to provide guidance. Some variables are filled -# out with commonly used values. +############################## +# JPO ODE Docker Environment # +############################## # # Variable usage guide: # _ACCESS_KEY_ID : The Amazon Web Services (AWS) bucket access key ID. @@ -291,10 +260,6 @@ GEOMETRY_OUTPUT_MODE= #################### # General Properties -# (Required) The IP address of Docker host machine which can be found by running "ifconfig" -# Hint: look for "inet addr:" within "eth0" or "en0" for OSX -DOCKER_HOST_IP= - # (Required) The full path of a directory on the host machine to be shared with docker containers. # Hint: usually the path to the `jpo-ode` directory. DOCKER_SHARED_VOLUME= @@ -319,19 +284,10 @@ CONFLUENT_SECRET= ### jpo-conflictvisualizer cvmanager api environment variables ### ################################################################## # Set the HOST IP of where the containers are running -KAFKA_BROKER_IP= -DOCKER_HOST_IP= KAFKA_BROKER_PORT=9092 -KEYCLOAK_ADMIN= -KEYCLOAK_ADMIN_PASSWORD= - # May be different from docker host ip for production deployments. Change as appropriate. CM_SERVER_URL=http://${DOCKER_HOST_IP}:8082 +AUTH_SERVER_URL=http://${DOCKER_HOST_IP}:8084 -DB_HOST_IP=${DOCKER_HOST_IP} DB_HOST_PORT=27017 - - -CM_MONGO_API_PASSWORD=api -CM_MONGO_API_USERNAME=api From a76b7b5ed03e8e3053a481c0c9f38fb530f20ae5 Mon Sep 17 00:00:00 2001 From: jacob6838 Date: Thu, 30 May 2024 17:07:38 -0600 Subject: [PATCH 046/189] Update sample.env --- sample.env | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sample.env b/sample.env index 3021be399..dbaf1bef5 100644 --- a/sample.env +++ b/sample.env @@ -202,7 +202,7 @@ KC_LOGGING_LEVEL="INFO" # Hint: look for "inet addr:" within "eth0" or "en0" for OSX # For Local deployments DB_HOST_IP and KAFKA_BROKER_IP should be set to DOCKER_HOST_IP, for remote deployments, these can be set independently. DB_HOST_IP= -KAFKA_BROKER_IP=172.24.31.138 +KAFKA_BROKER_IP= KAFKA_CONNECT_IP=localhost # Set to "CONFLUENT" if broker is a Confluent Cloud broker KAFKA_TYPE= From 4b76ca5f4a8232869f7b45efe4c947d12e35bcdf Mon Sep 17 00:00:00 2001 From: jacob6838 Date: Fri, 31 May 2024 13:02:19 -0600 Subject: [PATCH 047/189] Updating documentation to reflect the removal of the release-compose --- README.md | 17 +++++++++++++---- sample.env | 11 +++++++---- 2 files changed, 20 insertions(+), 8 deletions(-) diff --git a/README.md b/README.md index a84433269..2e39b2afa 100644 --- a/README.md +++ b/README.md @@ -59,12 +59,12 @@ The JPO CV Manager was originally developed for the Google Cloud Platform and a #### Running the ConflictMonitor and ConflictVisualizer API -Use the release-compose.yml to pull images for the jpo-conflictviualizer-api-cvmanager, jpo-conflictmonitor, jpo-geojsonconverter, and jpo-ode. -The release-compose.example.env file contains the necessary environment variables for the jpo-conflictvisualizer-api-cvmanager. Copy the release-compose.example.env file to your .env and fill in the necessary environment variables. +Use the docker-compose.yml to pull and run images for the jpo-conflictviualizer-api-cvmanager, jpo-conflictmonitor, jpo-geojsonconverter, and jpo-ode. +The example.env file contains the necessary environment variables for the jpo-conflictvisualizer-api-cvmanager. Copy the example.env file to your .env and fill in the necessary environment variables. For more information on these services and environment variables, please visit the usdot-jpo-ode repositories listed above. ``` -docker compose -f release-compose.yml up -d +docker compose up -d ``` #### Generating Sample Data @@ -78,6 +78,7 @@ The following steps are intended to help get a new user up and running the JPO C 1. Follow the Requirements and Limitations section and make sure all requirements are met. 2. Create a copy of the sample.env named ".env" and refer to the Environmental variables section below for more information on each variable. 1. Make sure at least the DOCKER_HOST_IP, KEYCLOAK_ADMIN_PASSWORD, KEYCLOAK_API_CLIENT_SECRET_KEY, and MAPBOX_TOKEN are set for this. + 2. Some of these variables, delineated by sections, pertain to the [jpo-conflictvisualizer (api)](https://github.com/usdot-jpo-ode/jpo-conflictvisualizer/tree/cvmgr-cimms-integration/api), [jpo-conflictmonitor](https://github.com/usdot-jpo-ode/jpo-conflictmonitor), [jpo-geojsonconverter](https://github.com/usdot-jpo-ode/jpo-geojsonconverter), [jpo-ode](https://github.com/usdot-jpo-ode/jpo-ode). Please see the documentation provided for these projects when setting these variables. 3. The CV Manager has four components that need to be containerized and deployed: the API, the PostgreSQL database, Keycloak, and the webapp. - If you are looking to deploy the CV Manager locally, you can simply run the docker-compose, make sure to fill out the .env file to ensure it launches properly. Also, edit your host file ([How to edit the host file](<[resources/kubernetes](https://docs.rackspace.com/support/how-to/modify-your-hosts-file/)>)) and add IP address of your docker host to these custom domains (remove the carrot brackets and just put the IP address): @@ -167,7 +168,15 @@ For the "Debug Solution" to run properly on Windows 10/11 using WSL, the followi 3. Apply the docker compose to start the required components: - docker compose up -d +``` +docker compose up -d +``` + +To run only the critical cvmanager components (no conflictmonitor/conflictvisualizer), use this command: + +``` +docker compose up -d cvmanager_api cvmanager_webapp cvmanager_postgres cvmanager_keycloak +``` 4. Access the website by going to: diff --git a/sample.env b/sample.env index dbaf1bef5..b184ace12 100644 --- a/sample.env +++ b/sample.env @@ -35,7 +35,6 @@ KEYCLOAK_ADMIN_PASSWORD= KEYCLOAK_REALM=cvmanager KEYCLOAK_API_CLIENT_ID=cvmanager-api KEYCLOAK_API_CLIENT_SECRET_KEY= -KEYCLOAK_CM_API_CLIENT_SECRET_KEY= KEYCLOAK_LOGIN_THEME_NAME=sample_theme # Note if using KEYCLOAK_DOMAIN for the docker-compose-webapp-deployment.yml file you will need to include http:// or https:// KEYCLOAK_DOMAIN=cvmanager.auth.com @@ -195,6 +194,7 @@ KC_LOGGING_LEVEL="INFO" ########################################## # JPO ConflictMonitor Docker Environment # +# Documentation: https://github.com/usdot-jpo-ode/jpo-conflictmonitor ########################################## # General Properties @@ -225,6 +225,7 @@ CM_MONGO_USER_PASSWORD=user ########################################### # JPO GeoJsonConverter Docker Environment # +# Documentation: https://github.com/usdot-jpo-ode/jpo-geojsonconverter ########################################### # General Properties @@ -246,6 +247,7 @@ GEOMETRY_OUTPUT_MODE= ############################## # JPO ODE Docker Environment # +# Documentation: https://github.com/usdot-jpo-ode/jpo-ode ############################## # # Variable usage guide: @@ -280,9 +282,10 @@ KAFKA_TYPE= CONFLUENT_KEY= CONFLUENT_SECRET= -################################################################## -### jpo-conflictvisualizer cvmanager api environment variables ### -################################################################## +############################################################## +# jpo-conflictvisualizer cvmanager api environment variables # +# Documentation: https://github.com/usdot-jpo-ode/jpo-conflictvisualizer/tree/cvmgr-cimms-integration/api +############################################################## # Set the HOST IP of where the containers are running KAFKA_BROKER_PORT=9092 From a05cbe942baa738433e09ff3abd32910decd66e7 Mon Sep 17 00:00:00 2001 From: Drew Johnston <31270488+drewjj@users.noreply.github.com> Date: Fri, 31 May 2024 15:12:26 -0600 Subject: [PATCH 048/189] Removed unused GCP permissions file and volume from CV Manager API docker-compose --- docker-compose.yml | 3 --- 1 file changed, 3 deletions(-) diff --git a/docker-compose.yml b/docker-compose.yml index 17d2bb501..89222a782 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -22,7 +22,6 @@ services: MONGO_DB_NAME: ${MONGO_DB_NAME} COUNTS_MSG_TYPES: ${COUNTS_MSG_TYPES} - GOOGLE_APPLICATION_CREDENTIALS: '/google/gcp_credentials.json' GEO_DB_NAME: ${GEO_DB_NAME} SSM_DB_NAME: ${SSM_DB_NAME} @@ -50,8 +49,6 @@ services: TIMEZONE: ${TIMEZONE} LOGGING_LEVEL: ${API_LOGGING_LEVEL} - volumes: - - ${GOOGLE_APPLICATION_CREDENTIALS}:/google/gcp_credentials.json logging: options: max-size: '10m' From fe2b2bd528f5fa7e974337710ce463e61626b16b Mon Sep 17 00:00:00 2001 From: Drew Johnston <31270488+drewjj@users.noreply.github.com> Date: Fri, 31 May 2024 15:13:03 -0600 Subject: [PATCH 049/189] Other docker compose modified --- docker-compose-no-cm.yml | 3 --- 1 file changed, 3 deletions(-) diff --git a/docker-compose-no-cm.yml b/docker-compose-no-cm.yml index 43752a3f8..554f5f866 100644 --- a/docker-compose-no-cm.yml +++ b/docker-compose-no-cm.yml @@ -22,7 +22,6 @@ services: MONGO_DB_NAME: ${MONGO_DB_NAME} COUNTS_MSG_TYPES: ${COUNTS_MSG_TYPES} - GOOGLE_APPLICATION_CREDENTIALS: '/google/gcp_credentials.json' GEO_DB_NAME: ${GEO_DB_NAME} SSM_DB_NAME: ${SSM_DB_NAME} @@ -50,8 +49,6 @@ services: TIMEZONE: ${TIMEZONE} LOGGING_LEVEL: ${API_LOGGING_LEVEL} - volumes: - - ${GOOGLE_APPLICATION_CREDENTIALS}:/google/gcp_credentials.json logging: options: max-size: '10m' From 2b298a5060f574dff2d9dc7b13886cb41fa344e3 Mon Sep 17 00:00:00 2001 From: jacob6838 Date: Fri, 31 May 2024 16:00:28 -0600 Subject: [PATCH 050/189] Re-naming CIMMS map and removing cimms mapbox style requirement --- docker-compose-no-cm.yml | 2 - docker-compose-webapp-deployment.yml | 2 - docker-compose.yml | 2 - sample.env | 2 - webapp/Dockerfile | 6 - .../icons/traffic-light-icon-green-1.png | Bin 0 -> 23229 bytes .../public/icons/traffic-light-icon-red-1.png | Bin 0 -> 22808 bytes .../icons/traffic-light-icon-red-flashing.png | Bin 0 -> 23547 bytes .../icons/traffic-light-icon-unknown.png | Bin 0 -> 22760 bytes .../icons/traffic-light-icon-yellow-1.png | Bin 0 -> 21823 bytes .../icons/traffic-light-icon-yellow-red-1.png | Bin 0 -> 22076 bytes webapp/src/EnvironmentVars.tsx | 2 - .../control-panel.tsx | 0 .../{map => intersectionMap}/custom-table.css | 0 .../{map => intersectionMap}/custom-table.tsx | 0 .../expandable-table.tsx | 0 .../map-component.tsx | 55 +- .../map-layer-style-slice.ts | 0 .../{map => intersectionMap}/map-legend.tsx | 0 .../{map => intersectionMap}/map-slice.tsx | 0 .../{map => intersectionMap}/popup.tsx | 0 .../{map => intersectionMap}/side-panel.tsx | 0 .../utilities/colors.ts | 0 .../utilities/file-utilities.ts | 0 .../utilities/map-utils.ts | 0 .../utilities/message-utils.ts | 0 .../src/components/map/processed_map_v4.json | 2459 -------- webapp/src/pages/IntersectionMapView.tsx | 4 +- webapp/src/store.tsx | 4 +- webapp/src/styles/intersectionMapStyle.json | 3326 ++++++++++ webapp/style/style.json | 5335 ----------------- 31 files changed, 3378 insertions(+), 7821 deletions(-) create mode 100644 webapp/public/icons/traffic-light-icon-green-1.png create mode 100644 webapp/public/icons/traffic-light-icon-red-1.png create mode 100644 webapp/public/icons/traffic-light-icon-red-flashing.png create mode 100644 webapp/public/icons/traffic-light-icon-unknown.png create mode 100644 webapp/public/icons/traffic-light-icon-yellow-1.png create mode 100644 webapp/public/icons/traffic-light-icon-yellow-red-1.png rename webapp/src/components/{map => intersectionMap}/control-panel.tsx (100%) rename webapp/src/components/{map => intersectionMap}/custom-table.css (100%) rename webapp/src/components/{map => intersectionMap}/custom-table.tsx (100%) rename webapp/src/components/{map => intersectionMap}/expandable-table.tsx (100%) rename webapp/src/components/{map => intersectionMap}/map-component.tsx (89%) rename webapp/src/components/{map => intersectionMap}/map-layer-style-slice.ts (100%) rename webapp/src/components/{map => intersectionMap}/map-legend.tsx (100%) rename webapp/src/components/{map => intersectionMap}/map-slice.tsx (100%) rename webapp/src/components/{map => intersectionMap}/popup.tsx (100%) rename webapp/src/components/{map => intersectionMap}/side-panel.tsx (100%) rename webapp/src/components/{map => intersectionMap}/utilities/colors.ts (100%) rename webapp/src/components/{map => intersectionMap}/utilities/file-utilities.ts (100%) rename webapp/src/components/{map => intersectionMap}/utilities/map-utils.ts (100%) rename webapp/src/components/{map => intersectionMap}/utilities/message-utils.ts (100%) delete mode 100644 webapp/src/components/map/processed_map_v4.json create mode 100644 webapp/src/styles/intersectionMapStyle.json delete mode 100644 webapp/style/style.json diff --git a/docker-compose-no-cm.yml b/docker-compose-no-cm.yml index 43752a3f8..6099748aa 100644 --- a/docker-compose-no-cm.yml +++ b/docker-compose-no-cm.yml @@ -71,8 +71,6 @@ services: MAPBOX_INIT_LATITUDE: ${MAPBOX_INIT_LATITUDE} MAPBOX_INIT_LONGITUDE: ${MAPBOX_INIT_LONGITUDE} MAPBOX_INIT_ZOOM: ${MAPBOX_INIT_ZOOM} - CVIZ_MAPBOX_TOKEN: ${CVIZ_MAPBOX_TOKEN} - CVIZ_MAPBOX_STYLE_URL: ${CVIZ_MAPBOX_STYLE_URL} CVIZ_API_SERVER_URL: ${CVIZ_API_SERVER_URL} CVIZ_API_WS_URL: ${CVIZ_API_WS_URL} image: jpo_cvmanager_webapp:latest diff --git a/docker-compose-webapp-deployment.yml b/docker-compose-webapp-deployment.yml index 4dc9af464..955390938 100644 --- a/docker-compose-webapp-deployment.yml +++ b/docker-compose-webapp-deployment.yml @@ -17,8 +17,6 @@ services: MAPBOX_INIT_LATITUDE: ${MAPBOX_INIT_LATITUDE} MAPBOX_INIT_LONGITUDE: ${MAPBOX_INIT_LONGITUDE} MAPBOX_INIT_ZOOM: ${MAPBOX_INIT_ZOOM} - CVIZ_MAPBOX_TOKEN: ${CVIZ_MAPBOX_TOKEN} - CVIZ_MAPBOX_STYLE_URL: ${CVIZ_MAPBOX_STYLE_URL} CVIZ_API_SERVER_URL: ${CVIZ_API_SERVER_URL} CVIZ_API_WS_URL: ${CVIZ_API_WS_URL} image: jpo_cvmanager_webapp:latest diff --git a/docker-compose.yml b/docker-compose.yml index 17d2bb501..4a3624d4a 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -71,8 +71,6 @@ services: MAPBOX_INIT_LATITUDE: ${MAPBOX_INIT_LATITUDE} MAPBOX_INIT_LONGITUDE: ${MAPBOX_INIT_LONGITUDE} MAPBOX_INIT_ZOOM: ${MAPBOX_INIT_ZOOM} - CVIZ_MAPBOX_TOKEN: ${CVIZ_MAPBOX_TOKEN} - CVIZ_MAPBOX_STYLE_URL: ${CVIZ_MAPBOX_STYLE_URL} CVIZ_API_SERVER_URL: ${CVIZ_API_SERVER_URL} CVIZ_API_WS_URL: ${CVIZ_API_WS_URL} image: jpo_cvmanager_webapp:latest diff --git a/sample.env b/sample.env index b184ace12..87ccb0dd4 100644 --- a/sample.env +++ b/sample.env @@ -96,8 +96,6 @@ MAPBOX_INIT_ZOOM="10" GCP_PROJECT_ID = '' # CIMMS IntersectionMap Variables -CVIZ_MAPBOX_STYLE_URL= -CVIZ_MAPBOX_TOKEN= CVIZ_API_SERVER_URL='http://localhost:8089' CVIZ_API_WS_URL='ws://localhost:8089' WEBAPP_CM_DOMAIN='cvmanager.local.com' diff --git a/webapp/Dockerfile b/webapp/Dockerfile index e653847e3..c0838860d 100644 --- a/webapp/Dockerfile +++ b/webapp/Dockerfile @@ -33,12 +33,6 @@ ENV REACT_APP_MAPBOX_INIT_LONGITUDE $MAPBOX_INIT_LONGITUDE ARG MAPBOX_INIT_ZOOM ENV REACT_APP_MAPBOX_INIT_ZOOM $MAPBOX_INIT_ZOOM -ARG CVIZ_MAPBOX_TOKEN -ENV REACT_APP_CVIZ_MAPBOX_TOKEN $CVIZ_MAPBOX_TOKEN - -ARG CVIZ_MAPBOX_STYLE_URL -ENV REACT_APP_CVIZ_MAPBOX_STYLE_URL $CVIZ_MAPBOX_STYLE_URL - ARG CVIZ_API_SERVER_URL ENV REACT_APP_CVIZ_API_SERVER_URL $CVIZ_API_SERVER_URL diff --git a/webapp/public/icons/traffic-light-icon-green-1.png b/webapp/public/icons/traffic-light-icon-green-1.png new file mode 100644 index 0000000000000000000000000000000000000000..912ea5be8f133a96c8227a583c6ba74497bd23b1 GIT binary patch literal 23229 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 A%>V!Z literal 0 HcmV?d00001 diff --git a/webapp/public/icons/traffic-light-icon-red-1.png b/webapp/public/icons/traffic-light-icon-red-1.png new file mode 100644 index 0000000000000000000000000000000000000000..4e8a21723c066e9c27dc3ff15d584946db536ffd GIT binary patch literal 22808 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%0AasdD(R-J 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 literal 0 HcmV?d00001 diff --git a/webapp/public/icons/traffic-light-icon-red-flashing.png b/webapp/public/icons/traffic-light-icon-red-flashing.png new file mode 100644 index 0000000000000000000000000000000000000000..f081820d0aa58c0bb4ba36f91a362d909cc2b869 GIT binary patch literal 23547 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 literal 0 HcmV?d00001 diff --git a/webapp/public/icons/traffic-light-icon-yellow-1.png b/webapp/public/icons/traffic-light-icon-yellow-1.png new file mode 100644 index 0000000000000000000000000000000000000000..cc6ae5c074f27908a3cc6db032effe1c408e3807 GIT binary patch literal 21823 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 RQBnW^002ovPDHLkV1gJs!JGg9 literal 0 HcmV?d00001 diff --git a/webapp/src/EnvironmentVars.tsx b/webapp/src/EnvironmentVars.tsx index 7d0797316..daa559b82 100644 --- a/webapp/src/EnvironmentVars.tsx +++ b/webapp/src/EnvironmentVars.tsx @@ -36,8 +36,6 @@ class EnvironmentVars { } static MAPBOX_TOKEN = process.env.REACT_APP_MAPBOX_TOKEN - static CVIZ_MAPBOX_TOKEN = process.env.REACT_APP_CVIZ_MAPBOX_TOKEN - static CVIZ_MAPBOX_STYLE_URL = process.env.REACT_APP_CVIZ_MAPBOX_STYLE_URL static CVIZ_API_SERVER_URL = process.env.REACT_APP_CVIZ_API_SERVER_URL static CVIZ_API_WS_URL = process.env.REACT_APP_CVIZ_API_WS_URL static KEYCLOAK_HOST_URL = process.env.REACT_APP_KEYCLOAK_URL diff --git a/webapp/src/components/map/control-panel.tsx b/webapp/src/components/intersectionMap/control-panel.tsx similarity index 100% rename from webapp/src/components/map/control-panel.tsx rename to webapp/src/components/intersectionMap/control-panel.tsx diff --git a/webapp/src/components/map/custom-table.css b/webapp/src/components/intersectionMap/custom-table.css similarity index 100% rename from webapp/src/components/map/custom-table.css rename to webapp/src/components/intersectionMap/custom-table.css diff --git a/webapp/src/components/map/custom-table.tsx b/webapp/src/components/intersectionMap/custom-table.tsx similarity index 100% rename from webapp/src/components/map/custom-table.tsx rename to webapp/src/components/intersectionMap/custom-table.tsx diff --git a/webapp/src/components/map/expandable-table.tsx b/webapp/src/components/intersectionMap/expandable-table.tsx similarity index 100% rename from webapp/src/components/map/expandable-table.tsx rename to webapp/src/components/intersectionMap/expandable-table.tsx diff --git a/webapp/src/components/map/map-component.tsx b/webapp/src/components/intersectionMap/map-component.tsx similarity index 89% rename from webapp/src/components/map/map-component.tsx rename to webapp/src/components/intersectionMap/map-component.tsx index c43acdbe4..e4c802e51 100644 --- a/webapp/src/components/map/map-component.tsx +++ b/webapp/src/components/intersectionMap/map-component.tsx @@ -1,5 +1,5 @@ import React, { useState, useEffect } from 'react' -import Map, { Source, Layer } from 'react-map-gl' +import Map, { Source, Layer, MapRef } from 'react-map-gl' import { Container, Col } from 'reactstrap' @@ -74,6 +74,7 @@ import { AnyAction, ThunkDispatch } from '@reduxjs/toolkit' import { RootState } from '../../store' import { MapLegend } from './map-legend' import { selectSelectedSrm } from '../../generalSlices/rsuSlice' +import mbStyle from '../../styles/intersectionMapStyle.json' const generateQueryParams = (source: MAP_PROPS['sourceData'], sourceDataType: MAP_PROPS['sourceDataType']) => { const startOffset = 1000 * 60 * 1 @@ -126,7 +127,7 @@ type timestamp = { timestamp: number } -const MapTab = (props: MAP_PROPS) => { +const IntersectionMap = (props: MAP_PROPS) => { const dispatch: ThunkDispatch = useDispatch() // userSlice @@ -171,7 +172,7 @@ const MapTab = (props: MAP_PROPS) => { const liveDataRestartTimeoutId = useSelector(selectLiveDataRestartTimeoutId) const liveDataRestart = useSelector(selectLiveDataRestart) - const mapRef = React.useRef(null) + const mapRef = React.useRef(null) const [bsmTrailLength, setBsmTrailLength] = useState(5) useEffect(() => { @@ -300,6 +301,27 @@ const MapTab = (props: MAP_PROPS) => { } }, [liveDataRestart]) + useEffect(() => { + const map = mapRef.current?.getMap() + console.log('MAP REF CHANGED', mapRef.current, map) + if (!map) return + const images = [ + 'traffic-light-icon-unknown', + 'traffic-light-icon-red-flashing', + 'traffic-light-icon-red-1', + 'traffic-light-icon-yellow-red-1', + 'traffic-light-icon-green-1', + 'traffic-light-icon-yellow-1', + ] + for (const image_name of images) { + map.loadImage(`/icons/${image_name}.png`, (error, image) => { + if (error) throw error + console.log('MAP IMAGE', image_name, map.hasImage(image_name)) + if (!map.hasImage(image_name)) map.addImage(image_name, image, { sdf: true }) + }) + } + }, [mapRef]) + return (
@@ -348,9 +370,8 @@ const MapTab = (props: MAP_PROPS) => { {}} - mapStyle={EnvironmentVars.CVIZ_MAPBOX_STYLE_URL} - mapboxAccessToken={EnvironmentVars.CVIZ_MAPBOX_TOKEN} + mapStyle={mbStyle as mapboxgl.Style} + mapboxAccessToken={EnvironmentVars.MAPBOX_TOKEN} attributionControl={true} customAttribution={['© CDOT']} styleDiffing @@ -362,6 +383,26 @@ const MapTab = (props: MAP_PROPS) => { onMouseMove={(e) => dispatch(onMapMouseMove({ features: e.features, lngLat: e.lngLat }))} onMouseEnter={(e) => dispatch(onMapMouseEnter({ features: e.features, lngLat: e.lngLat }))} onMouseLeave={(e) => dispatch(onMapMouseLeave())} + onLoad={(e: mapboxgl.MapboxEvent) => { + const map = e.target + console.log('MAP LOADED', mapRef.current, map, e.target) + if (!map) return + const images = [ + 'traffic-light-icon-unknown', + 'traffic-light-icon-red-flashing', + 'traffic-light-icon-red-1', + 'traffic-light-icon-yellow-red-1', + 'traffic-light-icon-green-1', + 'traffic-light-icon-yellow-1', + ] + for (const image_name of images) { + map.loadImage(`/icons/${image_name}.png`, (error, image) => { + if (error) throw error + console.log('MAP IMAGE', image_name, map.hasImage(image_name)) + if (!map.hasImage(image_name)) map.addImage(image_name, image) + }) + } + }} > @@ -476,4 +517,4 @@ const MapTab = (props: MAP_PROPS) => { ) } -export default MapTab +export default IntersectionMap diff --git a/webapp/src/components/map/map-layer-style-slice.ts b/webapp/src/components/intersectionMap/map-layer-style-slice.ts similarity index 100% rename from webapp/src/components/map/map-layer-style-slice.ts rename to webapp/src/components/intersectionMap/map-layer-style-slice.ts diff --git a/webapp/src/components/map/map-legend.tsx b/webapp/src/components/intersectionMap/map-legend.tsx similarity index 100% rename from webapp/src/components/map/map-legend.tsx rename to webapp/src/components/intersectionMap/map-legend.tsx diff --git a/webapp/src/components/map/map-slice.tsx b/webapp/src/components/intersectionMap/map-slice.tsx similarity index 100% rename from webapp/src/components/map/map-slice.tsx rename to webapp/src/components/intersectionMap/map-slice.tsx diff --git a/webapp/src/components/map/popup.tsx b/webapp/src/components/intersectionMap/popup.tsx similarity index 100% rename from webapp/src/components/map/popup.tsx rename to webapp/src/components/intersectionMap/popup.tsx diff --git a/webapp/src/components/map/side-panel.tsx b/webapp/src/components/intersectionMap/side-panel.tsx similarity index 100% rename from webapp/src/components/map/side-panel.tsx rename to webapp/src/components/intersectionMap/side-panel.tsx diff --git a/webapp/src/components/map/utilities/colors.ts b/webapp/src/components/intersectionMap/utilities/colors.ts similarity index 100% rename from webapp/src/components/map/utilities/colors.ts rename to webapp/src/components/intersectionMap/utilities/colors.ts diff --git a/webapp/src/components/map/utilities/file-utilities.ts b/webapp/src/components/intersectionMap/utilities/file-utilities.ts similarity index 100% rename from webapp/src/components/map/utilities/file-utilities.ts rename to webapp/src/components/intersectionMap/utilities/file-utilities.ts diff --git a/webapp/src/components/map/utilities/map-utils.ts b/webapp/src/components/intersectionMap/utilities/map-utils.ts similarity index 100% rename from webapp/src/components/map/utilities/map-utils.ts rename to webapp/src/components/intersectionMap/utilities/map-utils.ts diff --git a/webapp/src/components/map/utilities/message-utils.ts b/webapp/src/components/intersectionMap/utilities/message-utils.ts similarity index 100% rename from webapp/src/components/map/utilities/message-utils.ts rename to webapp/src/components/intersectionMap/utilities/message-utils.ts diff --git a/webapp/src/components/map/processed_map_v4.json b/webapp/src/components/map/processed_map_v4.json deleted file mode 100644 index 9b5c1c90e..000000000 --- a/webapp/src/components/map/processed_map_v4.json +++ /dev/null @@ -1,2459 +0,0 @@ -{ - "mapFeatureCollection": { - "type": "FeatureCollection", - "features": [ - { - "type": "Feature", - "id": 2, - "geometry": { - "type": "LineString", - "coordinates": [ - [-105.0911523, 39.5953376], - [-105.0899239, 39.5953231], - [-105.0896005, 39.5953332], - [-105.0888831, 39.595317], - [-105.088108, 39.5953159], - [-105.0876637, 39.5953153] - ] - }, - "properties": { - "nodes": [ - { - "delta": [2225, 808], - "stopLine": null, - "dwidth": null, - "delevation": -10 - }, - { - "delta": [10517, -161], - "stopLine": null, - "dwidth": null, - "delevation": -60 - }, - { - "delta": [2769, 112], - "stopLine": null, - "dwidth": null, - "delevation": -10 - }, - { - "delta": [6142, -180], - "stopLine": null, - "dwidth": null, - "delevation": -30 - }, - { - "delta": [6636, -12], - "stopLine": null, - "dwidth": null, - "delevation": -20 - }, - { - "delta": [3804, -7], - "stopLine": null, - "dwidth": null, - "delevation": null - } - ], - "laneId": 2, - "sharedWith": { - "busVehicleTraffic": false, - "trackedVehicleTraffic": false, - "individualMotorizedVehicleTraffic": false, - "taxiVehicleTraffic": false, - "overlappingLaneDescriptionProvided": false, - "cyclistVehicleTraffic": false, - "otherNonMotorizedTrafficTypes": false, - "multipleLanesTreatedAsOneLane": false, - "pedestrianTraffic": false, - "pedestriansTraffic": false - }, - "egressApproach": 0, - "ingressApproach": 1, - "ingressPath": true, - "egressPath": false, - "maneuvers": { - "maneuverStraightAllowed": true, - "maneuverNoStoppingAllowed": false, - "goWithHalt": false, - "maneuverLeftAllowed": false, - "maneuverUTurnAllowed": false, - "maneuverLeftTurnOnRedAllowed": false, - "reserved1": false, - "maneuverRightAllowed": false, - "maneuverLaneChangeAllowed": false, - "yieldAllwaysRequired": false, - "maneuverRightTurnOnRedAllowed": false, - "caution": false - }, - "connectsTo": [ - { - "connectingLane": { - "lane": 19, - "maneuver": { - "maneuverStraightAllowed": true, - "maneuverNoStoppingAllowed": false, - "goWithHalt": false, - "maneuverLeftAllowed": false, - "maneuverUTurnAllowed": false, - "maneuverLeftTurnOnRedAllowed": false, - "reserved1": false, - "maneuverRightAllowed": false, - "maneuverLaneChangeAllowed": false, - "yieldAllwaysRequired": false, - "maneuverRightTurnOnRedAllowed": false, - "caution": false - } - }, - "remoteIntersection": null, - "signalGroup": 8, - "userClass": null, - "connectionID": 1 - } - ] - } - }, - { - "type": "Feature", - "id": 3, - "geometry": { - "type": "LineString", - "coordinates": [ - [-105.0911527, 39.5953113], - [-105.0908101, 39.5953039], - [-105.0900791, 39.5952962], - [-105.0896801, 39.5952874], - [-105.0888781, 39.5952839], - [-105.0885678, 39.5952789], - [-105.0880704, 39.5952849], - [-105.0876638, 39.5952849] - ] - }, - "properties": { - "nodes": [ - { - "delta": [2222, 515], - "stopLine": null, - "dwidth": null, - "delevation": -10 - }, - { - "delta": [2933, -82], - "stopLine": null, - "dwidth": null, - "delevation": -10 - }, - { - "delta": [6259, -85], - "stopLine": null, - "dwidth": null, - "delevation": -40 - }, - { - "delta": [3416, -98], - "stopLine": null, - "dwidth": null, - "delevation": -20 - }, - { - "delta": [6867, -39], - "stopLine": null, - "dwidth": null, - "delevation": -30 - }, - { - "delta": [2657, -55], - "stopLine": null, - "dwidth": null, - "delevation": -10 - }, - { - "delta": [4259, 67], - "stopLine": null, - "dwidth": null, - "delevation": -10 - }, - { - "delta": [3481, 0], - "stopLine": null, - "dwidth": null, - "delevation": null - } - ], - "laneId": 3, - "sharedWith": { - "busVehicleTraffic": false, - "trackedVehicleTraffic": false, - "individualMotorizedVehicleTraffic": false, - "taxiVehicleTraffic": false, - "overlappingLaneDescriptionProvided": false, - "cyclistVehicleTraffic": false, - "otherNonMotorizedTrafficTypes": false, - "multipleLanesTreatedAsOneLane": false, - "pedestrianTraffic": false, - "pedestriansTraffic": false - }, - "egressApproach": 0, - "ingressApproach": 1, - "ingressPath": true, - "egressPath": false, - "maneuvers": { - "maneuverStraightAllowed": true, - "maneuverNoStoppingAllowed": false, - "goWithHalt": false, - "maneuverLeftAllowed": false, - "maneuverUTurnAllowed": false, - "maneuverLeftTurnOnRedAllowed": false, - "reserved1": false, - "maneuverRightAllowed": false, - "maneuverLaneChangeAllowed": false, - "yieldAllwaysRequired": false, - "maneuverRightTurnOnRedAllowed": false, - "caution": false - }, - "connectsTo": [ - { - "connectingLane": { - "lane": 18, - "maneuver": { - "maneuverStraightAllowed": true, - "maneuverNoStoppingAllowed": false, - "goWithHalt": false, - "maneuverLeftAllowed": false, - "maneuverUTurnAllowed": false, - "maneuverLeftTurnOnRedAllowed": false, - "reserved1": false, - "maneuverRightAllowed": false, - "maneuverLaneChangeAllowed": false, - "yieldAllwaysRequired": false, - "maneuverRightTurnOnRedAllowed": false, - "caution": false - } - }, - "remoteIntersection": null, - "signalGroup": 8, - "userClass": null, - "connectionID": 1 - } - ] - } - }, - { - "type": "Feature", - "id": 4, - "geometry": { - "type": "LineString", - "coordinates": [ - [-105.0911537, 39.5952537], - [-105.0903143, 39.5952555], - [-105.0902511, 39.5952591], - [-105.0900995, 39.5952797] - ] - }, - "properties": { - "nodes": [ - { - "delta": [2213, -124], - "stopLine": null, - "dwidth": null, - "delevation": -10 - }, - { - "delta": [7187, 20], - "stopLine": null, - "dwidth": null, - "delevation": -40 - }, - { - "delta": [541, 40], - "stopLine": null, - "dwidth": null, - "delevation": -10 - }, - { - "delta": [1298, 229], - "stopLine": null, - "dwidth": null, - "delevation": null - } - ], - "laneId": 4, - "sharedWith": { - "busVehicleTraffic": false, - "trackedVehicleTraffic": false, - "individualMotorizedVehicleTraffic": false, - "taxiVehicleTraffic": false, - "overlappingLaneDescriptionProvided": false, - "cyclistVehicleTraffic": false, - "otherNonMotorizedTrafficTypes": false, - "multipleLanesTreatedAsOneLane": false, - "pedestrianTraffic": false, - "pedestriansTraffic": false - }, - "egressApproach": 0, - "ingressApproach": 1, - "ingressPath": true, - "egressPath": false, - "maneuvers": { - "maneuverStraightAllowed": false, - "maneuverNoStoppingAllowed": false, - "goWithHalt": false, - "maneuverLeftAllowed": true, - "maneuverUTurnAllowed": false, - "maneuverLeftTurnOnRedAllowed": false, - "reserved1": false, - "maneuverRightAllowed": false, - "maneuverLaneChangeAllowed": false, - "yieldAllwaysRequired": false, - "maneuverRightTurnOnRedAllowed": false, - "caution": false - }, - "connectsTo": [ - { - "connectingLane": { - "lane": 12, - "maneuver": { - "maneuverStraightAllowed": false, - "maneuverNoStoppingAllowed": false, - "goWithHalt": false, - "maneuverLeftAllowed": true, - "maneuverUTurnAllowed": false, - "maneuverLeftTurnOnRedAllowed": false, - "reserved1": false, - "maneuverRightAllowed": false, - "maneuverLaneChangeAllowed": false, - "yieldAllwaysRequired": false, - "maneuverRightTurnOnRedAllowed": false, - "caution": false - } - }, - "remoteIntersection": null, - "signalGroup": 3, - "userClass": null, - "connectionID": 1 - } - ] - } - }, - { - "type": "Feature", - "id": 6, - "geometry": { - "type": "LineString", - "coordinates": [ - [-105.0911474, 39.5951732], - [-105.0911048, 39.5951738] - ] - }, - "properties": { - "nodes": [ - { - "delta": [2267, -1019], - "stopLine": null, - "dwidth": null, - "delevation": -10 - }, - { - "delta": [365, 7], - "stopLine": null, - "dwidth": null, - "delevation": null - } - ], - "laneId": 6, - "sharedWith": { - "busVehicleTraffic": false, - "trackedVehicleTraffic": false, - "individualMotorizedVehicleTraffic": false, - "taxiVehicleTraffic": false, - "overlappingLaneDescriptionProvided": false, - "cyclistVehicleTraffic": false, - "otherNonMotorizedTrafficTypes": false, - "multipleLanesTreatedAsOneLane": false, - "pedestrianTraffic": false, - "pedestriansTraffic": false - }, - "egressApproach": 2, - "ingressApproach": 0, - "ingressPath": false, - "egressPath": true - } - }, - { - "type": "Feature", - "id": 5, - "geometry": { - "type": "LineString", - "coordinates": [ - [-105.0911474, 39.5952053], - [-105.0911079, 39.5952053] - ] - }, - "properties": { - "nodes": [ - { - "delta": [2267, -662], - "stopLine": null, - "dwidth": null, - "delevation": -10 - }, - { - "delta": [338, 0], - "stopLine": null, - "dwidth": null, - "delevation": null - } - ], - "laneId": 5, - "sharedWith": { - "busVehicleTraffic": false, - "trackedVehicleTraffic": false, - "individualMotorizedVehicleTraffic": false, - "taxiVehicleTraffic": false, - "overlappingLaneDescriptionProvided": false, - "cyclistVehicleTraffic": false, - "otherNonMotorizedTrafficTypes": false, - "multipleLanesTreatedAsOneLane": false, - "pedestrianTraffic": false, - "pedestriansTraffic": false - }, - "egressApproach": 2, - "ingressApproach": 0, - "ingressPath": false, - "egressPath": true - } - }, - { - "type": "Feature", - "id": 7, - "geometry": { - "type": "LineString", - "coordinates": [ - [-105.0911443, 39.5951332], - [-105.0911017, 39.5951356] - ] - }, - "properties": { - "nodes": [ - { - "delta": [2294, -1463], - "stopLine": null, - "dwidth": null, - "delevation": -10 - }, - { - "delta": [365, 27], - "stopLine": null, - "dwidth": null, - "delevation": null - } - ], - "laneId": 7, - "sharedWith": { - "busVehicleTraffic": false, - "trackedVehicleTraffic": false, - "individualMotorizedVehicleTraffic": false, - "taxiVehicleTraffic": false, - "overlappingLaneDescriptionProvided": false, - "cyclistVehicleTraffic": false, - "otherNonMotorizedTrafficTypes": false, - "multipleLanesTreatedAsOneLane": false, - "pedestrianTraffic": false, - "pedestriansTraffic": false - }, - "egressApproach": 2, - "ingressApproach": 0, - "ingressPath": false, - "egressPath": true - } - }, - { - "type": "Feature", - "id": 12, - "geometry": { - "type": "LineString", - "coordinates": [ - [-105.0915234, 39.5950941], - [-105.0915234, 39.5950602] - ] - }, - "properties": { - "nodes": [ - { - "delta": [-952, -1898], - "stopLine": null, - "dwidth": null, - "delevation": null - }, - { - "delta": [0, -377], - "stopLine": null, - "dwidth": null, - "delevation": null - } - ], - "laneId": 12, - "sharedWith": { - "busVehicleTraffic": false, - "trackedVehicleTraffic": false, - "individualMotorizedVehicleTraffic": false, - "taxiVehicleTraffic": false, - "overlappingLaneDescriptionProvided": false, - "cyclistVehicleTraffic": false, - "otherNonMotorizedTrafficTypes": false, - "multipleLanesTreatedAsOneLane": false, - "pedestrianTraffic": false, - "pedestriansTraffic": false - }, - "egressApproach": 4, - "ingressApproach": 0, - "ingressPath": false, - "egressPath": true - } - }, - { - "type": "Feature", - "id": 13, - "geometry": { - "type": "LineString", - "coordinates": [ - [-105.0915804, 39.5950941], - [-105.0915796, 39.595059], - [-105.0915796, 39.595059], - [-105.0915796, 39.5950596] - ] - }, - "properties": { - "nodes": [ - { - "delta": [-1440, -1898], - "stopLine": null, - "dwidth": null, - "delevation": null - }, - { - "delta": [7, -390], - "stopLine": null, - "dwidth": null, - "delevation": null - }, - { - "delta": [0, 0], - "stopLine": null, - "dwidth": null, - "delevation": null - }, - { - "delta": [0, 7], - "stopLine": null, - "dwidth": null, - "delevation": null - } - ], - "laneId": 13, - "sharedWith": { - "busVehicleTraffic": false, - "trackedVehicleTraffic": false, - "individualMotorizedVehicleTraffic": false, - "taxiVehicleTraffic": false, - "overlappingLaneDescriptionProvided": false, - "cyclistVehicleTraffic": false, - "otherNonMotorizedTrafficTypes": false, - "multipleLanesTreatedAsOneLane": false, - "pedestrianTraffic": false, - "pedestriansTraffic": false - }, - "egressApproach": 4, - "ingressApproach": 0, - "ingressPath": false, - "egressPath": true - } - }, - { - "type": "Feature", - "id": 10, - "geometry": { - "type": "LineString", - "coordinates": [ - [-105.0913299, 39.5950922], - [-105.09133, 39.5944158], - [-105.0913348, 39.5939413], - [-105.0913368, 39.592921], - [-105.0913348, 39.5923866], - [-105.0913092, 39.5916736], - [-105.091258, 39.5909296] - ] - }, - "properties": { - "nodes": [ - { - "delta": [705, -1919], - "stopLine": null, - "dwidth": null, - "delevation": null - }, - { - "delta": [-1, -7515], - "stopLine": null, - "dwidth": null, - "delevation": -40 - }, - { - "delta": [-41, -5272], - "stopLine": null, - "dwidth": null, - "delevation": 10 - }, - { - "delta": [-17, -11337], - "stopLine": null, - "dwidth": null, - "delevation": null - }, - { - "delta": [17, -5938], - "stopLine": null, - "dwidth": null, - "delevation": 10 - }, - { - "delta": [219, -7922], - "stopLine": null, - "dwidth": null, - "delevation": 20 - }, - { - "delta": [438, -8267], - "stopLine": null, - "dwidth": null, - "delevation": 70 - } - ], - "laneId": 10, - "sharedWith": { - "busVehicleTraffic": false, - "trackedVehicleTraffic": false, - "individualMotorizedVehicleTraffic": false, - "taxiVehicleTraffic": false, - "overlappingLaneDescriptionProvided": false, - "cyclistVehicleTraffic": false, - "otherNonMotorizedTrafficTypes": false, - "multipleLanesTreatedAsOneLane": false, - "pedestrianTraffic": false, - "pedestriansTraffic": false - }, - "egressApproach": 0, - "ingressApproach": 3, - "ingressPath": true, - "egressPath": false, - "maneuvers": { - "maneuverStraightAllowed": true, - "maneuverNoStoppingAllowed": false, - "goWithHalt": false, - "maneuverLeftAllowed": false, - "maneuverUTurnAllowed": false, - "maneuverLeftTurnOnRedAllowed": false, - "reserved1": false, - "maneuverRightAllowed": false, - "maneuverLaneChangeAllowed": false, - "yieldAllwaysRequired": false, - "maneuverRightTurnOnRedAllowed": false, - "caution": false - }, - "connectsTo": [ - { - "connectingLane": { - "lane": 26, - "maneuver": { - "maneuverStraightAllowed": true, - "maneuverNoStoppingAllowed": false, - "goWithHalt": false, - "maneuverLeftAllowed": false, - "maneuverUTurnAllowed": false, - "maneuverLeftTurnOnRedAllowed": false, - "reserved1": false, - "maneuverRightAllowed": false, - "maneuverLaneChangeAllowed": false, - "yieldAllwaysRequired": false, - "maneuverRightTurnOnRedAllowed": false, - "caution": false - } - }, - "remoteIntersection": null, - "signalGroup": 2, - "userClass": null, - "connectionID": 1 - } - ] - } - }, - { - "type": "Feature", - "id": 9, - "geometry": { - "type": "LineString", - "coordinates": [ - [-105.0912849, 39.5950912], - [-105.0912838, 39.5946624], - [-105.0912861, 39.5940862], - [-105.0912901, 39.5933704], - [-105.091279, 39.5921455], - [-105.0912541, 39.5915398], - [-105.0912097, 39.5909346] - ] - }, - "properties": { - "nodes": [ - { - "delta": [1090, -1930], - "stopLine": null, - "dwidth": null, - "delevation": -10 - }, - { - "delta": [9, -4764], - "stopLine": null, - "dwidth": null, - "delevation": -10 - }, - { - "delta": [-20, -6402], - "stopLine": null, - "dwidth": null, - "delevation": -20 - }, - { - "delta": [-34, -7953], - "stopLine": null, - "dwidth": null, - "delevation": -10 - }, - { - "delta": [95, -13610], - "stopLine": null, - "dwidth": null, - "delevation": 30 - }, - { - "delta": [213, -6730], - "stopLine": null, - "dwidth": null, - "delevation": 40 - }, - { - "delta": [380, -6724], - "stopLine": null, - "dwidth": null, - "delevation": 60 - } - ], - "laneId": 9, - "sharedWith": { - "busVehicleTraffic": false, - "trackedVehicleTraffic": false, - "individualMotorizedVehicleTraffic": false, - "taxiVehicleTraffic": false, - "overlappingLaneDescriptionProvided": false, - "cyclistVehicleTraffic": false, - "otherNonMotorizedTrafficTypes": false, - "multipleLanesTreatedAsOneLane": false, - "pedestrianTraffic": false, - "pedestriansTraffic": false - }, - "egressApproach": 0, - "ingressApproach": 3, - "ingressPath": true, - "egressPath": false, - "maneuvers": { - "maneuverStraightAllowed": true, - "maneuverNoStoppingAllowed": false, - "goWithHalt": false, - "maneuverLeftAllowed": false, - "maneuverUTurnAllowed": false, - "maneuverLeftTurnOnRedAllowed": false, - "reserved1": false, - "maneuverRightAllowed": false, - "maneuverLaneChangeAllowed": false, - "yieldAllwaysRequired": false, - "maneuverRightTurnOnRedAllowed": false, - "caution": false - }, - "connectsTo": [ - { - "connectingLane": { - "lane": 27, - "maneuver": { - "maneuverStraightAllowed": true, - "maneuverNoStoppingAllowed": false, - "goWithHalt": false, - "maneuverLeftAllowed": false, - "maneuverUTurnAllowed": false, - "maneuverLeftTurnOnRedAllowed": false, - "reserved1": false, - "maneuverRightAllowed": false, - "maneuverLaneChangeAllowed": false, - "yieldAllwaysRequired": false, - "maneuverRightTurnOnRedAllowed": false, - "caution": false - } - }, - "remoteIntersection": null, - "signalGroup": 2, - "userClass": null, - "connectionID": 1 - } - ] - } - }, - { - "type": "Feature", - "id": 11, - "geometry": { - "type": "LineString", - "coordinates": [ - [-105.0913713, 39.5950916], - [-105.091375, 39.5944888], - [-105.0913568, 39.5942534] - ] - }, - "properties": { - "nodes": [ - { - "delta": [350, -1925], - "stopLine": null, - "dwidth": null, - "delevation": null - }, - { - "delta": [-32, -6698], - "stopLine": null, - "dwidth": null, - "delevation": -30 - }, - { - "delta": [156, -2616], - "stopLine": null, - "dwidth": null, - "delevation": null - } - ], - "laneId": 11, - "sharedWith": { - "busVehicleTraffic": false, - "trackedVehicleTraffic": false, - "individualMotorizedVehicleTraffic": false, - "taxiVehicleTraffic": false, - "overlappingLaneDescriptionProvided": false, - "cyclistVehicleTraffic": false, - "otherNonMotorizedTrafficTypes": false, - "multipleLanesTreatedAsOneLane": false, - "pedestrianTraffic": false, - "pedestriansTraffic": false - }, - "egressApproach": 0, - "ingressApproach": 3, - "ingressPath": true, - "egressPath": false, - "maneuvers": { - "maneuverStraightAllowed": false, - "maneuverNoStoppingAllowed": false, - "goWithHalt": false, - "maneuverLeftAllowed": true, - "maneuverUTurnAllowed": false, - "maneuverLeftTurnOnRedAllowed": false, - "reserved1": false, - "maneuverRightAllowed": false, - "maneuverLaneChangeAllowed": false, - "yieldAllwaysRequired": false, - "maneuverRightTurnOnRedAllowed": false, - "caution": false - }, - "connectsTo": [ - { - "connectingLane": { - "lane": 18, - "maneuver": { - "maneuverStraightAllowed": false, - "maneuverNoStoppingAllowed": false, - "goWithHalt": false, - "maneuverLeftAllowed": true, - "maneuverUTurnAllowed": false, - "maneuverLeftTurnOnRedAllowed": false, - "reserved1": false, - "maneuverRightAllowed": false, - "maneuverLaneChangeAllowed": false, - "yieldAllwaysRequired": false, - "maneuverRightTurnOnRedAllowed": false, - "caution": false - } - }, - "remoteIntersection": null, - "signalGroup": 1, - "userClass": null, - "connectionID": 1 - } - ] - } - }, - { - "type": "Feature", - "id": 8, - "geometry": { - "type": "LineString", - "coordinates": [ - [-105.0912303, 39.5950889], - [-105.0912421, 39.5949129], - [-105.0912427, 39.5942475], - [-105.0912568, 39.5941003] - ] - }, - "properties": { - "nodes": [ - { - "delta": [1557, -1956], - "stopLine": null, - "dwidth": null, - "delevation": -10 - }, - { - "delta": [-101, -1956], - "stopLine": null, - "dwidth": null, - "delevation": null - }, - { - "delta": [-5, -7393], - "stopLine": null, - "dwidth": null, - "delevation": -40 - }, - { - "delta": [-121, -1636], - "stopLine": null, - "dwidth": null, - "delevation": null - } - ], - "laneId": 8, - "sharedWith": { - "busVehicleTraffic": false, - "trackedVehicleTraffic": false, - "individualMotorizedVehicleTraffic": false, - "taxiVehicleTraffic": false, - "overlappingLaneDescriptionProvided": false, - "cyclistVehicleTraffic": false, - "otherNonMotorizedTrafficTypes": false, - "multipleLanesTreatedAsOneLane": false, - "pedestrianTraffic": false, - "pedestriansTraffic": false - }, - "egressApproach": 0, - "ingressApproach": 3, - "ingressPath": true, - "egressPath": false, - "maneuvers": { - "maneuverStraightAllowed": false, - "maneuverNoStoppingAllowed": false, - "goWithHalt": false, - "maneuverLeftAllowed": false, - "maneuverUTurnAllowed": false, - "maneuverLeftTurnOnRedAllowed": false, - "reserved1": false, - "maneuverRightAllowed": true, - "maneuverLaneChangeAllowed": false, - "yieldAllwaysRequired": false, - "maneuverRightTurnOnRedAllowed": false, - "caution": false - }, - "connectsTo": [ - { - "connectingLane": { - "lane": 7, - "maneuver": { - "maneuverStraightAllowed": false, - "maneuverNoStoppingAllowed": false, - "goWithHalt": false, - "maneuverLeftAllowed": false, - "maneuverUTurnAllowed": false, - "maneuverLeftTurnOnRedAllowed": false, - "reserved1": false, - "maneuverRightAllowed": true, - "maneuverLaneChangeAllowed": false, - "yieldAllwaysRequired": false, - "maneuverRightTurnOnRedAllowed": false, - "caution": false - } - }, - "remoteIntersection": null, - "signalGroup": null, - "userClass": null, - "connectionID": 1 - } - ] - } - }, - { - "type": "Feature", - "id": 15, - "geometry": { - "type": "LineString", - "coordinates": [ - [-105.0916931, 39.5951733], - [-105.0927839, 39.5951756], - [-105.0941732, 39.5951722], - [-105.0952027, 39.5951734] - ] - }, - "properties": { - "nodes": [ - { - "delta": [-2405, -1018], - "stopLine": null, - "dwidth": null, - "delevation": 10 - }, - { - "delta": [-9339, 26], - "stopLine": null, - "dwidth": null, - "delevation": 30 - }, - { - "delta": [-11895, -38], - "stopLine": null, - "dwidth": null, - "delevation": 50 - }, - { - "delta": [-8814, 13], - "stopLine": null, - "dwidth": null, - "delevation": null - } - ], - "laneId": 15, - "sharedWith": { - "busVehicleTraffic": false, - "trackedVehicleTraffic": false, - "individualMotorizedVehicleTraffic": false, - "taxiVehicleTraffic": false, - "overlappingLaneDescriptionProvided": false, - "cyclistVehicleTraffic": false, - "otherNonMotorizedTrafficTypes": false, - "multipleLanesTreatedAsOneLane": false, - "pedestrianTraffic": false, - "pedestriansTraffic": false - }, - "egressApproach": 0, - "ingressApproach": 5, - "ingressPath": true, - "egressPath": false, - "maneuvers": { - "maneuverStraightAllowed": true, - "maneuverNoStoppingAllowed": false, - "goWithHalt": false, - "maneuverLeftAllowed": false, - "maneuverUTurnAllowed": false, - "maneuverLeftTurnOnRedAllowed": false, - "reserved1": false, - "maneuverRightAllowed": false, - "maneuverLaneChangeAllowed": false, - "yieldAllwaysRequired": false, - "maneuverRightTurnOnRedAllowed": false, - "caution": false - }, - "connectsTo": [ - { - "connectingLane": { - "lane": 6, - "maneuver": { - "maneuverStraightAllowed": true, - "maneuverNoStoppingAllowed": false, - "goWithHalt": false, - "maneuverLeftAllowed": false, - "maneuverUTurnAllowed": false, - "maneuverLeftTurnOnRedAllowed": false, - "reserved1": false, - "maneuverRightAllowed": false, - "maneuverLaneChangeAllowed": false, - "yieldAllwaysRequired": false, - "maneuverRightTurnOnRedAllowed": false, - "caution": false - } - }, - "remoteIntersection": null, - "signalGroup": 4, - "userClass": null, - "connectionID": 1 - } - ] - } - }, - { - "type": "Feature", - "id": 16, - "geometry": { - "type": "LineString", - "coordinates": [ - [-105.0916852, 39.5952059], - [-105.0931135, 39.5952073], - [-105.0944406, 39.595204], - [-105.0952041, 39.5952041] - ] - }, - "properties": { - "nodes": [ - { - "delta": [-2337, -656], - "stopLine": null, - "dwidth": null, - "delevation": 10 - }, - { - "delta": [-12229, 15], - "stopLine": null, - "dwidth": null, - "delevation": 50 - }, - { - "delta": [-11362, -37], - "stopLine": null, - "dwidth": null, - "delevation": 30 - }, - { - "delta": [-6537, 1], - "stopLine": null, - "dwidth": null, - "delevation": null - } - ], - "laneId": 16, - "sharedWith": { - "busVehicleTraffic": false, - "trackedVehicleTraffic": false, - "individualMotorizedVehicleTraffic": false, - "taxiVehicleTraffic": false, - "overlappingLaneDescriptionProvided": false, - "cyclistVehicleTraffic": false, - "otherNonMotorizedTrafficTypes": false, - "multipleLanesTreatedAsOneLane": false, - "pedestrianTraffic": false, - "pedestriansTraffic": false - }, - "egressApproach": 0, - "ingressApproach": 5, - "ingressPath": true, - "egressPath": false, - "maneuvers": { - "maneuverStraightAllowed": true, - "maneuverNoStoppingAllowed": false, - "goWithHalt": false, - "maneuverLeftAllowed": false, - "maneuverUTurnAllowed": false, - "maneuverLeftTurnOnRedAllowed": false, - "reserved1": false, - "maneuverRightAllowed": false, - "maneuverLaneChangeAllowed": false, - "yieldAllwaysRequired": false, - "maneuverRightTurnOnRedAllowed": false, - "caution": false - }, - "connectsTo": [ - { - "connectingLane": { - "lane": 5, - "maneuver": { - "maneuverStraightAllowed": true, - "maneuverNoStoppingAllowed": false, - "goWithHalt": false, - "maneuverLeftAllowed": false, - "maneuverUTurnAllowed": false, - "maneuverLeftTurnOnRedAllowed": false, - "reserved1": false, - "maneuverRightAllowed": false, - "maneuverLaneChangeAllowed": false, - "yieldAllwaysRequired": false, - "maneuverRightTurnOnRedAllowed": false, - "caution": false - } - }, - "remoteIntersection": null, - "signalGroup": 4, - "userClass": null, - "connectionID": 1 - } - ] - } - }, - { - "type": "Feature", - "id": 17, - "geometry": { - "type": "LineString", - "coordinates": [ - [-105.0916853, 39.5952393], - [-105.0927239, 39.5952377], - [-105.0941207, 39.5952375], - [-105.0943034, 39.5952213] - ] - }, - "properties": { - "nodes": [ - { - "delta": [-2338, -284], - "stopLine": null, - "dwidth": null, - "delevation": 10 - }, - { - "delta": [-8892, -18], - "stopLine": null, - "dwidth": null, - "delevation": 30 - }, - { - "delta": [-11959, -2], - "stopLine": null, - "dwidth": null, - "delevation": 50 - }, - { - "delta": [-1564, -180], - "stopLine": null, - "dwidth": null, - "delevation": null - } - ], - "laneId": 17, - "sharedWith": { - "busVehicleTraffic": false, - "trackedVehicleTraffic": false, - "individualMotorizedVehicleTraffic": false, - "taxiVehicleTraffic": false, - "overlappingLaneDescriptionProvided": false, - "cyclistVehicleTraffic": false, - "otherNonMotorizedTrafficTypes": false, - "multipleLanesTreatedAsOneLane": false, - "pedestrianTraffic": false, - "pedestriansTraffic": false - }, - "egressApproach": 0, - "ingressApproach": 5, - "ingressPath": true, - "egressPath": false, - "maneuvers": { - "maneuverStraightAllowed": false, - "maneuverNoStoppingAllowed": false, - "goWithHalt": false, - "maneuverLeftAllowed": true, - "maneuverUTurnAllowed": false, - "maneuverLeftTurnOnRedAllowed": false, - "reserved1": false, - "maneuverRightAllowed": false, - "maneuverLaneChangeAllowed": false, - "yieldAllwaysRequired": false, - "maneuverRightTurnOnRedAllowed": false, - "caution": false - }, - "connectsTo": [ - { - "connectingLane": { - "lane": 26, - "maneuver": { - "maneuverStraightAllowed": false, - "maneuverNoStoppingAllowed": false, - "goWithHalt": false, - "maneuverLeftAllowed": true, - "maneuverUTurnAllowed": false, - "maneuverLeftTurnOnRedAllowed": false, - "reserved1": false, - "maneuverRightAllowed": false, - "maneuverLaneChangeAllowed": false, - "yieldAllwaysRequired": false, - "maneuverRightTurnOnRedAllowed": false, - "caution": false - } - }, - "remoteIntersection": null, - "signalGroup": 7, - "userClass": null, - "connectionID": 1 - } - ] - } - }, - { - "type": "Feature", - "id": 18, - "geometry": { - "type": "LineString", - "coordinates": [ - [-105.0916766, 39.595308], - [-105.0917443, 39.5953067] - ] - }, - "properties": { - "nodes": [ - { - "delta": [-2264, 479], - "stopLine": null, - "dwidth": null, - "delevation": 20 - }, - { - "delta": [-580, -14], - "stopLine": null, - "dwidth": null, - "delevation": null - } - ], - "laneId": 18, - "sharedWith": { - "busVehicleTraffic": false, - "trackedVehicleTraffic": false, - "individualMotorizedVehicleTraffic": false, - "taxiVehicleTraffic": false, - "overlappingLaneDescriptionProvided": false, - "cyclistVehicleTraffic": false, - "otherNonMotorizedTrafficTypes": false, - "multipleLanesTreatedAsOneLane": false, - "pedestrianTraffic": false, - "pedestriansTraffic": false - }, - "egressApproach": 6, - "ingressApproach": 0, - "ingressPath": false, - "egressPath": true - } - }, - { - "type": "Feature", - "id": 19, - "geometry": { - "type": "LineString", - "coordinates": [ - [-105.0916758, 39.5953365], - [-105.0917363, 39.5953365] - ] - }, - "properties": { - "nodes": [ - { - "delta": [-2257, 796], - "stopLine": null, - "dwidth": null, - "delevation": 20 - }, - { - "delta": [-518, 0], - "stopLine": null, - "dwidth": null, - "delevation": null - } - ], - "laneId": 19, - "sharedWith": { - "busVehicleTraffic": false, - "trackedVehicleTraffic": false, - "individualMotorizedVehicleTraffic": false, - "taxiVehicleTraffic": false, - "overlappingLaneDescriptionProvided": false, - "cyclistVehicleTraffic": false, - "otherNonMotorizedTrafficTypes": false, - "multipleLanesTreatedAsOneLane": false, - "pedestrianTraffic": false, - "pedestriansTraffic": false - }, - "egressApproach": 6, - "ingressApproach": 0, - "ingressPath": false, - "egressPath": true - } - }, - { - "type": "Feature", - "id": 20, - "geometry": { - "type": "LineString", - "coordinates": [ - [-105.0916734, 39.5953755], - [-105.0917299, 39.5953755] - ] - }, - "properties": { - "nodes": [ - { - "delta": [-2236, 1229], - "stopLine": null, - "dwidth": null, - "delevation": 20 - }, - { - "delta": [-484, 0], - "stopLine": null, - "dwidth": null, - "delevation": null - } - ], - "laneId": 20, - "sharedWith": { - "busVehicleTraffic": false, - "trackedVehicleTraffic": false, - "individualMotorizedVehicleTraffic": false, - "taxiVehicleTraffic": false, - "overlappingLaneDescriptionProvided": false, - "cyclistVehicleTraffic": false, - "otherNonMotorizedTrafficTypes": false, - "multipleLanesTreatedAsOneLane": false, - "pedestrianTraffic": false, - "pedestriansTraffic": false - }, - "egressApproach": 6, - "ingressApproach": 0, - "ingressPath": false, - "egressPath": true - } - }, - { - "type": "Feature", - "id": 23, - "geometry": { - "type": "LineString", - "coordinates": [ - [-105.091519, 39.5954362], - [-105.0914927, 39.5965421], - [-105.091488, 39.5974092], - [-105.0914843, 39.5981324], - [-105.0914843, 39.5981324], - [-105.09148, 39.599322] - ] - }, - "properties": { - "nodes": [ - { - "delta": [-914, 1903], - "stopLine": null, - "dwidth": null, - "delevation": 20 - }, - { - "delta": [225, 12288], - "stopLine": null, - "dwidth": null, - "delevation": 30 - }, - { - "delta": [40, 9634], - "stopLine": null, - "dwidth": null, - "delevation": -20 - }, - { - "delta": [32, 8035], - "stopLine": null, - "dwidth": null, - "delevation": -30 - }, - { - "delta": [0, 0], - "stopLine": null, - "dwidth": null, - "delevation": null - }, - { - "delta": [37, 13218], - "stopLine": null, - "dwidth": null, - "delevation": -10 - } - ], - "laneId": 23, - "sharedWith": { - "busVehicleTraffic": false, - "trackedVehicleTraffic": false, - "individualMotorizedVehicleTraffic": false, - "taxiVehicleTraffic": false, - "overlappingLaneDescriptionProvided": false, - "cyclistVehicleTraffic": false, - "otherNonMotorizedTrafficTypes": false, - "multipleLanesTreatedAsOneLane": false, - "pedestrianTraffic": false, - "pedestriansTraffic": false - }, - "egressApproach": 0, - "ingressApproach": 7, - "ingressPath": true, - "egressPath": false, - "maneuvers": { - "maneuverStraightAllowed": true, - "maneuverNoStoppingAllowed": false, - "goWithHalt": false, - "maneuverLeftAllowed": false, - "maneuverUTurnAllowed": false, - "maneuverLeftTurnOnRedAllowed": false, - "reserved1": false, - "maneuverRightAllowed": false, - "maneuverLaneChangeAllowed": false, - "yieldAllwaysRequired": false, - "maneuverRightTurnOnRedAllowed": false, - "caution": false - }, - "connectsTo": [ - { - "connectingLane": { - "lane": 12, - "maneuver": { - "maneuverStraightAllowed": true, - "maneuverNoStoppingAllowed": false, - "goWithHalt": false, - "maneuverLeftAllowed": false, - "maneuverUTurnAllowed": false, - "maneuverLeftTurnOnRedAllowed": false, - "reserved1": false, - "maneuverRightAllowed": false, - "maneuverLaneChangeAllowed": false, - "yieldAllwaysRequired": false, - "maneuverRightTurnOnRedAllowed": false, - "caution": false - } - }, - "remoteIntersection": null, - "signalGroup": 6, - "userClass": null, - "connectionID": 1 - } - ] - } - }, - { - "type": "Feature", - "id": 22, - "geometry": { - "type": "LineString", - "coordinates": [ - [-105.0915548, 39.5954372], - [-105.0915441, 39.59638], - [-105.0915337, 39.5974224], - [-105.0915232, 39.5993262] - ] - }, - "properties": { - "nodes": [ - { - "delta": [-1221, 1914], - "stopLine": null, - "dwidth": null, - "delevation": 20 - }, - { - "delta": [92, 10475], - "stopLine": null, - "dwidth": null, - "delevation": 30 - }, - { - "delta": [89, 11582], - "stopLine": null, - "dwidth": null, - "delevation": -20 - }, - { - "delta": [90, 21153], - "stopLine": null, - "dwidth": null, - "delevation": -40 - } - ], - "laneId": 22, - "sharedWith": { - "busVehicleTraffic": false, - "trackedVehicleTraffic": false, - "individualMotorizedVehicleTraffic": false, - "taxiVehicleTraffic": false, - "overlappingLaneDescriptionProvided": false, - "cyclistVehicleTraffic": false, - "otherNonMotorizedTrafficTypes": false, - "multipleLanesTreatedAsOneLane": false, - "pedestrianTraffic": false, - "pedestriansTraffic": false - }, - "egressApproach": 0, - "ingressApproach": 7, - "ingressPath": true, - "egressPath": false, - "maneuvers": { - "maneuverStraightAllowed": true, - "maneuverNoStoppingAllowed": false, - "goWithHalt": false, - "maneuverLeftAllowed": false, - "maneuverUTurnAllowed": false, - "maneuverLeftTurnOnRedAllowed": false, - "reserved1": false, - "maneuverRightAllowed": false, - "maneuverLaneChangeAllowed": false, - "yieldAllwaysRequired": false, - "maneuverRightTurnOnRedAllowed": false, - "caution": false - }, - "connectsTo": [ - { - "connectingLane": { - "lane": 13, - "maneuver": { - "maneuverStraightAllowed": true, - "maneuverNoStoppingAllowed": false, - "goWithHalt": false, - "maneuverLeftAllowed": false, - "maneuverUTurnAllowed": false, - "maneuverLeftTurnOnRedAllowed": false, - "reserved1": false, - "maneuverRightAllowed": false, - "maneuverLaneChangeAllowed": false, - "yieldAllwaysRequired": false, - "maneuverRightTurnOnRedAllowed": false, - "caution": false - } - }, - "remoteIntersection": null, - "signalGroup": 6, - "userClass": null, - "connectionID": 1 - } - ] - } - }, - { - "type": "Feature", - "id": 21, - "geometry": { - "type": "LineString", - "coordinates": [ - [-105.0916048, 39.5954391], - [-105.0916029, 39.5957255], - [-105.0916004, 39.5962647], - [-105.0915885, 39.5966976], - [-105.0915786, 39.5977599], - [-105.0915764, 39.5981344] - ] - }, - "properties": { - "nodes": [ - { - "delta": [-1649, 1935], - "stopLine": null, - "dwidth": null, - "delevation": 20 - }, - { - "delta": [16, 3182], - "stopLine": null, - "dwidth": null, - "delevation": 10 - }, - { - "delta": [21, 5991], - "stopLine": null, - "dwidth": null, - "delevation": 20 - }, - { - "delta": [102, 4810], - "stopLine": null, - "dwidth": null, - "delevation": null - }, - { - "delta": [85, 11803], - "stopLine": null, - "dwidth": null, - "delevation": -30 - }, - { - "delta": [19, 4161], - "stopLine": null, - "dwidth": null, - "delevation": -20 - } - ], - "laneId": 21, - "sharedWith": { - "busVehicleTraffic": false, - "trackedVehicleTraffic": false, - "individualMotorizedVehicleTraffic": false, - "taxiVehicleTraffic": false, - "overlappingLaneDescriptionProvided": false, - "cyclistVehicleTraffic": false, - "otherNonMotorizedTrafficTypes": false, - "multipleLanesTreatedAsOneLane": false, - "pedestrianTraffic": false, - "pedestriansTraffic": false - }, - "egressApproach": 0, - "ingressApproach": 7, - "ingressPath": true, - "egressPath": false, - "maneuvers": { - "maneuverStraightAllowed": false, - "maneuverNoStoppingAllowed": false, - "goWithHalt": false, - "maneuverLeftAllowed": false, - "maneuverUTurnAllowed": false, - "maneuverLeftTurnOnRedAllowed": false, - "reserved1": false, - "maneuverRightAllowed": true, - "maneuverLaneChangeAllowed": false, - "yieldAllwaysRequired": false, - "maneuverRightTurnOnRedAllowed": false, - "caution": false - }, - "connectsTo": [ - { - "connectingLane": { - "lane": 20, - "maneuver": { - "maneuverStraightAllowed": false, - "maneuverNoStoppingAllowed": false, - "goWithHalt": false, - "maneuverLeftAllowed": false, - "maneuverUTurnAllowed": false, - "maneuverLeftTurnOnRedAllowed": false, - "reserved1": false, - "maneuverRightAllowed": true, - "maneuverLaneChangeAllowed": false, - "yieldAllwaysRequired": false, - "maneuverRightTurnOnRedAllowed": false, - "caution": false - } - }, - "remoteIntersection": null, - "signalGroup": null, - "userClass": null, - "connectionID": 1 - } - ] - } - }, - { - "type": "Feature", - "id": 24, - "geometry": { - "type": "LineString", - "coordinates": [ - [-105.0914747, 39.5954382], - [-105.0914714, 39.5958506], - [-105.0914611, 39.596192], - [-105.0914778, 39.5963599] - ] - }, - "properties": { - "nodes": [ - { - "delta": [-535, 1926], - "stopLine": null, - "dwidth": null, - "delevation": 20 - }, - { - "delta": [28, 4582], - "stopLine": null, - "dwidth": null, - "delevation": 10 - }, - { - "delta": [88, 3793], - "stopLine": null, - "dwidth": null, - "delevation": 10 - }, - { - "delta": [-143, 1866], - "stopLine": null, - "dwidth": null, - "delevation": 10 - } - ], - "laneId": 24, - "sharedWith": { - "busVehicleTraffic": false, - "trackedVehicleTraffic": false, - "individualMotorizedVehicleTraffic": false, - "taxiVehicleTraffic": false, - "overlappingLaneDescriptionProvided": false, - "cyclistVehicleTraffic": false, - "otherNonMotorizedTrafficTypes": false, - "multipleLanesTreatedAsOneLane": false, - "pedestrianTraffic": false, - "pedestriansTraffic": false - }, - "egressApproach": 0, - "ingressApproach": 7, - "ingressPath": true, - "egressPath": false, - "maneuvers": { - "maneuverStraightAllowed": false, - "maneuverNoStoppingAllowed": false, - "goWithHalt": false, - "maneuverLeftAllowed": true, - "maneuverUTurnAllowed": false, - "maneuverLeftTurnOnRedAllowed": false, - "reserved1": false, - "maneuverRightAllowed": false, - "maneuverLaneChangeAllowed": false, - "yieldAllwaysRequired": false, - "maneuverRightTurnOnRedAllowed": false, - "caution": false - }, - "connectsTo": [ - { - "connectingLane": { - "lane": 6, - "maneuver": { - "maneuverStraightAllowed": false, - "maneuverNoStoppingAllowed": false, - "goWithHalt": false, - "maneuverLeftAllowed": true, - "maneuverUTurnAllowed": false, - "maneuverLeftTurnOnRedAllowed": false, - "reserved1": false, - "maneuverRightAllowed": false, - "maneuverLaneChangeAllowed": false, - "yieldAllwaysRequired": false, - "maneuverRightTurnOnRedAllowed": false, - "caution": false - } - }, - "remoteIntersection": null, - "signalGroup": 1, - "userClass": null, - "connectionID": 1 - } - ] - } - }, - { - "type": "Feature", - "id": 25, - "geometry": { - "type": "LineString", - "coordinates": [ - [-105.0914371, 39.5954371], - [-105.0914355, 39.5958506], - [-105.0914461, 39.596052] - ] - }, - "properties": { - "nodes": [ - { - "delta": [-213, 1913], - "stopLine": null, - "dwidth": null, - "delevation": 20 - }, - { - "delta": [14, 4594], - "stopLine": null, - "dwidth": null, - "delevation": 10 - }, - { - "delta": [-91, 2238], - "stopLine": null, - "dwidth": null, - "delevation": 10 - } - ], - "laneId": 25, - "sharedWith": { - "busVehicleTraffic": false, - "trackedVehicleTraffic": false, - "individualMotorizedVehicleTraffic": false, - "taxiVehicleTraffic": false, - "overlappingLaneDescriptionProvided": false, - "cyclistVehicleTraffic": false, - "otherNonMotorizedTrafficTypes": false, - "multipleLanesTreatedAsOneLane": false, - "pedestrianTraffic": false, - "pedestriansTraffic": false - }, - "egressApproach": 0, - "ingressApproach": 7, - "ingressPath": true, - "egressPath": false, - "maneuvers": { - "maneuverStraightAllowed": false, - "maneuverNoStoppingAllowed": false, - "goWithHalt": false, - "maneuverLeftAllowed": true, - "maneuverUTurnAllowed": false, - "maneuverLeftTurnOnRedAllowed": false, - "reserved1": false, - "maneuverRightAllowed": false, - "maneuverLaneChangeAllowed": false, - "yieldAllwaysRequired": false, - "maneuverRightTurnOnRedAllowed": false, - "caution": false - }, - "connectsTo": [ - { - "connectingLane": { - "lane": 5, - "maneuver": { - "maneuverStraightAllowed": false, - "maneuverNoStoppingAllowed": false, - "goWithHalt": false, - "maneuverLeftAllowed": true, - "maneuverUTurnAllowed": false, - "maneuverLeftTurnOnRedAllowed": false, - "reserved1": false, - "maneuverRightAllowed": false, - "maneuverLaneChangeAllowed": false, - "yieldAllwaysRequired": false, - "maneuverRightTurnOnRedAllowed": false, - "caution": false - } - }, - "remoteIntersection": null, - "signalGroup": 1, - "userClass": null, - "connectionID": 1 - } - ] - } - }, - { - "type": "Feature", - "id": 26, - "geometry": { - "type": "LineString", - "coordinates": [ - [-105.0913276, 39.5954361], - [-105.0913284, 39.5954678] - ] - }, - "properties": { - "nodes": [ - { - "delta": [724, 1902], - "stopLine": null, - "dwidth": null, - "delevation": 10 - }, - { - "delta": [-7, 352], - "stopLine": null, - "dwidth": null, - "delevation": 10 - } - ], - "laneId": 26, - "sharedWith": { - "busVehicleTraffic": false, - "trackedVehicleTraffic": false, - "individualMotorizedVehicleTraffic": false, - "taxiVehicleTraffic": false, - "overlappingLaneDescriptionProvided": false, - "cyclistVehicleTraffic": false, - "otherNonMotorizedTrafficTypes": false, - "multipleLanesTreatedAsOneLane": false, - "pedestrianTraffic": false, - "pedestriansTraffic": false - }, - "egressApproach": 8, - "ingressApproach": 0, - "ingressPath": false, - "egressPath": true - } - }, - { - "type": "Feature", - "id": 27, - "geometry": { - "type": "LineString", - "coordinates": [ - [-105.0912831, 39.5954348], - [-105.0912839, 39.5954647] - ] - }, - "properties": { - "nodes": [ - { - "delta": [1105, 1888], - "stopLine": null, - "dwidth": null, - "delevation": 10 - }, - { - "delta": [-7, 332], - "stopLine": null, - "dwidth": null, - "delevation": 10 - } - ], - "laneId": 27, - "sharedWith": { - "busVehicleTraffic": false, - "trackedVehicleTraffic": false, - "individualMotorizedVehicleTraffic": false, - "taxiVehicleTraffic": false, - "overlappingLaneDescriptionProvided": false, - "cyclistVehicleTraffic": false, - "otherNonMotorizedTrafficTypes": false, - "multipleLanesTreatedAsOneLane": false, - "pedestrianTraffic": false, - "pedestriansTraffic": false - }, - "egressApproach": 8, - "ingressApproach": 0, - "ingressPath": false, - "egressPath": true - } - }, - { - "type": "Feature", - "id": 28, - "geometry": { - "type": "LineString", - "coordinates": [ - [-105.0912264, 39.5954336], - [-105.091228, 39.5954647], - [-105.091228, 39.5954653] - ] - }, - "properties": { - "nodes": [ - { - "delta": [1591, 1874], - "stopLine": null, - "dwidth": null, - "delevation": 10 - }, - { - "delta": [-14, 346], - "stopLine": null, - "dwidth": null, - "delevation": null - }, - { - "delta": [0, 7], - "stopLine": null, - "dwidth": null, - "delevation": null - } - ], - "laneId": 28, - "sharedWith": { - "busVehicleTraffic": false, - "trackedVehicleTraffic": false, - "individualMotorizedVehicleTraffic": false, - "taxiVehicleTraffic": false, - "overlappingLaneDescriptionProvided": false, - "cyclistVehicleTraffic": false, - "otherNonMotorizedTrafficTypes": false, - "multipleLanesTreatedAsOneLane": false, - "pedestrianTraffic": false, - "pedestriansTraffic": false - }, - "egressApproach": 8, - "ingressApproach": 0, - "ingressPath": false, - "egressPath": true - } - } - ] - }, - "connectingLanesFeatureCollection": { - "type": "FeatureCollection", - "features": [ - { - "type": "Feature", - "id": "2-19", - "geometry": { - "type": "LineString", - "coordinates": [ - [-105.0911523, 39.5953376], - [-105.0916758, 39.5953365] - ] - }, - "properties": { - "signalGroupId": 8, - "ingressLaneId": 2, - "egressLaneId": 19 - } - }, - { - "type": "Feature", - "id": "3-18", - "geometry": { - "type": "LineString", - "coordinates": [ - [-105.0911527, 39.5953113], - [-105.0916766, 39.595308] - ] - }, - "properties": { - "signalGroupId": 8, - "ingressLaneId": 3, - "egressLaneId": 18 - } - }, - { - "type": "Feature", - "id": "4-12", - "geometry": { - "type": "LineString", - "coordinates": [ - [-105.0911537, 39.5952537], - [-105.0915234, 39.5950941] - ] - }, - "properties": { - "signalGroupId": 3, - "ingressLaneId": 4, - "egressLaneId": 12 - } - }, - { - "type": "Feature", - "id": "10-26", - "geometry": { - "type": "LineString", - "coordinates": [ - [-105.0913299, 39.5950922], - [-105.0913276, 39.5954361] - ] - }, - "properties": { - "signalGroupId": 2, - "ingressLaneId": 10, - "egressLaneId": 26 - } - }, - { - "type": "Feature", - "id": "9-27", - "geometry": { - "type": "LineString", - "coordinates": [ - [-105.0912849, 39.5950912], - [-105.0912831, 39.5954348] - ] - }, - "properties": { - "signalGroupId": 2, - "ingressLaneId": 9, - "egressLaneId": 27 - } - }, - { - "type": "Feature", - "id": "11-18", - "geometry": { - "type": "LineString", - "coordinates": [ - [-105.0913713, 39.5950916], - [-105.0916766, 39.595308] - ] - }, - "properties": { - "signalGroupId": 1, - "ingressLaneId": 11, - "egressLaneId": 18 - } - }, - { - "type": "Feature", - "id": "8-7", - "geometry": { - "type": "LineString", - "coordinates": [ - [-105.0912303, 39.5950889], - [-105.0911443, 39.5951332] - ] - }, - "properties": { - "signalGroupId": null, - "ingressLaneId": 8, - "egressLaneId": 7 - } - }, - { - "type": "Feature", - "id": "15-6", - "geometry": { - "type": "LineString", - "coordinates": [ - [-105.0916931, 39.5951733], - [-105.0911474, 39.5951732] - ] - }, - "properties": { - "signalGroupId": 4, - "ingressLaneId": 15, - "egressLaneId": 6 - } - }, - { - "type": "Feature", - "id": "16-5", - "geometry": { - "type": "LineString", - "coordinates": [ - [-105.0916852, 39.5952059], - [-105.0911474, 39.5952053] - ] - }, - "properties": { - "signalGroupId": 4, - "ingressLaneId": 16, - "egressLaneId": 5 - } - }, - { - "type": "Feature", - "id": "17-26", - "geometry": { - "type": "LineString", - "coordinates": [ - [-105.0916853, 39.5952393], - [-105.0913276, 39.5954361] - ] - }, - "properties": { - "signalGroupId": 7, - "ingressLaneId": 17, - "egressLaneId": 26 - } - }, - { - "type": "Feature", - "id": "23-12", - "geometry": { - "type": "LineString", - "coordinates": [ - [-105.091519, 39.5954362], - [-105.0915234, 39.5950941] - ] - }, - "properties": { - "signalGroupId": 6, - "ingressLaneId": 23, - "egressLaneId": 12 - } - }, - { - "type": "Feature", - "id": "22-13", - "geometry": { - "type": "LineString", - "coordinates": [ - [-105.0915548, 39.5954372], - [-105.0915804, 39.5950941] - ] - }, - "properties": { - "signalGroupId": 6, - "ingressLaneId": 22, - "egressLaneId": 13 - } - }, - { - "type": "Feature", - "id": "21-20", - "geometry": { - "type": "LineString", - "coordinates": [ - [-105.0916048, 39.5954391], - [-105.0916734, 39.5953755] - ] - }, - "properties": { - "signalGroupId": null, - "ingressLaneId": 21, - "egressLaneId": 20 - } - }, - { - "type": "Feature", - "id": "24-6", - "geometry": { - "type": "LineString", - "coordinates": [ - [-105.0914747, 39.5954382], - [-105.0911474, 39.5951732] - ] - }, - "properties": { - "signalGroupId": 1, - "ingressLaneId": 24, - "egressLaneId": 6 - } - }, - { - "type": "Feature", - "id": "25-5", - "geometry": { - "type": "LineString", - "coordinates": [ - [-105.0914371, 39.5954371], - [-105.0911474, 39.5952053] - ] - }, - "properties": { - "signalGroupId": 1, - "ingressLaneId": 25, - "egressLaneId": 5 - } - } - ] - }, - "properties": { - "messageType": "MAP", - "odeReceivedAt": "2022-12-08T16:47:18.558068Z", - "originIp": "172.19.0.1", - "intersectionId": 12110, - "msgIssueRevision": 2, - "revision": 2, - "refPoint": { - "latitude": 39.5952649, - "longitude": -105.0914122, - "elevation": 1677 - }, - "cti4501Conformant": false, - "validationMessages": [ - { - "message": "$.payload.data.intersections.intersectionGeometry[0].id.region: null found, integer expected", - "jsonPath": "$.payload.data.intersections.intersectionGeometry[0].id.region", - "schemaPath": "#/$defs/J2735RoadRegulatorID/type" - }, - { - "message": "$.payload.data.intersections.intersectionGeometry[0].laneSet.GenericLane[3].connectsTo: null found, object expected", - "jsonPath": "$.payload.data.intersections.intersectionGeometry[0].laneSet.GenericLane[3].connectsTo", - "schemaPath": "#/$defs/J2735ConnectsToList_Wrapper/type" - }, - { - "message": "$.payload.data.intersections.intersectionGeometry[0].laneSet.GenericLane[3].maneuvers: null found, object expected", - "jsonPath": "$.payload.data.intersections.intersectionGeometry[0].laneSet.GenericLane[3].maneuvers", - "schemaPath": "#/$defs/J2735AllowedManeuvers/type" - }, - { - "message": "$.payload.data.intersections.intersectionGeometry[0].laneSet.GenericLane[4].connectsTo: null found, object expected", - "jsonPath": "$.payload.data.intersections.intersectionGeometry[0].laneSet.GenericLane[4].connectsTo", - "schemaPath": "#/$defs/J2735ConnectsToList_Wrapper/type" - }, - { - "message": "$.payload.data.intersections.intersectionGeometry[0].laneSet.GenericLane[4].maneuvers: null found, object expected", - "jsonPath": "$.payload.data.intersections.intersectionGeometry[0].laneSet.GenericLane[4].maneuvers", - "schemaPath": "#/$defs/J2735AllowedManeuvers/type" - }, - { - "message": "$.payload.data.intersections.intersectionGeometry[0].laneSet.GenericLane[5].connectsTo: null found, object expected", - "jsonPath": "$.payload.data.intersections.intersectionGeometry[0].laneSet.GenericLane[5].connectsTo", - "schemaPath": "#/$defs/J2735ConnectsToList_Wrapper/type" - }, - { - "message": "$.payload.data.intersections.intersectionGeometry[0].laneSet.GenericLane[5].maneuvers: null found, object expected", - "jsonPath": "$.payload.data.intersections.intersectionGeometry[0].laneSet.GenericLane[5].maneuvers", - "schemaPath": "#/$defs/J2735AllowedManeuvers/type" - }, - { - "message": "$.payload.data.intersections.intersectionGeometry[0].laneSet.GenericLane[6].connectsTo: null found, object expected", - "jsonPath": "$.payload.data.intersections.intersectionGeometry[0].laneSet.GenericLane[6].connectsTo", - "schemaPath": "#/$defs/J2735ConnectsToList_Wrapper/type" - }, - { - "message": "$.payload.data.intersections.intersectionGeometry[0].laneSet.GenericLane[6].maneuvers: null found, object expected", - "jsonPath": "$.payload.data.intersections.intersectionGeometry[0].laneSet.GenericLane[6].maneuvers", - "schemaPath": "#/$defs/J2735AllowedManeuvers/type" - }, - { - "message": "$.payload.data.intersections.intersectionGeometry[0].laneSet.GenericLane[7].connectsTo: null found, object expected", - "jsonPath": "$.payload.data.intersections.intersectionGeometry[0].laneSet.GenericLane[7].connectsTo", - "schemaPath": "#/$defs/J2735ConnectsToList_Wrapper/type" - }, - { - "message": "$.payload.data.intersections.intersectionGeometry[0].laneSet.GenericLane[7].maneuvers: null found, object expected", - "jsonPath": "$.payload.data.intersections.intersectionGeometry[0].laneSet.GenericLane[7].maneuvers", - "schemaPath": "#/$defs/J2735AllowedManeuvers/type" - }, - { - "message": "$.payload.data.intersections.intersectionGeometry[0].laneSet.GenericLane[11].connectsTo.connectsTo[0].signalGroup: null found, integer expected", - "jsonPath": "$.payload.data.intersections.intersectionGeometry[0].laneSet.GenericLane[11].connectsTo.connectsTo[0].signalGroup", - "schemaPath": "#/$defs/J2735SignalGroupID/type" - }, - { - "message": "$.payload.data.intersections.intersectionGeometry[0].laneSet.GenericLane[15].connectsTo: null found, object expected", - "jsonPath": "$.payload.data.intersections.intersectionGeometry[0].laneSet.GenericLane[15].connectsTo", - "schemaPath": "#/$defs/J2735ConnectsToList_Wrapper/type" - }, - { - "message": "$.payload.data.intersections.intersectionGeometry[0].laneSet.GenericLane[15].maneuvers: null found, object expected", - "jsonPath": "$.payload.data.intersections.intersectionGeometry[0].laneSet.GenericLane[15].maneuvers", - "schemaPath": "#/$defs/J2735AllowedManeuvers/type" - }, - { - "message": "$.payload.data.intersections.intersectionGeometry[0].laneSet.GenericLane[16].connectsTo: null found, object expected", - "jsonPath": "$.payload.data.intersections.intersectionGeometry[0].laneSet.GenericLane[16].connectsTo", - "schemaPath": "#/$defs/J2735ConnectsToList_Wrapper/type" - }, - { - "message": "$.payload.data.intersections.intersectionGeometry[0].laneSet.GenericLane[16].maneuvers: null found, object expected", - "jsonPath": "$.payload.data.intersections.intersectionGeometry[0].laneSet.GenericLane[16].maneuvers", - "schemaPath": "#/$defs/J2735AllowedManeuvers/type" - }, - { - "message": "$.payload.data.intersections.intersectionGeometry[0].laneSet.GenericLane[17].connectsTo: null found, object expected", - "jsonPath": "$.payload.data.intersections.intersectionGeometry[0].laneSet.GenericLane[17].connectsTo", - "schemaPath": "#/$defs/J2735ConnectsToList_Wrapper/type" - }, - { - "message": "$.payload.data.intersections.intersectionGeometry[0].laneSet.GenericLane[17].maneuvers: null found, object expected", - "jsonPath": "$.payload.data.intersections.intersectionGeometry[0].laneSet.GenericLane[17].maneuvers", - "schemaPath": "#/$defs/J2735AllowedManeuvers/type" - }, - { - "message": "$.payload.data.intersections.intersectionGeometry[0].laneSet.GenericLane[20].connectsTo.connectsTo[0].signalGroup: null found, integer expected", - "jsonPath": "$.payload.data.intersections.intersectionGeometry[0].laneSet.GenericLane[20].connectsTo.connectsTo[0].signalGroup", - "schemaPath": "#/$defs/J2735SignalGroupID/type" - }, - { - "message": "$.payload.data.intersections.intersectionGeometry[0].laneSet.GenericLane[23].connectsTo: null found, object expected", - "jsonPath": "$.payload.data.intersections.intersectionGeometry[0].laneSet.GenericLane[23].connectsTo", - "schemaPath": "#/$defs/J2735ConnectsToList_Wrapper/type" - }, - { - "message": "$.payload.data.intersections.intersectionGeometry[0].laneSet.GenericLane[23].maneuvers: null found, object expected", - "jsonPath": "$.payload.data.intersections.intersectionGeometry[0].laneSet.GenericLane[23].maneuvers", - "schemaPath": "#/$defs/J2735AllowedManeuvers/type" - }, - { - "message": "$.payload.data.intersections.intersectionGeometry[0].laneSet.GenericLane[24].connectsTo: null found, object expected", - "jsonPath": "$.payload.data.intersections.intersectionGeometry[0].laneSet.GenericLane[24].connectsTo", - "schemaPath": "#/$defs/J2735ConnectsToList_Wrapper/type" - }, - { - "message": "$.payload.data.intersections.intersectionGeometry[0].laneSet.GenericLane[24].maneuvers: null found, object expected", - "jsonPath": "$.payload.data.intersections.intersectionGeometry[0].laneSet.GenericLane[24].maneuvers", - "schemaPath": "#/$defs/J2735AllowedManeuvers/type" - }, - { - "message": "$.payload.data.intersections.intersectionGeometry[0].laneSet.GenericLane[25].connectsTo: null found, object expected", - "jsonPath": "$.payload.data.intersections.intersectionGeometry[0].laneSet.GenericLane[25].connectsTo", - "schemaPath": "#/$defs/J2735ConnectsToList_Wrapper/type" - }, - { - "message": "$.payload.data.intersections.intersectionGeometry[0].laneSet.GenericLane[25].maneuvers: null found, object expected", - "jsonPath": "$.payload.data.intersections.intersectionGeometry[0].laneSet.GenericLane[25].maneuvers", - "schemaPath": "#/$defs/J2735AllowedManeuvers/type" - }, - { - "message": "$.payload.data.intersections.intersectionGeometry[0].speedLimits: null found, object expected", - "jsonPath": "$.payload.data.intersections.intersectionGeometry[0].speedLimits", - "schemaPath": "#/$defs/J2735SpeedLimitList_Wrapper/type" - } - ], - "laneWidth": 366, - "mapSource": "RSU", - "timeStamp": "2022-12-08T16:47:18.558068Z" - } -} diff --git a/webapp/src/pages/IntersectionMapView.tsx b/webapp/src/pages/IntersectionMapView.tsx index 1b1b4636c..e6c13b386 100644 --- a/webapp/src/pages/IntersectionMapView.tsx +++ b/webapp/src/pages/IntersectionMapView.tsx @@ -1,7 +1,7 @@ import React, { useEffect } from 'react' import { useDispatch, useSelector } from 'react-redux' import { Box, Container } from '@mui/material' -import MapTab from '../components/map/map-component' +import IntersectionMap from '../components/intersectionMap/map-component' import { getIntersections, selectSelectedIntersectionId, @@ -29,7 +29,7 @@ function IntersectionMapView() { maxWidth={false} style={{ width: '100%', height: 'calc(100vh - 135px)', display: 'flex', position: 'relative', padding: 0 }} > - { return configureStore({ diff --git a/webapp/src/styles/intersectionMapStyle.json b/webapp/src/styles/intersectionMapStyle.json new file mode 100644 index 000000000..4a0d8b291 --- /dev/null +++ b/webapp/src/styles/intersectionMapStyle.json @@ -0,0 +1,3326 @@ +{ + "version": 8, + "name": "CIMMS", + "metadata": { + "mapbox:type": "default", + "mapbox:origin": "satellite-streets-v12", + "mapbox:sdk-support": { + "android": "10.9.0", + "ios": "10.9.0", + "js": "2.11.1" + }, + "mapbox:autocomposite": true, + "mapbox:groups": { + "Transit, transit-labels": { + "name": "Transit, transit-labels", + "collapsed": true + }, + "Administrative boundaries, admin": { + "name": "Administrative boundaries, admin", + "collapsed": true + }, + "Transit, bridges": { "name": "Transit, bridges", "collapsed": true }, + "Road network, bridges": { + "name": "Road network, bridges", + "collapsed": true + }, + "Road network, tunnels": { + "name": "Road network, tunnels", + "collapsed": true + }, + "Road network, road-labels": { + "name": "Road network, road-labels", + "collapsed": true + }, + "Natural features, natural-labels": { + "name": "Natural features, natural-labels", + "collapsed": true + }, + "Road network, surface": { + "name": "Road network, surface", + "collapsed": true + }, + "Walking, cycling, etc., barriers-bridges": { + "name": "Walking, cycling, etc., barriers-bridges", + "collapsed": true + }, + "Place labels, place-labels": { + "name": "Place labels, place-labels", + "collapsed": true + }, + "Transit, elevated": { + "name": "Transit, elevated", + "collapsed": true + }, + "Point of interest labels, poi-labels": { + "name": "Point of interest labels, poi-labels", + "collapsed": true + }, + "Walking, cycling, etc., tunnels": { + "name": "Walking, cycling, etc., tunnels", + "collapsed": true + }, + "Road network, tunnels-case": { + "name": "Road network, tunnels-case", + "collapsed": true + }, + "Walking, cycling, etc., walking-cycling-labels": { + "name": "Walking, cycling, etc., walking-cycling-labels", + "collapsed": true + }, + "Walking, cycling, etc., surface": { + "name": "Walking, cycling, etc., surface", + "collapsed": true + }, + "Satellite imagery, land": { + "name": "Satellite imagery, land", + "collapsed": true + }, + "Transit, ferry-aerialway-labels": { + "name": "Transit, ferry-aerialway-labels", + "collapsed": true + } + }, + "mapbox:uiParadigm": "components", + "mapbox:decompiler": { + "id": "cld2b5mnd000201pcurww9h2w", + "componentVersion": "15.0.0", + "strata": [ + { + "id": "satellite-streets-v12", + "order": [ + ["transit", "built"], + ["satellite", "land"], + ["walking-cycling", "tunnels"], + ["road-network", "tunnels"], + ["walking-cycling", "surface"], + ["road-network", "surface"], + ["transit", "surface"], + ["walking-cycling", "barriers-bridges"], + ["road-network", "bridges"], + ["transit", "bridges"], + ["admin-boundaries", "admin"], + ["road-network", "road-labels"], + ["walking-cycling", "walking-cycling-labels"], + ["transit", "ferry-aerialway-labels"], + ["natural-features", "natural-labels"], + ["point-of-interest-labels", "poi-labels"], + ["transit", "transit-labels"], + ["place-labels", "place-labels"] + ] + } + ], + "components": { + "satellite": "18.0.0", + "road-network": "18.0.0", + "walking-cycling": "18.0.0", + "admin-boundaries": "18.0.0", + "natural-features": "18.0.0", + "point-of-interest-labels": "18.0.0", + "transit": "18.0.0", + "place-labels": "18.0.0" + }, + "propConfig": { + "satellite": {}, + "road-network": { + "roadsFont": ["DIN Pro Medium", "Arial Unicode MS Regular"], + "colorRoad": "hsla(0, 0%, 80%, 0.8)", + "iconSaturation": -0.5, + "construction": false, + "colorRoadLabel": "hsl(0, 0%, 100%)", + "fadeOutRoadsOnZoom": true, + "colorBase": "hsl(0, 0%, 0%)", + "roadsSize": 1.1, + "colorMotorwayTrunk": "hsla(40, 90%, 70%, 0.8)", + "polygonFeatures": false, + "colorPathOutline": "hsla(40, 95%, 90%, 0.8)", + "turningCircles": false, + "colorRoadOutline": "hsla(0, 1%, 10%, 0.7)" + }, + "walking-cycling": { + "roadsFont": ["DIN Pro Medium", "Arial Unicode MS Regular"], + "colorRoad": "hsla(0, 0%, 80%, 0.8)", + "golfHoleLabelLine": false, + "colorGreenspace": "hsl(94, 100%, 14%)", + "colorPoi": "hsl(0, 0%, 100%)", + "colorPoiParkLike": "hsl(110, 100%, 80%)", + "fadeOutPathsOnZoom": true, + "walkingCyclingPisteBackground": false, + "colorRoadLabel": "hsl(0, 0%, 100%)", + "colorBase": "hsl(0, 0%, 0%)", + "roadsSize": 1.1, + "poiEtcFont": ["DIN Pro Bold", "Arial Unicode MS Bold"], + "colorPathOutline": "hsla(40, 95%, 90%, 0.8)", + "pedestrianPolygonFeatures": false, + "colorRoadOutline": "hsla(0, 1%, 10%, 0.7)", + "labels": true + }, + "admin-boundaries": { + "admin0Width": 1.5, + "colorBase": "hsl(0, 0%, 0%)", + "colorPlaceLabel": "hsl(0, 0%, 100%)", + "colorAdminBoundary": "hsl(260, 15%, 80%)", + "admin0DashPattern": "Dash on zoom" + }, + "natural-features": { + "poiEtcFont": ["DIN Pro Bold", "Arial Unicode MS Bold"], + "colorBase": "hsl(0, 0%, 0%)", + "colorWater": "hsl(240, 100%, 62%)", + "colorPoi": "hsl(0, 0%, 100%)", + "labelHalo": true, + "poiIconScale": 0.8 + }, + "point-of-interest-labels": { + "poiIconBackground": "Circle", + "poiEtcFont": ["DIN Pro Bold", "Arial Unicode MS Bold"], + "colorBase": "hsl(0, 0%, 0%)", + "colorPoi": "hsl(0, 0%, 100%)", + "colorPoiParkLike": "hsl(110, 100%, 80%)", + "colorPoiMedical": "hsl(360, 100%, 80%)", + "colorPoiEducation": "hsl(30, 100%, 80%)", + "poiIconScale": 0.8 + }, + "transit": { + "matchLabelAndIcon": false, + "roadsFont": ["DIN Pro Medium", "Arial Unicode MS Regular"], + "aeroways": false, + "poiIconScale": 0.8, + "colorPoi": "hsl(0, 0%, 100%)", + "colorAirport": "hsl(0, 0%, 100%)", + "colorTransit": "hsl(204, 100%, 80%)", + "poiIconBackground": "Rectangle", + "colorBase": "hsl(0, 0%, 0%)", + "colorWater": "hsl(240, 100%, 62%)", + "roadsSize": 1.1, + "railways": false, + "ferries": false, + "poiEtcFont": ["DIN Pro Bold", "Arial Unicode MS Bold"], + "colorRoadOutline": "hsla(0, 1%, 10%, 0.7)" + }, + "place-labels": { + "continentsSize": 1.1, + "statesSize": 1.1, + "settlementsMinorSize": 1.1, + "settlementsMajorSize": 1.1, + "colorPlaceLabel": "hsl(0, 0%, 100%)", + "settlementSubdivisionsSize": 1.1, + "colorBase": "hsl(0, 0%, 0%)", + "countriesSize": 1.1, + "settlementSubdivisionsDensity": 1, + "settlementsMinorFont": ["DIN Pro Medium", "Arial Unicode MS Regular"], + "settlementSubdivisionsFont": ["DIN Pro Medium", "Arial Unicode MS Regular"] + } + } + } + }, + "center": [55.13, 25.12], + "zoom": 12, + "bearing": 0, + "pitch": 0, + "sources": { + "mapbox://mapbox.satellite": { + "url": "mapbox://mapbox.satellite", + "type": "raster", + "tileSize": 256 + }, + "composite": { + "url": "mapbox://mapbox.mapbox-streets-v8", + "type": "vector" + } + }, + "sprite": "mapbox://sprites/tonyenglish/cld2b5mnd000201pcurww9h2w/aisxnxnarmyjz9kth8rweopjr", + "glyphs": "mapbox://fonts/mapbox/{fontstack}/{range}.pbf", + "projection": { "name": "globe" }, + "layers": [ + { + "id": "background", + "type": "background", + "metadata": { + "mapbox:featureComponent": "satellite", + "mapbox:group": "Satellite imagery, land" + }, + "layout": {}, + "paint": { "background-color": "hsl(222, 56%, 4%)" } + }, + { + "id": "satellite", + "type": "raster", + "metadata": { + "mapbox:featureComponent": "satellite", + "mapbox:group": "Satellite imagery, land" + }, + "source": "mapbox://mapbox.satellite", + "layout": {}, + "paint": {} + }, + { + "id": "tunnel-path", + "type": "line", + "metadata": { + "mapbox:featureComponent": "walking-cycling", + "mapbox:group": "Walking, cycling, etc., tunnels" + }, + "source": "composite", + "source-layer": "road", + "minzoom": 15, + "filter": [ + "all", + ["==", ["get", "structure"], "tunnel"], + ["==", ["get", "class"], "path"], + ["!=", ["get", "type"], "steps"], + ["==", ["geometry-type"], "LineString"] + ], + "layout": {}, + "paint": { + "line-width": ["interpolate", ["exponential", 1.5], ["zoom"], 15, 1, 18, 4], + "line-color": "hsla(40, 88%, 77%, 0.8)", + "line-opacity": ["interpolate", ["linear"], ["zoom"], 17, 1, 18, 0], + "line-dasharray": [ + "step", + ["zoom"], + ["literal", [1, 0]], + 15, + ["literal", [1.75, 1]], + 16, + ["literal", [1, 0.75]], + 17, + ["literal", [1, 0.5]] + ] + } + }, + { + "id": "tunnel-steps", + "type": "line", + "metadata": { + "mapbox:featureComponent": "walking-cycling", + "mapbox:group": "Walking, cycling, etc., tunnels" + }, + "source": "composite", + "source-layer": "road", + "minzoom": 15, + "filter": [ + "all", + ["==", ["get", "structure"], "tunnel"], + ["==", ["get", "type"], "steps"], + ["==", ["geometry-type"], "LineString"] + ], + "paint": { + "line-width": ["interpolate", ["exponential", 1.5], ["zoom"], 15, 1, 16, 1.6, 18, 6], + "line-color": "hsla(40, 88%, 77%, 0.8)", + "line-opacity": ["interpolate", ["linear"], ["zoom"], 17, 1, 18, 0], + "line-dasharray": [ + "step", + ["zoom"], + ["literal", [1, 0]], + 15, + ["literal", [1.75, 1]], + 16, + ["literal", [1, 0.75]], + 17, + ["literal", [0.3, 0.3]] + ] + } + }, + { + "id": "tunnel-pedestrian", + "type": "line", + "metadata": { + "mapbox:featureComponent": "walking-cycling", + "mapbox:group": "Walking, cycling, etc., tunnels" + }, + "source": "composite", + "source-layer": "road", + "minzoom": 15, + "filter": [ + "all", + ["==", ["get", "structure"], "tunnel"], + ["==", ["get", "class"], "pedestrian"], + ["==", ["geometry-type"], "LineString"] + ], + "paint": { + "line-width": ["interpolate", ["exponential", 1.5], ["zoom"], 14, 0.5, 18, 12], + "line-color": "hsla(40, 95%, 90%, 0.8)", + "line-opacity": ["interpolate", ["linear"], ["zoom"], 17, 1, 18, 0], + "line-dasharray": [ + "step", + ["zoom"], + ["literal", [2, 0.3]], + 15, + ["literal", [1, 0.3]], + 16, + ["literal", [1, 0.3]], + 17, + ["literal", [1, 0.25]] + ] + } + }, + { + "id": "tunnel-minor-case", + "type": "line", + "metadata": { + "mapbox:featureComponent": "road-network", + "mapbox:group": "Road network, tunnels" + }, + "source": "composite", + "source-layer": "road", + "minzoom": 13, + "filter": [ + "all", + ["==", ["get", "structure"], "tunnel"], + ["match", ["get", "class"], ["track"], true, "service", ["step", ["zoom"], false, 14, true], false], + ["==", ["geometry-type"], "LineString"] + ], + "paint": { + "line-width": ["interpolate", ["exponential", 1.5], ["zoom"], 14, 0.8, 22, 2], + "line-color": "hsla(0, 0%, 0%, 0.7)", + "line-gap-width": ["interpolate", ["exponential", 1.5], ["zoom"], 14, 1, 18, 10, 22, 100], + "line-opacity": ["interpolate", ["linear"], ["zoom"], 13, 1, 15, 0], + "line-dasharray": [3, 3] + } + }, + { + "id": "tunnel-street-case", + "type": "line", + "metadata": { + "mapbox:featureComponent": "road-network", + "mapbox:group": "Road network, tunnels" + }, + "source": "composite", + "source-layer": "road", + "minzoom": 14, + "filter": [ + "all", + ["==", ["get", "structure"], "tunnel"], + ["match", ["get", "class"], ["street", "street_limited"], true, false], + ["==", ["geometry-type"], "LineString"] + ], + "paint": { + "line-width": ["interpolate", ["exponential", 1.5], ["zoom"], 14, 0.8, 22, 2], + "line-color": "hsla(0, 0%, 0%, 0.7)", + "line-gap-width": ["interpolate", ["exponential", 1.5], ["zoom"], 12, 0.5, 18, 20, 22, 200], + "line-opacity": ["interpolate", ["linear"], ["zoom"], 13, 1, 15, 0], + "line-dasharray": [3, 3] + } + }, + { + "id": "tunnel-minor-link-case", + "type": "line", + "metadata": { + "mapbox:featureComponent": "road-network", + "mapbox:group": "Road network, tunnels" + }, + "source": "composite", + "source-layer": "road", + "minzoom": 13, + "filter": [ + "all", + ["match", ["get", "class"], ["primary_link", "secondary_link", "tertiary_link"], true, false], + ["==", ["get", "structure"], "tunnel"], + ["==", ["geometry-type"], "LineString"] + ], + "layout": { + "line-cap": ["step", ["zoom"], "butt", 14, "round"], + "line-join": ["step", ["zoom"], "miter", 14, "round"] + }, + "paint": { + "line-width": ["interpolate", ["exponential", 1.5], ["zoom"], 14, 0.8, 22, 2], + "line-color": "hsla(0, 1%, 10%, 0.7)", + "line-gap-width": ["interpolate", ["exponential", 1.5], ["zoom"], 12, 0.4, 18, 18, 22, 180], + "line-opacity": ["interpolate", ["linear"], ["zoom"], 13, 1, 15, 0] + } + }, + { + "id": "tunnel-secondary-tertiary-case", + "type": "line", + "metadata": { + "mapbox:featureComponent": "road-network", + "mapbox:group": "Road network, tunnels" + }, + "source": "composite", + "source-layer": "road", + "minzoom": 11, + "filter": [ + "all", + ["==", ["get", "structure"], "tunnel"], + ["match", ["get", "class"], ["secondary", "tertiary"], true, false], + ["==", ["geometry-type"], "LineString"] + ], + "paint": { + "line-width": ["interpolate", ["exponential", 1.5], ["zoom"], 14, 1, 22, 2], + "line-color": "hsla(0, 0%, 0%, 0.7)", + "line-gap-width": ["interpolate", ["exponential", 1.5], ["zoom"], 3, 0, 18, 26, 22, 260], + "line-dasharray": [3, 3], + "line-opacity": ["interpolate", ["linear"], ["zoom"], 13, 0.3, 15, 0] + } + }, + { + "id": "tunnel-primary-case", + "type": "line", + "metadata": { + "mapbox:featureComponent": "road-network", + "mapbox:group": "Road network, tunnels" + }, + "source": "composite", + "source-layer": "road", + "minzoom": 10, + "filter": [ + "all", + ["==", ["get", "structure"], "tunnel"], + ["==", ["get", "class"], "primary"], + ["==", ["geometry-type"], "LineString"] + ], + "paint": { + "line-width": ["interpolate", ["exponential", 1.5], ["zoom"], 14, 1, 22, 2], + "line-color": "hsla(0, 0%, 0%, 0.7)", + "line-gap-width": ["interpolate", ["exponential", 1.5], ["zoom"], 3, 0.8, 18, 28, 22, 280], + "line-dasharray": [3, 3], + "line-opacity": ["interpolate", ["linear"], ["zoom"], 13, 0.3, 15, 0] + } + }, + { + "id": "tunnel-major-link-case", + "type": "line", + "metadata": { + "mapbox:featureComponent": "road-network", + "mapbox:group": "Road network, tunnels" + }, + "source": "composite", + "source-layer": "road", + "minzoom": 12, + "filter": [ + "all", + ["==", ["get", "structure"], "tunnel"], + ["match", ["get", "class"], ["motorway_link", "trunk_link"], true, false], + ["==", ["geometry-type"], "LineString"] + ], + "paint": { + "line-width": ["interpolate", ["exponential", 1.5], ["zoom"], 14, 0.8, 22, 2], + "line-color": "hsla(0, 0%, 80%, 0.8)", + "line-gap-width": ["interpolate", ["exponential", 1.5], ["zoom"], 12, 0.8, 18, 20, 22, 200], + "line-dasharray": [3, 3], + "line-opacity": ["interpolate", ["linear"], ["zoom"], 13, 1, 15, 0] + } + }, + { + "id": "tunnel-motorway-trunk-case", + "type": "line", + "metadata": { + "mapbox:featureComponent": "road-network", + "mapbox:group": "Road network, tunnels" + }, + "source": "composite", + "source-layer": "road", + "minzoom": 13, + "filter": [ + "all", + ["==", ["get", "structure"], "tunnel"], + ["match", ["get", "class"], ["motorway", "trunk"], true, false], + ["==", ["geometry-type"], "LineString"] + ], + "paint": { + "line-width": ["interpolate", ["exponential", 1.5], ["zoom"], 14, 1, 22, 2], + "line-color": "hsla(0, 1%, 22%, 0.7)", + "line-gap-width": ["interpolate", ["exponential", 1.5], ["zoom"], 3, 0.8, 18, 30, 22, 300], + "line-dasharray": [3, 3], + "line-opacity": ["interpolate", ["linear"], ["zoom"], 13, 1, 15, 0] + } + }, + { + "id": "tunnel-minor", + "type": "line", + "metadata": { + "mapbox:featureComponent": "road-network", + "mapbox:group": "Road network, tunnels" + }, + "source": "composite", + "source-layer": "road", + "minzoom": 13, + "filter": [ + "all", + ["==", ["get", "structure"], "tunnel"], + ["match", ["get", "class"], ["track"], true, "service", ["step", ["zoom"], false, 14, true], false], + ["==", ["geometry-type"], "LineString"] + ], + "paint": { + "line-width": ["interpolate", ["exponential", 1.5], ["zoom"], 14, 1, 18, 10, 22, 100], + "line-color": ["match", ["get", "class"], "street_limited", "hsl(0, 2%, 22%)", "hsla(0, 0%, 80%, 0.8)"], + "line-opacity": ["interpolate", ["linear"], ["zoom"], 13, 1, 15, 0] + } + }, + { + "id": "tunnel-minor-link", + "type": "line", + "metadata": { + "mapbox:featureComponent": "road-network", + "mapbox:group": "Road network, tunnels" + }, + "source": "composite", + "source-layer": "road", + "minzoom": 13, + "filter": [ + "all", + ["match", ["get", "class"], ["primary_link", "secondary_link", "tertiary_link"], true, false], + ["==", ["get", "structure"], "tunnel"], + ["==", ["geometry-type"], "LineString"] + ], + "layout": { + "line-cap": ["step", ["zoom"], "butt", 13, "round"], + "line-join": ["step", ["zoom"], "miter", 13, "round"] + }, + "paint": { + "line-width": ["interpolate", ["exponential", 1.5], ["zoom"], 12, 0.4, 18, 18, 22, 180], + "line-color": "hsla(0, 0%, 80%, 0.8)", + "line-opacity": ["interpolate", ["linear"], ["zoom"], 13, 1, 15, 0] + } + }, + { + "id": "tunnel-major-link", + "type": "line", + "metadata": { + "mapbox:featureComponent": "road-network", + "mapbox:group": "Road network, tunnels" + }, + "source": "composite", + "source-layer": "road", + "minzoom": 12, + "filter": [ + "all", + ["==", ["get", "structure"], "tunnel"], + ["match", ["get", "class"], ["motorway_link", "trunk_link"], true, false], + ["==", ["geometry-type"], "LineString"] + ], + "paint": { + "line-width": ["interpolate", ["exponential", 1.5], ["zoom"], 12, 0.8, 18, 20, 22, 200], + "line-color": [ + "match", + ["get", "class"], + "motorway_link", + "hsla(30, 100%, 80%, 0.8)", + "hsla(50, 78%, 80%, 0.8)" + ], + "line-opacity": ["interpolate", ["linear"], ["zoom"], 13, 1, 15, 0] + } + }, + { + "id": "tunnel-street", + "type": "line", + "metadata": { + "mapbox:featureComponent": "road-network", + "mapbox:group": "Road network, tunnels" + }, + "source": "composite", + "source-layer": "road", + "minzoom": 13, + "filter": [ + "all", + ["==", ["get", "structure"], "tunnel"], + ["match", ["get", "class"], ["street", "street_limited"], true, false], + ["==", ["geometry-type"], "LineString"] + ], + "paint": { + "line-width": ["interpolate", ["exponential", 1.5], ["zoom"], 12, 0.5, 18, 20, 22, 200], + "line-color": ["match", ["get", "class"], "street_limited", "hsl(0, 2%, 22%)", "hsla(0, 0%, 80%, 0.8)"], + "line-opacity": ["interpolate", ["linear"], ["zoom"], 13, 1, 15, 0] + } + }, + { + "id": "tunnel-street-low", + "type": "line", + "metadata": { + "mapbox:featureComponent": "road-network", + "mapbox:group": "Road network, tunnels" + }, + "source": "composite", + "source-layer": "road", + "minzoom": 13, + "maxzoom": 14, + "filter": [ + "all", + ["==", ["get", "structure"], "tunnel"], + ["match", ["get", "class"], ["street", "street_limited"], true, false], + ["==", ["geometry-type"], "LineString"] + ], + "layout": { + "line-cap": ["step", ["zoom"], "butt", 14, "round"], + "line-join": ["step", ["zoom"], "miter", 14, "round"] + }, + "paint": { + "line-width": ["interpolate", ["exponential", 1.5], ["zoom"], 12, 0.5, 18, 20, 22, 200], + "line-color": "hsla(0, 0%, 80%, 0.8)", + "line-opacity": ["interpolate", ["linear"], ["zoom"], 13, 0.3, 15, 0] + } + }, + { + "id": "tunnel-secondary-tertiary", + "type": "line", + "metadata": { + "mapbox:featureComponent": "road-network", + "mapbox:group": "Road network, tunnels" + }, + "source": "composite", + "source-layer": "road", + "minzoom": 13, + "filter": [ + "all", + ["==", ["get", "structure"], "tunnel"], + ["match", ["get", "class"], ["secondary", "tertiary"], true, false], + ["==", ["geometry-type"], "LineString"] + ], + "paint": { + "line-width": ["interpolate", ["exponential", 1.5], ["zoom"], 3, 0, 18, 26, 22, 260], + "line-color": "hsla(0, 0%, 80%, 0.8)", + "line-opacity": ["interpolate", ["linear"], ["zoom"], 13, 0.3, 15, 0] + } + }, + { + "id": "tunnel-primary", + "type": "line", + "metadata": { + "mapbox:featureComponent": "road-network", + "mapbox:group": "Road network, tunnels" + }, + "source": "composite", + "source-layer": "road", + "minzoom": 13, + "filter": [ + "all", + ["==", ["get", "structure"], "tunnel"], + ["==", ["get", "class"], "primary"], + ["==", ["geometry-type"], "LineString"] + ], + "paint": { + "line-width": ["interpolate", ["exponential", 1.5], ["zoom"], 3, 0.8, 18, 28, 22, 280], + "line-color": "hsla(0, 0%, 80%, 0.8)", + "line-opacity": ["interpolate", ["linear"], ["zoom"], 13, 0.3, 15, 0] + } + }, + { + "id": "tunnel-motorway-trunk", + "type": "line", + "metadata": { + "mapbox:featureComponent": "road-network", + "mapbox:group": "Road network, tunnels" + }, + "source": "composite", + "source-layer": "road", + "minzoom": 13, + "filter": [ + "all", + ["==", ["get", "structure"], "tunnel"], + ["match", ["get", "class"], ["motorway", "trunk"], true, false], + ["==", ["geometry-type"], "LineString"] + ], + "paint": { + "line-width": ["interpolate", ["exponential", 1.5], ["zoom"], 3, 0.8, 18, 30, 22, 300], + "line-color": ["match", ["get", "class"], "motorway", "hsla(30, 100%, 80%, 0.8)", "hsla(50, 78%, 80%, 0.8)"], + "line-opacity": ["interpolate", ["linear"], ["zoom"], 13, 1, 15, 0] + } + }, + { + "id": "road-path", + "type": "line", + "metadata": { + "mapbox:featureComponent": "walking-cycling", + "mapbox:group": "Walking, cycling, etc., surface" + }, + "source": "composite", + "source-layer": "road", + "minzoom": 12, + "filter": [ + "all", + ["==", ["get", "class"], "path"], + [ + "step", + ["zoom"], + ["!", ["match", ["get", "type"], ["steps", "sidewalk", "crossing"], true, false]], + 16, + ["!=", ["get", "type"], "steps"] + ], + ["match", ["get", "structure"], ["none", "ford"], true, false], + ["==", ["geometry-type"], "LineString"] + ], + "layout": { "line-join": ["step", ["zoom"], "miter", 14, "round"] }, + "paint": { + "line-width": ["interpolate", ["exponential", 1.5], ["zoom"], 13, 0.5, 14, 1, 15, 1, 18, 4], + "line-color": "hsla(40, 95%, 90%, 0.8)", + "line-opacity": ["interpolate", ["linear"], ["zoom"], 17, 1, 18, 0], + "line-dasharray": [ + "step", + ["zoom"], + ["literal", [4, 0.3]], + 15, + ["literal", [1.75, 0.3]], + 16, + ["literal", [1, 0.3]], + 17, + ["literal", [1, 0.25]] + ] + } + }, + { + "id": "road-steps", + "type": "line", + "metadata": { + "mapbox:featureComponent": "walking-cycling", + "mapbox:group": "Walking, cycling, etc., surface" + }, + "source": "composite", + "source-layer": "road", + "minzoom": 14, + "filter": [ + "all", + ["==", ["get", "type"], "steps"], + ["match", ["get", "structure"], ["none", "ford"], true, false], + ["==", ["geometry-type"], "LineString"] + ], + "layout": { "line-join": "round" }, + "paint": { + "line-width": ["interpolate", ["exponential", 1.5], ["zoom"], 15, 1, 16, 1.6, 18, 6], + "line-color": "hsla(40, 95%, 90%, 0.8)", + "line-opacity": ["interpolate", ["linear"], ["zoom"], 17, 1, 18, 0], + "line-dasharray": [ + "step", + ["zoom"], + ["literal", [1, 0]], + 15, + ["literal", [1.75, 1]], + 16, + ["literal", [1, 0.75]], + 17, + ["literal", [0.3, 0.3]] + ] + } + }, + { + "id": "road-pedestrian", + "type": "line", + "metadata": { + "mapbox:featureComponent": "walking-cycling", + "mapbox:group": "Walking, cycling, etc., surface" + }, + "source": "composite", + "source-layer": "road", + "minzoom": 12, + "filter": [ + "all", + ["==", ["get", "class"], "pedestrian"], + ["match", ["get", "structure"], ["none", "ford"], true, false], + ["case", ["has", "layer"], [">=", ["get", "layer"], 0], true], + ["==", ["geometry-type"], "LineString"] + ], + "layout": { "line-join": ["step", ["zoom"], "miter", 14, "round"] }, + "paint": { + "line-width": ["interpolate", ["exponential", 1.5], ["zoom"], 14, 0.5, 18, 12], + "line-color": "hsla(40, 95%, 90%, 0.8)", + "line-opacity": ["interpolate", ["linear"], ["zoom"], 17, 1, 18, 0], + "line-dasharray": [ + "step", + ["zoom"], + ["literal", [2, 0.3]], + 15, + ["literal", [1, 0.3]], + 16, + ["literal", [1, 0.3]], + 17, + ["literal", [1, 0.25]] + ] + } + }, + { + "id": "road-minor-case", + "type": "line", + "metadata": { + "mapbox:featureComponent": "road-network", + "mapbox:group": "Road network, surface" + }, + "source": "composite", + "source-layer": "road", + "minzoom": 13, + "filter": [ + "all", + ["match", ["get", "class"], ["track"], true, "service", ["step", ["zoom"], false, 14, true], false], + ["match", ["get", "structure"], ["none", "ford"], true, false], + ["==", ["geometry-type"], "LineString"] + ], + "layout": { + "line-cap": ["step", ["zoom"], "butt", 14, "round"], + "line-join": ["step", ["zoom"], "miter", 14, "round"] + }, + "paint": { + "line-width": ["interpolate", ["exponential", 1.5], ["zoom"], 14, 0.8, 22, 2], + "line-color": ["match", ["get", "class"], "track", "hsla(40, 95%, 90%, 0.8)", "hsla(0, 1%, 10%, 0.7)"], + "line-gap-width": ["interpolate", ["exponential", 1.5], ["zoom"], 14, 1, 18, 10, 22, 100], + "line-opacity": ["interpolate", ["linear"], ["zoom"], 13, 1, 15, 0] + } + }, + { + "id": "road-street-case", + "type": "line", + "metadata": { + "mapbox:featureComponent": "road-network", + "mapbox:group": "Road network, surface" + }, + "source": "composite", + "source-layer": "road", + "minzoom": 14, + "filter": [ + "all", + ["match", ["get", "class"], ["street", "street_limited"], true, false], + ["match", ["get", "structure"], ["none", "ford"], true, false], + ["==", ["geometry-type"], "LineString"] + ], + "layout": { + "line-cap": ["step", ["zoom"], "butt", 14, "round"], + "line-join": ["step", ["zoom"], "miter", 14, "round"] + }, + "paint": { + "line-width": ["interpolate", ["exponential", 1.5], ["zoom"], 14, 0.8, 22, 2], + "line-color": "hsla(0, 1%, 10%, 0.7)", + "line-gap-width": ["interpolate", ["exponential", 1.5], ["zoom"], 12, 0.5, 18, 20, 22, 200], + "line-opacity": ["interpolate", ["linear"], ["zoom"], 13, 0.3, 15, 0] + } + }, + { + "id": "road-minor-link-case", + "type": "line", + "metadata": { + "mapbox:featureComponent": "road-network", + "mapbox:group": "Road network, surface" + }, + "source": "composite", + "source-layer": "road", + "minzoom": 13, + "filter": [ + "all", + ["match", ["get", "class"], ["primary_link", "secondary_link", "tertiary_link"], true, false], + ["match", ["get", "structure"], ["none", "ford"], true, false], + ["==", ["geometry-type"], "LineString"] + ], + "layout": { + "line-cap": ["step", ["zoom"], "butt", 14, "round"], + "line-join": ["step", ["zoom"], "miter", 14, "round"] + }, + "paint": { + "line-width": ["interpolate", ["exponential", 1.5], ["zoom"], 14, 0.8, 22, 2], + "line-color": "hsla(0, 1%, 10%, 0.7)", + "line-gap-width": ["interpolate", ["exponential", 1.5], ["zoom"], 12, 0.4, 18, 18, 22, 180], + "line-opacity": ["interpolate", ["linear"], ["zoom"], 13, 1, 15, 0] + } + }, + { + "id": "road-secondary-tertiary-case", + "type": "line", + "metadata": { + "mapbox:featureComponent": "road-network", + "mapbox:group": "Road network, surface" + }, + "source": "composite", + "source-layer": "road", + "minzoom": 11, + "filter": [ + "all", + ["match", ["get", "class"], ["secondary", "tertiary"], true, false], + ["match", ["get", "structure"], ["none", "ford"], true, false], + ["==", ["geometry-type"], "LineString"] + ], + "layout": { + "line-cap": ["step", ["zoom"], "butt", 14, "round"], + "line-join": ["step", ["zoom"], "miter", 14, "round"] + }, + "paint": { + "line-width": ["interpolate", ["exponential", 1.5], ["zoom"], 14, 0.8, 22, 2], + "line-color": "hsla(0, 1%, 10%, 0.7)", + "line-gap-width": ["interpolate", ["exponential", 1.5], ["zoom"], 3, 0, 18, 26, 22, 260], + "line-opacity": ["interpolate", ["linear"], ["zoom"], 13, 0.3, 15, 0] + } + }, + { + "id": "road-primary-case", + "type": "line", + "metadata": { + "mapbox:featureComponent": "road-network", + "mapbox:group": "Road network, surface" + }, + "source": "composite", + "source-layer": "road", + "minzoom": 10, + "filter": [ + "all", + ["==", ["get", "class"], "primary"], + ["match", ["get", "structure"], ["none", "ford"], true, false], + ["==", ["geometry-type"], "LineString"] + ], + "layout": { + "line-cap": ["step", ["zoom"], "butt", 14, "round"], + "line-join": ["step", ["zoom"], "miter", 14, "round"] + }, + "paint": { + "line-width": ["interpolate", ["exponential", 1.5], ["zoom"], 14, 1, 22, 2], + "line-color": "hsla(0, 1%, 10%, 0.7)", + "line-gap-width": ["interpolate", ["exponential", 1.5], ["zoom"], 3, 0.8, 18, 28, 22, 280], + "line-opacity": ["interpolate", ["linear"], ["zoom"], 13, 1, 15, 0] + } + }, + { + "id": "road-major-link-case", + "type": "line", + "metadata": { + "mapbox:featureComponent": "road-network", + "mapbox:group": "Road network, surface" + }, + "source": "composite", + "source-layer": "road", + "minzoom": 12, + "filter": [ + "all", + ["match", ["get", "class"], ["motorway_link", "trunk_link"], true, false], + ["match", ["get", "structure"], ["none", "ford"], true, false], + ["==", ["geometry-type"], "LineString"] + ], + "layout": { + "line-cap": ["step", ["zoom"], "butt", 14, "round"], + "line-join": ["step", ["zoom"], "miter", 14, "round"] + }, + "paint": { + "line-width": ["interpolate", ["exponential", 1.5], ["zoom"], 14, 0.8, 22, 2], + "line-color": "hsla(0, 1%, 22%, 0.7)", + "line-gap-width": ["interpolate", ["exponential", 1.5], ["zoom"], 12, 0.8, 18, 20, 22, 200], + "line-opacity": ["interpolate", ["linear"], ["zoom"], 13, 1, 15, 0] + } + }, + { + "id": "road-motorway-trunk-case", + "type": "line", + "metadata": { + "mapbox:featureComponent": "road-network", + "mapbox:group": "Road network, surface" + }, + "source": "composite", + "source-layer": "road", + "minzoom": 3, + "filter": [ + "all", + [ + "step", + ["zoom"], + ["match", ["get", "class"], ["motorway", "trunk"], true, false], + 5, + [ + "all", + ["match", ["get", "class"], ["motorway", "trunk"], true, false], + ["match", ["get", "structure"], ["none", "ford"], true, false] + ] + ], + ["==", ["geometry-type"], "LineString"] + ], + "layout": { + "line-cap": ["step", ["zoom"], "butt", 14, "round"], + "line-join": ["step", ["zoom"], "miter", 14, "round"] + }, + "paint": { + "line-width": ["interpolate", ["exponential", 1.5], ["zoom"], 14, 1, 22, 2], + "line-color": "hsla(0, 1%, 22%, 0.7)", + "line-gap-width": ["interpolate", ["exponential", 1.5], ["zoom"], 3, 0.8, 18, 30, 22, 300], + "line-opacity": ["interpolate", ["linear"], ["zoom"], 3, 0, 3.5, 1, 13, 1, 15, 0] + } + }, + { + "id": "road-minor", + "type": "line", + "metadata": { + "mapbox:featureComponent": "road-network", + "mapbox:group": "Road network, surface" + }, + "source": "composite", + "source-layer": "road", + "minzoom": 13, + "filter": [ + "all", + ["match", ["get", "class"], ["track"], true, "service", ["step", ["zoom"], false, 14, true], false], + ["match", ["get", "structure"], ["none", "ford"], true, false], + ["==", ["geometry-type"], "LineString"] + ], + "layout": { + "line-cap": ["step", ["zoom"], "butt", 14, "round"], + "line-join": ["step", ["zoom"], "miter", 14, "round"] + }, + "paint": { + "line-width": ["interpolate", ["exponential", 1.5], ["zoom"], 14, 1, 18, 10, 22, 100], + "line-color": "hsla(0, 0%, 80%, 0.8)", + "line-opacity": ["interpolate", ["linear"], ["zoom"], 13, 1, 15, 0] + } + }, + { + "id": "road-minor-link", + "type": "line", + "metadata": { + "mapbox:featureComponent": "road-network", + "mapbox:group": "Road network, surface" + }, + "source": "composite", + "source-layer": "road", + "minzoom": 12, + "filter": [ + "all", + ["match", ["get", "class"], ["primary_link", "secondary_link", "tertiary_link"], true, false], + ["match", ["get", "structure"], ["none", "ford"], true, false], + ["==", ["geometry-type"], "LineString"] + ], + "layout": { + "line-cap": ["step", ["zoom"], "butt", 13, "round"], + "line-join": ["step", ["zoom"], "miter", 13, "round"] + }, + "paint": { + "line-width": ["interpolate", ["exponential", 1.5], ["zoom"], 12, 0.4, 18, 18, 22, 180], + "line-color": "hsla(0, 0%, 80%, 0.8)", + "line-opacity": ["interpolate", ["linear"], ["zoom"], 13, 1, 15, 0] + } + }, + { + "id": "road-major-link", + "type": "line", + "metadata": { + "mapbox:featureComponent": "road-network", + "mapbox:group": "Road network, surface" + }, + "source": "composite", + "source-layer": "road", + "minzoom": 12, + "filter": [ + "all", + ["match", ["get", "class"], ["motorway_link", "trunk_link"], true, false], + ["match", ["get", "structure"], ["none", "ford"], true, false], + ["==", ["geometry-type"], "LineString"] + ], + "layout": { + "line-cap": ["step", ["zoom"], "butt", 13, "round"], + "line-join": ["step", ["zoom"], "miter", 13, "round"] + }, + "paint": { + "line-width": ["interpolate", ["exponential", 1.5], ["zoom"], 12, 0.8, 18, 20, 22, 200], + "line-color": [ + "match", + ["get", "class"], + "motorway_link", + "hsla(30, 100%, 70%, 0.8)", + "hsla(50, 89%, 70%, 0.8)" + ], + "line-opacity": ["interpolate", ["linear"], ["zoom"], 13, 1, 15, 0] + } + }, + { + "id": "road-street", + "type": "line", + "metadata": { + "mapbox:featureComponent": "road-network", + "mapbox:group": "Road network, surface" + }, + "source": "composite", + "source-layer": "road", + "minzoom": 13, + "filter": [ + "all", + ["match", ["get", "class"], ["street", "street_limited"], true, false], + ["match", ["get", "structure"], ["none", "ford"], true, false], + ["==", ["geometry-type"], "LineString"] + ], + "layout": { + "line-cap": ["step", ["zoom"], "butt", 14, "round"], + "line-join": ["step", ["zoom"], "miter", 14, "round"] + }, + "paint": { + "line-width": ["interpolate", ["exponential", 1.5], ["zoom"], 12, 0.5, 18, 20, 22, 200], + "line-color": ["match", ["get", "class"], "street_limited", "hsl(0, 2%, 22%)", "hsla(0, 0%, 80%, 0.8)"], + "line-opacity": ["interpolate", ["linear"], ["zoom"], 13, 0.3, 15, 0] + } + }, + { + "id": "road-street-low", + "type": "line", + "metadata": { + "mapbox:featureComponent": "road-network", + "mapbox:group": "Road network, surface" + }, + "source": "composite", + "source-layer": "road", + "minzoom": 11, + "maxzoom": 14, + "filter": [ + "all", + ["match", ["get", "class"], ["street", "street_limited"], true, false], + ["match", ["get", "structure"], ["none", "ford"], true, false], + ["==", ["geometry-type"], "LineString"] + ], + "layout": { + "line-cap": ["step", ["zoom"], "butt", 14, "round"], + "line-join": ["step", ["zoom"], "miter", 14, "round"] + }, + "paint": { + "line-width": ["interpolate", ["exponential", 1.5], ["zoom"], 12, 0.5, 18, 20, 22, 200], + "line-color": "hsla(0, 0%, 80%, 0.8)", + "line-opacity": ["interpolate", ["linear"], ["zoom"], 13, 0.3, 15, 0] + } + }, + { + "id": "road-secondary-tertiary", + "type": "line", + "metadata": { + "mapbox:featureComponent": "road-network", + "mapbox:group": "Road network, surface" + }, + "source": "composite", + "source-layer": "road", + "minzoom": 9, + "filter": [ + "all", + ["match", ["get", "class"], ["secondary", "tertiary"], true, false], + ["match", ["get", "structure"], ["none", "ford"], true, false], + ["==", ["geometry-type"], "LineString"] + ], + "layout": { + "line-cap": ["step", ["zoom"], "butt", 14, "round"], + "line-join": ["step", ["zoom"], "miter", 14, "round"] + }, + "paint": { + "line-width": ["interpolate", ["exponential", 1.5], ["zoom"], 3, 0, 18, 26, 22, 260], + "line-color": "hsla(0, 0%, 80%, 0.8)", + "line-opacity": ["interpolate", ["linear"], ["zoom"], 13, 0.3, 15, 0] + } + }, + { + "id": "road-primary", + "type": "line", + "metadata": { + "mapbox:featureComponent": "road-network", + "mapbox:group": "Road network, surface" + }, + "source": "composite", + "source-layer": "road", + "minzoom": 6, + "filter": [ + "all", + ["==", ["get", "class"], "primary"], + ["match", ["get", "structure"], ["none", "ford"], true, false], + ["==", ["geometry-type"], "LineString"] + ], + "layout": { + "line-cap": ["step", ["zoom"], "butt", 14, "round"], + "line-join": ["step", ["zoom"], "miter", 14, "round"] + }, + "paint": { + "line-width": ["interpolate", ["exponential", 1.5], ["zoom"], 3, 0.8, 18, 28, 22, 280], + "line-color": "hsla(0, 0%, 80%, 0.8)", + "line-opacity": ["interpolate", ["linear"], ["zoom"], 13, 1, 15, 0] + } + }, + { + "id": "road-motorway-trunk", + "type": "line", + "metadata": { + "mapbox:featureComponent": "road-network", + "mapbox:group": "Road network, surface" + }, + "source": "composite", + "source-layer": "road", + "minzoom": 3, + "filter": [ + "all", + [ + "step", + ["zoom"], + ["match", ["get", "class"], ["motorway", "trunk"], true, false], + 5, + [ + "all", + ["match", ["get", "class"], ["motorway", "trunk"], true, false], + ["match", ["get", "structure"], ["none", "ford"], true, false] + ] + ], + ["==", ["geometry-type"], "LineString"] + ], + "layout": { + "line-cap": ["step", ["zoom"], "butt", 13, "round"], + "line-join": ["step", ["zoom"], "miter", 13, "round"] + }, + "paint": { + "line-width": ["interpolate", ["exponential", 1.5], ["zoom"], 3, 0.8, 18, 30, 22, 300], + "line-color": [ + "step", + ["zoom"], + [ + "match", + ["get", "class"], + "motorway", + "hsla(30, 88%, 64%, 0.8)", + "trunk", + "hsla(50, 81%, 54%, 0.8)", + "hsl(0, 0%, 22%)" + ], + 9, + ["match", ["get", "class"], "motorway", "hsla(30, 100%, 70%, 0.8)", "hsla(50, 89%, 70%, 0.8)"] + ], + "line-opacity": ["interpolate", ["linear"], ["zoom"], 3, 0, 3.5, 1, 13, 1, 15, 0] + } + }, + { + "id": "bridge-path", + "type": "line", + "metadata": { + "mapbox:featureComponent": "walking-cycling", + "mapbox:group": "Walking, cycling, etc., barriers-bridges" + }, + "source": "composite", + "source-layer": "road", + "minzoom": 14, + "filter": [ + "all", + ["==", ["get", "structure"], "bridge"], + ["==", ["get", "class"], "path"], + ["!=", ["get", "type"], "steps"], + ["==", ["geometry-type"], "LineString"] + ], + "layout": {}, + "paint": { + "line-width": ["interpolate", ["exponential", 1.5], ["zoom"], 15, 1, 18, 4], + "line-color": "hsla(40, 95%, 90%, 0.8)", + "line-opacity": ["interpolate", ["linear"], ["zoom"], 17, 1, 18, 0], + "line-dasharray": [ + "step", + ["zoom"], + ["literal", [4, 0.3]], + 15, + ["literal", [1.75, 0.3]], + 16, + ["literal", [1, 0.3]], + 17, + ["literal", [1, 0.25]] + ] + } + }, + { + "id": "bridge-steps", + "type": "line", + "metadata": { + "mapbox:featureComponent": "walking-cycling", + "mapbox:group": "Walking, cycling, etc., barriers-bridges" + }, + "source": "composite", + "source-layer": "road", + "minzoom": 14, + "filter": [ + "all", + ["==", ["get", "type"], "steps"], + ["==", ["get", "structure"], "bridge"], + ["==", ["geometry-type"], "LineString"] + ], + "paint": { + "line-width": ["interpolate", ["exponential", 1.5], ["zoom"], 15, 1, 16, 1.6, 18, 6], + "line-color": "hsla(40, 95%, 90%, 0.8)", + "line-opacity": ["interpolate", ["linear"], ["zoom"], 17, 1, 18, 0], + "line-dasharray": [ + "step", + ["zoom"], + ["literal", [1, 0]], + 15, + ["literal", [1.75, 1]], + 16, + ["literal", [1, 0.75]], + 17, + ["literal", [0.3, 0.3]] + ] + } + }, + { + "id": "bridge-pedestrian", + "type": "line", + "metadata": { + "mapbox:featureComponent": "walking-cycling", + "mapbox:group": "Walking, cycling, etc., barriers-bridges" + }, + "source": "composite", + "source-layer": "road", + "minzoom": 13, + "filter": [ + "all", + ["==", ["get", "structure"], "bridge"], + ["==", ["get", "class"], "pedestrian"], + ["==", ["geometry-type"], "LineString"] + ], + "paint": { + "line-width": ["interpolate", ["exponential", 1.5], ["zoom"], 14, 0.5, 18, 12], + "line-color": "hsla(40, 95%, 90%, 0.8)", + "line-opacity": ["interpolate", ["linear"], ["zoom"], 17, 1, 18, 0], + "line-dasharray": [ + "step", + ["zoom"], + ["literal", [2, 0.3]], + 15, + ["literal", [1, 0.3]], + 16, + ["literal", [1, 0.3]], + 17, + ["literal", [1, 0.25]] + ] + } + }, + { + "id": "bridge-minor-case", + "type": "line", + "metadata": { + "mapbox:featureComponent": "road-network", + "mapbox:group": "Road network, bridges" + }, + "source": "composite", + "source-layer": "road", + "minzoom": 13, + "filter": [ + "all", + ["==", ["get", "structure"], "bridge"], + ["match", ["get", "class"], ["track"], true, "service", ["step", ["zoom"], false, 14, true], false], + ["==", ["geometry-type"], "LineString"] + ], + "paint": { + "line-width": ["interpolate", ["exponential", 1.5], ["zoom"], 14, 0.8, 22, 2], + "line-color": ["match", ["get", "class"], "track", "hsla(40, 95%, 90%, 0.8)", "hsla(0, 1%, 10%, 0.7)"], + "line-gap-width": ["interpolate", ["exponential", 1.5], ["zoom"], 14, 1, 18, 10, 22, 100], + "line-opacity": ["interpolate", ["linear"], ["zoom"], 13, 1, 15, 0] + } + }, + { + "id": "bridge-street-case", + "type": "line", + "metadata": { + "mapbox:featureComponent": "road-network", + "mapbox:group": "Road network, bridges" + }, + "source": "composite", + "source-layer": "road", + "minzoom": 14, + "filter": [ + "all", + ["==", ["get", "structure"], "bridge"], + ["match", ["get", "class"], ["street", "street_limited"], true, false], + ["==", ["geometry-type"], "LineString"] + ], + "paint": { + "line-width": ["interpolate", ["exponential", 1.5], ["zoom"], 14, 0.8, 22, 2], + "line-color": ["match", ["get", "class"], "track", "hsla(40, 95%, 90%, 0.8)", "hsla(0, 1%, 10%, 0.7)"], + "line-gap-width": ["interpolate", ["exponential", 1.5], ["zoom"], 12, 0.5, 18, 20, 22, 200], + "line-opacity": ["interpolate", ["linear"], ["zoom"], 13, 1, 15, 0] + } + }, + { + "id": "bridge-minor-link-case", + "type": "line", + "metadata": { + "mapbox:featureComponent": "road-network", + "mapbox:group": "Road network, bridges" + }, + "source": "composite", + "source-layer": "road", + "minzoom": 13, + "filter": [ + "all", + ["match", ["get", "class"], ["primary_link", "secondary_link", "tertiary_link"], true, false], + ["==", ["get", "structure"], "bridge"], + ["==", ["geometry-type"], "LineString"] + ], + "layout": { "line-join": ["step", ["zoom"], "miter", 14, "round"] }, + "paint": { + "line-width": ["interpolate", ["exponential", 1.5], ["zoom"], 14, 0.8, 22, 2], + "line-color": "hsla(0, 1%, 10%, 0.7)", + "line-gap-width": ["interpolate", ["exponential", 1.5], ["zoom"], 12, 0.4, 18, 18, 22, 180], + "line-opacity": ["interpolate", ["linear"], ["zoom"], 13, 1, 15, 0] + } + }, + { + "id": "bridge-secondary-tertiary-case", + "type": "line", + "metadata": { + "mapbox:featureComponent": "road-network", + "mapbox:group": "Road network, bridges" + }, + "source": "composite", + "source-layer": "road", + "minzoom": 11, + "filter": [ + "all", + ["==", ["get", "structure"], "bridge"], + ["match", ["get", "class"], ["secondary", "tertiary"], true, false], + ["==", ["geometry-type"], "LineString"] + ], + "paint": { + "line-width": ["interpolate", ["exponential", 1.5], ["zoom"], 14, 1, 22, 2], + "line-color": "hsla(0, 1%, 10%, 0.7)", + "line-gap-width": ["interpolate", ["exponential", 1.5], ["zoom"], 3, 0, 18, 26, 22, 260], + "line-opacity": ["interpolate", ["linear"], ["zoom"], 13, 0.3, 15, 0] + } + }, + { + "id": "bridge-primary-case", + "type": "line", + "metadata": { + "mapbox:featureComponent": "road-network", + "mapbox:group": "Road network, bridges" + }, + "source": "composite", + "source-layer": "road", + "minzoom": 10, + "filter": [ + "all", + ["==", ["get", "structure"], "bridge"], + ["==", ["get", "class"], "primary"], + ["==", ["geometry-type"], "LineString"] + ], + "paint": { + "line-width": ["interpolate", ["exponential", 1.5], ["zoom"], 14, 1, 22, 2], + "line-color": "hsla(0, 1%, 10%, 0.7)", + "line-gap-width": ["interpolate", ["exponential", 1.5], ["zoom"], 3, 0.8, 18, 28, 22, 280], + "line-opacity": ["interpolate", ["linear"], ["zoom"], 13, 0.3, 15, 0] + } + }, + { + "id": "bridge-major-link-case", + "type": "line", + "metadata": { + "mapbox:featureComponent": "road-network", + "mapbox:group": "Road network, bridges" + }, + "source": "composite", + "source-layer": "road", + "minzoom": 12, + "filter": [ + "all", + ["==", ["get", "structure"], "bridge"], + ["match", ["get", "class"], ["motorway_link", "trunk_link"], true, false], + ["<=", ["get", "layer"], 1], + ["==", ["geometry-type"], "LineString"] + ], + "paint": { + "line-width": ["interpolate", ["exponential", 1.5], ["zoom"], 14, 0.8, 22, 2], + "line-color": "hsla(0, 1%, 22%, 0.7)", + "line-gap-width": ["interpolate", ["exponential", 1.5], ["zoom"], 12, 0.8, 18, 20, 22, 200], + "line-opacity": ["interpolate", ["linear"], ["zoom"], 13, 1, 15, 0] + } + }, + { + "id": "bridge-motorway-trunk-case", + "type": "line", + "metadata": { + "mapbox:featureComponent": "road-network", + "mapbox:group": "Road network, bridges" + }, + "source": "composite", + "source-layer": "road", + "minzoom": 13, + "filter": [ + "all", + ["==", ["get", "structure"], "bridge"], + ["match", ["get", "class"], ["motorway", "trunk"], true, false], + ["<=", ["get", "layer"], 1], + ["==", ["geometry-type"], "LineString"] + ], + "paint": { + "line-width": ["interpolate", ["exponential", 1.5], ["zoom"], 14, 1, 22, 2], + "line-color": "hsla(0, 1%, 22%, 0.7)", + "line-gap-width": ["interpolate", ["exponential", 1.5], ["zoom"], 3, 0.8, 18, 30, 22, 300], + "line-opacity": ["interpolate", ["linear"], ["zoom"], 13, 1, 15, 0] + } + }, + { + "id": "bridge-minor", + "type": "line", + "metadata": { + "mapbox:featureComponent": "road-network", + "mapbox:group": "Road network, bridges" + }, + "source": "composite", + "source-layer": "road", + "minzoom": 13, + "filter": [ + "all", + ["==", ["get", "structure"], "bridge"], + ["match", ["get", "class"], ["track"], true, "service", ["step", ["zoom"], false, 14, true], false], + ["==", ["geometry-type"], "LineString"] + ], + "layout": { "line-cap": ["step", ["zoom"], "butt", 14, "round"] }, + "paint": { + "line-width": ["interpolate", ["exponential", 1.5], ["zoom"], 14, 1, 18, 10, 22, 100], + "line-color": "hsla(0, 0%, 80%, 0.8)", + "line-opacity": ["interpolate", ["linear"], ["zoom"], 13, 1, 15, 0] + } + }, + { + "id": "bridge-minor-link", + "type": "line", + "metadata": { + "mapbox:featureComponent": "road-network", + "mapbox:group": "Road network, bridges" + }, + "source": "composite", + "source-layer": "road", + "minzoom": 13, + "filter": [ + "all", + ["match", ["get", "class"], ["primary_link", "secondary_link", "tertiary_link"], true, false], + ["==", ["get", "structure"], "bridge"], + ["==", ["geometry-type"], "LineString"] + ], + "layout": { "line-cap": ["step", ["zoom"], "butt", 14, "round"] }, + "paint": { + "line-width": ["interpolate", ["exponential", 1.5], ["zoom"], 12, 0.4, 18, 18, 22, 180], + "line-color": "hsla(0, 0%, 80%, 0.8)", + "line-opacity": ["interpolate", ["linear"], ["zoom"], 13, 1, 15, 0] + } + }, + { + "id": "bridge-major-link", + "type": "line", + "metadata": { + "mapbox:featureComponent": "road-network", + "mapbox:group": "Road network, bridges" + }, + "source": "composite", + "source-layer": "road", + "minzoom": 12, + "filter": [ + "all", + ["==", ["get", "structure"], "bridge"], + ["match", ["get", "class"], ["motorway_link", "trunk_link"], true, false], + ["<=", ["get", "layer"], 1], + ["==", ["geometry-type"], "LineString"] + ], + "layout": { "line-cap": ["step", ["zoom"], "butt", 13, "round"] }, + "paint": { + "line-width": ["interpolate", ["exponential", 1.5], ["zoom"], 12, 0.8, 18, 20, 22, 200], + "line-color": [ + "match", + ["get", "class"], + "motorway_link", + "hsla(30, 100%, 70%, 0.8)", + "hsla(50, 89%, 70%, 0.8)" + ], + "line-opacity": ["interpolate", ["linear"], ["zoom"], 13, 1, 15, 0] + } + }, + { + "id": "bridge-street", + "type": "line", + "metadata": { + "mapbox:featureComponent": "road-network", + "mapbox:group": "Road network, bridges" + }, + "source": "composite", + "source-layer": "road", + "minzoom": 13, + "filter": [ + "all", + ["==", ["get", "structure"], "bridge"], + ["match", ["get", "class"], ["street", "street_limited"], true, false], + ["==", ["geometry-type"], "LineString"] + ], + "layout": { "line-cap": ["step", ["zoom"], "butt", 14, "round"] }, + "paint": { + "line-width": ["interpolate", ["exponential", 1.5], ["zoom"], 12, 0.5, 18, 20, 22, 200], + "line-color": ["match", ["get", "class"], "street_limited", "hsl(0, 2%, 22%)", "hsla(0, 0%, 80%, 0.8)"], + "line-opacity": ["interpolate", ["linear"], ["zoom"], 13, 1, 15, 0] + } + }, + { + "id": "bridge-street-low", + "type": "line", + "metadata": { + "mapbox:featureComponent": "road-network", + "mapbox:group": "Road network, bridges" + }, + "source": "composite", + "source-layer": "road", + "minzoom": 13, + "maxzoom": 14, + "filter": [ + "all", + ["==", ["get", "structure"], "bridge"], + ["match", ["get", "class"], ["street", "street_limited"], true, false], + ["==", ["geometry-type"], "LineString"] + ], + "layout": { + "line-cap": ["step", ["zoom"], "butt", 14, "round"], + "line-join": ["step", ["zoom"], "miter", 14, "round"] + }, + "paint": { + "line-width": ["interpolate", ["exponential", 1.5], ["zoom"], 12, 0.5, 18, 20, 22, 200], + "line-color": "hsla(0, 0%, 80%, 0.8)", + "line-opacity": ["interpolate", ["linear"], ["zoom"], 13, 0.3, 15, 0] + } + }, + { + "id": "bridge-secondary-tertiary", + "type": "line", + "metadata": { + "mapbox:featureComponent": "road-network", + "mapbox:group": "Road network, bridges" + }, + "source": "composite", + "source-layer": "road", + "minzoom": 13, + "filter": [ + "all", + ["==", ["get", "structure"], "bridge"], + ["match", ["get", "class"], ["secondary", "tertiary"], true, false], + ["==", ["geometry-type"], "LineString"] + ], + "layout": { "line-cap": ["step", ["zoom"], "butt", 14, "round"] }, + "paint": { + "line-width": ["interpolate", ["exponential", 1.5], ["zoom"], 3, 0, 18, 26, 22, 260], + "line-color": "hsla(0, 0%, 80%, 0.8)", + "line-opacity": ["interpolate", ["linear"], ["zoom"], 13, 0.3, 15, 0] + } + }, + { + "id": "bridge-primary", + "type": "line", + "metadata": { + "mapbox:featureComponent": "road-network", + "mapbox:group": "Road network, bridges" + }, + "source": "composite", + "source-layer": "road", + "minzoom": 13, + "filter": [ + "all", + ["==", ["get", "structure"], "bridge"], + ["==", ["get", "class"], "primary"], + ["==", ["geometry-type"], "LineString"] + ], + "layout": { "line-cap": ["step", ["zoom"], "butt", 14, "round"] }, + "paint": { + "line-width": ["interpolate", ["exponential", 1.5], ["zoom"], 3, 0.8, 18, 28, 22, 280], + "line-color": "hsla(0, 0%, 80%, 0.8)", + "line-opacity": ["interpolate", ["linear"], ["zoom"], 13, 0.3, 15, 0] + } + }, + { + "id": "bridge-motorway-trunk", + "type": "line", + "metadata": { + "mapbox:featureComponent": "road-network", + "mapbox:group": "Road network, bridges" + }, + "source": "composite", + "source-layer": "road", + "minzoom": 13, + "filter": [ + "all", + ["==", ["get", "structure"], "bridge"], + ["match", ["get", "class"], ["motorway", "trunk"], true, false], + ["<=", ["get", "layer"], 1], + ["==", ["geometry-type"], "LineString"] + ], + "layout": { "line-cap": ["step", ["zoom"], "butt", 14, "round"] }, + "paint": { + "line-width": ["interpolate", ["exponential", 1.5], ["zoom"], 3, 0.8, 18, 30, 22, 300], + "line-color": ["match", ["get", "class"], "motorway", "hsla(30, 100%, 70%, 0.8)", "hsla(50, 89%, 70%, 0.8)"], + "line-opacity": ["interpolate", ["linear"], ["zoom"], 13, 1, 15, 0] + } + }, + { + "id": "bridge-major-link-2-case", + "type": "line", + "metadata": { + "mapbox:featureComponent": "road-network", + "mapbox:group": "Road network, bridges" + }, + "source": "composite", + "source-layer": "road", + "minzoom": 12, + "filter": [ + "all", + ["==", ["get", "structure"], "bridge"], + [">=", ["get", "layer"], 2], + ["match", ["get", "class"], ["motorway_link", "trunk_link"], true, false], + ["==", ["geometry-type"], "LineString"] + ], + "paint": { + "line-width": ["interpolate", ["exponential", 1.5], ["zoom"], 14, 0.8, 22, 2], + "line-color": "hsla(0, 1%, 22%, 0.7)", + "line-gap-width": ["interpolate", ["exponential", 1.5], ["zoom"], 12, 0.8, 18, 20, 22, 200], + "line-opacity": ["interpolate", ["linear"], ["zoom"], 13, 1, 15, 0] + } + }, + { + "id": "bridge-motorway-trunk-2-case", + "type": "line", + "metadata": { + "mapbox:featureComponent": "road-network", + "mapbox:group": "Road network, bridges" + }, + "source": "composite", + "source-layer": "road", + "minzoom": 13, + "filter": [ + "all", + ["==", ["get", "structure"], "bridge"], + [">=", ["get", "layer"], 2], + ["match", ["get", "class"], ["motorway", "trunk"], true, false], + ["==", ["geometry-type"], "LineString"] + ], + "paint": { + "line-width": ["interpolate", ["exponential", 1.5], ["zoom"], 14, 1, 22, 2], + "line-color": "hsla(0, 1%, 22%, 0.7)", + "line-gap-width": ["interpolate", ["exponential", 1.5], ["zoom"], 3, 0.8, 18, 30, 22, 300], + "line-opacity": ["interpolate", ["linear"], ["zoom"], 13, 1, 15, 0] + } + }, + { + "id": "bridge-major-link-2", + "type": "line", + "metadata": { + "mapbox:featureComponent": "road-network", + "mapbox:group": "Road network, bridges" + }, + "source": "composite", + "source-layer": "road", + "minzoom": 12, + "filter": [ + "all", + ["==", ["get", "structure"], "bridge"], + [">=", ["get", "layer"], 2], + ["match", ["get", "class"], ["motorway_link", "trunk_link"], true, false], + ["==", ["geometry-type"], "LineString"] + ], + "layout": { "line-cap": ["step", ["zoom"], "butt", 13, "round"] }, + "paint": { + "line-width": ["interpolate", ["exponential", 1.5], ["zoom"], 12, 0.8, 18, 20, 22, 200], + "line-color": [ + "match", + ["get", "class"], + "motorway_link", + "hsla(30, 100%, 70%, 0.8)", + "hsla(50, 89%, 70%, 0.8)" + ], + "line-opacity": ["interpolate", ["linear"], ["zoom"], 13, 1, 15, 0] + } + }, + { + "id": "bridge-motorway-trunk-2", + "type": "line", + "metadata": { + "mapbox:featureComponent": "road-network", + "mapbox:group": "Road network, bridges" + }, + "source": "composite", + "source-layer": "road", + "minzoom": 13, + "filter": [ + "all", + ["==", ["get", "structure"], "bridge"], + [">=", ["get", "layer"], 2], + ["match", ["get", "class"], ["motorway", "trunk"], true, false], + ["==", ["geometry-type"], "LineString"] + ], + "layout": { "line-cap": ["step", ["zoom"], "butt", 14, "round"] }, + "paint": { + "line-width": ["interpolate", ["exponential", 1.5], ["zoom"], 3, 0.8, 18, 30, 22, 300], + "line-color": ["match", ["get", "class"], "motorway", "hsla(30, 100%, 70%, 0.8)", "hsla(50, 89%, 70%, 0.8)"], + "line-opacity": ["interpolate", ["linear"], ["zoom"], 13, 1, 15, 0] + } + }, + { + "id": "aerialway", + "type": "line", + "metadata": { + "mapbox:featureComponent": "transit", + "mapbox:group": "Transit, bridges" + }, + "source": "composite", + "source-layer": "road", + "minzoom": 12, + "filter": ["==", ["get", "class"], "aerialway"], + "paint": { + "line-color": "hsl(204, 100%, 80%)", + "line-width": ["interpolate", ["exponential", 1.5], ["zoom"], 14, 1, 20, 2], + "line-dasharray": [4, 1] + } + }, + { + "id": "admin-1-boundary-bg", + "type": "line", + "metadata": { + "mapbox:featureComponent": "admin-boundaries", + "mapbox:group": "Administrative boundaries, admin" + }, + "source": "composite", + "source-layer": "admin", + "minzoom": 7, + "filter": [ + "all", + ["==", ["get", "admin_level"], 1], + ["==", ["get", "maritime"], "false"], + ["match", ["get", "worldview"], ["all", "US"], true, false] + ], + "paint": { + "line-color": "hsl(260, 45%, 0%)", + "line-width": ["interpolate", ["linear"], ["zoom"], 3, 3, 12, 6], + "line-opacity": ["interpolate", ["linear"], ["zoom"], 7, 0, 8, 0.5], + "line-dasharray": [1, 0], + "line-blur": ["interpolate", ["linear"], ["zoom"], 3, 0, 12, 3] + } + }, + { + "id": "admin-0-boundary-bg", + "type": "line", + "metadata": { + "mapbox:featureComponent": "admin-boundaries", + "mapbox:group": "Administrative boundaries, admin" + }, + "source": "composite", + "source-layer": "admin", + "minzoom": 1, + "filter": [ + "all", + ["==", ["get", "admin_level"], 0], + ["==", ["get", "maritime"], "false"], + ["match", ["get", "worldview"], ["all", "US"], true, false] + ], + "paint": { + "line-width": ["interpolate", ["linear"], ["zoom"], 3, 6, 12, 12], + "line-color": "hsl(260, 45%, 0%)", + "line-opacity": ["interpolate", ["linear"], ["zoom"], 3, 0, 4, 0.5], + "line-blur": ["interpolate", ["linear"], ["zoom"], 3, 0, 12, 3] + } + }, + { + "id": "admin-1-boundary", + "type": "line", + "metadata": { + "mapbox:featureComponent": "admin-boundaries", + "mapbox:group": "Administrative boundaries, admin" + }, + "source": "composite", + "source-layer": "admin", + "minzoom": 2, + "filter": [ + "all", + ["==", ["get", "admin_level"], 1], + ["==", ["get", "maritime"], "false"], + ["match", ["get", "worldview"], ["all", "US"], true, false] + ], + "layout": {}, + "paint": { + "line-dasharray": ["step", ["zoom"], ["literal", [2, 0]], 7, ["literal", [2, 2, 6, 2]]], + "line-width": ["interpolate", ["linear"], ["zoom"], 3, 0.3, 12, 1.5], + "line-opacity": ["interpolate", ["linear"], ["zoom"], 2, 0, 3, 1], + "line-color": "hsl(260, 15%, 85%)" + } + }, + { + "id": "admin-0-boundary", + "type": "line", + "metadata": { + "mapbox:featureComponent": "admin-boundaries", + "mapbox:group": "Administrative boundaries, admin" + }, + "source": "composite", + "source-layer": "admin", + "minzoom": 1, + "filter": [ + "all", + ["==", ["get", "admin_level"], 0], + ["==", ["get", "disputed"], "false"], + ["==", ["get", "maritime"], "false"], + ["match", ["get", "worldview"], ["all", "US"], true, false] + ], + "layout": {}, + "paint": { + "line-color": "hsl(260, 15%, 80%)", + "line-width": ["interpolate", ["linear"], ["zoom"], 3, 0.75, 12, 3], + "line-dasharray": ["step", ["zoom"], ["literal", [2, 0]], 7, ["literal", [2, 2, 6, 2]]] + } + }, + { + "id": "admin-0-boundary-disputed", + "type": "line", + "metadata": { + "mapbox:featureComponent": "admin-boundaries", + "mapbox:group": "Administrative boundaries, admin" + }, + "source": "composite", + "source-layer": "admin", + "minzoom": 1, + "filter": [ + "all", + ["==", ["get", "disputed"], "true"], + ["==", ["get", "admin_level"], 0], + ["==", ["get", "maritime"], "false"], + ["match", ["get", "worldview"], ["all", "US"], true, false] + ], + "paint": { + "line-color": "hsl(260, 15%, 80%)", + "line-width": ["interpolate", ["linear"], ["zoom"], 3, 0.75, 12, 3], + "line-dasharray": ["step", ["zoom"], ["literal", [3, 2, 5]], 7, ["literal", [2, 1.5]]] + } + }, + { + "id": "road-label", + "type": "symbol", + "metadata": { + "mapbox:featureComponent": "road-network", + "mapbox:group": "Road network, road-labels" + }, + "source": "composite", + "source-layer": "road", + "minzoom": 10, + "filter": [ + "all", + ["has", "name"], + [ + "step", + ["zoom"], + ["match", ["get", "class"], ["motorway", "trunk", "primary", "secondary", "tertiary"], true, false], + 12, + [ + "match", + ["get", "class"], + ["motorway", "trunk", "primary", "secondary", "tertiary", "street", "street_limited"], + true, + false + ], + 15, + ["match", ["get", "class"], ["path", "pedestrian", "golf", "ferry", "aerialway"], false, true] + ], + [ + "step", + ["pitch"], + true, + 50, + ["<", ["distance-from-center"], 2], + 60, + ["<", ["distance-from-center"], 2.5], + 70, + ["<", ["distance-from-center"], 3] + ] + ], + "layout": { + "text-size": [ + "interpolate", + ["linear"], + ["zoom"], + 10, + [ + "match", + ["get", "class"], + ["motorway", "trunk", "primary", "secondary", "tertiary"], + 11, + [ + "motorway_link", + "trunk_link", + "primary_link", + "secondary_link", + "tertiary_link", + "street", + "street_limited" + ], + 9.9, + 7.15 + ], + 18, + [ + "match", + ["get", "class"], + ["motorway", "trunk", "primary", "secondary", "tertiary"], + 17.6, + [ + "motorway_link", + "trunk_link", + "primary_link", + "secondary_link", + "tertiary_link", + "street", + "street_limited" + ], + 15.400000000000002, + 14.3 + ] + ], + "text-max-angle": 30, + "text-font": ["DIN Pro Medium", "Arial Unicode MS Regular"], + "symbol-placement": "line", + "text-padding": 5, + "text-rotation-alignment": "map", + "text-pitch-alignment": "viewport", + "text-field": ["coalesce", ["get", "name_en"], ["get", "name"]], + "text-letter-spacing": 0.01 + }, + "paint": { + "text-color": "hsl(0, 0%, 100%)", + "text-halo-color": [ + "match", + ["get", "class"], + ["motorway", "trunk"], + "hsla(0, 5%, 0%, 0.75)", + "hsl(0, 5%, 0%)" + ], + "text-halo-width": 1, + "text-halo-blur": 1 + } + }, + { + "id": "road-intersection", + "type": "symbol", + "metadata": { + "mapbox:featureComponent": "road-network", + "mapbox:group": "Road network, road-labels" + }, + "source": "composite", + "source-layer": "road", + "minzoom": 15, + "filter": [ + "all", + ["==", ["get", "class"], "intersection"], + ["has", "name"], + [ + "step", + ["pitch"], + true, + 50, + ["<", ["distance-from-center"], 1], + 60, + ["<", ["distance-from-center"], 1.5], + 70, + ["<", ["distance-from-center"], 2] + ] + ], + "layout": { + "text-field": ["coalesce", ["get", "name_en"], ["get", "name"]], + "icon-image": "intersection", + "icon-text-fit": "both", + "icon-text-fit-padding": [1, 2, 1, 2], + "text-size": ["interpolate", ["exponential", 1.2], ["zoom"], 15, 9, 18, 12], + "text-font": ["DIN Pro Bold", "Arial Unicode MS Bold"] + }, + "paint": { "text-color": "hsl(230, 36%, 64%)" } + }, + { + "id": "road-number-shield", + "type": "symbol", + "metadata": { + "mapbox:featureComponent": "road-network", + "mapbox:group": "Road network, road-labels" + }, + "source": "composite", + "source-layer": "road", + "minzoom": 6, + "filter": [ + "all", + ["case", ["has", "reflen"], ["<=", ["get", "reflen"], 6], ["has", "shield_beta"]], + ["match", ["get", "class"], ["pedestrian", "service"], false, true], + [ + "step", + ["zoom"], + ["==", ["geometry-type"], "Point"], + 11, + [">", ["get", "len"], 5000], + 12, + [">", ["get", "len"], 2500], + 13, + [">", ["get", "len"], 1000], + 14, + true + ], + [ + "step", + ["pitch"], + true, + 50, + ["<", ["distance-from-center"], 2], + 60, + ["<", ["distance-from-center"], 2.5], + 70, + ["<", ["distance-from-center"], 3] + ] + ], + "layout": { + "text-size": 9, + "icon-image": [ + "case", + ["has", "shield_beta"], + [ + "coalesce", + ["image", ["concat", ["get", "shield_beta"], "-", ["to-string", ["get", "reflen"]]]], + ["image", ["concat", ["get", "shield"], "-", ["to-string", ["get", "reflen"]]]], + ["image", ["concat", "default-", ["to-string", ["get", "reflen"]]]] + ], + ["concat", ["get", "shield"], "-", ["to-string", ["get", "reflen"]]] + ], + "icon-rotation-alignment": "viewport", + "text-max-angle": 38, + "symbol-spacing": ["interpolate", ["linear"], ["zoom"], 11, 400, 14, 600], + "text-font": ["DIN Pro Bold", "Arial Unicode MS Bold"], + "symbol-placement": ["step", ["zoom"], "point", 11, "line"], + "text-rotation-alignment": "viewport", + "text-field": ["get", "ref"], + "text-letter-spacing": 0.05 + }, + "paint": { + "text-color": [ + "case", + ["has", "shield_beta"], + [ + "case", + [ + "all", + ["has", "shield_text_color_beta"], + [ + "to-boolean", + ["coalesce", ["image", ["concat", ["get", "shield_beta"], "-", ["to-string", ["get", "reflen"]]]], ""] + ] + ], + [ + "match", + ["get", "shield_text_color_beta"], + "white", + "hsl(0, 0%, 100%)", + "yellow", + "hsl(50, 63%, 70%)", + "orange", + "hsl(25, 63%, 75%)", + "blue", + "hsl(230, 36%, 44%)", + "red", + "hsl(0, 54%, 59%)", + "green", + "hsl(140, 46%, 37%)", + "hsl(230, 11%, 13%)" + ], + "hsl(230, 11%, 13%)" + ], + [ + "match", + ["get", "shield_text_color"], + "white", + "hsl(0, 0%, 100%)", + "yellow", + "hsl(50, 63%, 70%)", + "orange", + "hsl(25, 63%, 75%)", + "blue", + "hsl(230, 36%, 44%)", + "red", + "hsl(0, 54%, 59%)", + "green", + "hsl(140, 46%, 37%)", + "hsl(230, 11%, 13%)" + ] + ] + } + }, + { + "id": "road-exit-shield", + "type": "symbol", + "metadata": { + "mapbox:featureComponent": "road-network", + "mapbox:group": "Road network, road-labels" + }, + "source": "composite", + "source-layer": "motorway_junction", + "minzoom": 14, + "filter": [ + "all", + ["has", "reflen"], + ["<=", ["get", "reflen"], 9], + [ + "step", + ["pitch"], + true, + 50, + ["<", ["distance-from-center"], 1], + 60, + ["<", ["distance-from-center"], 1.5], + 70, + ["<", ["distance-from-center"], 2] + ] + ], + "layout": { + "text-field": ["get", "ref"], + "text-size": 9, + "icon-image": ["concat", "motorway-exit-", ["to-string", ["get", "reflen"]]], + "text-font": ["DIN Pro Bold", "Arial Unicode MS Bold"] + }, + "paint": { + "text-color": "hsl(0, 0%, 100%)", + "text-translate": [0, 0] + } + }, + { + "id": "path-pedestrian-label", + "type": "symbol", + "metadata": { + "mapbox:featureComponent": "walking-cycling", + "mapbox:group": "Walking, cycling, etc., walking-cycling-labels" + }, + "source": "composite", + "source-layer": "road", + "minzoom": 12, + "filter": [ + "all", + ["case", ["has", "layer"], [">=", ["get", "layer"], 0], true], + [ + "step", + ["zoom"], + ["match", ["get", "class"], ["pedestrian"], true, false], + 15, + ["match", ["get", "class"], ["path", "pedestrian"], true, false] + ], + [ + "step", + ["pitch"], + true, + 50, + ["<", ["distance-from-center"], 1], + 60, + ["<", ["distance-from-center"], 1.5], + 70, + ["<", ["distance-from-center"], 2] + ] + ], + "layout": { + "text-size": [ + "interpolate", + ["linear"], + ["zoom"], + 10, + ["match", ["get", "class"], "pedestrian", 9.9, 7.15], + 18, + ["match", ["get", "class"], "pedestrian", 15.400000000000002, 14.3] + ], + "text-max-angle": 30, + "text-font": ["DIN Pro Medium", "Arial Unicode MS Regular"], + "symbol-placement": "line", + "text-padding": 1, + "text-rotation-alignment": "map", + "text-pitch-alignment": "viewport", + "text-field": ["coalesce", ["get", "name_en"], ["get", "name"]], + "text-letter-spacing": 0.01 + }, + "paint": { + "text-color": "hsl(0, 0%, 100%)", + "text-halo-color": "hsl(0, 5%, 0%)", + "text-halo-width": 1, + "text-halo-blur": 1 + } + }, + { + "id": "ferry-aerialway-label", + "type": "symbol", + "metadata": { + "mapbox:featureComponent": "transit", + "mapbox:group": "Transit, ferry-aerialway-labels" + }, + "source": "composite", + "source-layer": "road", + "minzoom": 15, + "filter": [ + "all", + ["match", ["get", "class"], "aerialway", true, false], + [ + "step", + ["pitch"], + true, + 50, + ["<", ["distance-from-center"], 1], + 60, + ["<", ["distance-from-center"], 1.5], + 70, + ["<", ["distance-from-center"], 2] + ] + ], + "layout": { + "text-size": ["interpolate", ["linear"], ["zoom"], 10, 7.15, 18, 14.3], + "text-max-angle": 30, + "text-font": ["DIN Pro Medium", "Arial Unicode MS Regular"], + "symbol-placement": "line", + "text-padding": 1, + "text-rotation-alignment": "map", + "text-pitch-alignment": "viewport", + "text-field": ["coalesce", ["get", "name_en"], ["get", "name"]], + "text-letter-spacing": 0.01 + }, + "paint": { + "text-color": ["match", ["get", "class"], "ferry", "hsl(240, 68%, 94%)", "hsl(204, 100%, 80%)"], + "text-halo-color": ["match", ["get", "class"], "ferry", "hsl(240, 100%, 62%)", "hsl(0, 0%, 10%)"], + "text-halo-width": 1, + "text-halo-blur": 1 + } + }, + { + "id": "waterway-label", + "type": "symbol", + "metadata": { + "mapbox:featureComponent": "natural-features", + "mapbox:group": "Natural features, natural-labels" + }, + "source": "composite", + "source-layer": "natural_label", + "minzoom": 13, + "filter": [ + "all", + [ + "match", + ["get", "class"], + ["canal", "river", "stream", "disputed_canal", "disputed_river", "disputed_stream"], + ["match", ["get", "worldview"], ["all", "US"], true, false], + false + ], + [ + "step", + ["pitch"], + true, + 50, + ["<", ["distance-from-center"], 1], + 60, + ["<", ["distance-from-center"], 1.5], + 70, + ["<", ["distance-from-center"], 2] + ], + ["==", ["geometry-type"], "LineString"] + ], + "layout": { + "text-font": ["DIN Pro Italic", "Arial Unicode MS Regular"], + "text-max-angle": 30, + "symbol-spacing": ["interpolate", ["linear", 1], ["zoom"], 15, 250, 17, 400], + "text-size": ["interpolate", ["linear"], ["zoom"], 13, 12, 18, 18], + "symbol-placement": "line", + "text-pitch-alignment": "viewport", + "text-field": ["coalesce", ["get", "name_en"], ["get", "name"]] + }, + "paint": { + "text-color": "hsl(240, 68%, 90%)", + "text-halo-color": "hsla(0, 0%, 0%, 0.5)", + "text-halo-width": 1, + "text-halo-blur": 1 + } + }, + { + "id": "natural-line-label", + "type": "symbol", + "metadata": { + "mapbox:featureComponent": "natural-features", + "mapbox:group": "Natural features, natural-labels" + }, + "source": "composite", + "source-layer": "natural_label", + "minzoom": 4, + "filter": [ + "all", + [ + "match", + ["get", "class"], + ["glacier", "landform", "disputed_glacier", "disputed_landform"], + ["match", ["get", "worldview"], ["all", "US"], true, false], + false + ], + ["<=", ["get", "filterrank"], 2], + [ + "step", + ["pitch"], + true, + 50, + ["<", ["distance-from-center"], 1], + 60, + ["<", ["distance-from-center"], 1.5], + 70, + ["<", ["distance-from-center"], 2] + ], + ["==", ["geometry-type"], "LineString"] + ], + "layout": { + "text-size": [ + "step", + ["zoom"], + ["step", ["get", "sizerank"], 18, 5, 12], + 17, + ["step", ["get", "sizerank"], 18, 13, 12] + ], + "text-max-angle": 30, + "text-field": ["coalesce", ["get", "name_en"], ["get", "name"]], + "text-font": ["DIN Pro Bold", "Arial Unicode MS Bold"], + "symbol-placement": "line-center", + "text-pitch-alignment": "viewport" + }, + "paint": { + "text-halo-width": 0.5, + "text-halo-color": "hsl(0, 0%, 0%)", + "text-halo-blur": 0.5, + "text-color": "hsl(0, 0%, 100%)" + } + }, + { + "id": "natural-point-label", + "type": "symbol", + "metadata": { + "mapbox:featureComponent": "natural-features", + "mapbox:group": "Natural features, natural-labels" + }, + "source": "composite", + "source-layer": "natural_label", + "minzoom": 4, + "filter": [ + "all", + [ + "match", + ["get", "class"], + [ + "dock", + "glacier", + "landform", + "water_feature", + "wetland", + "disputed_dock", + "disputed_glacier", + "disputed_landform", + "disputed_water_feature", + "disputed_wetland" + ], + ["match", ["get", "worldview"], ["all", "US"], true, false], + false + ], + ["<=", ["get", "filterrank"], 2], + [ + "step", + ["pitch"], + true, + 50, + ["<", ["distance-from-center"], 1], + 60, + ["<", ["distance-from-center"], 1.5], + 70, + ["<", ["distance-from-center"], 2] + ], + ["==", ["geometry-type"], "Point"] + ], + "layout": { + "text-size": [ + "step", + ["zoom"], + ["step", ["get", "sizerank"], 18, 5, 12], + 17, + ["step", ["get", "sizerank"], 18, 13, 12] + ], + "icon-image": [ + "case", + ["has", "maki_beta"], + ["coalesce", ["image", ["get", "maki_beta"]], ["image", ["get", "maki"]]], + ["image", ["get", "maki"]] + ], + "text-font": ["DIN Pro Bold", "Arial Unicode MS Bold"], + "text-offset": [ + "step", + ["zoom"], + ["step", ["get", "sizerank"], ["literal", [0, 0]], 5, ["literal", [0, 0.55]]], + 17, + ["step", ["get", "sizerank"], ["literal", [0, 0]], 13, ["literal", [0, 0.6000000000000001]]] + ], + "text-anchor": [ + "step", + ["zoom"], + ["step", ["get", "sizerank"], "center", 5, "top"], + 17, + ["step", ["get", "sizerank"], "center", 13, "top"] + ], + "text-field": ["coalesce", ["get", "name_en"], ["get", "name"]] + }, + "paint": { + "icon-opacity": [ + "step", + ["zoom"], + ["step", ["get", "sizerank"], 0, 5, 1], + 17, + ["step", ["get", "sizerank"], 0, 13, 1] + ], + "text-halo-color": "hsl(0, 0%, 10%)", + "text-halo-width": 0.5, + "text-halo-blur": 0.5, + "text-color": "hsl(0, 0%, 100%)" + } + }, + { + "id": "water-line-label", + "type": "symbol", + "metadata": { + "mapbox:featureComponent": "natural-features", + "mapbox:group": "Natural features, natural-labels" + }, + "source": "composite", + "source-layer": "natural_label", + "minzoom": 1, + "filter": [ + "all", + [ + "match", + ["get", "class"], + [ + "bay", + "ocean", + "reservoir", + "sea", + "water", + "disputed_bay", + "disputed_ocean", + "disputed_reservoir", + "disputed_sea", + "disputed_water" + ], + ["match", ["get", "worldview"], ["all", "US"], true, false], + false + ], + [ + "step", + ["pitch"], + true, + 50, + ["<", ["distance-from-center"], 1], + 60, + ["<", ["distance-from-center"], 1.5], + 70, + ["<", ["distance-from-center"], 2] + ], + ["==", ["geometry-type"], "LineString"] + ], + "layout": { + "text-size": [ + "interpolate", + ["linear"], + ["zoom"], + 0, + ["*", ["-", 16, ["sqrt", ["get", "sizerank"]]], 1], + 22, + ["*", ["-", 22, ["sqrt", ["get", "sizerank"]]], 1] + ], + "text-max-angle": 30, + "text-letter-spacing": ["match", ["get", "class"], "ocean", 0.25, ["sea", "bay"], 0.15, 0], + "text-font": ["DIN Pro Italic", "Arial Unicode MS Regular"], + "symbol-placement": "line-center", + "text-pitch-alignment": "viewport", + "text-field": ["coalesce", ["get", "name_en"], ["get", "name"]] + }, + "paint": { + "text-color": ["match", ["get", "class"], ["bay", "ocean", "sea"], "hsl(240, 96%, 82%)", "hsl(240, 68%, 90%)"], + "text-halo-color": "hsla(0, 0%, 0%, 0.5)", + "text-halo-width": 1, + "text-halo-blur": 1 + } + }, + { + "id": "water-point-label", + "type": "symbol", + "metadata": { + "mapbox:featureComponent": "natural-features", + "mapbox:group": "Natural features, natural-labels" + }, + "source": "composite", + "source-layer": "natural_label", + "minzoom": 1, + "filter": [ + "all", + [ + "match", + ["get", "class"], + [ + "bay", + "ocean", + "reservoir", + "sea", + "water", + "disputed_bay", + "disputed_ocean", + "disputed_reservoir", + "disputed_sea", + "disputed_water" + ], + ["match", ["get", "worldview"], ["all", "US"], true, false], + false + ], + [ + "step", + ["pitch"], + true, + 50, + ["<", ["distance-from-center"], 1], + 60, + ["<", ["distance-from-center"], 1.5], + 70, + ["<", ["distance-from-center"], 2] + ], + ["==", ["geometry-type"], "Point"] + ], + "layout": { + "text-line-height": 1.3, + "text-size": [ + "interpolate", + ["linear"], + ["zoom"], + 0, + ["*", ["-", 16, ["sqrt", ["get", "sizerank"]]], 1], + 22, + ["*", ["-", 22, ["sqrt", ["get", "sizerank"]]], 1] + ], + "text-font": ["DIN Pro Italic", "Arial Unicode MS Regular"], + "text-field": ["coalesce", ["get", "name_en"], ["get", "name"]], + "text-letter-spacing": ["match", ["get", "class"], "ocean", 0.25, ["bay", "sea"], 0.15, 0.01], + "text-max-width": ["match", ["get", "class"], "ocean", 4, "sea", 5, ["bay", "water"], 7, 10] + }, + "paint": { + "text-color": ["match", ["get", "class"], ["bay", "ocean", "sea"], "hsl(240, 96%, 82%)", "hsl(240, 68%, 90%)"], + "text-halo-color": "hsla(0, 0%, 0%, 0.5)", + "text-halo-width": 1, + "text-halo-blur": 1 + } + }, + { + "id": "poi-label", + "type": "symbol", + "metadata": { + "mapbox:featureComponent": "point-of-interest-labels", + "mapbox:group": "Point of interest labels, poi-labels" + }, + "source": "composite", + "source-layer": "poi_label", + "minzoom": 6, + "filter": [ + "all", + ["<=", ["get", "filterrank"], ["+", ["step", ["zoom"], 0, 16, 1, 17, 2], 3]], + [ + "step", + ["pitch"], + true, + 50, + ["<", ["distance-from-center"], 2], + 60, + ["<", ["distance-from-center"], 2.5], + 70, + ["<", ["distance-from-center"], 3] + ] + ], + "layout": { + "text-size": [ + "step", + ["zoom"], + ["step", ["get", "sizerank"], 18, 5, 12], + 17, + ["step", ["get", "sizerank"], 18, 13, 12] + ], + "icon-image": [ + "case", + ["has", "maki_beta"], + ["coalesce", ["image", ["get", "maki_beta"]], ["image", ["get", "maki"]]], + ["image", ["get", "maki"]] + ], + "text-font": ["DIN Pro Bold", "Arial Unicode MS Bold"], + "text-offset": [ + "step", + ["zoom"], + ["step", ["get", "sizerank"], ["literal", [0, 0]], 5, ["literal", [0, 0.8]]], + 17, + ["step", ["get", "sizerank"], ["literal", [0, 0]], 13, ["literal", [0, 0.8]]] + ], + "text-anchor": [ + "step", + ["zoom"], + ["step", ["get", "sizerank"], "center", 5, "top"], + 17, + ["step", ["get", "sizerank"], "center", 13, "top"] + ], + "text-field": ["coalesce", ["get", "name_en"], ["get", "name"]] + }, + "paint": { + "icon-opacity": [ + "step", + ["zoom"], + ["step", ["get", "sizerank"], 0, 5, 1], + 17, + ["step", ["get", "sizerank"], 0, 13, 1] + ], + "text-halo-color": "hsl(0, 0%, 10%)", + "text-halo-width": 0.5, + "text-halo-blur": 0.5, + "text-color": [ + "match", + ["get", "class"], + "park_like", + "hsl(110, 100%, 85%)", + "education", + "hsl(30, 100%, 85%)", + "medical", + "hsl(0, 100%, 85%)", + "hsl(0, 0%, 100%)" + ] + } + }, + { + "id": "transit-label", + "type": "symbol", + "metadata": { + "mapbox:featureComponent": "transit", + "mapbox:group": "Transit, transit-labels" + }, + "source": "composite", + "source-layer": "transit_stop_label", + "minzoom": 12, + "filter": [ + "all", + [ + "step", + ["zoom"], + [ + "all", + ["<=", ["get", "filterrank"], 4], + ["match", ["get", "mode"], "rail", true, "metro_rail", true, false], + ["!=", ["get", "stop_type"], "entrance"] + ], + 14, + [ + "all", + ["match", ["get", "mode"], "rail", true, "metro_rail", true, false], + ["!=", ["get", "stop_type"], "entrance"] + ], + 15, + [ + "all", + ["match", ["get", "mode"], "rail", true, "metro_rail", true, "light_rail", true, false], + ["!=", ["get", "stop_type"], "entrance"] + ], + 16, + [ + "all", + ["match", ["get", "mode"], "ferry", false, "bus", false, true], + ["!=", ["get", "stop_type"], "entrance"] + ], + 17, + ["all", ["match", ["get", "mode"], "ferry", false, true], ["!=", ["get", "stop_type"], "entrance"]], + 19, + ["match", ["get", "mode"], "ferry", false, true] + ], + [ + "step", + ["pitch"], + true, + 50, + ["<", ["distance-from-center"], 1], + 60, + ["<", ["distance-from-center"], 1.5], + 70, + ["<", ["distance-from-center"], 2] + ] + ], + "layout": { + "text-size": 12, + "icon-image": ["get", "network"], + "text-font": ["DIN Pro Bold", "Arial Unicode MS Bold"], + "text-justify": ["match", ["get", "stop_type"], "entrance", "left", "center"], + "text-offset": ["match", ["get", "stop_type"], "entrance", ["literal", [1, 0]], ["literal", [0, 0.8]]], + "text-anchor": ["match", ["get", "stop_type"], "entrance", "left", "top"], + "text-field": [ + "step", + ["zoom"], + "", + 13, + ["match", ["get", "mode"], ["rail", "metro_rail"], ["coalesce", ["get", "name_en"], ["get", "name"]], ""], + 14, + ["match", ["get", "mode"], ["bus", "bicycle"], "", ["coalesce", ["get", "name_en"], ["get", "name"]]], + 18, + ["coalesce", ["get", "name_en"], ["get", "name"]] + ], + "text-letter-spacing": 0.01, + "text-max-width": ["match", ["get", "stop_type"], "entrance", 15, 9] + }, + "paint": { + "text-halo-color": "hsl(0, 0%, 10%)", + "text-color": "hsl(204, 100%, 80%)", + "text-halo-blur": 0.5, + "text-halo-width": 0.5 + } + }, + { + "id": "airport-label", + "type": "symbol", + "metadata": { + "mapbox:featureComponent": "transit", + "mapbox:group": "Transit, transit-labels" + }, + "source": "composite", + "source-layer": "airport_label", + "minzoom": 8, + "filter": [ + "all", + [ + "match", + ["get", "class"], + ["military", "civil", "disputed_military", "disputed_civil"], + ["match", ["get", "worldview"], ["all", "US"], true, false], + false + ], + [ + "step", + ["pitch"], + true, + 50, + ["<", ["distance-from-center"], 3], + 60, + ["<", ["distance-from-center"], 4], + 70, + ["<", ["distance-from-center"], 5] + ] + ], + "layout": { + "text-line-height": 1.1, + "text-size": ["step", ["get", "sizerank"], 18, 9, 12], + "icon-image": ["get", "maki"], + "text-font": ["DIN Pro Bold", "Arial Unicode MS Bold"], + "text-offset": [0, 0.8], + "text-rotation-alignment": "viewport", + "text-anchor": "top", + "text-field": [ + "step", + ["get", "sizerank"], + [ + "case", + ["has", "ref"], + ["concat", ["get", "ref"], " -\n", ["coalesce", ["get", "name_en"], ["get", "name"]]], + ["coalesce", ["get", "name_en"], ["get", "name"]] + ], + 15, + ["get", "ref"] + ], + "text-letter-spacing": 0.01, + "text-max-width": 9 + }, + "paint": { + "text-color": "hsl(0, 0%, 100%)", + "text-halo-color": "hsl(0, 0%, 10%)", + "text-halo-width": 1 + } + }, + { + "id": "settlement-subdivision-label", + "type": "symbol", + "metadata": { + "mapbox:featureComponent": "place-labels", + "mapbox:group": "Place labels, place-labels" + }, + "source": "composite", + "source-layer": "place_label", + "minzoom": 10, + "maxzoom": 15, + "filter": [ + "all", + [ + "match", + ["get", "class"], + ["settlement_subdivision", "disputed_settlement_subdivision"], + ["match", ["get", "worldview"], ["all", "US"], true, false], + false + ], + ["<=", ["get", "filterrank"], 1], + [ + "step", + ["pitch"], + true, + 50, + ["<", ["distance-from-center"], 3], + 60, + ["<", ["distance-from-center"], 4], + 70, + ["<", ["distance-from-center"], 5] + ] + ], + "layout": { + "text-field": ["coalesce", ["get", "name_en"], ["get", "name"]], + "text-transform": "uppercase", + "text-font": ["DIN Pro Medium", "Arial Unicode MS Regular"], + "text-letter-spacing": ["match", ["get", "type"], "suburb", 0.15, 0.05], + "text-max-width": 7, + "text-padding": 3, + "text-size": [ + "interpolate", + ["cubic-bezier", 0.5, 0, 1, 1], + ["zoom"], + 11, + ["match", ["get", "type"], "suburb", 12.100000000000001, 11.55], + 15, + ["match", ["get", "type"], "suburb", 16.5, 15.400000000000002] + ] + }, + "paint": { + "text-halo-color": "hsla(0, 5%, 0%, 0.75)", + "text-halo-width": 1, + "text-color": "hsl(0, 0%, 100%)", + "text-halo-blur": 0.5 + } + }, + { + "id": "settlement-minor-label", + "type": "symbol", + "metadata": { + "mapbox:featureComponent": "place-labels", + "mapbox:group": "Place labels, place-labels" + }, + "source": "composite", + "source-layer": "place_label", + "minzoom": 2, + "maxzoom": 13, + "filter": [ + "all", + ["<=", ["get", "filterrank"], 3], + [ + "match", + ["get", "class"], + ["settlement", "disputed_settlement"], + ["match", ["get", "worldview"], ["all", "US"], true, false], + false + ], + [ + "step", + ["zoom"], + [">", ["get", "symbolrank"], 6], + 4, + [">=", ["get", "symbolrank"], 7], + 6, + [">=", ["get", "symbolrank"], 8], + 7, + [">=", ["get", "symbolrank"], 10], + 10, + [">=", ["get", "symbolrank"], 11], + 11, + [">=", ["get", "symbolrank"], 13], + 12, + [">=", ["get", "symbolrank"], 15] + ], + [ + "step", + ["pitch"], + true, + 50, + ["<", ["distance-from-center"], 3], + 60, + ["<", ["distance-from-center"], 4], + 70, + ["<", ["distance-from-center"], 5] + ] + ], + "layout": { + "text-line-height": 1.1, + "text-size": [ + "interpolate", + ["cubic-bezier", 0.2, 0, 0.9, 1], + ["zoom"], + 3, + ["step", ["get", "symbolrank"], 12.100000000000001, 9, 11], + 6, + ["step", ["get", "symbolrank"], 15.400000000000002, 9, 13.200000000000001, 12, 11], + 8, + ["step", ["get", "symbolrank"], 17.6, 9, 15.400000000000002, 12, 13.200000000000001, 15, 11], + 13, + ["step", ["get", "symbolrank"], 24.200000000000003, 9, 22, 12, 17.6, 15, 15.400000000000002] + ], + "text-radial-offset": ["step", ["zoom"], ["match", ["get", "capital"], 2, 0.6, 0.55], 8, 0], + "symbol-sort-key": ["get", "symbolrank"], + "icon-image": [ + "step", + ["zoom"], + [ + "case", + ["==", ["get", "capital"], 2], + "border-dot-13", + ["step", ["get", "symbolrank"], "dot-11", 9, "dot-10", 11, "dot-9"] + ], + 8, + "" + ], + "text-font": ["DIN Pro Medium", "Arial Unicode MS Regular"], + "text-justify": "auto", + "text-anchor": ["step", ["zoom"], ["get", "text_anchor"], 8, "center"], + "text-field": ["coalesce", ["get", "name_en"], ["get", "name"]], + "text-max-width": 7 + }, + "paint": { + "text-color": "hsl(0, 0%, 95%)", + "text-halo-color": "hsl(0, 5%, 0%)", + "text-halo-width": 1, + "text-halo-blur": 1 + } + }, + { + "id": "settlement-major-label", + "type": "symbol", + "metadata": { + "mapbox:featureComponent": "place-labels", + "mapbox:group": "Place labels, place-labels" + }, + "source": "composite", + "source-layer": "place_label", + "minzoom": 2, + "maxzoom": 15, + "filter": [ + "all", + ["<=", ["get", "filterrank"], 3], + [ + "match", + ["get", "class"], + ["settlement", "disputed_settlement"], + ["match", ["get", "worldview"], ["all", "US"], true, false], + false + ], + [ + "step", + ["zoom"], + false, + 2, + ["<=", ["get", "symbolrank"], 6], + 4, + ["<", ["get", "symbolrank"], 7], + 6, + ["<", ["get", "symbolrank"], 8], + 7, + ["<", ["get", "symbolrank"], 10], + 10, + ["<", ["get", "symbolrank"], 11], + 11, + ["<", ["get", "symbolrank"], 13], + 12, + ["<", ["get", "symbolrank"], 15], + 13, + [">=", ["get", "symbolrank"], 11], + 14, + [">=", ["get", "symbolrank"], 15] + ], + [ + "step", + ["pitch"], + true, + 50, + ["<", ["distance-from-center"], 3], + 60, + ["<", ["distance-from-center"], 4], + 70, + ["<", ["distance-from-center"], 5] + ] + ], + "layout": { + "text-line-height": 1.1, + "text-size": [ + "interpolate", + ["cubic-bezier", 0.2, 0, 0.9, 1], + ["zoom"], + 3, + ["step", ["get", "symbolrank"], 14.3, 6, 12.100000000000001], + 6, + ["step", ["get", "symbolrank"], 19.8, 6, 17.6, 7, 15.400000000000002], + 8, + ["step", ["get", "symbolrank"], 22, 9, 17.6, 10, 15.400000000000002], + 15, + ["step", ["get", "symbolrank"], 26.400000000000002, 9, 22, 12, 17.6, 15, 15.400000000000002] + ], + "text-radial-offset": ["step", ["zoom"], ["match", ["get", "capital"], 2, 0.6, 0.55], 8, 0], + "symbol-sort-key": ["get", "symbolrank"], + "icon-image": [ + "step", + ["zoom"], + [ + "case", + ["==", ["get", "capital"], 2], + "border-dot-13", + ["step", ["get", "symbolrank"], "dot-11", 9, "dot-10", 11, "dot-9"] + ], + 8, + "" + ], + "text-font": ["DIN Pro Medium", "Arial Unicode MS Regular"], + "text-justify": [ + "step", + ["zoom"], + [ + "match", + ["get", "text_anchor"], + ["left", "bottom-left", "top-left"], + "left", + ["right", "bottom-right", "top-right"], + "right", + "center" + ], + 8, + "center" + ], + "text-anchor": ["step", ["zoom"], ["get", "text_anchor"], 8, "center"], + "text-field": ["coalesce", ["get", "name_en"], ["get", "name"]], + "text-max-width": 7 + }, + "paint": { + "text-color": "hsl(0, 0%, 95%)", + "text-halo-color": "hsl(0, 5%, 0%)", + "text-halo-width": 1, + "text-halo-blur": 1 + } + }, + { + "id": "state-label", + "type": "symbol", + "metadata": { + "mapbox:featureComponent": "place-labels", + "mapbox:group": "Place labels, place-labels" + }, + "source": "composite", + "source-layer": "place_label", + "minzoom": 3, + "maxzoom": 9, + "filter": [ + "all", + [ + "match", + ["get", "class"], + ["state", "disputed_state"], + ["match", ["get", "worldview"], ["all", "US"], true, false], + false + ], + [ + "step", + ["pitch"], + true, + 50, + ["<", ["distance-from-center"], 3], + 60, + ["<", ["distance-from-center"], 4], + 70, + ["<", ["distance-from-center"], 5] + ] + ], + "layout": { + "text-size": [ + "interpolate", + ["cubic-bezier", 0.85, 0.7, 0.65, 1], + ["zoom"], + 4, + ["step", ["get", "symbolrank"], 9.9, 6, 8.8, 7, 7.700000000000001], + 9, + ["step", ["get", "symbolrank"], 23.1, 6, 17.6, 7, 15.400000000000002] + ], + "text-transform": "uppercase", + "text-font": ["DIN Pro Bold", "Arial Unicode MS Bold"], + "text-field": ["coalesce", ["get", "name_en"], ["get", "name"]], + "text-letter-spacing": 0.15, + "text-max-width": 6 + }, + "paint": { + "text-color": "hsl(0, 0%, 95%)", + "text-halo-color": "hsl(0, 5%, 0%)", + "text-halo-width": 1, + "text-opacity": 0.5 + } + }, + { + "id": "country-label", + "type": "symbol", + "metadata": { + "mapbox:featureComponent": "place-labels", + "mapbox:group": "Place labels, place-labels" + }, + "source": "composite", + "source-layer": "place_label", + "minzoom": 1, + "maxzoom": 10, + "filter": [ + "all", + [ + "match", + ["get", "class"], + ["country", "disputed_country"], + ["match", ["get", "worldview"], ["all", "US"], true, false], + false + ], + [ + "step", + ["pitch"], + true, + 50, + ["<", ["distance-from-center"], 3], + 60, + ["<", ["distance-from-center"], 4], + 70, + ["<", ["distance-from-center"], 5] + ] + ], + "layout": { + "icon-image": "", + "text-field": ["coalesce", ["get", "name_en"], ["get", "name"]], + "text-line-height": 1.1, + "text-max-width": 6, + "text-font": ["DIN Pro Medium", "Arial Unicode MS Regular"], + "text-radial-offset": ["step", ["zoom"], 0.6, 8, 0], + "text-justify": [ + "step", + ["zoom"], + [ + "match", + ["get", "text_anchor"], + ["left", "bottom-left", "top-left"], + "left", + ["right", "bottom-right", "top-right"], + "right", + "center" + ], + 7, + "auto" + ], + "text-size": [ + "interpolate", + ["cubic-bezier", 0.2, 0, 0.7, 1], + ["zoom"], + 1, + ["step", ["get", "symbolrank"], 12.100000000000001, 4, 9.9, 5, 8.8], + 9, + ["step", ["get", "symbolrank"], 24.200000000000003, 4, 20.900000000000002, 5, 18.700000000000003] + ] + }, + "paint": { + "icon-opacity": ["step", ["zoom"], ["case", ["has", "text_anchor"], 1, 0], 7, 0], + "text-color": "hsl(0, 0%, 95%)", + "text-halo-color": ["interpolate", ["linear"], ["zoom"], 2, "hsla(0, 5%, 0%, 0.75)", 3, "hsl(0, 5%, 0%)"], + "text-halo-width": 1.25 + } + }, + { + "id": "continent-label", + "type": "symbol", + "metadata": { + "mapbox:featureComponent": "place-labels", + "mapbox:group": "Place labels, place-labels" + }, + "source": "composite", + "source-layer": "natural_label", + "minzoom": 0.75, + "maxzoom": 3, + "filter": ["==", ["get", "class"], "continent"], + "layout": { + "text-field": ["coalesce", ["get", "name_en"], ["get", "name"]], + "text-line-height": 1.1, + "text-max-width": 6, + "text-font": ["DIN Pro Medium", "Arial Unicode MS Regular"], + "text-size": ["interpolate", ["exponential", 0.5], ["zoom"], 0, 11, 2.5, 16.5], + "text-transform": "uppercase", + "text-letter-spacing": 0.05 + }, + "paint": { + "text-color": "hsl(0, 0%, 95%)", + "text-halo-color": ["interpolate", ["linear"], ["zoom"], 0, "hsla(0, 5%, 0%, 0.75)", 3, "hsl(0, 5%, 0%)"], + "text-halo-width": 1.5, + "text-opacity": ["interpolate", ["linear"], ["zoom"], 0, 0.8, 1.5, 0.5, 2.5, 0] + } + } + ], + "created": "2023-01-18T23:41:27.166Z", + "modified": "2023-12-12T18:08:46.874Z", + "id": "cld2b5mnd000201pcurww9h2w", + "owner": "tonyenglish", + "visibility": "private", + "protected": false, + "draft": false +} diff --git a/webapp/style/style.json b/webapp/style/style.json deleted file mode 100644 index e786aa18b..000000000 --- a/webapp/style/style.json +++ /dev/null @@ -1,5335 +0,0 @@ -{ - "version": 8, - "name": "Navigation", - "metadata": { - "mapbox:type": "default", - "mapbox:origin": "navigation-night-v1", - "mapbox:sdk-support": { - "android": "9.3.0", - "ios": "5.10.0", - "js": "2.3.0" - }, - "mapbox:autocomposite": true, - "mapbox:groups": { - "Land, water, & sky, land": { - "name": "Land, water, & sky, land", - "collapsed": true - }, - "Land, water, & sky, water": { - "name": "Land, water, & sky, water", - "collapsed": true - }, - "Land, water, & sky, built": { - "name": "Land, water, & sky, built", - "collapsed": true - }, - "Transit, built": {"name": "Transit, built", "collapsed": true}, - "Buildings, built": {"name": "Buildings, built", "collapsed": true}, - "Road network, tunnels-case": { - "name": "Road network, tunnels-case", - "collapsed": true - }, - "Road network, tunnels": { - "name": "Road network, tunnels", - "collapsed": true - }, - "Transit, ferries": {"name": "Transit, ferries", "collapsed": true}, - "Road network, surface": { - "name": "Road network, surface", - "collapsed": true - }, - "Transit, surface": {"name": "Transit, surface", "collapsed": true}, - "Road network, surface-icons": { - "name": "Road network, surface-icons", - "collapsed": true - }, - "Road network, bridges": { - "name": "Road network, bridges", - "collapsed": true - }, - "Transit, bridges": {"name": "Transit, bridges", "collapsed": true}, - "Transit, elevated": { - "name": "Transit, elevated", - "collapsed": true - }, - "Administrative boundaries, admin": { - "name": "Administrative boundaries, admin", - "collapsed": true - }, - "Road network, road-labels": { - "name": "Road network, road-labels", - "collapsed": true - }, - "Transit, ferry-aerialway-labels": { - "name": "Transit, ferry-aerialway-labels", - "collapsed": true - }, - "Natural features, natural-labels": { - "name": "Natural features, natural-labels", - "collapsed": true - }, - "Point of interest labels, poi-labels": { - "name": "Point of interest labels, poi-labels", - "collapsed": true - }, - "Transit, transit-labels": { - "name": "Transit, transit-labels", - "collapsed": true - }, - "Place labels, place-labels": { - "name": "Place labels, place-labels", - "collapsed": true - } - }, - "mapbox:decompiler": { - "id": "navigation-night-v1", - "componentVersion": "10.1.0", - "strata": [ - { - "id": "navigation-night-v1", - "order": [ - ["land-and-water", "land"], - ["land-and-water", "water"], - ["land-and-water", "built"], - ["transit", "built"], - ["buildings", "built"], - ["road-network", "tunnels-case"], - ["road-network", "tunnels"], - ["transit", "ferries"], - ["road-network", "surface"], - ["transit", "surface"], - ["road-network", "surface-icons"], - ["road-network", "bridges"], - ["transit", "bridges"], - ["road-network", "traffic-and-closures"], - ["buildings", "extruded"], - ["transit", "elevated"], - ["admin-boundaries", "admin"], - ["buildings", "building-labels"], - ["road-network", "road-labels"], - ["transit", "ferry-aerialway-labels"], - ["natural-features", "natural-labels"], - ["point-of-interest-labels", "poi-labels"], - ["transit", "transit-labels"], - ["place-labels", "place-labels"], - ["land-and-water", "sky"] - ] - } - ], - "components": { - "land-and-water": "10.1.0", - "buildings": "10.1.0", - "road-network": "10.1.0", - "admin-boundaries": "10.1.0", - "natural-features": "10.1.0", - "point-of-interest-labels": "10.1.0", - "transit": "10.1.0", - "place-labels": "10.1.0" - }, - "propConfig": { - "land-and-water": { - "color-airport": "hsl(230, 18%, 77%)", - "color-hospital": "hsl(252, 4%, 74%)", - "color-greenspace": "hsl(170, 18%, 32%)", - "color-water": "hsl(197, 15%, 43%)", - "transitionLandOnZoom": false, - "waterStyle": "Simple", - "color-base": "hsl(214, 19%, 45%)", - "color-school": "hsl(200, 10%, 80%)", - "color-motorist": "hsl(215, 15%, 37%)" - }, - "buildings": { - "color-base": "hsl(214, 19%, 45%)", - "houseNumbers": false, - "houseNumbersFont": [ - "DIN Pro Medium", - "Arial Unicode MS Regular" - ], - "houseNumbersSize": 1.3 - }, - "road-network": { - "roadsFont": ["DIN Pro Medium", "Arial Unicode MS Regular"], - "color-road": "hsl(213, 9%, 19%)", - "color-motorway-trunk": "hsl(215, 28%, 48%)", - "roadNetwork": "Navigation", - "color-road-label": "hsl(0, 0%, 90%)", - "color-traffic": "hsl(0, 80%, 65%)", - "roadsSize": 1.1, - "icon-lightness": -0.3, - "lowCongestion": false, - "color-base": "hsl(214, 19%, 45%)", - "color-road-outline": "hsl(217, 11%, 32%)" - }, - "admin-boundaries": { - "color-base": "hsl(214, 19%, 45%)", - "color-place-label": "hsl(215, 30%, 80%)", - "color-admin-boundary": "hsl(250, 10%, 65%)" - }, - "natural-features": { - "color-base": "hsl(214, 19%, 45%)", - "color-water": "hsl(197, 15%, 43%)", - "color-poi": "hsl(236, 6%, 98%)", - "density": 0, - "poiEtcSize": 1.1 - }, - "point-of-interest-labels": { - "educationDensity": 1, - "color-poi": "hsl(236, 6%, 98%)", - "commercialServicesDensity": 0, - "generalDensity": 0, - "color-hospital": "hsl(252, 4%, 74%)", - "sportAndLeisureDensity": 0, - "artsAndEntertainmentDensity": 0, - "parkLikeDensity": 1, - "foodAndDrinkDensity": 0, - "historicDensity": 0, - "placeLikeDensity": 0, - "poiEtcSize": 1.1, - "lodgingDensity": 0, - "color-greenspace": "hsl(170, 18%, 32%)", - "industrialDensity": 0, - "storeLikeDensity": 0, - "landmarkDensity": 2, - "motoristDensity": 3, - "color-greenspace-label": "hsl(150, 51%, 67%)", - "buildingDensity": 0, - "controlDensityByClass": true, - "medicalDensity": 1, - "religionDensity": 0, - "foodAndDrinkStoresDensity": 0, - "color-base": "hsl(214, 19%, 45%)", - "color-school": "hsl(200, 10%, 80%)", - "publicFacilitiesDensity": 0, - "visitorAmenitiesDensity": 0 - }, - "transit": { - "color-airport": "hsl(230, 18%, 77%)", - "color-transit": "hsl(230, 54%, 85%)", - "color-road": "hsl(213, 9%, 19%)", - "poiEtcSize": 1.1, - "color-water": "hsl(197, 15%, 43%)", - "color-road-label": "hsl(0, 0%, 90%)", - "transitLabels": false, - "roadsSize": 1.3, - "icon-lightness": -0.3, - "color-base": "hsl(214, 19%, 45%)", - "color-road-outline": "hsl(217, 11%, 32%)" - }, - "place-labels": { - "statesSize": 1.2, - "color-place-label": "hsl(215, 30%, 80%)", - "settlementsMinorSize": 1.1, - "settlementsMajorSize": 1.3, - "settlementSubdivisionsSize": 1.1, - "countriesSize": 1.4, - "settlementSubdivisionsDensity": 3, - "color-base": "hsl(214, 19%, 45%)", - "settlementsDensity": 2, - "settlementSubdivisionsFont": [ - "DIN Pro Medium", - "Arial Unicode MS Regular" - ] - } - } - } - }, - "center": [-73.97, 40.72], - "zoom": 9, - "sources": { - "mapbox://mapbox.mapbox-traffic-v1": { - "url": "mapbox://mapbox.mapbox-traffic-v1", - "type": "vector" - }, - "composite": { - "url": "mapbox://mapbox.mapbox-streets-v8,mapbox.mapbox-terrain-v2", - "type": "vector" - } - }, - "sprite": "mapbox://sprites/drewjj/ckr1p5ulb4uys18quax950ia4/3vypha5gkrx23f8957zqxj1l5", - "glyphs": "mapbox://fonts/mapbox/{fontstack}/{range}.pbf", - "layers": [ - { - "id": "land", - "type": "background", - "metadata": { - "mapbox:featureComponent": "land-and-water", - "mapbox:group": "Land, water, & sky, land" - }, - "layout": {}, - "paint": {"background-color": "hsl(214, 17%, 31%)"} - }, - { - "id": "landcover", - "type": "fill", - "metadata": { - "mapbox:featureComponent": "land-and-water", - "mapbox:group": "Land, water, & sky, land" - }, - "source": "composite", - "source-layer": "landcover", - "maxzoom": 7, - "layout": {}, - "paint": { - "fill-color": [ - "match", - ["get", "class"], - "snow", - "hsl(214, 19%, 53%)", - "hsl(152, 18%, 19%)" - ], - "fill-opacity": [ - "interpolate", - ["exponential", 1.5], - ["zoom"], - 2, - 0.3, - 7, - 0 - ], - "fill-antialias": false - } - }, - { - "id": "national-park", - "type": "fill", - "metadata": { - "mapbox:featureComponent": "land-and-water", - "mapbox:group": "Land, water, & sky, land" - }, - "source": "composite", - "source-layer": "landuse_overlay", - "minzoom": 5, - "filter": ["==", ["get", "class"], "national_park"], - "layout": {}, - "paint": { - "fill-color": "hsl(170, 18%, 32%)", - "fill-opacity": [ - "interpolate", - ["linear"], - ["zoom"], - 5, - 0, - 6, - 0.5, - 10, - 0.5 - ] - } - }, - { - "id": "landuse", - "type": "fill", - "metadata": { - "mapbox:featureComponent": "land-and-water", - "mapbox:group": "Land, water, & sky, land" - }, - "source": "composite", - "source-layer": "landuse", - "minzoom": 5, - "filter": [ - "match", - ["get", "class"], - ["park", "airport", "glacier", "pitch", "sand", "facility"], - true, - "cemetery", - true, - "school", - true, - "hospital", - true, - "parking", - ["step", ["zoom"], false, 15, true], - false - ], - "layout": {}, - "paint": { - "fill-color": [ - "interpolate", - ["linear"], - ["zoom"], - 15, - [ - "match", - ["get", "class"], - "park", - "hsl(170, 18%, 32%)", - "airport", - "hsl(230, 20%, 36%)", - "cemetery", - "hsl(152, 19%, 38%)", - "glacier", - "hsl(197, 5%, 63%)", - "hospital", - "hsl(252, 13%, 32%)", - "pitch", - "hsl(170, 19%, 27%)", - "sand", - "hsl(213, 18%, 20%)", - "school", - "hsl(200, 15%, 26%)", - "parking", - "hsl(215, 15%, 37%)", - "hsl(214, 21%, 29%)" - ], - 16, - [ - "match", - ["get", "class"], - "park", - "hsl(170, 18%, 32%)", - "airport", - "hsl(230, 34%, 34%)", - "cemetery", - "hsl(152, 19%, 38%)", - "glacier", - "hsl(197, 5%, 63%)", - "hospital", - "hsl(252, 7%, 34%)", - "pitch", - "hsl(170, 19%, 27%)", - "sand", - "hsl(213, 18%, 20%)", - "school", - "hsl(200, 15%, 26%)", - "parking", - "hsl(215, 15%, 37%)", - "hsl(214, 21%, 29%)" - ] - ], - "fill-opacity": [ - "interpolate", - ["linear"], - ["zoom"], - 5, - 0, - 6, - ["match", ["get", "class"], "glacier", 0.5, 1] - ] - } - }, - { - "id": "pitch-outline", - "type": "line", - "metadata": { - "mapbox:featureComponent": "land-and-water", - "mapbox:group": "Land, water, & sky, land" - }, - "source": "composite", - "source-layer": "landuse", - "minzoom": 15, - "filter": ["==", ["get", "class"], "pitch"], - "layout": {}, - "paint": {"line-color": "hsl(152, 10%, 19%)"} - }, - { - "id": "waterway", - "type": "line", - "metadata": { - "mapbox:featureComponent": "land-and-water", - "mapbox:group": "Land, water, & sky, water" - }, - "source": "composite", - "source-layer": "waterway", - "minzoom": 8, - "layout": { - "line-cap": ["step", ["zoom"], "butt", 11, "round"], - "line-join": "round" - }, - "paint": { - "line-color": "hsl(197, 15%, 43%)", - "line-width": [ - "interpolate", - ["exponential", 1.3], - ["zoom"], - 9, - ["match", ["get", "class"], ["canal", "river"], 0.1, 0], - 20, - ["match", ["get", "class"], ["canal", "river"], 8, 3] - ], - "line-opacity": [ - "interpolate", - ["linear"], - ["zoom"], - 8, - 0, - 8.5, - 1 - ] - } - }, - { - "id": "water", - "type": "fill", - "metadata": { - "mapbox:featureComponent": "land-and-water", - "mapbox:group": "Land, water, & sky, water" - }, - "source": "composite", - "source-layer": "water", - "layout": {}, - "paint": {"fill-color": "hsl(197, 15%, 43%)"} - }, - { - "id": "land-structure-polygon", - "type": "fill", - "metadata": { - "mapbox:featureComponent": "land-and-water", - "mapbox:group": "Land, water, & sky, built" - }, - "source": "composite", - "source-layer": "structure", - "minzoom": 13, - "filter": [ - "all", - ["==", ["geometry-type"], "Polygon"], - ["==", ["get", "class"], "land"] - ], - "layout": {}, - "paint": {"fill-color": "hsl(214, 17%, 31%)"} - }, - { - "id": "land-structure-line", - "type": "line", - "metadata": { - "mapbox:featureComponent": "land-and-water", - "mapbox:group": "Land, water, & sky, built" - }, - "source": "composite", - "source-layer": "structure", - "minzoom": 13, - "filter": [ - "all", - ["==", ["geometry-type"], "LineString"], - ["==", ["get", "class"], "land"] - ], - "layout": {"line-cap": "round"}, - "paint": { - "line-width": [ - "interpolate", - ["exponential", 1.99], - ["zoom"], - 14, - 0.75, - 20, - 40 - ], - "line-color": "hsl(214, 17%, 31%)" - } - }, - { - "id": "aeroway-polygon", - "type": "fill", - "metadata": { - "mapbox:featureComponent": "transit", - "mapbox:group": "Transit, built" - }, - "source": "composite", - "source-layer": "aeroway", - "minzoom": 11, - "filter": [ - "all", - ["==", ["geometry-type"], "Polygon"], - [ - "match", - ["get", "type"], - ["runway", "taxiway", "helipad"], - true, - false - ] - ], - "layout": {}, - "paint": { - "fill-color": [ - "interpolate", - ["linear"], - ["zoom"], - 15, - "hsl(230, 19%, 27%)", - 16, - "hsl(230, 18%, 29%)" - ], - "fill-opacity": [ - "interpolate", - ["linear"], - ["zoom"], - 11, - 0, - 11.5, - 1 - ] - } - }, - { - "id": "aeroway-line", - "type": "line", - "metadata": { - "mapbox:featureComponent": "transit", - "mapbox:group": "Transit, built" - }, - "source": "composite", - "source-layer": "aeroway", - "minzoom": 9, - "filter": ["==", ["geometry-type"], "LineString"], - "layout": {}, - "paint": { - "line-color": [ - "interpolate", - ["linear"], - ["zoom"], - 15, - "hsl(230, 19%, 27%)", - 16, - "hsl(230, 18%, 29%)" - ], - "line-width": [ - "interpolate", - ["exponential", 1.5], - ["zoom"], - 9, - ["match", ["get", "type"], "runway", 1, 0.5], - 18, - ["match", ["get", "type"], "runway", 80, 20] - ] - } - }, - { - "id": "building-outline", - "type": "line", - "metadata": { - "mapbox:featureComponent": "buildings", - "mapbox:group": "Buildings, built" - }, - "source": "composite", - "source-layer": "building", - "minzoom": 15, - "filter": [ - "all", - ["!=", ["get", "type"], "building:part"], - ["==", ["get", "underground"], "false"] - ], - "layout": {}, - "paint": { - "line-color": "hsl(214, 13%, 26%)", - "line-width": [ - "interpolate", - ["exponential", 1.5], - ["zoom"], - 15, - 0.75, - 20, - 3 - ], - "line-opacity": [ - "interpolate", - ["linear"], - ["zoom"], - 15, - 0, - 16, - 1 - ] - } - }, - { - "id": "building", - "type": "fill", - "metadata": { - "mapbox:featureComponent": "buildings", - "mapbox:group": "Buildings, built" - }, - "source": "composite", - "source-layer": "building", - "minzoom": 15, - "filter": [ - "all", - ["!=", ["get", "type"], "building:part"], - ["==", ["get", "underground"], "false"] - ], - "layout": {}, - "paint": { - "fill-color": [ - "interpolate", - ["linear"], - ["zoom"], - 15, - "hsl(214, 16%, 31%)", - 16, - "hsl(214, 12%, 29%)" - ], - "fill-opacity": [ - "interpolate", - ["linear"], - ["zoom"], - 15, - 0, - 16, - 1 - ], - "fill-outline-color": "hsl(214, 13%, 26%)" - } - }, - { - "id": "tunnel-minor-case-navigation", - "type": "line", - "metadata": { - "mapbox:featureComponent": "road-network", - "mapbox:group": "Road network, tunnels-case" - }, - "source": "composite", - "source-layer": "road", - "minzoom": 15, - "filter": [ - "all", - ["==", ["get", "structure"], "tunnel"], - [ - "match", - ["get", "class"], - ["track", "secondary_link", "tertiary_link", "service"], - true, - false - ], - ["==", ["geometry-type"], "LineString"] - ], - "layout": {}, - "paint": { - "line-width": [ - "interpolate", - ["exponential", 1.2], - ["zoom"], - 15, - 0.75, - 18, - 1.5 - ], - "line-color": "hsl(217, 4%, 19%)", - "line-gap-width": [ - "interpolate", - ["exponential", 1.5], - ["zoom"], - 15, - ["match", ["get", "class"], "track", 1, 0.5], - 18, - 10 - ], - "line-dasharray": [3, 3] - } - }, - { - "id": "tunnel-street-low-navigation", - "type": "line", - "metadata": { - "mapbox:featureComponent": "road-network", - "mapbox:group": "Road network, tunnels-case" - }, - "source": "composite", - "source-layer": "road", - "minzoom": 13, - "maxzoom": 14, - "filter": [ - "all", - ["==", ["get", "structure"], "tunnel"], - [ - "match", - ["get", "class"], - ["street", "street_limited", "primary_link"], - true, - false - ], - ["==", ["geometry-type"], "LineString"] - ], - "layout": {}, - "paint": { - "line-width": [ - "interpolate", - ["exponential", 1.5], - ["zoom"], - 12, - 0.5, - 14, - 2 - ], - "line-color": "hsl(213, 9%, 19%)" - } - }, - { - "id": "tunnel-street-case-navigation", - "type": "line", - "metadata": { - "mapbox:featureComponent": "road-network", - "mapbox:group": "Road network, tunnels-case" - }, - "source": "composite", - "source-layer": "road", - "minzoom": 14, - "filter": [ - "all", - ["==", ["get", "structure"], "tunnel"], - [ - "match", - ["get", "class"], - ["street", "street_limited", "primary_link"], - true, - false - ], - ["==", ["geometry-type"], "LineString"] - ], - "layout": {}, - "paint": { - "line-width": [ - "interpolate", - ["exponential", 1.2], - ["zoom"], - 10, - 0.5, - 18, - 2 - ], - "line-color": "hsl(217, 4%, 19%)", - "line-gap-width": [ - "interpolate", - ["exponential", 1.5], - ["zoom"], - 14, - 2, - 18, - 20 - ], - "line-dasharray": [3, 3] - } - }, - { - "id": "tunnel-secondary-tertiary-case-navigation", - "type": "line", - "metadata": { - "mapbox:featureComponent": "road-network", - "mapbox:group": "Road network, tunnels-case" - }, - "source": "composite", - "source-layer": "road", - "minzoom": 12, - "filter": [ - "all", - ["==", ["get", "structure"], "tunnel"], - [ - "match", - ["get", "class"], - ["secondary", "tertiary"], - true, - false - ], - ["==", ["geometry-type"], "LineString"] - ], - "layout": {}, - "paint": { - "line-width": [ - "interpolate", - ["exponential", 1.2], - ["zoom"], - 10, - 0.75, - 18, - 2 - ], - "line-color": "hsl(217, 4%, 19%)", - "line-gap-width": [ - "interpolate", - ["exponential", 1.5], - ["zoom"], - 10, - 0.75, - 18, - 28 - ], - "line-dasharray": [3, 3] - } - }, - { - "id": "tunnel-primary-case-navigation", - "type": "line", - "metadata": { - "mapbox:featureComponent": "road-network", - "mapbox:group": "Road network, tunnels-case" - }, - "source": "composite", - "source-layer": "road", - "minzoom": 10, - "filter": [ - "all", - ["==", ["get", "structure"], "tunnel"], - ["==", ["get", "class"], "primary"], - ["==", ["geometry-type"], "LineString"] - ], - "layout": {}, - "paint": { - "line-width": [ - "interpolate", - ["exponential", 1.2], - ["zoom"], - 10, - 1, - 18, - 2 - ], - "line-color": "hsl(217, 4%, 19%)", - "line-gap-width": [ - "interpolate", - ["exponential", 1.5], - ["zoom"], - 10, - 1.125, - 18, - 32 - ], - "line-dasharray": [3, 3] - } - }, - { - "id": "tunnel-major-link-case-navigation", - "type": "line", - "metadata": { - "mapbox:featureComponent": "road-network", - "mapbox:group": "Road network, tunnels-case" - }, - "source": "composite", - "source-layer": "road", - "minzoom": 13, - "filter": [ - "all", - ["==", ["get", "structure"], "tunnel"], - [ - "match", - ["get", "class"], - ["motorway_link", "trunk_link"], - true, - false - ], - ["==", ["geometry-type"], "LineString"] - ], - "layout": {}, - "paint": { - "line-width": [ - "interpolate", - ["exponential", 1.2], - ["zoom"], - 10, - 0.75, - 18, - 2 - ], - "line-color": [ - "match", - ["get", "class"], - "motorway_link", - "hsl(215, 18%, 28%)", - "hsl(221, 13%, 28%)" - ], - "line-gap-width": [ - "interpolate", - ["exponential", 1.5], - ["zoom"], - 12, - 0.75, - 14, - 2, - 18, - 20 - ], - "line-dasharray": [3, 3] - } - }, - { - "id": "tunnel-motorway-trunk-case-navigation", - "type": "line", - "metadata": { - "mapbox:featureComponent": "road-network", - "mapbox:group": "Road network, tunnels-case" - }, - "source": "composite", - "source-layer": "road", - "minzoom": 13, - "filter": [ - "all", - ["==", ["get", "structure"], "tunnel"], - ["match", ["get", "class"], ["motorway", "trunk"], true, false], - ["==", ["geometry-type"], "LineString"] - ], - "layout": {}, - "paint": { - "line-width": [ - "interpolate", - ["exponential", 1.2], - ["zoom"], - 10, - 1, - 18, - 2 - ], - "line-color": [ - "match", - ["get", "class"], - "motorway", - "hsl(215, 18%, 28%)", - "hsl(221, 13%, 28%)" - ], - "line-gap-width": [ - "interpolate", - ["exponential", 1.5], - ["zoom"], - 5, - 1.25, - 18, - 32 - ], - "line-dasharray": [3, 3] - } - }, - { - "id": "tunnel-construction-navigation", - "type": "line", - "metadata": { - "mapbox:featureComponent": "road-network", - "mapbox:group": "Road network, tunnels-case" - }, - "source": "composite", - "source-layer": "road", - "minzoom": 14, - "filter": [ - "all", - ["==", ["get", "structure"], "tunnel"], - ["==", ["get", "class"], "construction"], - ["==", ["geometry-type"], "LineString"] - ], - "layout": {}, - "paint": { - "line-width": [ - "interpolate", - ["exponential", 1.5], - ["zoom"], - 14, - 2, - 18, - 20 - ], - "line-color": "hsl(217, 11%, 32%)", - "line-dasharray": [ - "step", - ["zoom"], - ["literal", [0.4, 0.8]], - 15, - ["literal", [0.3, 0.6]], - 16, - ["literal", [0.2, 0.3]], - 17, - ["literal", [0.2, 0.25]], - 18, - ["literal", [0.15, 0.15]] - ] - } - }, - { - "id": "tunnel-major-link-navigation", - "type": "line", - "metadata": { - "mapbox:featureComponent": "road-network", - "mapbox:group": "Road network, tunnels" - }, - "source": "composite", - "source-layer": "road", - "minzoom": 13, - "filter": [ - "all", - ["==", ["get", "structure"], "tunnel"], - [ - "match", - ["get", "class"], - ["motorway_link", "trunk_link"], - true, - false - ], - ["==", ["geometry-type"], "LineString"] - ], - "layout": {}, - "paint": { - "line-width": [ - "interpolate", - ["exponential", 1.5], - ["zoom"], - 12, - 0.75, - 14, - 2, - 18, - 20 - ], - "line-color": [ - "match", - ["get", "class"], - "motorway", - "hsl(215, 21%, 50%)", - "hsl(221, 21%, 50%)" - ] - } - }, - { - "id": "tunnel-minor-navigation", - "type": "line", - "metadata": { - "mapbox:featureComponent": "road-network", - "mapbox:group": "Road network, tunnels" - }, - "source": "composite", - "source-layer": "road", - "minzoom": 15, - "filter": [ - "all", - ["==", ["get", "structure"], "tunnel"], - [ - "match", - ["get", "class"], - ["track", "secondary_link", "tertiary_link", "service"], - true, - false - ], - ["==", ["geometry-type"], "LineString"] - ], - "layout": {}, - "paint": { - "line-width": [ - "interpolate", - ["exponential", 1.5], - ["zoom"], - 15, - ["match", ["get", "class"], "track", 1, 0.5], - 18, - 10 - ], - "line-color": "hsl(213, 9%, 19%)" - } - }, - { - "id": "tunnel-street-navigation", - "type": "line", - "metadata": { - "mapbox:featureComponent": "road-network", - "mapbox:group": "Road network, tunnels" - }, - "source": "composite", - "source-layer": "road", - "minzoom": 14, - "filter": [ - "all", - ["==", ["get", "structure"], "tunnel"], - [ - "match", - ["get", "class"], - ["street", "street_limited", "primary_link"], - true, - false - ], - ["==", ["geometry-type"], "LineString"] - ], - "layout": {"line-cap": "round", "line-join": "round"}, - "paint": { - "line-width": [ - "interpolate", - ["exponential", 1.5], - ["zoom"], - 12, - 0.5, - 14, - 2, - 18, - 20 - ], - "line-color": "hsl(213, 9%, 19%)" - } - }, - { - "id": "tunnel-secondary-tertiary-navigation", - "type": "line", - "metadata": { - "mapbox:featureComponent": "road-network", - "mapbox:group": "Road network, tunnels" - }, - "source": "composite", - "source-layer": "road", - "minzoom": 13, - "filter": [ - "all", - ["==", ["get", "structure"], "tunnel"], - [ - "match", - ["get", "class"], - ["secondary", "tertiary"], - true, - false - ], - ["==", ["geometry-type"], "LineString"] - ], - "layout": {}, - "paint": { - "line-width": [ - "interpolate", - ["exponential", 1.5], - ["zoom"], - 10, - 0.75, - 18, - 28 - ], - "line-color": "hsl(213, 9%, 19%)" - } - }, - { - "id": "tunnel-primary-navigation", - "type": "line", - "metadata": { - "mapbox:featureComponent": "road-network", - "mapbox:group": "Road network, tunnels" - }, - "source": "composite", - "source-layer": "road", - "minzoom": 13, - "filter": [ - "all", - ["==", ["get", "structure"], "tunnel"], - ["==", ["get", "class"], "primary"], - ["==", ["geometry-type"], "LineString"] - ], - "layout": {}, - "paint": { - "line-width": [ - "interpolate", - ["exponential", 1.5], - ["zoom"], - 10, - 1.125, - 18, - 32 - ], - "line-color": "hsl(213, 9%, 19%)" - } - }, - { - "id": "tunnel-oneway-arrow-blue-navigation", - "type": "symbol", - "metadata": { - "mapbox:featureComponent": "road-network", - "mapbox:group": "Road network, tunnels" - }, - "source": "composite", - "source-layer": "road", - "minzoom": 15, - "filter": [ - "all", - ["==", ["get", "structure"], "tunnel"], - ["==", ["get", "oneway"], "true"], - [ - "step", - ["zoom"], - [ - "match", - ["get", "class"], - [ - "primary", - "secondary", - "street", - "street_limited", - "tertiary" - ], - true, - false - ], - 16, - [ - "match", - ["get", "class"], - [ - "primary", - "secondary", - "tertiary", - "street", - "street_limited", - "primary_link", - "secondary_link", - "tertiary_link", - "service", - "track" - ], - true, - false - ] - ] - ], - "layout": { - "symbol-placement": "line", - "symbol-spacing": [ - "interpolate", - ["linear"], - ["zoom"], - 15, - 400, - 18, - 600, - 22, - 1200 - ], - "icon-image": [ - "step", - ["zoom"], - "oneway-small", - 17, - [ - "match", - ["get", "class"], - [ - "primary", - "secondary", - "tertiary", - "street", - "street_limited" - ], - "oneway-large", - "oneway-small" - ], - 18, - "oneway-large" - ], - "icon-size": [ - "interpolate", - ["exponential", 1.5], - ["zoom"], - 15, - 0.25, - 20, - 1 - ], - "icon-rotation-alignment": "map" - }, - "paint": {} - }, - { - "id": "tunnel-motorway-trunk-navigation", - "type": "line", - "metadata": { - "mapbox:featureComponent": "road-network", - "mapbox:group": "Road network, tunnels" - }, - "source": "composite", - "source-layer": "road", - "minzoom": 13, - "filter": [ - "all", - ["==", ["get", "structure"], "tunnel"], - ["match", ["get", "class"], ["motorway", "trunk"], true, false], - ["==", ["geometry-type"], "LineString"] - ], - "layout": {}, - "paint": { - "line-width": [ - "interpolate", - ["exponential", 1.5], - ["zoom"], - 5, - 1.25, - 18, - 32 - ], - "line-color": [ - "match", - ["get", "class"], - "motorway", - "hsl(215, 21%, 50%)", - "hsl(221, 21%, 50%)" - ] - } - }, - { - "id": "tunnel-oneway-arrow-white-navigation", - "type": "symbol", - "metadata": { - "mapbox:featureComponent": "road-network", - "mapbox:group": "Road network, tunnels" - }, - "source": "composite", - "source-layer": "road", - "minzoom": 15, - "filter": [ - "all", - ["==", ["get", "structure"], "tunnel"], - [ - "match", - ["get", "class"], - ["motorway", "motorway_link", "trunk", "trunk_link"], - true, - false - ], - ["==", ["get", "oneway"], "true"] - ], - "layout": { - "symbol-placement": "line", - "symbol-spacing": [ - "interpolate", - ["linear"], - ["zoom"], - 15, - 400, - 18, - 600, - 22, - 1200 - ], - "icon-image": [ - "step", - ["zoom"], - "oneway-white-small", - 17, - "oneway-white-large" - ], - "icon-size": [ - "interpolate", - ["exponential", 1.5], - ["zoom"], - 15, - 0.4, - 20, - 1 - ] - }, - "paint": {} - }, - { - "id": "ferry", - "type": "line", - "metadata": { - "mapbox:featureComponent": "transit", - "mapbox:group": "Transit, ferries" - }, - "source": "composite", - "source-layer": "road", - "minzoom": 8, - "filter": ["==", ["get", "type"], "ferry"], - "layout": {"line-join": ["step", ["zoom"], "miter", 14, "round"]}, - "paint": { - "line-color": [ - "interpolate", - ["linear"], - ["zoom"], - 15, - "hsl(206, 8%, 36%)", - 17, - "hsl(231, 8%, 36%)" - ], - "line-width": [ - "interpolate", - ["exponential", 1.5], - ["zoom"], - 14, - 0.5, - 20, - 1 - ], - "line-dasharray": [ - "step", - ["zoom"], - ["literal", [1, 0]], - 13, - ["literal", [12, 4]] - ] - } - }, - { - "id": "ferry-auto", - "type": "line", - "metadata": { - "mapbox:featureComponent": "transit", - "mapbox:group": "Transit, ferries" - }, - "source": "composite", - "source-layer": "road", - "filter": ["==", ["get", "type"], "ferry_auto"], - "layout": {"line-join": ["step", ["zoom"], "miter", 14, "round"]}, - "paint": { - "line-color": [ - "interpolate", - ["linear"], - ["zoom"], - 15, - "hsl(206, 8%, 36%)", - 17, - "hsl(231, 8%, 36%)" - ], - "line-width": [ - "interpolate", - ["exponential", 1.5], - ["zoom"], - 14, - 0.5, - 20, - 1 - ] - } - }, - { - "id": "turning-feature-outline-navigation", - "type": "symbol", - "metadata": { - "mapbox:featureComponent": "road-network", - "mapbox:group": "Road network, surface" - }, - "source": "composite", - "source-layer": "road", - "minzoom": 15, - "filter": [ - "all", - ["==", ["geometry-type"], "Point"], - [ - "match", - ["get", "class"], - ["turning_circle", "turning_loop"], - true, - false - ] - ], - "layout": { - "icon-image": "turning-circle-outline", - "icon-size": [ - "interpolate", - ["exponential", 1.5], - ["zoom"], - 15, - 0.06, - 22, - 1.4 - ], - "icon-allow-overlap": true, - "icon-ignore-placement": true, - "icon-padding": 0, - "icon-rotation-alignment": "map" - }, - "paint": {} - }, - { - "id": "road-minor-case-navigation", - "type": "line", - "metadata": { - "mapbox:featureComponent": "road-network", - "mapbox:group": "Road network, surface" - }, - "source": "composite", - "source-layer": "road", - "minzoom": 15, - "filter": [ - "all", - [ - "match", - ["get", "class"], - ["track", "secondary_link", "tertiary_link", "service"], - true, - false - ], - ["match", ["get", "structure"], ["none", "ford"], true, false], - ["==", ["geometry-type"], "LineString"] - ], - "layout": {"line-join": "round", "line-cap": "round"}, - "paint": { - "line-width": [ - "interpolate", - ["exponential", 1.2], - ["zoom"], - 15, - 0.75, - 18, - 1.5 - ], - "line-color": "hsl(217, 11%, 32%)", - "line-gap-width": [ - "interpolate", - ["exponential", 1.5], - ["zoom"], - 15, - ["match", ["get", "class"], "track", 1, 0.5], - 18, - 10 - ] - } - }, - { - "id": "road-street-low-navigation", - "type": "line", - "metadata": { - "mapbox:featureComponent": "road-network", - "mapbox:group": "Road network, surface" - }, - "source": "composite", - "source-layer": "road", - "minzoom": 11, - "maxzoom": 14, - "filter": [ - "all", - [ - "match", - ["get", "class"], - ["street", "street_limited", "primary_link"], - true, - false - ], - ["match", ["get", "structure"], ["none", "ford"], true, false], - ["==", ["geometry-type"], "LineString"] - ], - "layout": { - "line-cap": ["step", ["zoom"], "butt", 14, "round"], - "line-join": ["step", ["zoom"], "miter", 14, "round"] - }, - "paint": { - "line-width": [ - "interpolate", - ["exponential", 1.5], - ["zoom"], - 12, - 0.5, - 14, - 2 - ], - "line-color": "hsl(213, 9%, 19%)" - } - }, - { - "id": "road-street-case-navigation", - "type": "line", - "metadata": { - "mapbox:featureComponent": "road-network", - "mapbox:group": "Road network, surface" - }, - "source": "composite", - "source-layer": "road", - "minzoom": 14, - "filter": [ - "all", - [ - "match", - ["get", "class"], - ["street", "street_limited", "primary_link"], - true, - false - ], - ["match", ["get", "structure"], ["none", "ford"], true, false], - ["==", ["geometry-type"], "LineString"] - ], - "layout": { - "line-cap": ["step", ["zoom"], "butt", 13, "round"], - "line-join": ["step", ["zoom"], "miter", 13, "round"] - }, - "paint": { - "line-width": [ - "interpolate", - ["exponential", 1.2], - ["zoom"], - 10, - 0.5, - 18, - 2 - ], - "line-color": "hsl(217, 11%, 32%)", - "line-gap-width": [ - "interpolate", - ["exponential", 1.5], - ["zoom"], - 14, - 2, - 18, - 20 - ] - } - }, - { - "id": "road-secondary-tertiary-case-navigation", - "type": "line", - "metadata": { - "mapbox:featureComponent": "road-network", - "mapbox:group": "Road network, surface" - }, - "source": "composite", - "source-layer": "road", - "minzoom": 12, - "filter": [ - "all", - [ - "match", - ["get", "class"], - ["secondary", "tertiary"], - true, - false - ], - ["match", ["get", "structure"], ["none", "ford"], true, false], - ["==", ["geometry-type"], "LineString"] - ], - "layout": {"line-cap": "round", "line-join": "round"}, - "paint": { - "line-width": [ - "interpolate", - ["exponential", 1.2], - ["zoom"], - 10, - 0.75, - 18, - 2 - ], - "line-color": "hsl(217, 11%, 32%)", - "line-gap-width": [ - "interpolate", - ["exponential", 1.5], - ["zoom"], - 10, - 0.75, - 18, - 28 - ] - } - }, - { - "id": "road-primary-case-navigation", - "type": "line", - "metadata": { - "mapbox:featureComponent": "road-network", - "mapbox:group": "Road network, surface" - }, - "source": "composite", - "source-layer": "road", - "minzoom": 12, - "filter": [ - "all", - ["==", ["get", "class"], "primary"], - ["match", ["get", "structure"], ["none", "ford"], true, false], - ["==", ["geometry-type"], "LineString"] - ], - "layout": {"line-cap": "round", "line-join": "round"}, - "paint": { - "line-width": [ - "interpolate", - ["exponential", 1.2], - ["zoom"], - 10, - 1, - 18, - 2 - ], - "line-color": "hsl(217, 11%, 32%)", - "line-gap-width": [ - "interpolate", - ["exponential", 1.5], - ["zoom"], - 10, - 1.125, - 18, - 32 - ] - } - }, - { - "id": "road-major-link-case-navigation", - "type": "line", - "metadata": { - "mapbox:featureComponent": "road-network", - "mapbox:group": "Road network, surface" - }, - "source": "composite", - "source-layer": "road", - "minzoom": 11, - "filter": [ - "all", - [ - "match", - ["get", "class"], - ["motorway_link", "trunk_link"], - true, - false - ], - ["match", ["get", "structure"], ["none", "ford"], true, false], - ["==", ["geometry-type"], "LineString"] - ], - "layout": { - "line-cap": ["step", ["zoom"], "butt", 13, "round"], - "line-join": ["step", ["zoom"], "miter", 13, "round"] - }, - "paint": { - "line-width": [ - "interpolate", - ["exponential", 1.2], - ["zoom"], - 10, - 0.75, - 18, - 2 - ], - "line-color": [ - "match", - ["get", "class"], - "motorway_link", - "hsl(215, 18%, 28%)", - "hsl(221, 13%, 28%)" - ], - "line-gap-width": [ - "interpolate", - ["exponential", 1.5], - ["zoom"], - 12, - 0.75, - 14, - 2, - 18, - 20 - ] - } - }, - { - "id": "road-motorway-trunk-case-navigation", - "type": "line", - "metadata": { - "mapbox:featureComponent": "road-network", - "mapbox:group": "Road network, surface" - }, - "source": "composite", - "source-layer": "road", - "minzoom": 13, - "filter": [ - "all", - ["match", ["get", "class"], ["motorway", "trunk"], true, false], - ["match", ["get", "structure"], ["none", "ford"], true, false], - ["==", ["geometry-type"], "LineString"] - ], - "layout": {"line-cap": "round", "line-join": "round"}, - "paint": { - "line-width": [ - "interpolate", - ["exponential", 1.2], - ["zoom"], - 10, - 1, - 18, - 2 - ], - "line-color": [ - "match", - ["get", "class"], - "motorway", - "hsl(215, 18%, 28%)", - "hsl(221, 13%, 28%)" - ], - "line-gap-width": [ - "interpolate", - ["exponential", 1.5], - ["zoom"], - 5, - 1.25, - 18, - 32 - ] - } - }, - { - "id": "road-construction-navigation", - "type": "line", - "metadata": { - "mapbox:featureComponent": "road-network", - "mapbox:group": "Road network, surface" - }, - "source": "composite", - "source-layer": "road", - "minzoom": 14, - "filter": [ - "all", - ["==", ["get", "class"], "construction"], - ["match", ["get", "structure"], ["none", "ford"], true, false], - ["==", ["geometry-type"], "LineString"] - ], - "layout": {}, - "paint": { - "line-width": [ - "interpolate", - ["exponential", 1.5], - ["zoom"], - 14, - 2, - 18, - 20 - ], - "line-color": "hsl(213, 9%, 19%)", - "line-dasharray": [ - "step", - ["zoom"], - ["literal", [0.4, 0.8]], - 15, - ["literal", [0.3, 0.6]], - 16, - ["literal", [0.2, 0.3]], - 17, - ["literal", [0.2, 0.25]], - 18, - ["literal", [0.15, 0.15]] - ] - } - }, - { - "id": "road-major-link-navigation", - "type": "line", - "metadata": { - "mapbox:featureComponent": "road-network", - "mapbox:group": "Road network, surface" - }, - "source": "composite", - "source-layer": "road", - "minzoom": 11, - "filter": [ - "all", - [ - "match", - ["get", "class"], - ["motorway_link", "trunk_link"], - true, - false - ], - ["match", ["get", "structure"], ["none", "ford"], true, false], - ["==", ["geometry-type"], "LineString"] - ], - "layout": { - "line-cap": ["step", ["zoom"], "butt", 13, "round"], - "line-join": ["step", ["zoom"], "miter", 13, "round"] - }, - "paint": { - "line-width": [ - "interpolate", - ["exponential", 1.5], - ["zoom"], - 12, - 0.75, - 14, - 2, - 18, - 20 - ], - "line-color": [ - "match", - ["get", "class"], - "motorway_link", - "hsl(215, 28%, 48%)", - "hsl(221, 20%, 44%)" - ] - } - }, - { - "id": "road-minor-navigation", - "type": "line", - "metadata": { - "mapbox:featureComponent": "road-network", - "mapbox:group": "Road network, surface" - }, - "source": "composite", - "source-layer": "road", - "minzoom": 15, - "filter": [ - "all", - [ - "match", - ["get", "class"], - ["track", "secondary_link", "tertiary_link", "service"], - true, - false - ], - ["match", ["get", "structure"], ["none", "ford"], true, false], - ["==", ["geometry-type"], "LineString"] - ], - "layout": {"line-join": "round", "line-cap": "round"}, - "paint": { - "line-width": [ - "interpolate", - ["exponential", 1.5], - ["zoom"], - 15, - ["match", ["get", "class"], "track", 1, 0.5], - 18, - 10 - ], - "line-color": "hsl(213, 9%, 19%)" - } - }, - { - "id": "road-street-navigation", - "type": "line", - "metadata": { - "mapbox:featureComponent": "road-network", - "mapbox:group": "Road network, surface" - }, - "source": "composite", - "source-layer": "road", - "minzoom": 14, - "filter": [ - "all", - [ - "match", - ["get", "class"], - ["street", "street_limited", "primary_link"], - true, - false - ], - ["match", ["get", "structure"], ["none", "ford"], true, false], - ["==", ["geometry-type"], "LineString"] - ], - "layout": {"line-cap": "round", "line-join": "round"}, - "paint": { - "line-width": [ - "interpolate", - ["exponential", 1.5], - ["zoom"], - 12, - 0.5, - 14, - 2, - 18, - 20 - ], - "line-color": "hsl(213, 9%, 19%)" - } - }, - { - "id": "road-secondary-tertiary-navigation", - "type": "line", - "metadata": { - "mapbox:featureComponent": "road-network", - "mapbox:group": "Road network, surface" - }, - "source": "composite", - "source-layer": "road", - "minzoom": 8, - "filter": [ - "all", - [ - "match", - ["get", "class"], - ["secondary", "tertiary"], - true, - false - ], - ["match", ["get", "structure"], ["none", "ford"], true, false], - ["==", ["geometry-type"], "LineString"] - ], - "layout": { - "line-cap": ["step", ["zoom"], "butt", 11, "round"], - "line-join": ["step", ["zoom"], "miter", 11, "round"] - }, - "paint": { - "line-width": [ - "interpolate", - ["exponential", 1.5], - ["zoom"], - 5, - 0.175, - 10, - 0.75, - 18, - 28 - ], - "line-color": "hsl(213, 9%, 19%)" - } - }, - { - "id": "road-primary-navigation", - "type": "line", - "metadata": { - "mapbox:featureComponent": "road-network", - "mapbox:group": "Road network, surface" - }, - "source": "composite", - "source-layer": "road", - "minzoom": 6, - "filter": [ - "all", - ["==", ["get", "class"], "primary"], - ["match", ["get", "structure"], ["none", "ford"], true, false], - ["==", ["geometry-type"], "LineString"] - ], - "layout": { - "line-cap": ["step", ["zoom"], "butt", 11, "round"], - "line-join": ["step", ["zoom"], "miter", 11, "round"] - }, - "paint": { - "line-width": [ - "interpolate", - ["exponential", 1.5], - ["zoom"], - 10, - 1.125, - 18, - 32 - ], - "line-color": "hsl(213, 9%, 19%)" - } - }, - { - "id": "road-motorway-trunk-case-low-navigation", - "type": "line", - "metadata": { - "mapbox:featureComponent": "road-network", - "mapbox:group": "Road network, surface" - }, - "source": "composite", - "source-layer": "road", - "minzoom": 5, - "maxzoom": 13, - "filter": [ - "all", - [ - "step", - ["zoom"], - ["==", ["get", "class"], "motorway"], - 6, - [ - "match", - ["get", "class"], - ["motorway", "trunk"], - true, - false - ] - ], - ["match", ["get", "structure"], ["none", "ford"], true, false], - ["==", ["geometry-type"], "LineString"] - ], - "layout": {"line-cap": "round", "line-join": "round"}, - "paint": { - "line-width": [ - "interpolate", - ["exponential", 1.2], - ["zoom"], - 10, - 1, - 18, - 2 - ], - "line-color": [ - "match", - ["get", "class"], - "motorway", - "hsl(215, 18%, 28%)", - "hsl(221, 13%, 28%)" - ], - "line-gap-width": [ - "interpolate", - ["exponential", 1.5], - ["zoom"], - 5, - 1.25, - 18, - 32 - ] - } - }, - { - "id": "road-oneway-arrow-blue-navigation", - "type": "symbol", - "metadata": { - "mapbox:featureComponent": "road-network", - "mapbox:group": "Road network, surface" - }, - "source": "composite", - "source-layer": "road", - "minzoom": 15, - "filter": [ - "all", - ["==", ["get", "oneway"], "true"], - [ - "step", - ["zoom"], - [ - "match", - ["get", "class"], - [ - "primary", - "secondary", - "tertiary", - "street", - "street_limited" - ], - true, - false - ], - 16, - [ - "match", - ["get", "class"], - [ - "primary", - "secondary", - "tertiary", - "street", - "street_limited", - "primary_link", - "secondary_link", - "tertiary_link", - "service", - "track" - ], - true, - false - ] - ], - ["match", ["get", "structure"], ["none", "ford"], true, false] - ], - "layout": { - "symbol-placement": "line", - "symbol-spacing": [ - "interpolate", - ["linear"], - ["zoom"], - 15, - 400, - 18, - 600, - 22, - 1200 - ], - "icon-image": [ - "step", - ["zoom"], - "oneway-small", - 17, - [ - "match", - ["get", "class"], - [ - "primary", - "secondary", - "tertiary", - "street", - "street_limited" - ], - "oneway-large", - "oneway-small" - ], - 18, - "oneway-large" - ], - "icon-size": [ - "interpolate", - ["exponential", 1.5], - ["zoom"], - 15, - 0.25, - 20, - 1 - ], - "icon-rotation-alignment": "map" - }, - "paint": {} - }, - { - "id": "road-motorway-trunk-navigation", - "type": "line", - "metadata": { - "mapbox:featureComponent": "road-network", - "mapbox:group": "Road network, surface" - }, - "source": "composite", - "source-layer": "road", - "minzoom": 5, - "filter": [ - "all", - ["match", ["get", "class"], ["motorway", "trunk"], true, false], - ["match", ["get", "structure"], ["none", "ford"], true, false], - ["==", ["geometry-type"], "LineString"] - ], - "layout": { - "line-cap": ["step", ["zoom"], "butt", 8, "round"], - "line-join": ["step", ["zoom"], "miter", 8, "round"] - }, - "paint": { - "line-width": [ - "interpolate", - ["exponential", 1.5], - ["zoom"], - 5, - 1.25, - 18, - 32 - ], - "line-color": [ - "step", - ["zoom"], - [ - "match", - ["get", "class"], - "motorway", - "hsl(215, 28%, 48%)", - "hsl(213, 9%, 19%)" - ], - 6, - [ - "match", - ["get", "class"], - "motorway", - "hsl(215, 28%, 48%)", - "hsl(221, 20%, 44%)" - ] - ] - } - }, - { - "id": "road-rail", - "type": "line", - "metadata": { - "mapbox:featureComponent": "transit", - "mapbox:group": "Transit, surface" - }, - "source": "composite", - "source-layer": "road", - "minzoom": 13, - "filter": [ - "all", - [ - "match", - ["get", "class"], - ["major_rail", "minor_rail"], - true, - false - ], - ["match", ["get", "structure"], ["none", "ford"], true, false] - ], - "layout": {"line-join": "round"}, - "paint": { - "line-color": [ - "interpolate", - ["linear"], - ["zoom"], - 13, - "hsl(229, 24%, 26%)", - 16, - "hsl(217, 0%, 47%)" - ], - "line-width": [ - "interpolate", - ["exponential", 1.5], - ["zoom"], - 14, - 0.5, - 20, - 1 - ] - } - }, - { - "id": "road-rail-tracks", - "type": "line", - "metadata": { - "mapbox:featureComponent": "transit", - "mapbox:group": "Transit, surface" - }, - "source": "composite", - "source-layer": "road", - "minzoom": 13, - "filter": [ - "all", - [ - "match", - ["get", "class"], - ["major_rail", "minor_rail"], - true, - false - ], - ["match", ["get", "structure"], ["none", "ford"], true, false] - ], - "layout": {"line-join": "round"}, - "paint": { - "line-color": [ - "interpolate", - ["linear"], - ["zoom"], - 13, - "hsl(229, 24%, 26%)", - 16, - "hsl(217, 0%, 47%)" - ], - "line-width": [ - "interpolate", - ["exponential", 1.5], - ["zoom"], - 14, - 4, - 20, - 8 - ], - "line-dasharray": [0.1, 15], - "line-opacity": [ - "interpolate", - ["linear"], - ["zoom"], - 13.75, - 0, - 14, - 1 - ] - } - }, - { - "id": "level-crossing-navigation", - "type": "symbol", - "metadata": { - "mapbox:featureComponent": "road-network", - "mapbox:group": "Road network, surface-icons" - }, - "source": "composite", - "source-layer": "road", - "minzoom": 16, - "filter": ["==", ["get", "class"], "level_crossing"], - "layout": { - "icon-image": "level-crossing", - "icon-size": [ - "interpolate", - ["exponential", 1.5], - ["zoom"], - 16, - 0.25, - 22, - 1 - ], - "icon-allow-overlap": true - }, - "paint": {} - }, - { - "id": "road-oneway-arrow-white-navigation", - "type": "symbol", - "metadata": { - "mapbox:featureComponent": "road-network", - "mapbox:group": "Road network, surface-icons" - }, - "source": "composite", - "source-layer": "road", - "minzoom": 15, - "filter": [ - "all", - ["==", ["get", "oneway"], "true"], - [ - "match", - ["get", "class"], - ["motorway", "trunk", "motorway_link", "trunk_link"], - true, - false - ], - ["match", ["get", "structure"], ["none", "ford"], true, false] - ], - "layout": { - "symbol-placement": "line", - "symbol-spacing": [ - "interpolate", - ["linear"], - ["zoom"], - 15, - 400, - 18, - 600, - 22, - 1200 - ], - "icon-image": [ - "step", - ["zoom"], - "oneway-white-small", - 17, - "oneway-white-large" - ], - "icon-size": [ - "interpolate", - ["exponential", 1.5], - ["zoom"], - 15, - 0.4, - 20, - 1 - ] - }, - "paint": {} - }, - { - "id": "turning-feature-navigation", - "type": "symbol", - "metadata": { - "mapbox:featureComponent": "road-network", - "mapbox:group": "Road network, surface-icons" - }, - "source": "composite", - "source-layer": "road", - "minzoom": 15, - "filter": [ - "all", - ["==", ["geometry-type"], "Point"], - [ - "match", - ["get", "class"], - ["turning_circle", "turning_loop"], - true, - false - ] - ], - "layout": { - "icon-image": "turning-circle", - "icon-size": [ - "interpolate", - ["exponential", 1.5], - ["zoom"], - 15, - 0.05, - 22, - 1.5 - ], - "icon-allow-overlap": true, - "icon-ignore-placement": true, - "icon-padding": 0, - "icon-rotation-alignment": "map" - }, - "paint": {} - }, - { - "id": "bridge-minor-case-navigation", - "type": "line", - "metadata": { - "mapbox:featureComponent": "road-network", - "mapbox:group": "Road network, bridges" - }, - "source": "composite", - "source-layer": "road", - "minzoom": 15, - "filter": [ - "all", - ["==", ["get", "structure"], "bridge"], - [ - "match", - ["get", "class"], - ["track", "secondary_link", "tertiary_link", "service"], - true, - false - ], - ["==", ["geometry-type"], "LineString"] - ], - "layout": {"line-join": "round"}, - "paint": { - "line-width": [ - "interpolate", - ["exponential", 1.2], - ["zoom"], - 15, - 0.75, - 18, - 1.5 - ], - "line-color": "hsl(217, 11%, 32%)", - "line-gap-width": [ - "interpolate", - ["exponential", 1.5], - ["zoom"], - 15, - ["match", ["get", "class"], "track", 1, 0.5], - 18, - 10 - ] - } - }, - { - "id": "bridge-street-low-navigation", - "type": "line", - "metadata": { - "mapbox:featureComponent": "road-network", - "mapbox:group": "Road network, bridges" - }, - "source": "composite", - "source-layer": "road", - "minzoom": 13, - "maxzoom": 14, - "filter": [ - "all", - ["==", ["get", "structure"], "bridge"], - [ - "match", - ["get", "class"], - ["street", "street_limited", "primary_link"], - true, - false - ], - ["==", ["geometry-type"], "LineString"] - ], - "layout": {}, - "paint": { - "line-width": [ - "interpolate", - ["exponential", 1.5], - ["zoom"], - 12, - 0.5, - 14, - 2 - ], - "line-color": "hsl(213, 9%, 19%)" - } - }, - { - "id": "bridge-street-case-navigation", - "type": "line", - "metadata": { - "mapbox:featureComponent": "road-network", - "mapbox:group": "Road network, bridges" - }, - "source": "composite", - "source-layer": "road", - "minzoom": 14, - "filter": [ - "all", - ["==", ["get", "structure"], "bridge"], - [ - "match", - ["get", "class"], - ["street", "street_limited", "primary_link"], - true, - false - ], - ["==", ["geometry-type"], "LineString"] - ], - "layout": {"line-join": "round"}, - "paint": { - "line-width": [ - "interpolate", - ["exponential", 1.2], - ["zoom"], - 10, - 0.5, - 18, - 2 - ], - "line-color": "hsl(217, 11%, 32%)", - "line-gap-width": [ - "interpolate", - ["exponential", 1.5], - ["zoom"], - 14, - 2, - 18, - 20 - ] - } - }, - { - "id": "bridge-secondary-tertiary-case-navigation", - "type": "line", - "metadata": { - "mapbox:featureComponent": "road-network", - "mapbox:group": "Road network, bridges" - }, - "source": "composite", - "source-layer": "road", - "minzoom": 12, - "filter": [ - "all", - ["==", ["get", "structure"], "bridge"], - [ - "match", - ["get", "class"], - ["secondary", "tertiary"], - true, - false - ], - ["==", ["geometry-type"], "LineString"] - ], - "layout": {"line-join": "round"}, - "paint": { - "line-width": [ - "interpolate", - ["exponential", 1.2], - ["zoom"], - 10, - 0.75, - 18, - 2 - ], - "line-color": "hsl(217, 11%, 32%)", - "line-gap-width": [ - "interpolate", - ["exponential", 1.5], - ["zoom"], - 10, - 0.75, - 18, - 28 - ] - } - }, - { - "id": "bridge-primary-case-navigation", - "type": "line", - "metadata": { - "mapbox:featureComponent": "road-network", - "mapbox:group": "Road network, bridges" - }, - "source": "composite", - "source-layer": "road", - "minzoom": 12, - "filter": [ - "all", - ["==", ["get", "structure"], "bridge"], - ["==", ["get", "class"], "primary"], - ["==", ["geometry-type"], "LineString"] - ], - "layout": {"line-join": "round"}, - "paint": { - "line-width": [ - "interpolate", - ["exponential", 1.2], - ["zoom"], - 10, - 1, - 18, - 2 - ], - "line-color": "hsl(217, 11%, 32%)", - "line-gap-width": [ - "interpolate", - ["exponential", 1.5], - ["zoom"], - 10, - 1.125, - 18, - 32 - ] - } - }, - { - "id": "bridge-major-link-case-navigation", - "type": "line", - "metadata": { - "mapbox:featureComponent": "road-network", - "mapbox:group": "Road network, bridges" - }, - "source": "composite", - "source-layer": "road", - "minzoom": 13, - "filter": [ - "all", - ["==", ["get", "structure"], "bridge"], - [ - "match", - ["get", "class"], - ["motorway_link", "trunk_link"], - true, - false - ], - ["<=", ["get", "layer"], 1], - ["==", ["geometry-type"], "LineString"] - ], - "layout": {"line-join": "round"}, - "paint": { - "line-width": [ - "interpolate", - ["exponential", 1.2], - ["zoom"], - 10, - 0.75, - 18, - 2 - ], - "line-color": [ - "match", - ["get", "class"], - "motorway_link", - "hsl(215, 18%, 28%)", - "hsl(221, 13%, 28%)" - ], - "line-gap-width": [ - "interpolate", - ["exponential", 1.5], - ["zoom"], - 12, - 0.75, - 14, - 2, - 18, - 20 - ] - } - }, - { - "id": "bridge-motorway-trunk-case-navigation", - "type": "line", - "metadata": { - "mapbox:featureComponent": "road-network", - "mapbox:group": "Road network, bridges" - }, - "source": "composite", - "source-layer": "road", - "minzoom": 13, - "filter": [ - "all", - ["==", ["get", "structure"], "bridge"], - ["match", ["get", "class"], ["motorway", "trunk"], true, false], - ["<=", ["get", "layer"], 1], - ["==", ["geometry-type"], "LineString"] - ], - "layout": {"line-join": "round"}, - "paint": { - "line-width": [ - "interpolate", - ["exponential", 1.2], - ["zoom"], - 10, - 1, - 18, - 2 - ], - "line-color": [ - "match", - ["get", "class"], - "motorway", - "hsl(215, 18%, 28%)", - "hsl(221, 13%, 28%)" - ], - "line-gap-width": [ - "interpolate", - ["exponential", 1.5], - ["zoom"], - 5, - 1.25, - 18, - 32 - ] - } - }, - { - "id": "bridge-construction-navigation", - "type": "line", - "metadata": { - "mapbox:featureComponent": "road-network", - "mapbox:group": "Road network, bridges" - }, - "source": "composite", - "source-layer": "road", - "minzoom": 14, - "filter": [ - "all", - ["==", ["get", "structure"], "bridge"], - ["==", ["get", "class"], "construction"], - ["==", ["geometry-type"], "LineString"] - ], - "layout": {}, - "paint": { - "line-width": [ - "interpolate", - ["exponential", 1.5], - ["zoom"], - 14, - 2, - 18, - 20 - ], - "line-color": "hsl(217, 11%, 32%)", - "line-dasharray": [ - "step", - ["zoom"], - ["literal", [0.4, 0.8]], - 15, - ["literal", [0.3, 0.6]], - 16, - ["literal", [0.2, 0.3]], - 17, - ["literal", [0.2, 0.25]], - 18, - ["literal", [0.15, 0.15]] - ] - } - }, - { - "id": "bridge-major-link-navigation", - "type": "line", - "metadata": { - "mapbox:featureComponent": "road-network", - "mapbox:group": "Road network, bridges" - }, - "source": "composite", - "source-layer": "road", - "minzoom": 13, - "filter": [ - "all", - ["==", ["get", "structure"], "bridge"], - [ - "match", - ["get", "class"], - ["motorway_link", "trunk_link"], - true, - false - ], - ["<=", ["get", "layer"], 1], - ["==", ["geometry-type"], "LineString"] - ], - "layout": {"line-cap": "round", "line-join": "round"}, - "paint": { - "line-width": [ - "interpolate", - ["exponential", 1.5], - ["zoom"], - 12, - 0.75, - 14, - 2, - 18, - 20 - ], - "line-color": [ - "match", - ["get", "class"], - "motorway_link", - "hsl(215, 28%, 48%)", - "hsl(221, 20%, 44%)" - ] - } - }, - { - "id": "bridge-minor-navigation", - "type": "line", - "metadata": { - "mapbox:featureComponent": "road-network", - "mapbox:group": "Road network, bridges" - }, - "source": "composite", - "source-layer": "road", - "minzoom": 15, - "filter": [ - "all", - ["==", ["get", "structure"], "bridge"], - [ - "match", - ["get", "class"], - ["track", "secondary_link", "tertiary_link", "service"], - true, - false - ], - ["==", ["geometry-type"], "LineString"] - ], - "layout": {"line-cap": "round", "line-join": "round"}, - "paint": { - "line-width": [ - "interpolate", - ["exponential", 1.5], - ["zoom"], - 15, - ["match", ["get", "class"], "track", 1, 0.5], - 18, - 10 - ], - "line-color": "hsl(213, 9%, 19%)" - } - }, - { - "id": "bridge-street-navigation", - "type": "line", - "metadata": { - "mapbox:featureComponent": "road-network", - "mapbox:group": "Road network, bridges" - }, - "source": "composite", - "source-layer": "road", - "minzoom": 14, - "filter": [ - "all", - ["==", ["get", "structure"], "bridge"], - [ - "match", - ["get", "class"], - ["street", "street_limited", "primary_link"], - true, - false - ], - ["==", ["geometry-type"], "LineString"] - ], - "layout": {"line-cap": "round", "line-join": "round"}, - "paint": { - "line-width": [ - "interpolate", - ["exponential", 1.5], - ["zoom"], - 12, - 0.5, - 14, - 2, - 18, - 20 - ], - "line-color": "hsl(213, 9%, 19%)" - } - }, - { - "id": "bridge-secondary-tertiary-navigation", - "type": "line", - "metadata": { - "mapbox:featureComponent": "road-network", - "mapbox:group": "Road network, bridges" - }, - "source": "composite", - "source-layer": "road", - "minzoom": 13, - "filter": [ - "all", - ["==", ["get", "structure"], "bridge"], - [ - "match", - ["get", "class"], - ["secondary", "tertiary"], - true, - false - ], - ["==", ["geometry-type"], "LineString"] - ], - "layout": { - "line-cap": ["step", ["zoom"], "butt", 11, "round"], - "line-join": ["step", ["zoom"], "miter", 11, "round"] - }, - "paint": { - "line-width": [ - "interpolate", - ["exponential", 1.5], - ["zoom"], - 10, - 0.75, - 18, - 28 - ], - "line-color": "hsl(213, 9%, 19%)" - } - }, - { - "id": "bridge-primary-navigation", - "type": "line", - "metadata": { - "mapbox:featureComponent": "road-network", - "mapbox:group": "Road network, bridges" - }, - "source": "composite", - "source-layer": "road", - "minzoom": 13, - "filter": [ - "all", - ["==", ["get", "structure"], "bridge"], - ["==", ["get", "class"], "primary"], - ["==", ["geometry-type"], "LineString"] - ], - "layout": {"line-cap": "round", "line-join": "round"}, - "paint": { - "line-width": [ - "interpolate", - ["exponential", 1.5], - ["zoom"], - 10, - 1.125, - 18, - 32 - ], - "line-color": "hsl(213, 9%, 19%)" - } - }, - { - "id": "bridge-oneway-arrow-blue-navigation", - "type": "symbol", - "metadata": { - "mapbox:featureComponent": "road-network", - "mapbox:group": "Road network, bridges" - }, - "source": "composite", - "source-layer": "road", - "minzoom": 15, - "filter": [ - "all", - ["==", ["get", "structure"], "bridge"], - ["==", ["get", "oneway"], "true"], - [ - "step", - ["zoom"], - [ - "match", - ["get", "class"], - [ - "primary", - "secondary", - "tertiary", - "street", - "street_limited" - ], - true, - false - ], - 16, - [ - "match", - ["get", "class"], - [ - "primary", - "secondary", - "tertiary", - "street", - "street_limited", - "primary_link", - "secondary_link", - "tertiary_link", - "service", - "track" - ], - true, - false - ] - ] - ], - "layout": { - "symbol-placement": "line", - "symbol-spacing": [ - "interpolate", - ["linear"], - ["zoom"], - 15, - 400, - 18, - 600, - 22, - 1200 - ], - "icon-image": [ - "step", - ["zoom"], - "oneway-small", - 17, - [ - "match", - ["get", "class"], - [ - "primary", - "secondary", - "tertiary", - "street", - "street_limited" - ], - "oneway-large", - "oneway-small" - ], - 18, - "oneway-large" - ], - "icon-size": [ - "interpolate", - ["exponential", 1.5], - ["zoom"], - 15, - 0.25, - 20, - 1 - ], - "icon-rotation-alignment": "map" - }, - "paint": {} - }, - { - "id": "bridge-motorway-trunk-navigation", - "type": "line", - "metadata": { - "mapbox:featureComponent": "road-network", - "mapbox:group": "Road network, bridges" - }, - "source": "composite", - "source-layer": "road", - "minzoom": 13, - "filter": [ - "all", - ["==", ["get", "structure"], "bridge"], - ["match", ["get", "class"], ["motorway", "trunk"], true, false], - ["<=", ["get", "layer"], 1], - ["==", ["geometry-type"], "LineString"] - ], - "layout": {"line-cap": "round", "line-join": "round"}, - "paint": { - "line-width": [ - "interpolate", - ["exponential", 1.5], - ["zoom"], - 5, - 1.25, - 18, - 32 - ], - "line-color": [ - "match", - ["get", "class"], - "motorway", - "hsl(215, 28%, 48%)", - "hsl(221, 20%, 44%)" - ] - } - }, - { - "id": "bridge-major-link-2-case-navigation", - "type": "line", - "metadata": { - "mapbox:featureComponent": "road-network", - "mapbox:group": "Road network, bridges" - }, - "source": "composite", - "source-layer": "road", - "minzoom": 13, - "filter": [ - "all", - ["==", ["get", "structure"], "bridge"], - [">=", ["get", "layer"], 2], - [ - "match", - ["get", "class"], - ["motorway_link", "trunk_link"], - true, - false - ], - ["==", ["geometry-type"], "LineString"] - ], - "layout": {"line-join": "round"}, - "paint": { - "line-width": [ - "interpolate", - ["exponential", 1.2], - ["zoom"], - 10, - 0.75, - 18, - 2 - ], - "line-color": [ - "match", - ["get", "class"], - "motorway_link", - "hsl(215, 18%, 28%)", - "hsl(221, 13%, 28%)" - ], - "line-gap-width": [ - "interpolate", - ["exponential", 1.5], - ["zoom"], - 12, - 0.75, - 14, - 2, - 18, - 20 - ] - } - }, - { - "id": "bridge-motorway-trunk-2-case-navigation", - "type": "line", - "metadata": { - "mapbox:featureComponent": "road-network", - "mapbox:group": "Road network, bridges" - }, - "source": "composite", - "source-layer": "road", - "minzoom": 13, - "filter": [ - "all", - ["==", ["get", "structure"], "bridge"], - [">=", ["get", "layer"], 2], - ["match", ["get", "class"], ["motorway", "trunk"], true, false], - ["==", ["geometry-type"], "LineString"] - ], - "layout": {"line-join": "round"}, - "paint": { - "line-width": [ - "interpolate", - ["exponential", 1.2], - ["zoom"], - 10, - 1, - 18, - 2 - ], - "line-color": [ - "match", - ["get", "class"], - "motorway", - "hsl(215, 18%, 28%)", - "hsl(221, 13%, 28%)" - ], - "line-gap-width": [ - "interpolate", - ["exponential", 1.5], - ["zoom"], - 5, - 1.25, - 18, - 32 - ] - } - }, - { - "id": "bridge-major-link-2-navigation", - "type": "line", - "metadata": { - "mapbox:featureComponent": "road-network", - "mapbox:group": "Road network, bridges" - }, - "source": "composite", - "source-layer": "road", - "minzoom": 13, - "filter": [ - "all", - ["==", ["get", "structure"], "bridge"], - [">=", ["get", "layer"], 2], - [ - "match", - ["get", "class"], - ["motorway_link", "trunk_link"], - true, - false - ], - ["==", ["geometry-type"], "LineString"] - ], - "layout": {"line-cap": "round", "line-join": "round"}, - "paint": { - "line-width": [ - "interpolate", - ["exponential", 1.5], - ["zoom"], - 12, - 0.75, - 14, - 2, - 18, - 20 - ], - "line-color": [ - "match", - ["get", "class"], - "motorway_link", - "hsl(215, 28%, 48%)", - "hsl(221, 20%, 44%)" - ] - } - }, - { - "id": "bridge-motorway-trunk-2-navigation", - "type": "line", - "metadata": { - "mapbox:featureComponent": "road-network", - "mapbox:group": "Road network, bridges" - }, - "source": "composite", - "source-layer": "road", - "minzoom": 13, - "filter": [ - "all", - ["==", ["get", "structure"], "bridge"], - [">=", ["get", "layer"], 2], - ["match", ["get", "class"], ["motorway", "trunk"], true, false], - ["==", ["geometry-type"], "LineString"] - ], - "layout": {"line-cap": "round", "line-join": "round"}, - "paint": { - "line-width": [ - "interpolate", - ["exponential", 1.5], - ["zoom"], - 5, - 1.25, - 18, - 32 - ], - "line-color": [ - "match", - ["get", "class"], - "motorway", - "hsl(215, 28%, 48%)", - "hsl(221, 20%, 44%)" - ] - } - }, - { - "id": "bridge-oneway-arrow-white-navigation", - "type": "symbol", - "metadata": { - "mapbox:featureComponent": "road-network", - "mapbox:group": "Road network, bridges" - }, - "source": "composite", - "source-layer": "road", - "minzoom": 15, - "filter": [ - "all", - ["==", ["get", "structure"], "bridge"], - [ - "match", - ["get", "class"], - ["motorway", "trunk", "motorway_link", "trunk_link"], - true, - false - ], - ["==", ["get", "oneway"], "true"] - ], - "layout": { - "symbol-placement": "line", - "symbol-spacing": [ - "interpolate", - ["linear"], - ["zoom"], - 15, - 400, - 18, - 600, - 22, - 1200 - ], - "icon-image": [ - "step", - ["zoom"], - "oneway-white-small", - 17, - "oneway-white-large" - ], - "icon-size": [ - "interpolate", - ["exponential", 1.5], - ["zoom"], - 15, - 0.45, - 20, - 1 - ] - }, - "paint": {} - }, - { - "id": "bridge-rail", - "type": "line", - "metadata": { - "mapbox:featureComponent": "transit", - "mapbox:group": "Transit, bridges" - }, - "source": "composite", - "source-layer": "road", - "minzoom": 13, - "filter": [ - "all", - ["==", ["get", "structure"], "bridge"], - [ - "match", - ["get", "class"], - ["major_rail", "minor_rail"], - true, - false - ] - ], - "layout": {"line-join": "round"}, - "paint": { - "line-color": [ - "interpolate", - ["linear"], - ["zoom"], - 13, - "hsl(229, 24%, 26%)", - 16, - "hsl(217, 0%, 47%)" - ], - "line-width": [ - "interpolate", - ["exponential", 1.5], - ["zoom"], - 14, - 0.5, - 20, - 1 - ] - } - }, - { - "id": "bridge-rail-tracks", - "type": "line", - "metadata": { - "mapbox:featureComponent": "transit", - "mapbox:group": "Transit, bridges" - }, - "source": "composite", - "source-layer": "road", - "minzoom": 13, - "filter": [ - "all", - ["==", ["get", "structure"], "bridge"], - [ - "match", - ["get", "class"], - ["major_rail", "minor_rail"], - true, - false - ] - ], - "layout": {"line-join": "round"}, - "paint": { - "line-color": [ - "interpolate", - ["linear"], - ["zoom"], - 13, - "hsl(229, 24%, 26%)", - 16, - "hsl(217, 0%, 47%)" - ], - "line-width": [ - "interpolate", - ["exponential", 1.5], - ["zoom"], - 14, - 4, - 20, - 8 - ], - "line-dasharray": [0.1, 15], - "line-opacity": [ - "interpolate", - ["linear"], - ["zoom"], - 13.75, - 0, - 14, - 1 - ] - } - }, - { - "id": "aerialway", - "type": "line", - "metadata": { - "mapbox:featureComponent": "transit", - "mapbox:group": "Transit, elevated" - }, - "source": "composite", - "source-layer": "road", - "minzoom": 12, - "filter": ["==", ["get", "class"], "aerialway"], - "layout": {"line-join": "round"}, - "paint": { - "line-color": "hsl(217, 0%, 47%)", - "line-width": [ - "interpolate", - ["exponential", 1.5], - ["zoom"], - 14, - 0.5, - 20, - 1 - ] - } - }, - { - "id": "admin-1-boundary-bg", - "type": "line", - "metadata": { - "mapbox:featureComponent": "admin-boundaries", - "mapbox:group": "Administrative boundaries, admin" - }, - "source": "composite", - "source-layer": "admin", - "minzoom": 7, - "filter": [ - "all", - ["==", ["get", "admin_level"], 1], - ["==", ["get", "maritime"], "false"], - ["match", ["get", "worldview"], ["all", "US"], true, false] - ], - "layout": {"line-join": "bevel"}, - "paint": { - "line-color": [ - "interpolate", - ["linear"], - ["zoom"], - 8, - "hsl(214, 17%, 31%)", - 16, - "hsl(250, 30%, 46%)" - ], - "line-width": [ - "interpolate", - ["linear"], - ["zoom"], - 7, - 3.75, - 12, - 5.5 - ], - "line-opacity": [ - "interpolate", - ["linear"], - ["zoom"], - 7, - 0, - 8, - 0.75 - ], - "line-dasharray": [1, 0], - "line-blur": ["interpolate", ["linear"], ["zoom"], 3, 0, 8, 3] - } - }, - { - "id": "admin-0-boundary-bg", - "type": "line", - "metadata": { - "mapbox:featureComponent": "admin-boundaries", - "mapbox:group": "Administrative boundaries, admin" - }, - "source": "composite", - "source-layer": "admin", - "minzoom": 1, - "filter": [ - "all", - ["==", ["get", "admin_level"], 0], - ["==", ["get", "maritime"], "false"], - ["match", ["get", "worldview"], ["all", "US"], true, false] - ], - "layout": {}, - "paint": { - "line-width": [ - "interpolate", - ["linear"], - ["zoom"], - 3, - 3.5, - 10, - 8 - ], - "line-color": [ - "interpolate", - ["linear"], - ["zoom"], - 6, - "hsl(214, 17%, 31%)", - 8, - "hsl(250, 30%, 46%)" - ], - "line-opacity": [ - "interpolate", - ["linear"], - ["zoom"], - 3, - 0, - 4, - 0.5 - ], - "line-blur": ["interpolate", ["linear"], ["zoom"], 3, 0, 10, 2] - } - }, - { - "id": "admin-1-boundary", - "type": "line", - "metadata": { - "mapbox:featureComponent": "admin-boundaries", - "mapbox:group": "Administrative boundaries, admin" - }, - "source": "composite", - "source-layer": "admin", - "minzoom": 2, - "filter": [ - "all", - ["==", ["get", "admin_level"], 1], - ["==", ["get", "maritime"], "false"], - ["match", ["get", "worldview"], ["all", "US"], true, false] - ], - "layout": {"line-join": "round", "line-cap": "round"}, - "paint": { - "line-dasharray": [ - "step", - ["zoom"], - ["literal", [2, 0]], - 7, - ["literal", [2, 2, 6, 2]] - ], - "line-width": [ - "interpolate", - ["linear"], - ["zoom"], - 7, - 0.75, - 12, - 1.5 - ], - "line-opacity": [ - "interpolate", - ["linear"], - ["zoom"], - 2, - 0, - 3, - 1 - ], - "line-color": [ - "interpolate", - ["linear"], - ["zoom"], - 3, - "hsl(250, 6%, 60%)", - 7, - "hsl(250, 10%, 70%)" - ] - } - }, - { - "id": "admin-0-boundary", - "type": "line", - "metadata": { - "mapbox:featureComponent": "admin-boundaries", - "mapbox:group": "Administrative boundaries, admin" - }, - "source": "composite", - "source-layer": "admin", - "minzoom": 1, - "filter": [ - "all", - ["==", ["get", "admin_level"], 0], - ["==", ["get", "disputed"], "false"], - ["==", ["get", "maritime"], "false"], - ["match", ["get", "worldview"], ["all", "US"], true, false] - ], - "layout": {"line-join": "round", "line-cap": "round"}, - "paint": { - "line-color": "hsl(250, 10%, 65%)", - "line-width": [ - "interpolate", - ["linear"], - ["zoom"], - 3, - 0.5, - 10, - 2 - ], - "line-dasharray": [10, 0] - } - }, - { - "id": "admin-0-boundary-disputed", - "type": "line", - "metadata": { - "mapbox:featureComponent": "admin-boundaries", - "mapbox:group": "Administrative boundaries, admin" - }, - "source": "composite", - "source-layer": "admin", - "minzoom": 1, - "filter": [ - "all", - ["==", ["get", "disputed"], "true"], - ["==", ["get", "admin_level"], 0], - ["==", ["get", "maritime"], "false"], - ["match", ["get", "worldview"], ["all", "US"], true, false] - ], - "layout": {"line-join": "round"}, - "paint": { - "line-color": "hsl(250, 10%, 65%)", - "line-width": [ - "interpolate", - ["linear"], - ["zoom"], - 3, - 0.5, - 10, - 2 - ], - "line-dasharray": [ - "step", - ["zoom"], - ["literal", [3.25, 3.25]], - 6, - ["literal", [2.5, 2.5]], - 7, - ["literal", [2, 2.25]], - 8, - ["literal", [1.75, 2]] - ] - } - }, - { - "id": "road-intersection", - "type": "symbol", - "metadata": { - "mapbox:featureComponent": "road-network", - "mapbox:group": "Road network, road-labels" - }, - "source": "composite", - "source-layer": "road", - "minzoom": 15, - "filter": [ - "all", - ["==", ["get", "class"], "intersection"], - ["has", "name"] - ], - "layout": { - "text-field": ["coalesce", ["get", "name_en"], ["get", "name"]], - "icon-image": "intersection", - "icon-text-fit": "both", - "icon-text-fit-padding": [1, 2, 1, 2], - "text-size": 12, - "text-font": ["DIN Pro Bold", "Arial Unicode MS Bold"] - }, - "paint": {"text-color": "hsl(230, 57%, 52%)"} - }, - { - "id": "road-label-navigation", - "type": "symbol", - "metadata": { - "mapbox:featureComponent": "road-network", - "mapbox:group": "Road network, road-labels" - }, - "source": "composite", - "source-layer": "road", - "minzoom": 13, - "filter": [ - "step", - ["zoom"], - [ - "match", - ["get", "class"], - ["motorway", "trunk", "primary", "secondary", "tertiary"], - true, - false - ], - 15.25, - [ - "match", - ["get", "class"], - [ - "motorway", - "trunk", - "primary", - "secondary", - "tertiary", - "street" - ], - true, - false - ], - 16, - [ - "match", - ["get", "class"], - [ - "motorway", - "trunk", - "primary", - "secondary", - "tertiary", - "street", - "street_limited" - ], - true, - false - ], - 16.5, - [ - "match", - ["get", "class"], - ["pedestrian", "golf", "ferry", "aerialway", "path"], - false, - true - ] - ], - "layout": { - "text-size": [ - "interpolate", - ["linear"], - ["zoom"], - 10, - [ - "match", - ["get", "class"], - [ - "motorway", - "trunk", - "primary", - "secondary", - "tertiary" - ], - 11, - [ - "motorway_link", - "trunk_link", - "primary_link", - "secondary_link", - "tertiary_link", - "street", - "street_limited" - ], - 8.8, - 7.15 - ], - 18, - [ - "match", - ["get", "class"], - [ - "motorway", - "trunk", - "primary", - "secondary", - "tertiary" - ], - 17.6, - [ - "motorway_link", - "trunk_link", - "primary_link", - "secondary_link", - "tertiary_link", - "street", - "street_limited" - ], - 15.400000000000002, - 14.3 - ], - 22, - [ - "match", - ["get", "class"], - [ - "motorway", - "trunk", - "primary", - "secondary", - "tertiary" - ], - 55.00000000000001, - [ - "motorway_link", - "trunk_link", - "primary_link", - "secondary_link", - "tertiary_link", - "street", - "street_limited" - ], - 44, - 33 - ] - ], - "text-max-angle": 30, - "symbol-spacing": [ - "interpolate", - ["linear"], - ["zoom"], - 10, - 150, - 18, - 450, - 22, - 1500 - ], - "text-font": ["DIN Pro Medium", "Arial Unicode MS Regular"], - "symbol-placement": "line", - "text-padding": 1, - "text-rotation-alignment": "map", - "text-pitch-alignment": "viewport", - "text-field": ["coalesce", ["get", "name_en"], ["get", "name"]], - "text-letter-spacing": 0.01 - }, - "paint": { - "text-color": "hsl(0, 0%, 90%)", - "text-halo-color": [ - "match", - ["get", "class"], - ["motorway", "trunk"], - "hsla(214, 24%, 30%, 0.75)", - "hsl(213, 9%, 19%)" - ], - "text-halo-width": 1, - "text-halo-blur": 1 - } - }, - { - "id": "road-number-shield-navigation", - "type": "symbol", - "metadata": { - "mapbox:featureComponent": "road-network", - "mapbox:group": "Road network, road-labels" - }, - "source": "composite", - "source-layer": "road", - "minzoom": 6, - "filter": [ - "all", - ["has", "reflen"], - ["<=", ["get", "reflen"], 6], - [ - "step", - ["zoom"], - ["==", ["geometry-type"], "Point"], - 11, - [">", ["get", "len"], 5000], - 12, - [">", ["get", "len"], 2500], - 13, - [">", ["get", "len"], 1000], - 14, - true - ] - ], - "layout": { - "text-size": [ - "interpolate", - ["exponential", 1.5], - ["zoom"], - 6, - 14, - 22, - 26 - ], - "icon-image": [ - "concat", - ["get", "shield"], - "-", - ["to-string", ["get", "reflen"]] - ], - "icon-rotation-alignment": "viewport", - "text-max-angle": 38, - "symbol-spacing": [ - "interpolate", - ["linear"], - ["zoom"], - 11, - 400, - 14, - 600, - 16, - 800, - 22, - 1200 - ], - "text-font": ["DIN Pro Bold", "Arial Unicode MS Bold"], - "symbol-placement": ["step", ["zoom"], "point", 11, "line"], - "text-rotation-alignment": "viewport", - "icon-size": [ - "interpolate", - ["exponential", 1.5], - ["zoom"], - 6, - 0.5, - 13, - 0.5, - 22, - 1 - ], - "text-field": ["get", "ref"], - "text-letter-spacing": 0.05 - }, - "paint": { - "text-color": [ - "match", - ["get", "shield_text_color"], - "white", - "hsl(0, 0%, 78%)", - "yellow", - "hsl(50, 100%, 54%)", - "orange", - "hsl(25, 100%, 61%)", - "blue", - "hsl(230, 57%, 32%)", - "hsl(230, 18%, 10%)" - ] - } - }, - { - "id": "road-exit-shield-navigation", - "type": "symbol", - "metadata": { - "mapbox:featureComponent": "road-network", - "mapbox:group": "Road network, road-labels" - }, - "source": "composite", - "source-layer": "motorway_junction", - "minzoom": 14, - "filter": ["all", ["has", "reflen"], ["<=", ["get", "reflen"], 9]], - "layout": { - "text-field": ["get", "ref"], - "text-size": [ - "interpolate", - ["exponential", 1.5], - ["zoom"], - 6, - 14, - 22, - 26 - ], - "text-font": ["DIN Pro Bold", "Arial Unicode MS Bold"], - "icon-image": [ - "concat", - "motorway-exit-", - ["to-string", ["get", "reflen"]] - ], - "icon-size": [ - "interpolate", - ["exponential", 1.5], - ["zoom"], - 6, - 0.5, - 13, - 0.5, - 22, - 1 - ] - }, - "paint": {"text-color": "hsl(0, 0%, 78%)"} - }, - { - "id": "ferry-aerialway-label", - "type": "symbol", - "metadata": { - "mapbox:featureComponent": "transit", - "mapbox:group": "Transit, ferry-aerialway-labels" - }, - "source": "composite", - "source-layer": "road", - "minzoom": 15, - "filter": [ - "match", - ["get", "class"], - "aerialway", - true, - "ferry", - true, - false - ], - "layout": { - "text-size": [ - "interpolate", - ["linear"], - ["zoom"], - 10, - 8.450000000000001, - 18, - 16.900000000000002 - ], - "text-max-angle": 30, - "text-font": ["DIN Pro Regular", "Arial Unicode MS Regular"], - "symbol-placement": "line", - "text-padding": 1, - "text-rotation-alignment": "map", - "text-pitch-alignment": "viewport", - "text-field": ["coalesce", ["get", "name_en"], ["get", "name"]], - "text-letter-spacing": 0.01 - }, - "paint": { - "text-color": [ - "match", - ["get", "class"], - "ferry", - "hsl(197, 0%, 76%)", - "hsl(0, 0%, 90%)" - ], - "text-halo-color": [ - "match", - ["get", "class"], - "ferry", - "hsl(197, 15%, 43%)", - "hsl(214, 24%, 30%)" - ], - "text-halo-width": 1, - "text-halo-blur": 1 - } - }, - { - "id": "waterway-label", - "type": "symbol", - "metadata": { - "mapbox:featureComponent": "natural-features", - "mapbox:group": "Natural features, natural-labels" - }, - "source": "composite", - "source-layer": "natural_label", - "minzoom": 13, - "filter": [ - "all", - [ - "match", - ["get", "class"], - ["canal", "river", "stream"], - ["match", ["get", "worldview"], ["all", "US"], true, false], - ["disputed_canal", "disputed_river", "disputed_stream"], - [ - "all", - ["==", ["get", "disputed"], "true"], - [ - "match", - ["get", "worldview"], - ["all", "US"], - true, - false - ] - ], - false - ], - ["==", ["geometry-type"], "LineString"] - ], - "layout": { - "text-font": ["DIN Pro Italic", "Arial Unicode MS Regular"], - "text-max-angle": 30, - "symbol-spacing": [ - "interpolate", - ["linear", 1], - ["zoom"], - 15, - 250, - 17, - 400 - ], - "text-size": [ - "interpolate", - ["linear"], - ["zoom"], - 13, - 12, - 18, - 16 - ], - "symbol-placement": "line", - "text-pitch-alignment": "viewport", - "text-field": ["coalesce", ["get", "name_en"], ["get", "name"]] - }, - "paint": {"text-color": "hsl(197, 0%, 90%)"} - }, - { - "id": "natural-line-label", - "type": "symbol", - "metadata": { - "mapbox:featureComponent": "natural-features", - "mapbox:group": "Natural features, natural-labels" - }, - "source": "composite", - "source-layer": "natural_label", - "minzoom": 4, - "filter": [ - "all", - [ - "match", - ["get", "class"], - ["glacier", "landform"], - ["match", ["get", "worldview"], ["all", "US"], true, false], - ["disputed_glacier", "disputed_landform"], - [ - "all", - ["==", ["get", "disputed"], "true"], - [ - "match", - ["get", "worldview"], - ["all", "US"], - true, - false - ] - ], - false - ], - ["==", ["geometry-type"], "LineString"], - ["<=", ["get", "filterrank"], 0] - ], - "layout": { - "text-size": [ - "step", - ["zoom"], - ["step", ["get", "sizerank"], 19.8, 5, 13.200000000000001], - 17, - ["step", ["get", "sizerank"], 19.8, 13, 13.200000000000001] - ], - "text-max-angle": 30, - "text-field": ["coalesce", ["get", "name_en"], ["get", "name"]], - "text-font": ["DIN Pro Medium", "Arial Unicode MS Regular"], - "symbol-placement": "line-center", - "text-pitch-alignment": "viewport" - }, - "paint": { - "text-halo-width": 0.5, - "text-halo-color": "hsl(214, 24%, 30%)", - "text-halo-blur": 0.5, - "text-color": [ - "step", - ["zoom"], - [ - "step", - ["get", "sizerank"], - "hsl(236, 1%, 100%)", - 5, - "hsl(236, 6%, 98%)" - ], - 17, - [ - "step", - ["get", "sizerank"], - "hsl(236, 1%, 100%)", - 13, - "hsl(236, 6%, 98%)" - ] - ] - } - }, - { - "id": "natural-point-label", - "type": "symbol", - "metadata": { - "mapbox:featureComponent": "natural-features", - "mapbox:group": "Natural features, natural-labels" - }, - "source": "composite", - "source-layer": "natural_label", - "minzoom": 4, - "filter": [ - "all", - [ - "match", - ["get", "class"], - ["dock", "glacier", "landform", "water_feature", "wetland"], - ["match", ["get", "worldview"], ["all", "US"], true, false], - [ - "disputed_dock", - "disputed_glacier", - "disputed_landform", - "disputed_water_feature", - "disputed_wetland" - ], - [ - "all", - ["==", ["get", "disputed"], "true"], - [ - "match", - ["get", "worldview"], - ["all", "US"], - true, - false - ] - ], - false - ], - ["==", ["geometry-type"], "Point"], - ["<=", ["get", "filterrank"], 0] - ], - "layout": { - "text-size": [ - "step", - ["zoom"], - ["step", ["get", "sizerank"], 19.8, 5, 13.200000000000001], - 17, - ["step", ["get", "sizerank"], 19.8, 13, 13.200000000000001] - ], - "icon-image": [ - "step", - ["zoom"], - ["concat", ["get", "maki"], "-11"], - 15, - ["concat", ["get", "maki"], "-15"] - ], - "text-font": ["DIN Pro Medium", "Arial Unicode MS Regular"], - "text-offset": [ - "step", - ["zoom"], - [ - "step", - ["get", "sizerank"], - ["literal", [0, 0]], - 5, - ["literal", [0, 0.75]] - ], - 17, - [ - "step", - ["get", "sizerank"], - ["literal", [0, 0]], - 13, - ["literal", [0, 0.75]] - ] - ], - "text-anchor": [ - "step", - ["zoom"], - ["step", ["get", "sizerank"], "center", 5, "top"], - 17, - ["step", ["get", "sizerank"], "center", 13, "top"] - ], - "text-field": ["coalesce", ["get", "name_en"], ["get", "name"]] - }, - "paint": { - "icon-opacity": [ - "step", - ["zoom"], - ["step", ["get", "sizerank"], 0, 5, 1], - 17, - ["step", ["get", "sizerank"], 0, 13, 1] - ], - "text-halo-color": "hsl(214, 24%, 30%)", - "text-halo-width": 0.5, - "text-halo-blur": 0.5, - "text-color": [ - "step", - ["zoom"], - [ - "step", - ["get", "sizerank"], - "hsl(236, 1%, 100%)", - 5, - "hsl(236, 6%, 98%)" - ], - 17, - [ - "step", - ["get", "sizerank"], - "hsl(236, 1%, 100%)", - 13, - "hsl(236, 6%, 98%)" - ] - ] - } - }, - { - "id": "water-line-label", - "type": "symbol", - "metadata": { - "mapbox:featureComponent": "natural-features", - "mapbox:group": "Natural features, natural-labels" - }, - "source": "composite", - "source-layer": "natural_label", - "filter": [ - "all", - [ - "match", - ["get", "class"], - ["bay", "ocean", "reservoir", "sea", "water"], - ["match", ["get", "worldview"], ["all", "US"], true, false], - [ - "disputed_bay", - "disputed_ocean", - "disputed_reservoir", - "disputed_sea", - "disputed_water" - ], - [ - "all", - ["==", ["get", "disputed"], "true"], - [ - "match", - ["get", "worldview"], - ["all", "US"], - true, - false - ] - ], - false - ], - ["==", ["geometry-type"], "LineString"] - ], - "layout": { - "text-size": [ - "interpolate", - ["linear"], - ["zoom"], - 7, - ["step", ["get", "sizerank"], 20, 6, 18, 12, 12], - 10, - ["step", ["get", "sizerank"], 15, 9, 12], - 18, - ["step", ["get", "sizerank"], 15, 9, 14] - ], - "text-max-angle": 30, - "text-letter-spacing": [ - "match", - ["get", "class"], - "ocean", - 0.25, - ["sea", "bay"], - 0.15, - 0 - ], - "text-font": ["DIN Pro Italic", "Arial Unicode MS Regular"], - "symbol-placement": "line-center", - "text-pitch-alignment": "viewport", - "text-field": ["coalesce", ["get", "name_en"], ["get", "name"]] - }, - "paint": { - "text-color": [ - "match", - ["get", "class"], - ["bay", "ocean", "sea"], - "hsl(197, 11%, 63%)", - "hsl(197, 0%, 90%)" - ] - } - }, - { - "id": "water-point-label", - "type": "symbol", - "metadata": { - "mapbox:featureComponent": "natural-features", - "mapbox:group": "Natural features, natural-labels" - }, - "source": "composite", - "source-layer": "natural_label", - "filter": [ - "all", - [ - "match", - ["get", "class"], - ["bay", "ocean", "reservoir", "sea", "water"], - ["match", ["get", "worldview"], ["all", "US"], true, false], - [ - "disputed_bay", - "disputed_ocean", - "disputed_reservoir", - "disputed_sea", - "disputed_water" - ], - [ - "all", - ["==", ["get", "disputed"], "true"], - [ - "match", - ["get", "worldview"], - ["all", "US"], - true, - false - ] - ], - false - ], - ["==", ["geometry-type"], "Point"] - ], - "layout": { - "text-line-height": 1.3, - "text-size": [ - "interpolate", - ["linear"], - ["zoom"], - 7, - ["step", ["get", "sizerank"], 20, 6, 15, 12, 12], - 10, - ["step", ["get", "sizerank"], 15, 9, 12] - ], - "text-font": ["DIN Pro Italic", "Arial Unicode MS Regular"], - "text-field": ["coalesce", ["get", "name_en"], ["get", "name"]], - "text-letter-spacing": [ - "match", - ["get", "class"], - "ocean", - 0.25, - ["bay", "sea"], - 0.15, - 0.01 - ], - "text-max-width": [ - "match", - ["get", "class"], - "ocean", - 4, - "sea", - 5, - ["bay", "water"], - 7, - 10 - ] - }, - "paint": { - "text-color": [ - "match", - ["get", "class"], - ["bay", "ocean", "sea"], - "hsl(197, 11%, 63%)", - "hsl(197, 0%, 90%)" - ] - } - }, - { - "id": "poi-label", - "type": "symbol", - "metadata": { - "mapbox:featureComponent": "point-of-interest-labels", - "mapbox:group": "Point of interest labels, poi-labels" - }, - "source": "composite", - "source-layer": "poi_label", - "minzoom": 6, - "filter": [ - "<=", - ["get", "filterrank"], - [ - "+", - ["step", ["zoom"], 0, 16, 1, 17, 2], - [ - "match", - ["get", "class"], - "education", - 1, - "landmark", - 2, - "medical", - 1, - "motorist", - 3, - "park_like", - 1, - 0 - ] - ] - ], - "layout": { - "text-size": [ - "step", - ["zoom"], - ["step", ["get", "sizerank"], 19.8, 5, 13.200000000000001], - 17, - ["step", ["get", "sizerank"], 19.8, 13, 13.200000000000001] - ], - "icon-image": [ - "step", - ["zoom"], - [ - "case", - ["has", "maki_beta"], - [ - "coalesce", - ["image", ["concat", ["get", "maki_beta"], "-11"]], - ["image", ["concat", ["get", "maki"], "-11"]] - ], - ["image", ["concat", ["get", "maki"], "-11"]] - ], - 15, - [ - "case", - ["has", "maki_beta"], - [ - "coalesce", - ["image", ["concat", ["get", "maki_beta"], "-15"]], - ["image", ["concat", ["get", "maki"], "-15"]] - ], - ["image", ["concat", ["get", "maki"], "-15"]] - ] - ], - "text-font": ["DIN Pro Medium", "Arial Unicode MS Regular"], - "text-offset": [ - "step", - ["zoom"], - [ - "step", - ["get", "sizerank"], - ["literal", [0, 0]], - 5, - ["literal", [0, 0.75]] - ], - 17, - [ - "step", - ["get", "sizerank"], - ["literal", [0, 0]], - 13, - ["literal", [0, 0.75]] - ] - ], - "text-anchor": [ - "step", - ["zoom"], - ["step", ["get", "sizerank"], "center", 5, "top"], - 17, - ["step", ["get", "sizerank"], "center", 13, "top"] - ], - "text-field": ["coalesce", ["get", "name_en"], ["get", "name"]] - }, - "paint": { - "icon-opacity": [ - "step", - ["zoom"], - ["step", ["get", "sizerank"], 0, 5, 1], - 17, - ["step", ["get", "sizerank"], 0, 13, 1] - ], - "text-halo-color": [ - "match", - ["get", "class"], - "park_like", - "hsl(170, 23%, 17%)", - "education", - "hsl(200, 20%, 14%)", - "medical", - "hsl(252, 12%, 19%)", - "hsl(214, 24%, 30%)" - ], - "text-halo-width": 0.5, - "text-halo-blur": 0.5, - "text-color": [ - "step", - ["zoom"], - [ - "step", - ["get", "sizerank"], - [ - "match", - ["get", "class"], - "food_and_drink", - "hsl(232, 13%, 100%)", - "park_like", - "hsl(150, 52%, 77%)", - "education", - "hsl(200, 4%, 100%)", - "medical", - "hsl(252, 2%, 94%)", - "hsl(236, 1%, 100%)" - ], - 5, - [ - "match", - ["get", "class"], - "food_and_drink", - "hsl(232, 20%, 100%)", - "park_like", - "hsl(150, 51%, 66%)", - "education", - "hsl(200, 10%, 100%)", - "medical", - "hsl(252, 2%, 99%)", - "hsl(236, 6%, 98%)" - ] - ], - 17, - [ - "step", - ["get", "sizerank"], - [ - "match", - ["get", "class"], - "food_and_drink", - "hsl(232, 13%, 100%)", - "park_like", - "hsl(150, 52%, 77%)", - "education", - "hsl(200, 4%, 100%)", - "medical", - "hsl(252, 2%, 94%)", - "hsl(236, 1%, 100%)" - ], - 13, - [ - "match", - ["get", "class"], - "food_and_drink", - "hsl(232, 20%, 100%)", - "park_like", - "hsl(150, 51%, 66%)", - "education", - "hsl(200, 10%, 100%)", - "medical", - "hsl(252, 2%, 99%)", - "hsl(236, 6%, 98%)" - ] - ] - ] - } - }, - { - "id": "airport-label", - "type": "symbol", - "metadata": { - "mapbox:featureComponent": "transit", - "mapbox:group": "Transit, transit-labels" - }, - "source": "composite", - "source-layer": "airport_label", - "minzoom": 8, - "filter": [ - "match", - ["get", "class"], - ["military", "civil"], - ["match", ["get", "worldview"], ["all", "US"], true, false], - ["disputed_military", "disputed_civil"], - [ - "all", - ["==", ["get", "disputed"], "true"], - ["match", ["get", "worldview"], ["all", "US"], true, false] - ], - false - ], - "layout": { - "text-line-height": 1.1, - "text-size": [ - "step", - ["get", "sizerank"], - 19.8, - 9, - 13.200000000000001 - ], - "icon-image": [ - "step", - ["get", "sizerank"], - ["concat", ["get", "maki"], "-15"], - 9, - ["concat", ["get", "maki"], "-11"] - ], - "text-font": ["DIN Pro Medium", "Arial Unicode MS Regular"], - "text-offset": [0, 0.75], - "text-rotation-alignment": "viewport", - "text-anchor": "top", - "text-field": [ - "step", - ["get", "sizerank"], - ["coalesce", ["get", "name_en"], ["get", "name"]], - 15, - ["get", "ref"] - ], - "text-letter-spacing": 0.01, - "text-max-width": 9 - }, - "paint": { - "text-color": "hsl(230, 18%, 100%)", - "text-halo-color": "hsl(230, 39%, 19%)", - "text-halo-width": 1 - } - }, - { - "id": "settlement-subdivision-label", - "type": "symbol", - "metadata": { - "mapbox:featureComponent": "place-labels", - "mapbox:group": "Place labels, place-labels" - }, - "source": "composite", - "source-layer": "place_label", - "minzoom": 10, - "maxzoom": 15, - "filter": [ - "all", - [ - "match", - ["get", "class"], - "settlement_subdivision", - ["match", ["get", "worldview"], ["all", "US"], true, false], - "disputed_settlement_subdivision", - [ - "all", - ["==", ["get", "disputed"], "true"], - [ - "match", - ["get", "worldview"], - ["all", "US"], - true, - false - ] - ], - false - ], - ["<=", ["get", "filterrank"], 3] - ], - "layout": { - "text-field": ["coalesce", ["get", "name_en"], ["get", "name"]], - "text-transform": "uppercase", - "text-font": ["DIN Pro Medium", "Arial Unicode MS Regular"], - "text-letter-spacing": [ - "match", - ["get", "type"], - "suburb", - 0.15, - 0.1 - ], - "text-max-width": 7, - "text-padding": 3, - "text-size": [ - "interpolate", - ["cubic-bezier", 0.5, 0, 1, 1], - ["zoom"], - 11, - [ - "match", - ["get", "type"], - "suburb", - 12.100000000000001, - 11.55 - ], - 15, - [ - "match", - ["get", "type"], - "suburb", - 16.5, - 15.400000000000002 - ] - ] - }, - "paint": { - "text-halo-color": "hsla(214, 24%, 30%, 0.75)", - "text-halo-width": 1, - "text-color": "hsl(215, 30%, 85%)", - "text-halo-blur": 0.5 - } - }, - { - "id": "settlement-minor-label", - "type": "symbol", - "metadata": { - "mapbox:featureComponent": "place-labels", - "mapbox:group": "Place labels, place-labels" - }, - "source": "composite", - "source-layer": "place_label", - "minzoom": 3, - "maxzoom": 13, - "filter": [ - "all", - ["<=", ["get", "filterrank"], 2], - [ - "match", - ["get", "class"], - "settlement", - ["match", ["get", "worldview"], ["all", "US"], true, false], - "disputed_settlement", - [ - "all", - ["==", ["get", "disputed"], "true"], - [ - "match", - ["get", "worldview"], - ["all", "US"], - true, - false - ] - ], - false - ], - [ - "step", - ["zoom"], - [">", ["get", "symbolrank"], 6], - 4, - [">=", ["get", "symbolrank"], 7], - 6, - [">=", ["get", "symbolrank"], 8], - 7, - [">=", ["get", "symbolrank"], 10], - 10, - [">=", ["get", "symbolrank"], 11], - 11, - [">=", ["get", "symbolrank"], 13], - 12, - [">=", ["get", "symbolrank"], 15] - ] - ], - "layout": { - "text-line-height": 1.1, - "text-size": [ - "interpolate", - ["cubic-bezier", 0.2, 0, 0.9, 1], - ["zoom"], - 3, - [ - "step", - ["get", "symbolrank"], - 13.200000000000001, - 9, - 12.100000000000001, - 10, - 11.55, - 12, - 10.450000000000001, - 14, - 9.350000000000001, - 16, - 7.15, - 17, - 4.4 - ], - 13, - [ - "step", - ["get", "symbolrank"], - 25.3, - 9, - 23.1, - 10, - 20.900000000000002, - 11, - 18.700000000000003, - 12, - 17.6, - 13, - 16.5, - 15, - 14.3 - ] - ], - "text-radial-offset": [ - "step", - ["zoom"], - ["match", ["get", "capital"], 2, 0.6, 0.55], - 8, - 0 - ], - "icon-image": [ - "step", - ["zoom"], - [ - "case", - ["==", ["get", "capital"], 2], - "border-dot-13", - [ - "step", - ["get", "symbolrank"], - "dot-11", - 9, - "dot-10", - 11, - "dot-9" - ] - ], - 8, - "" - ], - "text-font": ["DIN Pro Regular", "Arial Unicode MS Regular"], - "text-justify": "auto", - "text-anchor": [ - "step", - ["zoom"], - ["get", "text_anchor"], - 8, - "center" - ], - "text-field": ["coalesce", ["get", "name_en"], ["get", "name"]], - "text-max-width": 7 - }, - "paint": { - "text-color": "hsl(215, 30%, 75%)", - "text-halo-color": "hsl(214, 24%, 30%)", - "text-halo-width": 1, - "text-halo-blur": 1 - } - }, - { - "id": "settlement-major-label", - "type": "symbol", - "metadata": { - "mapbox:featureComponent": "place-labels", - "mapbox:group": "Place labels, place-labels" - }, - "source": "composite", - "source-layer": "place_label", - "minzoom": 3, - "maxzoom": 15, - "filter": [ - "all", - ["<=", ["get", "filterrank"], 2], - [ - "match", - ["get", "class"], - "settlement", - ["match", ["get", "worldview"], ["all", "US"], true, false], - "disputed_settlement", - [ - "all", - ["==", ["get", "disputed"], "true"], - [ - "match", - ["get", "worldview"], - ["all", "US"], - true, - false - ] - ], - false - ], - [ - "step", - ["zoom"], - false, - 3, - ["<=", ["get", "symbolrank"], 6], - 4, - ["<", ["get", "symbolrank"], 7], - 6, - ["<", ["get", "symbolrank"], 8], - 7, - ["<", ["get", "symbolrank"], 10], - 10, - ["<", ["get", "symbolrank"], 11], - 11, - ["<", ["get", "symbolrank"], 13], - 12, - ["<", ["get", "symbolrank"], 15], - 13, - [">=", ["get", "symbolrank"], 11], - 14, - [">=", ["get", "symbolrank"], 15] - ] - ], - "layout": { - "text-line-height": 1.1, - "text-size": [ - "interpolate", - ["cubic-bezier", 0.2, 0, 0.9, 1], - ["zoom"], - 3, - [ - "step", - ["get", "symbolrank"], - 16.900000000000002, - 6, - 15.600000000000001 - ], - 6, - ["step", ["get", "symbolrank"], 20.8, 6, 19.5, 7, 18.2], - 8, - [ - "step", - ["get", "symbolrank"], - 23.400000000000002, - 9, - 22.1, - 10, - 19.5 - ], - 15, - [ - "step", - ["get", "symbolrank"], - 29.900000000000002, - 9, - 28.6, - 10, - 26, - 11, - 23.400000000000002, - 12, - 20.8, - 13, - 19.5, - 15, - 16.900000000000002 - ] - ], - "text-radial-offset": [ - "step", - ["zoom"], - ["match", ["get", "capital"], 2, 0.6, 0.55], - 8, - 0 - ], - "icon-image": [ - "step", - ["zoom"], - [ - "case", - ["==", ["get", "capital"], 2], - "border-dot-13", - [ - "step", - ["get", "symbolrank"], - "dot-11", - 9, - "dot-10", - 11, - "dot-9" - ] - ], - 8, - "" - ], - "text-font": ["DIN Pro Medium", "Arial Unicode MS Regular"], - "text-justify": [ - "step", - ["zoom"], - [ - "match", - ["get", "text_anchor"], - ["left", "bottom-left", "top-left"], - "left", - ["right", "bottom-right", "top-right"], - "right", - "center" - ], - 8, - "center" - ], - "text-anchor": [ - "step", - ["zoom"], - ["get", "text_anchor"], - 8, - "center" - ], - "text-field": ["coalesce", ["get", "name_en"], ["get", "name"]], - "text-max-width": 7 - }, - "paint": { - "text-color": "hsl(215, 30%, 75%)", - "text-halo-color": "hsl(214, 24%, 30%)", - "text-halo-width": 1, - "text-halo-blur": 1 - } - }, - { - "id": "state-label", - "type": "symbol", - "metadata": { - "mapbox:featureComponent": "place-labels", - "mapbox:group": "Place labels, place-labels" - }, - "source": "composite", - "source-layer": "place_label", - "minzoom": 3, - "maxzoom": 9, - "filter": [ - "match", - ["get", "class"], - "state", - ["match", ["get", "worldview"], ["all", "US"], true, false], - "disputed_state", - [ - "all", - ["==", ["get", "disputed"], "true"], - ["match", ["get", "worldview"], ["all", "US"], true, false] - ], - false - ], - "layout": { - "text-size": [ - "interpolate", - ["cubic-bezier", 0.85, 0.7, 0.65, 1], - ["zoom"], - 4, - [ - "step", - ["get", "symbolrank"], - 12, - 6, - 11.4, - 7, - 10.799999999999999 - ], - 9, - ["step", ["get", "symbolrank"], 25.2, 6, 19.2, 7, 15.6] - ], - "text-transform": "uppercase", - "text-font": ["DIN Pro Bold", "Arial Unicode MS Bold"], - "text-field": [ - "step", - ["zoom"], - [ - "step", - ["get", "symbolrank"], - ["coalesce", ["get", "name_en"], ["get", "name"]], - 5, - [ - "coalesce", - ["get", "abbr"], - ["get", "name_en"], - ["get", "name"] - ] - ], - 5, - ["coalesce", ["get", "name_en"], ["get", "name"]] - ], - "text-letter-spacing": 0.15, - "text-max-width": 6 - }, - "paint": { - "text-color": "hsl(215, 30%, 75%)", - "text-halo-color": "hsl(214, 24%, 30%)", - "text-halo-width": 1 - } - }, - { - "id": "country-label", - "type": "symbol", - "metadata": { - "mapbox:featureComponent": "place-labels", - "mapbox:group": "Place labels, place-labels" - }, - "source": "composite", - "source-layer": "place_label", - "minzoom": 1, - "maxzoom": 10, - "filter": [ - "match", - ["get", "class"], - "country", - ["match", ["get", "worldview"], ["all", "US"], true, false], - "disputed_country", - [ - "all", - ["==", ["get", "disputed"], "true"], - ["match", ["get", "worldview"], ["all", "US"], true, false] - ], - false - ], - "layout": { - "text-line-height": 1.1, - "text-size": [ - "interpolate", - ["cubic-bezier", 0.2, 0, 0.7, 1], - ["zoom"], - 1, - [ - "step", - ["get", "symbolrank"], - 15.399999999999999, - 4, - 12.6, - 5, - 11.2 - ], - 9, - [ - "step", - ["get", "symbolrank"], - 30.799999999999997, - 4, - 26.599999999999998, - 5, - 23.799999999999997 - ] - ], - "text-radial-offset": ["step", ["zoom"], 0.6, 8, 0], - "icon-image": "", - "text-font": ["DIN Pro Medium", "Arial Unicode MS Regular"], - "text-justify": [ - "step", - ["zoom"], - [ - "match", - ["get", "text_anchor"], - ["left", "bottom-left", "top-left"], - "left", - ["right", "bottom-right", "top-right"], - "right", - "center" - ], - 7, - "auto" - ], - "text-field": ["coalesce", ["get", "name_en"], ["get", "name"]], - "text-max-width": 6 - }, - "paint": { - "icon-opacity": [ - "step", - ["zoom"], - ["case", ["has", "text_anchor"], 1, 0], - 7, - 0 - ], - "text-color": "hsl(215, 30%, 75%)", - "text-halo-color": [ - "interpolate", - ["linear"], - ["zoom"], - 2, - "hsla(214, 24%, 30%, 0.75)", - 3, - "hsl(214, 24%, 30%)" - ], - "text-halo-width": 1.25 - } - } - ], - "created": "2021-07-13T06:52:14.515Z", - "modified": "2021-08-02T12:49:47.521Z", - "id": "ckr1p5ulb4uys18quax950ia4", - "owner": "drewjj", - "visibility": "public", - "draft": false -} \ No newline at end of file From 7c3824934a78fb92990ceda445f40a9342d317a5 Mon Sep 17 00:00:00 2001 From: jacob6838 Date: Fri, 31 May 2024 16:02:52 -0600 Subject: [PATCH 051/189] Update b_create_indexes.js --- conflictmonitor/docker/mongo/b_create_indexes.js | 1 - 1 file changed, 1 deletion(-) diff --git a/conflictmonitor/docker/mongo/b_create_indexes.js b/conflictmonitor/docker/mongo/b_create_indexes.js index 2c54340ae..f038eaaf8 100644 --- a/conflictmonitor/docker/mongo/b_create_indexes.js +++ b/conflictmonitor/docker/mongo/b_create_indexes.js @@ -6,7 +6,6 @@ This script is responsible for creating users, creating collections, adding inde For more information see the header in a_init_replicas.js */ -console.log('') console.log('Running create_indexes.js') // Setup Username and Password Definitions From 8cf3a0724895de77a8ad9a40abc8290251e18616 Mon Sep 17 00:00:00 2001 From: jacob6838 Date: Mon, 3 Jun 2024 09:37:28 -0600 Subject: [PATCH 052/189] Creating run-conflictmonitor and run-conflictmonitor-api-only tasks --- .vscode/launch.json | 18 +++++++++++++++++- .vscode/tasks.json | 37 +++++++++++++++++++++++++++++++++++++ 2 files changed, 54 insertions(+), 1 deletion(-) diff --git a/.vscode/launch.json b/.vscode/launch.json index fcb0ccac1..e3bc90050 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -6,7 +6,23 @@ "configurations": [ { "command": "npm start", - "name": "Launch web app", + "name": "Launch web app with CM API only", + "request": "launch", + "cwd": "${workspaceFolder}/webapp", + "type": "node-terminal", + "preLaunchTask": "run-conflictmonitor-api-only" + }, + { + "command": "npm start", + "name": "Launch web app with CM", + "request": "launch", + "cwd": "${workspaceFolder}/webapp", + "type": "node-terminal", + "preLaunchTask": "run-conflictmonitor" + }, + { + "command": "npm start", + "name": "Launch web app without CM", "request": "launch", "cwd": "${workspaceFolder}/webapp", "type": "node-terminal", diff --git a/.vscode/tasks.json b/.vscode/tasks.json index e34b83b7f..0a88b32fd 100644 --- a/.vscode/tasks.json +++ b/.vscode/tasks.json @@ -26,6 +26,43 @@ "files": ["${workspaceFolder}/docker-compose.yml"], "envFile": "${workspaceFolder}/.env" } + }, + { + "label": "run-conflictmonitor", + "type": "docker-compose", + "dockerCompose": { + "up": { + "detached": true, + "build": true, + "services": [ + "zookeeper", + "kafka", + "ode", + "geojsonconverter", + "conflictmonitor", + "conflictvisualizer-api", + "mongodb_container", + "connect" + ] + }, + "files": ["${workspaceFolder}/docker-compose.yml"], + "envFile": "${workspaceFolder}/.env" + }, + "dependsOn": ["run-keycloak-and-postgres"] + }, + { + "label": "run-conflictmonitor-api-only", + "type": "docker-compose", + "dockerCompose": { + "up": { + "detached": true, + "build": true, + "services": ["conflictvisualizer-api", "mongodb_container"] + }, + "files": ["${workspaceFolder}/docker-compose.yml"], + "envFile": "${workspaceFolder}/.env" + }, + "dependsOn": ["run-keycloak-and-postgres"] } ] } From 1e89d286296cb2e039c011ab42d90ada674f09cd Mon Sep 17 00:00:00 2001 From: Michael7371 <40476797+Michael7371@users.noreply.github.com> Date: Mon, 3 Jun 2024 17:13:12 -0600 Subject: [PATCH 053/189] bug fixes --- docker-compose-obu-ota-server.yml | 2 +- .../obu_ota_server/commsginia_manifest.py | 92 +++++++++---------- services/common/gcs_utils.py | 2 +- 3 files changed, 48 insertions(+), 48 deletions(-) diff --git a/docker-compose-obu-ota-server.yml b/docker-compose-obu-ota-server.yml index 7bf7cce6b..9e154a448 100644 --- a/docker-compose-obu-ota-server.yml +++ b/docker-compose-obu-ota-server.yml @@ -18,7 +18,7 @@ services: OTA_USERNAME: ${OTA_USERNAME} OTA_PASSWORD: ${OTA_PASSWORD} volumes: - - ./resources/ota:/firmwares + - ./resources/ota/firmwares:/firmwares logging: options: max-size: '10m' diff --git a/services/addons/images/obu_ota_server/commsginia_manifest.py b/services/addons/images/obu_ota_server/commsginia_manifest.py index 38cdad2b3..4e1a8c124 100644 --- a/services/addons/images/obu_ota_server/commsginia_manifest.py +++ b/services/addons/images/obu_ota_server/commsginia_manifest.py @@ -6,40 +6,35 @@ import copy document = { - 'content': [], - 'pageable': { - 'sort': { - 'empty': False, - 'unsorted': False, - 'sorted': True - }, - 'offset': 0, - 'pageNumber': 0, - 'pageSize': 32, - 'paged': True, - 'unpaged': False + "content": [], + "pageable": { + "sort": {"empty": False, "unsorted": False, "sorted": True}, + "offset": 0, + "pageNumber": 0, + "pageSize": 32, + "paged": True, + "unpaged": False, }, - 'last': True, - 'totalPages': 1, - 'totalElements': 0, - 'size': 32, - 'number': 0, - 'sort': { - 'empty': False, - 'unsorted': False, - 'sorted': True - }, - 'numberOfElements': 0, - 'first': True, - 'empty': False + "last": True, + "totalPages": 1, + "totalElements": 0, + "size": 32, + "number": 0, + "sort": {"empty": False, "unsorted": False, "sorted": True}, + "numberOfElements": 0, + "first": True, + "empty": False, } -magic_regex=re.compile('(?P(?Pob4|rs4)-generic(?:-.*)?)-(?:(?Prw)|ro)(?P-secureboot)?-(?P.*).tar.sig') +magic_regex = re.compile( + "(?P(?Pob4|rs4)-generic(?:-.*)?)-(?:(?Prw)|ro)(?P-secureboot)?-(?P.*).tar.sig" +) + def add_contents(server: str, firmware_list: List): manifest = copy.deepcopy(document) - + for firmware in firmware_list: fname = firmware.split("/")[-1] @@ -49,23 +44,28 @@ def add_contents(server: str, firmware_list: List): content = {} - content['id'] = "" - content['name'] = fname - content['variant'] = info['variant'] - content['releaseVersion'] = info['release'] - content['type'] = "RSU" if info['type'] == "rs4" else "OBU" - content['writableSystemPartition'] = bool(info['writable']) - content['secure'] = bool(info['secure']) - content['uploadedAt'] = datetime.datetime.fromtimestamp(os.path.getmtime(firmware)).strftime('%Y-%m-%dT%H:%M:%SZ') - content['size'] = os.stat(firmware).st_size - content['links'] = [{'rel': 'local-file', - 'href': server + '/firmwares/' + fname, - 'type': 'application/octet-stream'}] - manifest['content'].append(content) - manifest['totalElements'] += 1 - manifest['numberOfElements'] += 1 + content["id"] = "" + content["name"] = fname + content["variant"] = info["variant"] + content["releaseVersion"] = info["release"] + content["type"] = "RSU" if info["type"] == "rs4" else "OBU" + content["writableSystemPartition"] = bool(info["writable"]) + content["secure"] = bool(info["secure"]) + content["uploadedAt"] = datetime.datetime.fromtimestamp( + os.path.getmtime(firmware) + ).strftime("%Y-%m-%dT%H:%M:%SZ") + content["size"] = os.stat(firmware).st_size + content["links"] = [ + { + "rel": "local-file", + "href": server + "/firmwares/commsignia/" + fname, + "type": "application/octet-stream", + } + ] + manifest["content"].append(content) + manifest["totalElements"] += 1 + manifest["numberOfElements"] += 1 - if manifest['size'] <= manifest['totalElements']: - raise AttributeError("Maximum number of firmwares is:", - manifest['size']) - return manifest \ No newline at end of file + if manifest["size"] <= manifest["totalElements"]: + raise AttributeError("Maximum number of firmwares is:", manifest["size"]) + return manifest diff --git a/services/common/gcs_utils.py b/services/common/gcs_utils.py index 9244566d2..7a9df0603 100644 --- a/services/common/gcs_utils.py +++ b/services/common/gcs_utils.py @@ -33,7 +33,7 @@ def list_gcs_blobs(gcs_prefix, file_extension): if blob.name.endswith(file_extension): path = f"/firmwares/{blob.name.split(gcs_prefix)[-1]}" files.append(path) - if not os.path.exists(os.path.dirname(path)): + if not os.path.exists(path): blob.download_to_filename(path) logging.debug(f"Downloaded blob {blob.name} to {path}") download_count += 1 From 220db918ff925f5b5f7c227b46684f588f4bdc75 Mon Sep 17 00:00:00 2001 From: Michael7371 <40476797+Michael7371@users.noreply.github.com> Date: Mon, 3 Jun 2024 17:42:00 -0600 Subject: [PATCH 054/189] started documentation --- .../addons/images/obu_ota_server/README.md | 69 +++++++++++++++++++ 1 file changed, 69 insertions(+) create mode 100644 services/addons/images/obu_ota_server/README.md diff --git a/services/addons/images/obu_ota_server/README.md b/services/addons/images/obu_ota_server/README.md new file mode 100644 index 000000000..6d680d201 --- /dev/null +++ b/services/addons/images/obu_ota_server/README.md @@ -0,0 +1,69 @@ +# OBU OTA Server + +## Table of Contents + +- [OBU OTA Server](#obu-ota-server) + - [Table of Contents](#table-of-contents) + - [About ](#about-) + - [Requirements ](#requirements-) + - [Common required variables ](#common-required-variables-) + - [GCP required variables ](#gcp-required-variables-) + - [Local Required variables ](#local-required-variables-) + +## About + +This directory contains a microservice that runs within the CV Manager GKE Cluster. This service can take either local or GCP blob stored firmware files and serve them for OBU devices to receive Over the Air (OTA) updates. OBU devices will query the OTA server for a manifest of available firmware files and will send another request for the full firmware file if it has a newer version number. For local deployments the OBU OTA server is deployed behind a NGINX proxy to allow for TLS encryption. In k8's an ingress resource is used to handle certificates and TLS. + +List of currently supported vendors: + +- Commsignia + +Available REST endpoints: + +- / [ **GET** ] + - Used as a health check path for K8 deployment. This path is not secured with basic authentication +- /firmwares/commsignia [ **GET** ] + - Secured with basic authentication. + - Firmware file name must comply with the following regex naming filter: + `(?P(?Pob4|rs4)-generic(?:-.*)?)-(?:(?Prw)|ro)(?P-secureboot)?-(?P.*).tar.sig` + - Will either query GCS or the specified local path for a latest list of firmware files. Generates a manifest that includes details about each firmware file along with a `href` field that the OBU uses to get generate the download request. Here is a sample response: + `{"content":[{"id":"","name":"ob4-generic-ro-secureboot-y20.48.2-b228647.tar.sig","variant":"ob4-generic","releaseVersion":"y20.48.2-b228647","type":"OBU","writableSystemPartition":false,"secure":true,"uploadedAt":"2024-05-29T22:12:33Z","size":135055616,"links":[{"rel":"local-file","href":"localhost/firmwares/commsignia/ob4-generic-ro-secureboot-y20.48.2-b228647.tar.sig","type":"application/octet-stream"}]}],"pageable":{"sort":{"empty":false,"unsorted":false,"sorted":true},"offset":0,"pageNumber":0,"pageSize":32,"paged":true,"unpaged":false},"last":true,"totalPages":1,"totalElements":1,"size":32,"number":0,"sort":{"empty":false,"unsorted":false,"sorted":true},"numberOfElements":1,"first":true,"empty":false}` +- /firmwares/commsignia/{firmware_id} [ **GET** ] + - Secured with basic authentication. + - Used as an endpoint for the OBU to download a firmware file from. + - Supports a range header to allow for partial downloads of files. + - NOTE: Commsignia currently doesn't send these range headers so as of now this functionality has not be tested. + +## Requirements + +The following environmental variables must be set: + +### Common required variables + +LOGGING_LEVEL: The logging level of the deployment. Options are: 'critical', 'error', 'warning', 'info' and 'debug'. If not specified, will default to 'info'. Refer to Python's documentation for more info: [Python logging](https://docs.python.org/3/howto/logging.html). + +SERVER_HOST: The base URL of the OTA server, this must be resolvable from the OBU. Generally this should be set to the IP address of the server or the DNS name. + +BLOB_STORAGE_PROVIDER: Set to either "LOCAL" or "GCP" depending on deployment environment. + +OTA_USERNAME: Username to be used with basic authentication. + +OTA_PASSWORD: Password to be used with basic authentication + +### GCP required variables + +BLOB_STORAGE_BUCKET: Cloud blob storage bucket for firmware storage. + +BLOB_STORAGE_PATH: Path to firmware files directory within the storage bucket. + +GCP_PROJECT: GCP project for the firmware cloud storage bucket. + +GOOGLE_APPLICATION_CREDENTIALS: Service account location. Recommended to attach as a volume. + +### Local Required variables + +NGINX_ENCRYPTION: Used for the NGINX proxy configuration, set to "plain" or "ssl" depending on if you want to secure traffic with TLS. + +SERVER_CERT_FILE: Path to the server cert file if using SSL. + +SERVER_KEY_FILE: Path to the server key file if using SSL. From 78ff9209a1db3f317d21e79585751f6872a8501d Mon Sep 17 00:00:00 2001 From: Michael7371 <40476797+Michael7371@users.noreply.github.com> Date: Tue, 4 Jun 2024 08:56:57 -0600 Subject: [PATCH 055/189] documentation, sample.env, and docker-compose updates --- docker-compose-addons.yml | 2 +- docker-compose-obu-ota-server.yml | 3 ++- sample.env | 26 ++++++++++++++++++- .../addons/images/obu_ota_server/README.md | 2 +- .../obu_ota_server/test_obu_ota_server.py | 12 +++------ 5 files changed, 32 insertions(+), 13 deletions(-) diff --git a/docker-compose-addons.yml b/docker-compose-addons.yml index f4c8b47da..421196253 100644 --- a/docker-compose-addons.yml +++ b/docker-compose-addons.yml @@ -140,7 +140,7 @@ services: BLOB_STORAGE_PROVIDER: ${BLOB_STORAGE_PROVIDER} BLOB_STORAGE_BUCKET: ${BLOB_STORAGE_BUCKET} - GCP_PROJECT: ${GCP_PROJECT} + GCP_PROJECT: ${GCP_PROJECT_ID} GOOGLE_APPLICATION_CREDENTIALS: '/google/gcp_credentials.json' FW_EMAIL_RECIPIENTS: ${FW_EMAIL_RECIPIENTS} diff --git a/docker-compose-obu-ota-server.yml b/docker-compose-obu-ota-server.yml index 9e154a448..5479013ed 100644 --- a/docker-compose-obu-ota-server.yml +++ b/docker-compose-obu-ota-server.yml @@ -13,7 +13,8 @@ services: SERVER_HOST: ${OBU_OTA_SERVER_HOST} LOGGING_LEVEL: ${OBU_OTA_SERVER_LOGGING_LEVEL} BLOB_STORAGE_PROVIDER: ${BLOB_STORAGE_PROVIDER} - BLOB_STORAGE_PATH: ${BLOB_STORAGE_PATH} + BLOB_STORAGE_BUCKET: ${OBU_OTA_BLOB_STORAGE_BUCKET} + BLOB_STORAGE_PATH: ${OBU_OTA_BLOB_STORAGE_PATH} OTA_USERNAME: ${OTA_USERNAME} OTA_PASSWORD: ${OTA_PASSWORD} diff --git a/sample.env b/sample.env index 55abc2726..2b3ffa445 100644 --- a/sample.env +++ b/sample.env @@ -133,7 +133,6 @@ KAFKA_BIGQUERY_TABLENAME = '' BLOB_STORAGE_PROVIDER=GCP BLOB_STORAGE_BUCKET= BLOB_STORAGE_PATH= -GCP_PROJECT= # --------------------------------------------------------------------- # Geo-spatial message query Addon: @@ -176,6 +175,30 @@ ZABBIX_PASSWORD= STALE_PERIOD=24 # --------------------------------------------------------------------- +# OBU OTA Server Addon: + +# Routeable host name for the server +OBU_OTA_SERVER_HOST = "localhost" + +# For users using GCP cloud storage +OBU_OTA_BLOB_STORAGE_BUCKET="" +OBU_OTA_BLOB_STORAGE_PATH="" + +# Nginx basic auth username and password +OTA_USERNAME = "admin" +OTA_PASSWORD = "admin" + +# Nginx encryption options: "plain", "ssl" +# Note that this just changes the config file attached as a volume to the Nginx container +NGINX_ENCRYPTION="plain" + +# SSL file name in path /docker/nginx/ssl/ +SERVER_CERT_FILE="ota_server.crt" +SERVER_KEY_FILE="ota_server.key" + +# --------------------------------------------------------------------- + + # Levels are "DEBUG", "INFO", "WARNING", and "ERROR" API_LOGGING_LEVEL="INFO" FIRMWARE_MANAGER_LOGGING_LEVEL="INFO" @@ -183,5 +206,6 @@ GEO_LOGGING_LEVEL="INFO" ISS_LOGGING_LEVEL="INFO" RSU_STATUS_LOGGING_LEVEL="INFO" COUNTS_LOGGING_LEVEL="INFO" +OBU_OTA_LOGGING_LEVEL="INFO" # Supported log levels are "ALL", "DEBUG", "ERROR", "FATAL", "INFO", "OFF", "TRACE" and "WARN" KC_LOGGING_LEVEL="INFO" \ No newline at end of file diff --git a/services/addons/images/obu_ota_server/README.md b/services/addons/images/obu_ota_server/README.md index 6d680d201..38e8af00c 100644 --- a/services/addons/images/obu_ota_server/README.md +++ b/services/addons/images/obu_ota_server/README.md @@ -12,7 +12,7 @@ ## About -This directory contains a microservice that runs within the CV Manager GKE Cluster. This service can take either local or GCP blob stored firmware files and serve them for OBU devices to receive Over the Air (OTA) updates. OBU devices will query the OTA server for a manifest of available firmware files and will send another request for the full firmware file if it has a newer version number. For local deployments the OBU OTA server is deployed behind a NGINX proxy to allow for TLS encryption. In k8's an ingress resource is used to handle certificates and TLS. +This directory contains a microservice that runs within the CV Manager GKE Cluster. This service can take either local or GCP stored firmware files and serves them for OBU devices to receive Over the Air (OTA) updates. OBU devices will query the OTA server for a manifest of available firmware files and will send another request for the full firmware file if it has a newer version number. For local deployments the OBU OTA server is deployed behind a NGINX proxy to allow for TLS encryption. In k8's an ingress resource is used to handle certificates and TLS. List of currently supported vendors: diff --git a/services/addons/tests/obu_ota_server/test_obu_ota_server.py b/services/addons/tests/obu_ota_server/test_obu_ota_server.py index 6dd63e6f2..9d8fb44aa 100644 --- a/services/addons/tests/obu_ota_server/test_obu_ota_server.py +++ b/services/addons/tests/obu_ota_server/test_obu_ota_server.py @@ -189,9 +189,7 @@ async def test_read_file_no_end_range(): os.remove(temp_path) -@patch.dict( - "os.environ", {"BASIC_AUTH_USERNAME": "username", "BASIC_AUTH_PASSWORD": "password"} -) +@patch.dict("os.environ", {"OTA_USERNAME": "username", "OTA_PASSWORD": "password"}) @pytest.mark.anyio async def test_read_root(): async with AsyncClient(app=app, base_url="http://test") as ac: @@ -200,9 +198,7 @@ async def test_read_root(): assert response.json() == {"message": "obu ota server healthcheck", "root_path": ""} -@patch.dict( - "os.environ", {"BASIC_AUTH_USERNAME": "username", "BASIC_AUTH_PASSWORD": "password"} -) +@patch.dict("os.environ", {"OTA_USERNAME": "username", "OTA_PASSWORD": "password"}) @pytest.mark.anyio @patch("addons.images.obu_ota_server.obu_ota_server.get_firmware_list") @patch("addons.images.obu_ota_server.obu_ota_server.commsginia_manifest.add_contents") @@ -220,9 +216,7 @@ async def test_get_manifest(mock_commsginia_manifest, mock_get_firmware_list): assert response.json() == {"json": "data"} -@patch.dict( - "os.environ", {"BASIC_AUTH_USERNAME": "username", "BASIC_AUTH_PASSWORD": "password"} -) +@patch.dict("os.environ", {"OTA_USERNAME": "username", "OTA_PASSWORD": "password"}) @pytest.mark.anyio @patch("addons.images.obu_ota_server.obu_ota_server.get_firmware") @patch("addons.images.obu_ota_server.obu_ota_server.parse_range_header") From b934cad75511c6d15f2328fe917bbba3c7d773e9 Mon Sep 17 00:00:00 2001 From: jacob6838 Date: Tue, 4 Jun 2024 15:24:38 -0600 Subject: [PATCH 056/189] Adding static data to load into MongoDB --- .vscode/tasks.json | 1 + .../datasets/uturn_export/OdeBsmJson.json | 23801 ++++++++++++ .../datasets/uturn_export/ProcessedMap.json | 2188 ++ .../datasets/uturn_export/ProcessedSpat.json | 30241 ++++++++++++++++ docker-compose.yml | 35 +- 5 files changed, 56256 insertions(+), 10 deletions(-) create mode 100644 conflictmonitor/datasets/uturn_export/OdeBsmJson.json create mode 100644 conflictmonitor/datasets/uturn_export/ProcessedMap.json create mode 100644 conflictmonitor/datasets/uturn_export/ProcessedSpat.json diff --git a/.vscode/tasks.json b/.vscode/tasks.json index 0a88b32fd..83349e107 100644 --- a/.vscode/tasks.json +++ b/.vscode/tasks.json @@ -41,6 +41,7 @@ "geojsonconverter", "conflictmonitor", "conflictvisualizer-api", + "deduplicator", "mongodb_container", "connect" ] diff --git a/conflictmonitor/datasets/uturn_export/OdeBsmJson.json b/conflictmonitor/datasets/uturn_export/OdeBsmJson.json new file mode 100644 index 000000000..e61b6578d --- /dev/null +++ b/conflictmonitor/datasets/uturn_export/OdeBsmJson.json @@ -0,0 +1,23801 @@ +[{ + "_id": { + "$oid": "665f3fc08c25216ef4f53d61" + }, + "metadata": { + "securityResultCode": "success", + "bsmSource": "RV", + "schemaVersion": 6, + "odePacketID": "", + "sanitized": false, + "recordType": "bsmTx", + "recordGeneratedAt": "", + "maxDurationTime": 0, + "odeTimStartDateTime": "", + "receivedMessageDetails": { + "locationData": { + "elevation": "", + "heading": "", + "latitude": "", + "speed": "", + "longitude": "" + }, + "rxSource": "RV" + }, + "payloadType": "us.dot.its.jpo.ode.model.OdeBsmPayload", + "serialId": { + "recordId": 0, + "serialNumber": 0, + "streamId": "849c3ac6-4747-426f-ae5e-28ac8531b32c", + "bundleSize": 1, + "bundleId": 0 + }, + "logFileName": "", + "odeReceivedAt": "2024-06-04T16:22:40.658Z", + "originIp": "10.11.81.12" + }, + "payload": { + "data": { + "partII": [ + { + "id": "VehicleSafetyExtensions", + "value": { + "pathHistory": { + "crumbData": [ + { + "elevationOffset": 0.6, + "lonOffset": 0.0000387, + "timeOffset": 0.8, + "latOffset": -0.0001604 + }, + { + "elevationOffset": 4.3, + "lonOffset": 0.000231, + "timeOffset": 5.6, + "latOffset": -0.0011603 + }, + { + "elevationOffset": 9.1, + "lonOffset": 0.0003834, + "timeOffset": 11, + "latOffset": -0.0023425 + } + ] + }, + "pathPrediction": { + "confidence": 80, + "radiusOfCurve": -1622.5 + } + } + } + ], + "coreData": { + "secMark": 40658, + "transmission": "UNAVAILABLE", + "size": { + "width": 180, + "length": 480 + }, + "heading": 169.2, + "accelSet": { + "accelYaw": -0.65, + "accelLong": -0.05 + }, + "brakes": { + "scs": "unavailable", + "wheelBrakes": { + "leftFront": false, + "rightFront": false, + "unavailable": true, + "leftRear": false, + "rightRear": false + }, + "abs": "off", + "traction": "unavailable", + "brakeBoost": "unavailable", + "auxBrakes": "unavailable" + }, + "accuracy": { + "semiMinor": 2, + "orientation": 0, + "semiMajor": 2 + }, + "id": "D51E0ECA", + "position": { + "elevation": 1691.9, + "latitude": 39.587279346902015, + "longitude": -105.09059802750382 + }, + "msgCnt": 1, + "speed": 22.62 + } + }, + "dataType": "us.dot.its.jpo.ode.plugin.j2735.J2735Bsm" + }, + "recordGeneratedAt": { + "$date": { + "$numberLong": "1717518160664" + } + } +},{ + "_id": { + "$oid": "665f3fc08c25216ef4f53d62" + }, + "metadata": { + "securityResultCode": "success", + "bsmSource": "RV", + "schemaVersion": 6, + "odePacketID": "", + "sanitized": false, + "recordType": "bsmTx", + "recordGeneratedAt": "", + "maxDurationTime": 0, + "odeTimStartDateTime": "", + "receivedMessageDetails": { + "locationData": { + "elevation": "", + "heading": "", + "latitude": "", + "speed": "", + "longitude": "" + }, + "rxSource": "RV" + }, + "payloadType": "us.dot.its.jpo.ode.model.OdeBsmPayload", + "serialId": { + "recordId": 0, + "serialNumber": 0, + "streamId": "849c3ac6-4747-426f-ae5e-28ac8531b32c", + "bundleSize": 1, + "bundleId": 0 + }, + "logFileName": "", + "odeReceivedAt": "2024-06-04T16:22:40.77Z", + "originIp": "10.11.81.12" + }, + "payload": { + "data": { + "partII": [ + { + "id": "VehicleSafetyExtensions", + "value": { + "pathHistory": { + "crumbData": [ + { + "elevationOffset": 0.6, + "lonOffset": 0.0000387, + "timeOffset": 0.8, + "latOffset": -0.0001604 + }, + { + "elevationOffset": 4.3, + "lonOffset": 0.000231, + "timeOffset": 5.6, + "latOffset": -0.0011603 + }, + { + "elevationOffset": 9.1, + "lonOffset": 0.0003834, + "timeOffset": 11, + "latOffset": -0.0023425 + } + ] + }, + "pathPrediction": { + "confidence": 80, + "radiusOfCurve": -1622.5 + } + } + } + ], + "coreData": { + "secMark": 40770, + "transmission": "UNAVAILABLE", + "size": { + "width": 180, + "length": 480 + }, + "heading": 342, + "accelSet": { + "accelYaw": -0.65, + "accelLong": -0.05 + }, + "brakes": { + "scs": "unavailable", + "wheelBrakes": { + "leftFront": false, + "rightFront": false, + "unavailable": true, + "leftRear": false, + "rightRear": false + }, + "abs": "off", + "traction": "unavailable", + "brakeBoost": "unavailable", + "auxBrakes": "unavailable" + }, + "accuracy": { + "semiMinor": 2, + "orientation": 0, + "semiMajor": 2 + }, + "id": "D51E0ECA", + "position": { + "elevation": 1691.9, + "latitude": 39.58731758744779, + "longitude": -105.09061412339413 + }, + "msgCnt": 2, + "speed": 44.7 + } + }, + "dataType": "us.dot.its.jpo.ode.plugin.j2735.J2735Bsm" + }, + "recordGeneratedAt": { + "$date": { + "$numberLong": "1717518160779" + } + } +},{ + "_id": { + "$oid": "665f3fc08c25216ef4f53d63" + }, + "metadata": { + "securityResultCode": "success", + "bsmSource": "RV", + "schemaVersion": 6, + "odePacketID": "", + "sanitized": false, + "recordType": "bsmTx", + "recordGeneratedAt": "", + "maxDurationTime": 0, + "odeTimStartDateTime": "", + "receivedMessageDetails": { + "locationData": { + "elevation": "", + "heading": "", + "latitude": "", + "speed": "", + "longitude": "" + }, + "rxSource": "RV" + }, + "payloadType": "us.dot.its.jpo.ode.model.OdeBsmPayload", + "serialId": { + "recordId": 0, + "serialNumber": 0, + "streamId": "849c3ac6-4747-426f-ae5e-28ac8531b32c", + "bundleSize": 1, + "bundleId": 0 + }, + "logFileName": "", + "odeReceivedAt": "2024-06-04T16:22:40.877Z", + "originIp": "10.11.81.12" + }, + "payload": { + "data": { + "partII": [ + { + "id": "VehicleSafetyExtensions", + "value": { + "pathHistory": { + "crumbData": [ + { + "elevationOffset": 0.6, + "lonOffset": 0.0000387, + "timeOffset": 0.8, + "latOffset": -0.0001604 + }, + { + "elevationOffset": 4.3, + "lonOffset": 0.000231, + "timeOffset": 5.6, + "latOffset": -0.0011603 + }, + { + "elevationOffset": 9.1, + "lonOffset": 0.0003834, + "timeOffset": 11, + "latOffset": -0.0023425 + } + ] + }, + "pathPrediction": { + "confidence": 80, + "radiusOfCurve": -1622.5 + } + } + } + ], + "coreData": { + "secMark": 40877, + "transmission": "UNAVAILABLE", + "size": { + "width": 180, + "length": 480 + }, + "heading": 339.9, + "accelSet": { + "accelYaw": -0.65, + "accelLong": -0.05 + }, + "brakes": { + "scs": "unavailable", + "wheelBrakes": { + "leftFront": false, + "rightFront": false, + "unavailable": true, + "leftRear": false, + "rightRear": false + }, + "abs": "off", + "traction": "unavailable", + "brakeBoost": "unavailable", + "auxBrakes": "unavailable" + }, + "accuracy": { + "semiMinor": 2, + "orientation": 0, + "semiMajor": 2 + }, + "id": "D51E0ECA", + "position": { + "elevation": 1691.9, + "latitude": 39.58734859328024, + "longitude": -105.09062887796024 + }, + "msgCnt": 3, + "speed": 36.72 + } + }, + "dataType": "us.dot.its.jpo.ode.plugin.j2735.J2735Bsm" + }, + "recordGeneratedAt": { + "$date": { + "$numberLong": "1717518160887" + } + } +},{ + "_id": { + "$oid": "665f3fc08c25216ef4f53d64" + }, + "metadata": { + "securityResultCode": "success", + "bsmSource": "RV", + "schemaVersion": 6, + "odePacketID": "", + "sanitized": false, + "recordType": "bsmTx", + "recordGeneratedAt": "", + "maxDurationTime": 0, + "odeTimStartDateTime": "", + "receivedMessageDetails": { + "locationData": { + "elevation": "", + "heading": "", + "latitude": "", + "speed": "", + "longitude": "" + }, + "rxSource": "RV" + }, + "payloadType": "us.dot.its.jpo.ode.model.OdeBsmPayload", + "serialId": { + "recordId": 0, + "serialNumber": 0, + "streamId": "849c3ac6-4747-426f-ae5e-28ac8531b32c", + "bundleSize": 1, + "bundleId": 0 + }, + "logFileName": "", + "odeReceivedAt": "2024-06-04T16:22:40.983Z", + "originIp": "10.11.81.12" + }, + "payload": { + "data": { + "partII": [ + { + "id": "VehicleSafetyExtensions", + "value": { + "pathHistory": { + "crumbData": [ + { + "elevationOffset": 0.6, + "lonOffset": 0.0000387, + "timeOffset": 0.8, + "latOffset": -0.0001604 + }, + { + "elevationOffset": 4.3, + "lonOffset": 0.000231, + "timeOffset": 5.6, + "latOffset": -0.0011603 + }, + { + "elevationOffset": 9.1, + "lonOffset": 0.0003834, + "timeOffset": 11, + "latOffset": -0.0023425 + } + ] + }, + "pathPrediction": { + "confidence": 80, + "radiusOfCurve": -1622.5 + } + } + } + ], + "coreData": { + "secMark": 40983, + "transmission": "UNAVAILABLE", + "size": { + "width": 180, + "length": 480 + }, + "heading": 347.9, + "accelSet": { + "accelYaw": -0.65, + "accelLong": -0.05 + }, + "brakes": { + "scs": "unavailable", + "wheelBrakes": { + "leftFront": false, + "rightFront": false, + "unavailable": true, + "leftRear": false, + "rightRear": false + }, + "abs": "off", + "traction": "unavailable", + "brakeBoost": "unavailable", + "auxBrakes": "unavailable" + }, + "accuracy": { + "semiMinor": 2, + "orientation": 0, + "semiMajor": 2 + }, + "id": "D51E0ECA", + "position": { + "elevation": 1691.9, + "latitude": 39.587377532044655, + "longitude": -105.09063692590537 + }, + "msgCnt": 4, + "speed": 32.91 + } + }, + "dataType": "us.dot.its.jpo.ode.plugin.j2735.J2735Bsm" + }, + "recordGeneratedAt": { + "$date": { + "$numberLong": "1717518160994" + } + } +},{ + "_id": { + "$oid": "665f3fc08c25216ef4f53d65" + }, + "metadata": { + "securityResultCode": "success", + "bsmSource": "RV", + "schemaVersion": 6, + "odePacketID": "", + "sanitized": false, + "recordType": "bsmTx", + "recordGeneratedAt": "", + "maxDurationTime": 0, + "odeTimStartDateTime": "", + "receivedMessageDetails": { + "locationData": { + "elevation": "", + "heading": "", + "latitude": "", + "speed": "", + "longitude": "" + }, + "rxSource": "RV" + }, + "payloadType": "us.dot.its.jpo.ode.model.OdeBsmPayload", + "serialId": { + "recordId": 0, + "serialNumber": 0, + "streamId": "849c3ac6-4747-426f-ae5e-28ac8531b32c", + "bundleSize": 1, + "bundleId": 0 + }, + "logFileName": "", + "odeReceivedAt": "2024-06-04T16:22:41.091Z", + "originIp": "10.11.81.12" + }, + "payload": { + "data": { + "partII": [ + { + "id": "VehicleSafetyExtensions", + "value": { + "pathHistory": { + "crumbData": [ + { + "elevationOffset": 0.6, + "lonOffset": 0.0000387, + "timeOffset": 0.8, + "latOffset": -0.0001604 + }, + { + "elevationOffset": 4.3, + "lonOffset": 0.000231, + "timeOffset": 5.6, + "latOffset": -0.0011603 + }, + { + "elevationOffset": 9.1, + "lonOffset": 0.0003834, + "timeOffset": 11, + "latOffset": -0.0023425 + } + ] + }, + "pathPrediction": { + "confidence": 80, + "radiusOfCurve": -1622.5 + } + } + } + ], + "coreData": { + "secMark": 41091, + "transmission": "UNAVAILABLE", + "size": { + "width": 180, + "length": 480 + }, + "heading": 347.7, + "accelSet": { + "accelYaw": -0.65, + "accelLong": -0.05 + }, + "brakes": { + "scs": "unavailable", + "wheelBrakes": { + "leftFront": false, + "rightFront": false, + "unavailable": true, + "leftRear": false, + "rightRear": false + }, + "abs": "off", + "traction": "unavailable", + "brakeBoost": "unavailable", + "auxBrakes": "unavailable" + }, + "accuracy": { + "semiMinor": 2, + "orientation": 0, + "semiMajor": 2 + }, + "id": "D51E0ECA", + "position": { + "elevation": 1691.9, + "latitude": 39.58741060490348, + "longitude": -105.09064631517474 + }, + "msgCnt": 5, + "speed": 37.65 + } + }, + "dataType": "us.dot.its.jpo.ode.plugin.j2735.J2735Bsm" + }, + "recordGeneratedAt": { + "$date": { + "$numberLong": "1717518161100" + } + } +},{ + "_id": { + "$oid": "665f3fc08c25216ef4f53d66" + }, + "metadata": { + "securityResultCode": "success", + "bsmSource": "RV", + "schemaVersion": 6, + "odePacketID": "", + "sanitized": false, + "recordType": "bsmTx", + "recordGeneratedAt": "", + "maxDurationTime": 0, + "odeTimStartDateTime": "", + "receivedMessageDetails": { + "locationData": { + "elevation": "", + "heading": "", + "latitude": "", + "speed": "", + "longitude": "" + }, + "rxSource": "RV" + }, + "payloadType": "us.dot.its.jpo.ode.model.OdeBsmPayload", + "serialId": { + "recordId": 0, + "serialNumber": 0, + "streamId": "849c3ac6-4747-426f-ae5e-28ac8531b32c", + "bundleSize": 1, + "bundleId": 0 + }, + "logFileName": "", + "odeReceivedAt": "2024-06-04T16:22:41.199Z", + "originIp": "10.11.81.12" + }, + "payload": { + "data": { + "partII": [ + { + "id": "VehicleSafetyExtensions", + "value": { + "pathHistory": { + "crumbData": [ + { + "elevationOffset": 0.6, + "lonOffset": 0.0000387, + "timeOffset": 0.8, + "latOffset": -0.0001604 + }, + { + "elevationOffset": 4.3, + "lonOffset": 0.000231, + "timeOffset": 5.6, + "latOffset": -0.0011603 + }, + { + "elevationOffset": 9.1, + "lonOffset": 0.0003834, + "timeOffset": 11, + "latOffset": -0.0023425 + } + ] + }, + "pathPrediction": { + "confidence": 80, + "radiusOfCurve": -1622.5 + } + } + } + ], + "coreData": { + "secMark": 41199, + "transmission": "UNAVAILABLE", + "size": { + "width": 180, + "length": 480 + }, + "heading": 343.3, + "accelSet": { + "accelYaw": -0.65, + "accelLong": -0.05 + }, + "brakes": { + "scs": "unavailable", + "wheelBrakes": { + "leftFront": false, + "rightFront": false, + "unavailable": true, + "leftRear": false, + "rightRear": false + }, + "abs": "off", + "traction": "unavailable", + "brakeBoost": "unavailable", + "auxBrakes": "unavailable" + }, + "accuracy": { + "semiMinor": 2, + "orientation": 0, + "semiMajor": 2 + }, + "id": "D51E0ECA", + "position": { + "elevation": 1691.9, + "latitude": 39.58744161069427, + "longitude": -105.09065838709249 + }, + "msgCnt": 6, + "speed": 36 + } + }, + "dataType": "us.dot.its.jpo.ode.plugin.j2735.J2735Bsm" + }, + "recordGeneratedAt": { + "$date": { + "$numberLong": "1717518161206" + } + } +},{ + "_id": { + "$oid": "665f3fc08c25216ef4f53d67" + }, + "metadata": { + "securityResultCode": "success", + "bsmSource": "RV", + "schemaVersion": 6, + "odePacketID": "", + "sanitized": false, + "recordType": "bsmTx", + "recordGeneratedAt": "", + "maxDurationTime": 0, + "odeTimStartDateTime": "", + "receivedMessageDetails": { + "locationData": { + "elevation": "", + "heading": "", + "latitude": "", + "speed": "", + "longitude": "" + }, + "rxSource": "RV" + }, + "payloadType": "us.dot.its.jpo.ode.model.OdeBsmPayload", + "serialId": { + "recordId": 0, + "serialNumber": 0, + "streamId": "849c3ac6-4747-426f-ae5e-28ac8531b32c", + "bundleSize": 1, + "bundleId": 0 + }, + "logFileName": "", + "odeReceivedAt": "2024-06-04T16:22:41.307Z", + "originIp": "10.11.81.12" + }, + "payload": { + "data": { + "partII": [ + { + "id": "VehicleSafetyExtensions", + "value": { + "pathHistory": { + "crumbData": [ + { + "elevationOffset": 0.6, + "lonOffset": 0.0000387, + "timeOffset": 0.8, + "latOffset": -0.0001604 + }, + { + "elevationOffset": 4.3, + "lonOffset": 0.000231, + "timeOffset": 5.6, + "latOffset": -0.0011603 + }, + { + "elevationOffset": 9.1, + "lonOffset": 0.0003834, + "timeOffset": 11, + "latOffset": -0.0023425 + } + ] + }, + "pathPrediction": { + "confidence": 80, + "radiusOfCurve": -1622.5 + } + } + } + ], + "coreData": { + "secMark": 41307, + "transmission": "UNAVAILABLE", + "size": { + "width": 180, + "length": 480 + }, + "heading": 348, + "accelSet": { + "accelYaw": -0.65, + "accelLong": -0.05 + }, + "brakes": { + "scs": "unavailable", + "wheelBrakes": { + "leftFront": false, + "rightFront": false, + "unavailable": true, + "leftRear": false, + "rightRear": false + }, + "abs": "off", + "traction": "unavailable", + "brakeBoost": "unavailable", + "auxBrakes": "unavailable" + }, + "accuracy": { + "semiMinor": 2, + "orientation": 0, + "semiMajor": 2 + }, + "id": "D51E0ECA", + "position": { + "elevation": 1691.9, + "latitude": 39.587475717048164, + "longitude": -105.09066777636183 + }, + "msgCnt": 7, + "speed": 38.77 + } + }, + "dataType": "us.dot.its.jpo.ode.plugin.j2735.J2735Bsm" + }, + "recordGeneratedAt": { + "$date": { + "$numberLong": "1717518161312" + } + } +},{ + "_id": { + "$oid": "665f3fc08c25216ef4f53d68" + }, + "metadata": { + "securityResultCode": "success", + "bsmSource": "RV", + "schemaVersion": 6, + "odePacketID": "", + "sanitized": false, + "recordType": "bsmTx", + "recordGeneratedAt": "", + "maxDurationTime": 0, + "odeTimStartDateTime": "", + "receivedMessageDetails": { + "locationData": { + "elevation": "", + "heading": "", + "latitude": "", + "speed": "", + "longitude": "" + }, + "rxSource": "RV" + }, + "payloadType": "us.dot.its.jpo.ode.model.OdeBsmPayload", + "serialId": { + "recordId": 0, + "serialNumber": 0, + "streamId": "849c3ac6-4747-426f-ae5e-28ac8531b32c", + "bundleSize": 1, + "bundleId": 0 + }, + "logFileName": "", + "odeReceivedAt": "2024-06-04T16:22:41.412Z", + "originIp": "10.11.81.12" + }, + "payload": { + "data": { + "partII": [ + { + "id": "VehicleSafetyExtensions", + "value": { + "pathHistory": { + "crumbData": [ + { + "elevationOffset": 0.6, + "lonOffset": 0.0000387, + "timeOffset": 0.8, + "latOffset": -0.0001604 + }, + { + "elevationOffset": 4.3, + "lonOffset": 0.000231, + "timeOffset": 5.6, + "latOffset": -0.0011603 + }, + { + "elevationOffset": 9.1, + "lonOffset": 0.0003834, + "timeOffset": 11, + "latOffset": -0.0023425 + } + ] + }, + "pathPrediction": { + "confidence": 80, + "radiusOfCurve": -1622.5 + } + } + } + ], + "coreData": { + "secMark": 41412, + "transmission": "UNAVAILABLE", + "size": { + "width": 180, + "length": 480 + }, + "heading": 344.6, + "accelSet": { + "accelYaw": -0.65, + "accelLong": -0.05 + }, + "brakes": { + "scs": "unavailable", + "wheelBrakes": { + "leftFront": false, + "rightFront": false, + "unavailable": true, + "leftRear": false, + "rightRear": false + }, + "abs": "off", + "traction": "unavailable", + "brakeBoost": "unavailable", + "auxBrakes": "unavailable" + }, + "accuracy": { + "semiMinor": 2, + "orientation": 0, + "semiMajor": 2 + }, + "id": "D51E0ECA", + "position": { + "elevation": 1691.9, + "latitude": 39.58750568928466, + "longitude": -105.09067850695536 + }, + "msgCnt": 8, + "speed": 34.57 + } + }, + "dataType": "us.dot.its.jpo.ode.plugin.j2735.J2735Bsm" + }, + "recordGeneratedAt": { + "$date": { + "$numberLong": "1717518161419" + } + } +},{ + "_id": { + "$oid": "665f3fc08c25216ef4f53d69" + }, + "metadata": { + "securityResultCode": "success", + "bsmSource": "RV", + "schemaVersion": 6, + "odePacketID": "", + "sanitized": false, + "recordType": "bsmTx", + "recordGeneratedAt": "", + "maxDurationTime": 0, + "odeTimStartDateTime": "", + "receivedMessageDetails": { + "locationData": { + "elevation": "", + "heading": "", + "latitude": "", + "speed": "", + "longitude": "" + }, + "rxSource": "RV" + }, + "payloadType": "us.dot.its.jpo.ode.model.OdeBsmPayload", + "serialId": { + "recordId": 0, + "serialNumber": 0, + "streamId": "849c3ac6-4747-426f-ae5e-28ac8531b32c", + "bundleSize": 1, + "bundleId": 0 + }, + "logFileName": "", + "odeReceivedAt": "2024-06-04T16:22:41.521Z", + "originIp": "10.11.81.12" + }, + "payload": { + "data": { + "partII": [ + { + "id": "VehicleSafetyExtensions", + "value": { + "pathHistory": { + "crumbData": [ + { + "elevationOffset": 0.6, + "lonOffset": 0.0000387, + "timeOffset": 0.8, + "latOffset": -0.0001604 + }, + { + "elevationOffset": 4.3, + "lonOffset": 0.000231, + "timeOffset": 5.6, + "latOffset": -0.0011603 + }, + { + "elevationOffset": 9.1, + "lonOffset": 0.0003834, + "timeOffset": 11, + "latOffset": -0.0023425 + } + ] + }, + "pathPrediction": { + "confidence": 80, + "radiusOfCurve": -1622.5 + } + } + } + ], + "coreData": { + "secMark": 41521, + "transmission": "UNAVAILABLE", + "size": { + "width": 180, + "length": 480 + }, + "heading": 346, + "accelSet": { + "accelYaw": -0.65, + "accelLong": -0.05 + }, + "brakes": { + "scs": "unavailable", + "wheelBrakes": { + "leftFront": false, + "rightFront": false, + "unavailable": true, + "leftRear": false, + "rightRear": false + }, + "abs": "off", + "traction": "unavailable", + "brakeBoost": "unavailable", + "auxBrakes": "unavailable" + }, + "accuracy": { + "semiMinor": 2, + "orientation": 0, + "semiMajor": 2 + }, + "id": "D51E0ECA", + "position": { + "elevation": 1691.9, + "latitude": 39.58753049388438, + "longitude": -105.09068655490051 + }, + "msgCnt": 9, + "speed": 28.43 + } + }, + "dataType": "us.dot.its.jpo.ode.plugin.j2735.J2735Bsm" + }, + "recordGeneratedAt": { + "$date": { + "$numberLong": "1717518161527" + } + } +},{ + "_id": { + "$oid": "665f3fc08c25216ef4f53d6a" + }, + "metadata": { + "securityResultCode": "success", + "bsmSource": "RV", + "schemaVersion": 6, + "odePacketID": "", + "sanitized": false, + "recordType": "bsmTx", + "recordGeneratedAt": "", + "maxDurationTime": 0, + "odeTimStartDateTime": "", + "receivedMessageDetails": { + "locationData": { + "elevation": "", + "heading": "", + "latitude": "", + "speed": "", + "longitude": "" + }, + "rxSource": "RV" + }, + "payloadType": "us.dot.its.jpo.ode.model.OdeBsmPayload", + "serialId": { + "recordId": 0, + "serialNumber": 0, + "streamId": "849c3ac6-4747-426f-ae5e-28ac8531b32c", + "bundleSize": 1, + "bundleId": 0 + }, + "logFileName": "", + "odeReceivedAt": "2024-06-04T16:22:41.628Z", + "originIp": "10.11.81.12" + }, + "payload": { + "data": { + "partII": [ + { + "id": "VehicleSafetyExtensions", + "value": { + "pathHistory": { + "crumbData": [ + { + "elevationOffset": 0.6, + "lonOffset": 0.0000387, + "timeOffset": 0.8, + "latOffset": -0.0001604 + }, + { + "elevationOffset": 4.3, + "lonOffset": 0.000231, + "timeOffset": 5.6, + "latOffset": -0.0011603 + }, + { + "elevationOffset": 9.1, + "lonOffset": 0.0003834, + "timeOffset": 11, + "latOffset": -0.0023425 + } + ] + }, + "pathPrediction": { + "confidence": 80, + "radiusOfCurve": -1622.5 + } + } + } + ], + "coreData": { + "secMark": 41628, + "transmission": "UNAVAILABLE", + "size": { + "width": 180, + "length": 480 + }, + "heading": 347, + "accelSet": { + "accelYaw": -0.65, + "accelLong": -0.05 + }, + "brakes": { + "scs": "unavailable", + "wheelBrakes": { + "leftFront": false, + "rightFront": false, + "unavailable": true, + "leftRear": false, + "rightRear": false + }, + "abs": "off", + "traction": "unavailable", + "brakeBoost": "unavailable", + "auxBrakes": "unavailable" + }, + "accuracy": { + "semiMinor": 2, + "orientation": 0, + "semiMajor": 2 + }, + "id": "D51E0ECA", + "position": { + "elevation": 1691.9, + "latitude": 39.58755633199964, + "longitude": -105.09069429121115 + }, + "msgCnt": 10, + "speed": 29.49 + } + }, + "dataType": "us.dot.its.jpo.ode.plugin.j2735.J2735Bsm" + }, + "recordGeneratedAt": { + "$date": { + "$numberLong": "1717518161636" + } + } +},{ + "_id": { + "$oid": "665f3fc08c25216ef4f53d6b" + }, + "metadata": { + "securityResultCode": "success", + "bsmSource": "RV", + "schemaVersion": 6, + "odePacketID": "", + "sanitized": false, + "recordType": "bsmTx", + "recordGeneratedAt": "", + "maxDurationTime": 0, + "odeTimStartDateTime": "", + "receivedMessageDetails": { + "locationData": { + "elevation": "", + "heading": "", + "latitude": "", + "speed": "", + "longitude": "" + }, + "rxSource": "RV" + }, + "payloadType": "us.dot.its.jpo.ode.model.OdeBsmPayload", + "serialId": { + "recordId": 0, + "serialNumber": 0, + "streamId": "849c3ac6-4747-426f-ae5e-28ac8531b32c", + "bundleSize": 1, + "bundleId": 0 + }, + "logFileName": "", + "odeReceivedAt": "2024-06-04T16:22:41.734Z", + "originIp": "10.11.81.12" + }, + "payload": { + "data": { + "partII": [ + { + "id": "VehicleSafetyExtensions", + "value": { + "pathHistory": { + "crumbData": [ + { + "elevationOffset": 0.6, + "lonOffset": 0.0000387, + "timeOffset": 0.8, + "latOffset": -0.0001604 + }, + { + "elevationOffset": 4.3, + "lonOffset": 0.000231, + "timeOffset": 5.6, + "latOffset": -0.0011603 + }, + { + "elevationOffset": 9.1, + "lonOffset": 0.0003834, + "timeOffset": 11, + "latOffset": -0.0023425 + } + ] + }, + "pathPrediction": { + "confidence": 80, + "radiusOfCurve": -1622.5 + } + } + } + ], + "coreData": { + "secMark": 41734, + "transmission": "UNAVAILABLE", + "size": { + "width": 180, + "length": 480 + }, + "heading": 347.2, + "accelSet": { + "accelYaw": -0.65, + "accelLong": -0.05 + }, + "brakes": { + "scs": "unavailable", + "wheelBrakes": { + "leftFront": false, + "rightFront": false, + "unavailable": true, + "leftRear": false, + "rightRear": false + }, + "abs": "off", + "traction": "unavailable", + "brakeBoost": "unavailable", + "auxBrakes": "unavailable" + }, + "accuracy": { + "semiMinor": 2, + "orientation": 0, + "semiMajor": 2 + }, + "id": "D51E0ECA", + "position": { + "elevation": 1691.9, + "latitude": 39.58758578743928, + "longitude": -105.09070300981843 + }, + "msgCnt": 11, + "speed": 33.59 + } + }, + "dataType": "us.dot.its.jpo.ode.plugin.j2735.J2735Bsm" + }, + "recordGeneratedAt": { + "$date": { + "$numberLong": "1717518161740" + } + } +},{ + "_id": { + "$oid": "665f3fc08c25216ef4f53d6c" + }, + "metadata": { + "securityResultCode": "success", + "bsmSource": "RV", + "schemaVersion": 6, + "odePacketID": "", + "sanitized": false, + "recordType": "bsmTx", + "recordGeneratedAt": "", + "maxDurationTime": 0, + "odeTimStartDateTime": "", + "receivedMessageDetails": { + "locationData": { + "elevation": "", + "heading": "", + "latitude": "", + "speed": "", + "longitude": "" + }, + "rxSource": "RV" + }, + "payloadType": "us.dot.its.jpo.ode.model.OdeBsmPayload", + "serialId": { + "recordId": 0, + "serialNumber": 0, + "streamId": "849c3ac6-4747-426f-ae5e-28ac8531b32c", + "bundleSize": 1, + "bundleId": 0 + }, + "logFileName": "", + "odeReceivedAt": "2024-06-04T16:22:41.842Z", + "originIp": "10.11.81.12" + }, + "payload": { + "data": { + "partII": [ + { + "id": "VehicleSafetyExtensions", + "value": { + "pathHistory": { + "crumbData": [ + { + "elevationOffset": 0.6, + "lonOffset": 0.0000387, + "timeOffset": 0.8, + "latOffset": -0.0001604 + }, + { + "elevationOffset": 4.3, + "lonOffset": 0.000231, + "timeOffset": 5.6, + "latOffset": -0.0011603 + }, + { + "elevationOffset": 9.1, + "lonOffset": 0.0003834, + "timeOffset": 11, + "latOffset": -0.0023425 + } + ] + }, + "pathPrediction": { + "confidence": 80, + "radiusOfCurve": -1622.5 + } + } + } + ], + "coreData": { + "secMark": 41842, + "transmission": "UNAVAILABLE", + "size": { + "width": 180, + "length": 480 + }, + "heading": 345.2, + "accelSet": { + "accelYaw": -0.65, + "accelLong": -0.05 + }, + "brakes": { + "scs": "unavailable", + "wheelBrakes": { + "leftFront": false, + "rightFront": false, + "unavailable": true, + "leftRear": false, + "rightRear": false + }, + "abs": "off", + "traction": "unavailable", + "brakeBoost": "unavailable", + "auxBrakes": "unavailable" + }, + "accuracy": { + "semiMinor": 2, + "orientation": 0, + "semiMajor": 2 + }, + "id": "D51E0ECA", + "position": { + "elevation": 1691.9, + "latitude": 39.587613175819314, + "longitude": -105.09071239908775 + }, + "msgCnt": 12, + "speed": 31.5 + } + }, + "dataType": "us.dot.its.jpo.ode.plugin.j2735.J2735Bsm" + }, + "recordGeneratedAt": { + "$date": { + "$numberLong": "1717518161848" + } + } +},{ + "_id": { + "$oid": "665f3fc08c25216ef4f53d6d" + }, + "metadata": { + "securityResultCode": "success", + "bsmSource": "RV", + "schemaVersion": 6, + "odePacketID": "", + "sanitized": false, + "recordType": "bsmTx", + "recordGeneratedAt": "", + "maxDurationTime": 0, + "odeTimStartDateTime": "", + "receivedMessageDetails": { + "locationData": { + "elevation": "", + "heading": "", + "latitude": "", + "speed": "", + "longitude": "" + }, + "rxSource": "RV" + }, + "payloadType": "us.dot.its.jpo.ode.model.OdeBsmPayload", + "serialId": { + "recordId": 0, + "serialNumber": 0, + "streamId": "849c3ac6-4747-426f-ae5e-28ac8531b32c", + "bundleSize": 1, + "bundleId": 0 + }, + "logFileName": "", + "odeReceivedAt": "2024-06-04T16:22:41.949Z", + "originIp": "10.11.81.12" + }, + "payload": { + "data": { + "partII": [ + { + "id": "VehicleSafetyExtensions", + "value": { + "pathHistory": { + "crumbData": [ + { + "elevationOffset": 0.6, + "lonOffset": 0.0000387, + "timeOffset": 0.8, + "latOffset": -0.0001604 + }, + { + "elevationOffset": 4.3, + "lonOffset": 0.000231, + "timeOffset": 5.6, + "latOffset": -0.0011603 + }, + { + "elevationOffset": 9.1, + "lonOffset": 0.0003834, + "timeOffset": 11, + "latOffset": -0.0023425 + } + ] + }, + "pathPrediction": { + "confidence": 80, + "radiusOfCurve": -1622.5 + } + } + } + ], + "coreData": { + "secMark": 41949, + "transmission": "UNAVAILABLE", + "size": { + "width": 180, + "length": 480 + }, + "heading": 346.2, + "accelSet": { + "accelYaw": -0.65, + "accelLong": -0.05 + }, + "brakes": { + "scs": "unavailable", + "wheelBrakes": { + "leftFront": false, + "rightFront": false, + "unavailable": true, + "leftRear": false, + "rightRear": false + }, + "abs": "off", + "traction": "unavailable", + "brakeBoost": "unavailable", + "auxBrakes": "unavailable" + }, + "accuracy": { + "semiMinor": 2, + "orientation": 0, + "semiMajor": 2 + }, + "id": "D51E0ECA", + "position": { + "elevation": 1691.9, + "latitude": 39.587642631234786, + "longitude": -105.09072178835711 + }, + "msgCnt": 13, + "speed": 33.73 + } + }, + "dataType": "us.dot.its.jpo.ode.plugin.j2735.J2735Bsm" + }, + "recordGeneratedAt": { + "$date": { + "$numberLong": "1717518161954" + } + } +},{ + "_id": { + "$oid": "665f3fc08c25216ef4f53d6e" + }, + "metadata": { + "securityResultCode": "success", + "bsmSource": "RV", + "schemaVersion": 6, + "odePacketID": "", + "sanitized": false, + "recordType": "bsmTx", + "recordGeneratedAt": "", + "maxDurationTime": 0, + "odeTimStartDateTime": "", + "receivedMessageDetails": { + "locationData": { + "elevation": "", + "heading": "", + "latitude": "", + "speed": "", + "longitude": "" + }, + "rxSource": "RV" + }, + "payloadType": "us.dot.its.jpo.ode.model.OdeBsmPayload", + "serialId": { + "recordId": 0, + "serialNumber": 0, + "streamId": "849c3ac6-4747-426f-ae5e-28ac8531b32c", + "bundleSize": 1, + "bundleId": 0 + }, + "logFileName": "", + "odeReceivedAt": "2024-06-04T16:22:42.056Z", + "originIp": "10.11.81.12" + }, + "payload": { + "data": { + "partII": [ + { + "id": "VehicleSafetyExtensions", + "value": { + "pathHistory": { + "crumbData": [ + { + "elevationOffset": 0.6, + "lonOffset": 0.0000387, + "timeOffset": 0.8, + "latOffset": -0.0001604 + }, + { + "elevationOffset": 4.3, + "lonOffset": 0.000231, + "timeOffset": 5.6, + "latOffset": -0.0011603 + }, + { + "elevationOffset": 9.1, + "lonOffset": 0.0003834, + "timeOffset": 11, + "latOffset": -0.0023425 + } + ] + }, + "pathPrediction": { + "confidence": 80, + "radiusOfCurve": -1622.5 + } + } + } + ], + "coreData": { + "secMark": 42056, + "transmission": "UNAVAILABLE", + "size": { + "width": 180, + "length": 480 + }, + "heading": 345.6, + "accelSet": { + "accelYaw": -0.65, + "accelLong": -0.05 + }, + "brakes": { + "scs": "unavailable", + "wheelBrakes": { + "leftFront": false, + "rightFront": false, + "unavailable": true, + "leftRear": false, + "rightRear": false + }, + "abs": "off", + "traction": "unavailable", + "brakeBoost": "unavailable", + "auxBrakes": "unavailable" + }, + "accuracy": { + "semiMinor": 2, + "orientation": 0, + "semiMajor": 2 + }, + "id": "D51E0ECA", + "position": { + "elevation": 1691.9, + "latitude": 39.58767673748972, + "longitude": -105.09073318961273 + }, + "msgCnt": 14, + "speed": 39.16 + } + }, + "dataType": "us.dot.its.jpo.ode.plugin.j2735.J2735Bsm" + }, + "recordGeneratedAt": { + "$date": { + "$numberLong": "1717518162061" + } + } +},{ + "_id": { + "$oid": "665f3fc08c25216ef4f53d6f" + }, + "metadata": { + "securityResultCode": "success", + "bsmSource": "RV", + "schemaVersion": 6, + "odePacketID": "", + "sanitized": false, + "recordType": "bsmTx", + "recordGeneratedAt": "", + "maxDurationTime": 0, + "odeTimStartDateTime": "", + "receivedMessageDetails": { + "locationData": { + "elevation": "", + "heading": "", + "latitude": "", + "speed": "", + "longitude": "" + }, + "rxSource": "RV" + }, + "payloadType": "us.dot.its.jpo.ode.model.OdeBsmPayload", + "serialId": { + "recordId": 0, + "serialNumber": 0, + "streamId": "849c3ac6-4747-426f-ae5e-28ac8531b32c", + "bundleSize": 1, + "bundleId": 0 + }, + "logFileName": "", + "odeReceivedAt": "2024-06-04T16:22:42.163Z", + "originIp": "10.11.81.12" + }, + "payload": { + "data": { + "partII": [ + { + "id": "VehicleSafetyExtensions", + "value": { + "pathHistory": { + "crumbData": [ + { + "elevationOffset": 0.6, + "lonOffset": 0.0000387, + "timeOffset": 0.8, + "latOffset": -0.0001604 + }, + { + "elevationOffset": 4.3, + "lonOffset": 0.000231, + "timeOffset": 5.6, + "latOffset": -0.0011603 + }, + { + "elevationOffset": 9.1, + "lonOffset": 0.0003834, + "timeOffset": 11, + "latOffset": -0.0023425 + } + ] + }, + "pathPrediction": { + "confidence": 80, + "radiusOfCurve": -1622.5 + } + } + } + ], + "coreData": { + "secMark": 42163, + "transmission": "UNAVAILABLE", + "size": { + "width": 180, + "length": 480 + }, + "heading": 350, + "accelSet": { + "accelYaw": -0.65, + "accelLong": -0.05 + }, + "brakes": { + "scs": "unavailable", + "wheelBrakes": { + "leftFront": false, + "rightFront": false, + "unavailable": true, + "leftRear": false, + "rightRear": false + }, + "abs": "off", + "traction": "unavailable", + "brakeBoost": "unavailable", + "auxBrakes": "unavailable" + }, + "accuracy": { + "semiMinor": 2, + "orientation": 0, + "semiMajor": 2 + }, + "id": "D51E0ECA", + "position": { + "elevation": 1691.9, + "latitude": 39.58769430737206, + "longitude": -105.09073721358531 + }, + "msgCnt": 15, + "speed": 19.84 + } + }, + "dataType": "us.dot.its.jpo.ode.plugin.j2735.J2735Bsm" + }, + "recordGeneratedAt": { + "$date": { + "$numberLong": "1717518162167" + } + } +},{ + "_id": { + "$oid": "665f3fc08c25216ef4f53d70" + }, + "metadata": { + "securityResultCode": "success", + "bsmSource": "RV", + "schemaVersion": 6, + "odePacketID": "", + "sanitized": false, + "recordType": "bsmTx", + "recordGeneratedAt": "", + "maxDurationTime": 0, + "odeTimStartDateTime": "", + "receivedMessageDetails": { + "locationData": { + "elevation": "", + "heading": "", + "latitude": "", + "speed": "", + "longitude": "" + }, + "rxSource": "RV" + }, + "payloadType": "us.dot.its.jpo.ode.model.OdeBsmPayload", + "serialId": { + "recordId": 0, + "serialNumber": 0, + "streamId": "849c3ac6-4747-426f-ae5e-28ac8531b32c", + "bundleSize": 1, + "bundleId": 0 + }, + "logFileName": "", + "odeReceivedAt": "2024-06-04T16:22:42.271Z", + "originIp": "10.11.81.12" + }, + "payload": { + "data": { + "partII": [ + { + "id": "VehicleSafetyExtensions", + "value": { + "pathHistory": { + "crumbData": [ + { + "elevationOffset": 0.6, + "lonOffset": 0.0000387, + "timeOffset": 0.8, + "latOffset": -0.0001604 + }, + { + "elevationOffset": 4.3, + "lonOffset": 0.000231, + "timeOffset": 5.6, + "latOffset": -0.0011603 + }, + { + "elevationOffset": 9.1, + "lonOffset": 0.0003834, + "timeOffset": 11, + "latOffset": -0.0023425 + } + ] + }, + "pathPrediction": { + "confidence": 80, + "radiusOfCurve": -1622.5 + } + } + } + ], + "coreData": { + "secMark": 42271, + "transmission": "UNAVAILABLE", + "size": { + "width": 180, + "length": 480 + }, + "heading": 349, + "accelSet": { + "accelYaw": -0.65, + "accelLong": -0.05 + }, + "brakes": { + "scs": "unavailable", + "wheelBrakes": { + "leftFront": false, + "rightFront": false, + "unavailable": true, + "leftRear": false, + "rightRear": false + }, + "abs": "off", + "traction": "unavailable", + "brakeBoost": "unavailable", + "auxBrakes": "unavailable" + }, + "accuracy": { + "semiMinor": 2, + "orientation": 0, + "semiMajor": 2 + }, + "id": "D51E0ECA", + "position": { + "elevation": 1691.9, + "latitude": 39.58771032696678, + "longitude": -105.09074123755789 + }, + "msgCnt": 16, + "speed": 18.14 + } + }, + "dataType": "us.dot.its.jpo.ode.plugin.j2735.J2735Bsm" + }, + "recordGeneratedAt": { + "$date": { + "$numberLong": "1717518162273" + } + } +},{ + "_id": { + "$oid": "665f3fc08c25216ef4f53d71" + }, + "metadata": { + "securityResultCode": "success", + "bsmSource": "RV", + "schemaVersion": 6, + "odePacketID": "", + "sanitized": false, + "recordType": "bsmTx", + "recordGeneratedAt": "", + "maxDurationTime": 0, + "odeTimStartDateTime": "", + "receivedMessageDetails": { + "locationData": { + "elevation": "", + "heading": "", + "latitude": "", + "speed": "", + "longitude": "" + }, + "rxSource": "RV" + }, + "payloadType": "us.dot.its.jpo.ode.model.OdeBsmPayload", + "serialId": { + "recordId": 0, + "serialNumber": 0, + "streamId": "849c3ac6-4747-426f-ae5e-28ac8531b32c", + "bundleSize": 1, + "bundleId": 0 + }, + "logFileName": "", + "odeReceivedAt": "2024-06-04T16:22:42.378Z", + "originIp": "10.11.81.12" + }, + "payload": { + "data": { + "partII": [ + { + "id": "VehicleSafetyExtensions", + "value": { + "pathHistory": { + "crumbData": [ + { + "elevationOffset": 0.6, + "lonOffset": 0.0000387, + "timeOffset": 0.8, + "latOffset": -0.0001604 + }, + { + "elevationOffset": 4.3, + "lonOffset": 0.000231, + "timeOffset": 5.6, + "latOffset": -0.0011603 + }, + { + "elevationOffset": 9.1, + "lonOffset": 0.0003834, + "timeOffset": 11, + "latOffset": -0.0023425 + } + ] + }, + "pathPrediction": { + "confidence": 80, + "radiusOfCurve": -1622.5 + } + } + } + ], + "coreData": { + "secMark": 42378, + "transmission": "UNAVAILABLE", + "size": { + "width": 180, + "length": 480 + }, + "heading": 345.2, + "accelSet": { + "accelYaw": -0.65, + "accelLong": -0.05 + }, + "brakes": { + "scs": "unavailable", + "wheelBrakes": { + "leftFront": false, + "rightFront": false, + "unavailable": true, + "leftRear": false, + "rightRear": false + }, + "abs": "off", + "traction": "unavailable", + "brakeBoost": "unavailable", + "auxBrakes": "unavailable" + }, + "accuracy": { + "semiMinor": 2, + "orientation": 0, + "semiMajor": 2 + }, + "id": "D51E0ECA", + "position": { + "elevation": 1691.9, + "latitude": 39.58772789684062, + "longitude": -105.09074727351678 + }, + "msgCnt": 17, + "speed": 20.21 + } + }, + "dataType": "us.dot.its.jpo.ode.plugin.j2735.J2735Bsm" + }, + "recordGeneratedAt": { + "$date": { + "$numberLong": "1717518162382" + } + } +},{ + "_id": { + "$oid": "665f3fc08c25216ef4f53d72" + }, + "metadata": { + "securityResultCode": "success", + "bsmSource": "RV", + "schemaVersion": 6, + "odePacketID": "", + "sanitized": false, + "recordType": "bsmTx", + "recordGeneratedAt": "", + "maxDurationTime": 0, + "odeTimStartDateTime": "", + "receivedMessageDetails": { + "locationData": { + "elevation": "", + "heading": "", + "latitude": "", + "speed": "", + "longitude": "" + }, + "rxSource": "RV" + }, + "payloadType": "us.dot.its.jpo.ode.model.OdeBsmPayload", + "serialId": { + "recordId": 0, + "serialNumber": 0, + "streamId": "849c3ac6-4747-426f-ae5e-28ac8531b32c", + "bundleSize": 1, + "bundleId": 0 + }, + "logFileName": "", + "odeReceivedAt": "2024-06-04T16:22:42.485Z", + "originIp": "10.11.81.12" + }, + "payload": { + "data": { + "partII": [ + { + "id": "VehicleSafetyExtensions", + "value": { + "pathHistory": { + "crumbData": [ + { + "elevationOffset": 0.6, + "lonOffset": 0.0000387, + "timeOffset": 0.8, + "latOffset": -0.0001604 + }, + { + "elevationOffset": 4.3, + "lonOffset": 0.000231, + "timeOffset": 5.6, + "latOffset": -0.0011603 + }, + { + "elevationOffset": 9.1, + "lonOffset": 0.0003834, + "timeOffset": 11, + "latOffset": -0.0023425 + } + ] + }, + "pathPrediction": { + "confidence": 80, + "radiusOfCurve": -1622.5 + } + } + } + ], + "coreData": { + "secMark": 42485, + "transmission": "UNAVAILABLE", + "size": { + "width": 180, + "length": 480 + }, + "heading": 346.3, + "accelSet": { + "accelYaw": -0.65, + "accelLong": -0.05 + }, + "brakes": { + "scs": "unavailable", + "wheelBrakes": { + "leftFront": false, + "rightFront": false, + "unavailable": true, + "leftRear": false, + "rightRear": false + }, + "abs": "off", + "traction": "unavailable", + "brakeBoost": "unavailable", + "auxBrakes": "unavailable" + }, + "accuracy": { + "semiMinor": 2, + "orientation": 0, + "semiMajor": 2 + }, + "id": "D51E0ECA", + "position": { + "elevation": 1691.9, + "latitude": 39.58774908403548, + "longitude": -105.09075398013772 + }, + "msgCnt": 18, + "speed": 24.25 + } + }, + "dataType": "us.dot.its.jpo.ode.plugin.j2735.J2735Bsm" + }, + "recordGeneratedAt": { + "$date": { + "$numberLong": "1717518162486" + } + } +},{ + "_id": { + "$oid": "665f3fc08c25216ef4f53d73" + }, + "metadata": { + "securityResultCode": "success", + "bsmSource": "RV", + "schemaVersion": 6, + "odePacketID": "", + "sanitized": false, + "recordType": "bsmTx", + "recordGeneratedAt": "", + "maxDurationTime": 0, + "odeTimStartDateTime": "", + "receivedMessageDetails": { + "locationData": { + "elevation": "", + "heading": "", + "latitude": "", + "speed": "", + "longitude": "" + }, + "rxSource": "RV" + }, + "payloadType": "us.dot.its.jpo.ode.model.OdeBsmPayload", + "serialId": { + "recordId": 0, + "serialNumber": 0, + "streamId": "849c3ac6-4747-426f-ae5e-28ac8531b32c", + "bundleSize": 1, + "bundleId": 0 + }, + "logFileName": "", + "odeReceivedAt": "2024-06-04T16:22:42.593Z", + "originIp": "10.11.81.12" + }, + "payload": { + "data": { + "partII": [ + { + "id": "VehicleSafetyExtensions", + "value": { + "pathHistory": { + "crumbData": [ + { + "elevationOffset": 0.6, + "lonOffset": 0.0000387, + "timeOffset": 0.8, + "latOffset": -0.0001604 + }, + { + "elevationOffset": 4.3, + "lonOffset": 0.000231, + "timeOffset": 5.6, + "latOffset": -0.0011603 + }, + { + "elevationOffset": 9.1, + "lonOffset": 0.0003834, + "timeOffset": 11, + "latOffset": -0.0023425 + } + ] + }, + "pathPrediction": { + "confidence": 80, + "radiusOfCurve": -1622.5 + } + } + } + ], + "coreData": { + "secMark": 42593, + "transmission": "UNAVAILABLE", + "size": { + "width": 180, + "length": 480 + }, + "heading": 351.4, + "accelSet": { + "accelYaw": -0.65, + "accelLong": -0.05 + }, + "brakes": { + "scs": "unavailable", + "wheelBrakes": { + "leftFront": false, + "rightFront": false, + "unavailable": true, + "leftRear": false, + "rightRear": false + }, + "abs": "off", + "traction": "unavailable", + "brakeBoost": "unavailable", + "auxBrakes": "unavailable" + }, + "accuracy": { + "semiMinor": 2, + "orientation": 0, + "semiMajor": 2 + }, + "id": "D51E0ECA", + "position": { + "elevation": 1691.9, + "latitude": 39.58776613713885, + "longitude": -105.0907573334482 + }, + "msgCnt": 19, + "speed": 19.18 + } + }, + "dataType": "us.dot.its.jpo.ode.plugin.j2735.J2735Bsm" + }, + "recordGeneratedAt": { + "$date": { + "$numberLong": "1717518162593" + } + } +},{ + "_id": { + "$oid": "665f3fc08c25216ef4f53d74" + }, + "metadata": { + "securityResultCode": "success", + "bsmSource": "RV", + "schemaVersion": 6, + "odePacketID": "", + "sanitized": false, + "recordType": "bsmTx", + "recordGeneratedAt": "", + "maxDurationTime": 0, + "odeTimStartDateTime": "", + "receivedMessageDetails": { + "locationData": { + "elevation": "", + "heading": "", + "latitude": "", + "speed": "", + "longitude": "" + }, + "rxSource": "RV" + }, + "payloadType": "us.dot.its.jpo.ode.model.OdeBsmPayload", + "serialId": { + "recordId": 0, + "serialNumber": 0, + "streamId": "849c3ac6-4747-426f-ae5e-28ac8531b32c", + "bundleSize": 1, + "bundleId": 0 + }, + "logFileName": "", + "odeReceivedAt": "2024-06-04T16:22:42.7Z", + "originIp": "10.11.81.12" + }, + "payload": { + "data": { + "partII": [ + { + "id": "VehicleSafetyExtensions", + "value": { + "pathHistory": { + "crumbData": [ + { + "elevationOffset": 0.6, + "lonOffset": 0.0000387, + "timeOffset": 0.8, + "latOffset": -0.0001604 + }, + { + "elevationOffset": 4.3, + "lonOffset": 0.000231, + "timeOffset": 5.6, + "latOffset": -0.0011603 + }, + { + "elevationOffset": 9.1, + "lonOffset": 0.0003834, + "timeOffset": 11, + "latOffset": -0.0023425 + } + ] + }, + "pathPrediction": { + "confidence": 80, + "radiusOfCurve": -1622.5 + } + } + } + ], + "coreData": { + "secMark": 42700, + "transmission": "UNAVAILABLE", + "size": { + "width": 180, + "length": 480 + }, + "heading": 346.7, + "accelSet": { + "accelYaw": -0.65, + "accelLong": -0.05 + }, + "brakes": { + "scs": "unavailable", + "wheelBrakes": { + "leftFront": false, + "rightFront": false, + "unavailable": true, + "leftRear": false, + "rightRear": false + }, + "abs": "off", + "traction": "unavailable", + "brakeBoost": "unavailable", + "auxBrakes": "unavailable" + }, + "accuracy": { + "semiMinor": 2, + "orientation": 0, + "semiMajor": 2 + }, + "id": "D51E0ECA", + "position": { + "elevation": 1691.9, + "latitude": 39.587785774040526, + "longitude": -105.09076336940707 + }, + "msgCnt": 20, + "speed": 22.44 + } + }, + "dataType": "us.dot.its.jpo.ode.plugin.j2735.J2735Bsm" + }, + "recordGeneratedAt": { + "$date": { + "$numberLong": "1717518162715" + } + } +},{ + "_id": { + "$oid": "665f3fc08c25216ef4f53d75" + }, + "metadata": { + "securityResultCode": "success", + "bsmSource": "RV", + "schemaVersion": 6, + "odePacketID": "", + "sanitized": false, + "recordType": "bsmTx", + "recordGeneratedAt": "", + "maxDurationTime": 0, + "odeTimStartDateTime": "", + "receivedMessageDetails": { + "locationData": { + "elevation": "", + "heading": "", + "latitude": "", + "speed": "", + "longitude": "" + }, + "rxSource": "RV" + }, + "payloadType": "us.dot.its.jpo.ode.model.OdeBsmPayload", + "serialId": { + "recordId": 0, + "serialNumber": 0, + "streamId": "849c3ac6-4747-426f-ae5e-28ac8531b32c", + "bundleSize": 1, + "bundleId": 0 + }, + "logFileName": "", + "odeReceivedAt": "2024-06-04T16:22:42.807Z", + "originIp": "10.11.81.12" + }, + "payload": { + "data": { + "partII": [ + { + "id": "VehicleSafetyExtensions", + "value": { + "pathHistory": { + "crumbData": [ + { + "elevationOffset": 0.6, + "lonOffset": 0.0000387, + "timeOffset": 0.8, + "latOffset": -0.0001604 + }, + { + "elevationOffset": 4.3, + "lonOffset": 0.000231, + "timeOffset": 5.6, + "latOffset": -0.0011603 + }, + { + "elevationOffset": 9.1, + "lonOffset": 0.0003834, + "timeOffset": 11, + "latOffset": -0.0023425 + } + ] + }, + "pathPrediction": { + "confidence": 80, + "radiusOfCurve": -1622.5 + } + } + } + ], + "coreData": { + "secMark": 42807, + "transmission": "UNAVAILABLE", + "size": { + "width": 180, + "length": 480 + }, + "heading": 347.5, + "accelSet": { + "accelYaw": -0.65, + "accelLong": -0.05 + }, + "brakes": { + "scs": "unavailable", + "wheelBrakes": { + "leftFront": false, + "rightFront": false, + "unavailable": true, + "leftRear": false, + "rightRear": false + }, + "abs": "off", + "traction": "unavailable", + "brakeBoost": "unavailable", + "auxBrakes": "unavailable" + }, + "accuracy": { + "semiMinor": 2, + "orientation": 0, + "semiMajor": 2 + }, + "id": "D51E0ECA", + "position": { + "elevation": 1691.9, + "latitude": 39.587804377415964, + "longitude": -105.09076873470384 + }, + "msgCnt": 21, + "speed": 21.19 + } + }, + "dataType": "us.dot.its.jpo.ode.plugin.j2735.J2735Bsm" + }, + "recordGeneratedAt": { + "$date": { + "$numberLong": "1717518162823" + } + } +},{ + "_id": { + "$oid": "665f3fc08c25216ef4f53d76" + }, + "metadata": { + "securityResultCode": "success", + "bsmSource": "RV", + "schemaVersion": 6, + "odePacketID": "", + "sanitized": false, + "recordType": "bsmTx", + "recordGeneratedAt": "", + "maxDurationTime": 0, + "odeTimStartDateTime": "", + "receivedMessageDetails": { + "locationData": { + "elevation": "", + "heading": "", + "latitude": "", + "speed": "", + "longitude": "" + }, + "rxSource": "RV" + }, + "payloadType": "us.dot.its.jpo.ode.model.OdeBsmPayload", + "serialId": { + "recordId": 0, + "serialNumber": 0, + "streamId": "849c3ac6-4747-426f-ae5e-28ac8531b32c", + "bundleSize": 1, + "bundleId": 0 + }, + "logFileName": "", + "odeReceivedAt": "2024-06-04T16:22:42.915Z", + "originIp": "10.11.81.12" + }, + "payload": { + "data": { + "partII": [ + { + "id": "VehicleSafetyExtensions", + "value": { + "pathHistory": { + "crumbData": [ + { + "elevationOffset": 0.6, + "lonOffset": 0.0000387, + "timeOffset": 0.8, + "latOffset": -0.0001604 + }, + { + "elevationOffset": 4.3, + "lonOffset": 0.000231, + "timeOffset": 5.6, + "latOffset": -0.0011603 + }, + { + "elevationOffset": 9.1, + "lonOffset": 0.0003834, + "timeOffset": 11, + "latOffset": -0.0023425 + } + ] + }, + "pathPrediction": { + "confidence": 80, + "radiusOfCurve": -1622.5 + } + } + } + ], + "coreData": { + "secMark": 42915, + "transmission": "UNAVAILABLE", + "size": { + "width": 180, + "length": 480 + }, + "heading": 349, + "accelSet": { + "accelYaw": -0.65, + "accelLong": -0.05 + }, + "brakes": { + "scs": "unavailable", + "wheelBrakes": { + "leftFront": false, + "rightFront": false, + "unavailable": true, + "leftRear": false, + "rightRear": false + }, + "abs": "off", + "traction": "unavailable", + "brakeBoost": "unavailable", + "auxBrakes": "unavailable" + }, + "accuracy": { + "semiMinor": 2, + "orientation": 0, + "semiMajor": 2 + }, + "id": "D51E0ECA", + "position": { + "elevation": 1691.9, + "latitude": 39.587820396985244, + "longitude": -105.09077275867644 + }, + "msgCnt": 22, + "speed": 18.14 + } + }, + "dataType": "us.dot.its.jpo.ode.plugin.j2735.J2735Bsm" + }, + "recordGeneratedAt": { + "$date": { + "$numberLong": "1717518162930" + } + } +},{ + "_id": { + "$oid": "665f3fc08c25216ef4f53d77" + }, + "metadata": { + "securityResultCode": "success", + "bsmSource": "RV", + "schemaVersion": 6, + "odePacketID": "", + "sanitized": false, + "recordType": "bsmTx", + "recordGeneratedAt": "", + "maxDurationTime": 0, + "odeTimStartDateTime": "", + "receivedMessageDetails": { + "locationData": { + "elevation": "", + "heading": "", + "latitude": "", + "speed": "", + "longitude": "" + }, + "rxSource": "RV" + }, + "payloadType": "us.dot.its.jpo.ode.model.OdeBsmPayload", + "serialId": { + "recordId": 0, + "serialNumber": 0, + "streamId": "849c3ac6-4747-426f-ae5e-28ac8531b32c", + "bundleSize": 1, + "bundleId": 0 + }, + "logFileName": "", + "odeReceivedAt": "2024-06-04T16:22:43.023Z", + "originIp": "10.11.81.12" + }, + "payload": { + "data": { + "partII": [ + { + "id": "VehicleSafetyExtensions", + "value": { + "pathHistory": { + "crumbData": [ + { + "elevationOffset": 0.6, + "lonOffset": 0.0000387, + "timeOffset": 0.8, + "latOffset": -0.0001604 + }, + { + "elevationOffset": 4.3, + "lonOffset": 0.000231, + "timeOffset": 5.6, + "latOffset": -0.0011603 + }, + { + "elevationOffset": 9.1, + "lonOffset": 0.0003834, + "timeOffset": 11, + "latOffset": -0.0023425 + } + ] + }, + "pathPrediction": { + "confidence": 80, + "radiusOfCurve": -1622.5 + } + } + } + ], + "coreData": { + "secMark": 43023, + "transmission": "UNAVAILABLE", + "size": { + "width": 180, + "length": 480 + }, + "heading": 349.7, + "accelSet": { + "accelYaw": -0.65, + "accelLong": -0.05 + }, + "brakes": { + "scs": "unavailable", + "wheelBrakes": { + "leftFront": false, + "rightFront": false, + "unavailable": true, + "leftRear": false, + "rightRear": false + }, + "abs": "off", + "traction": "unavailable", + "brakeBoost": "unavailable", + "auxBrakes": "unavailable" + }, + "accuracy": { + "semiMinor": 2, + "orientation": 0, + "semiMajor": 2 + }, + "id": "D51E0ECA", + "position": { + "elevation": 1691.9, + "latitude": 39.587837450071035, + "longitude": -105.09077678264897 + }, + "msgCnt": 23, + "speed": 19.27 + } + }, + "dataType": "us.dot.its.jpo.ode.plugin.j2735.J2735Bsm" + }, + "recordGeneratedAt": { + "$date": { + "$numberLong": "1717518163035" + } + } +},{ + "_id": { + "$oid": "665f3fc08c25216ef4f53d78" + }, + "metadata": { + "securityResultCode": "success", + "bsmSource": "RV", + "schemaVersion": 6, + "odePacketID": "", + "sanitized": false, + "recordType": "bsmTx", + "recordGeneratedAt": "", + "maxDurationTime": 0, + "odeTimStartDateTime": "", + "receivedMessageDetails": { + "locationData": { + "elevation": "", + "heading": "", + "latitude": "", + "speed": "", + "longitude": "" + }, + "rxSource": "RV" + }, + "payloadType": "us.dot.its.jpo.ode.model.OdeBsmPayload", + "serialId": { + "recordId": 0, + "serialNumber": 0, + "streamId": "849c3ac6-4747-426f-ae5e-28ac8531b32c", + "bundleSize": 1, + "bundleId": 0 + }, + "logFileName": "", + "odeReceivedAt": "2024-06-04T16:22:43.128Z", + "originIp": "10.11.81.12" + }, + "payload": { + "data": { + "partII": [ + { + "id": "VehicleSafetyExtensions", + "value": { + "pathHistory": { + "crumbData": [ + { + "elevationOffset": 0.6, + "lonOffset": 0.0000387, + "timeOffset": 0.8, + "latOffset": -0.0001604 + }, + { + "elevationOffset": 4.3, + "lonOffset": 0.000231, + "timeOffset": 5.6, + "latOffset": -0.0011603 + }, + { + "elevationOffset": 9.1, + "lonOffset": 0.0003834, + "timeOffset": 11, + "latOffset": -0.0023425 + } + ] + }, + "pathPrediction": { + "confidence": 80, + "radiusOfCurve": -1622.5 + } + } + } + ], + "coreData": { + "secMark": 43128, + "transmission": "UNAVAILABLE", + "size": { + "width": 180, + "length": 480 + }, + "heading": 349.7, + "accelSet": { + "accelYaw": -0.65, + "accelLong": -0.05 + }, + "brakes": { + "scs": "unavailable", + "wheelBrakes": { + "leftFront": false, + "rightFront": false, + "unavailable": true, + "leftRear": false, + "rightRear": false + }, + "abs": "off", + "traction": "unavailable", + "brakeBoost": "unavailable", + "auxBrakes": "unavailable" + }, + "accuracy": { + "semiMinor": 2, + "orientation": 0, + "semiMajor": 2 + }, + "id": "D51E0ECA", + "position": { + "elevation": 1691.9, + "latitude": 39.58785450315265, + "longitude": -105.0907808066216 + }, + "msgCnt": 24, + "speed": 19.27 + } + }, + "dataType": "us.dot.its.jpo.ode.plugin.j2735.J2735Bsm" + }, + "recordGeneratedAt": { + "$date": { + "$numberLong": "1717518163143" + } + } +},{ + "_id": { + "$oid": "665f3fc08c25216ef4f53d79" + }, + "metadata": { + "securityResultCode": "success", + "bsmSource": "RV", + "schemaVersion": 6, + "odePacketID": "", + "sanitized": false, + "recordType": "bsmTx", + "recordGeneratedAt": "", + "maxDurationTime": 0, + "odeTimStartDateTime": "", + "receivedMessageDetails": { + "locationData": { + "elevation": "", + "heading": "", + "latitude": "", + "speed": "", + "longitude": "" + }, + "rxSource": "RV" + }, + "payloadType": "us.dot.its.jpo.ode.model.OdeBsmPayload", + "serialId": { + "recordId": 0, + "serialNumber": 0, + "streamId": "849c3ac6-4747-426f-ae5e-28ac8531b32c", + "bundleSize": 1, + "bundleId": 0 + }, + "logFileName": "", + "odeReceivedAt": "2024-06-04T16:22:43.237Z", + "originIp": "10.11.81.12" + }, + "payload": { + "data": { + "partII": [ + { + "id": "VehicleSafetyExtensions", + "value": { + "pathHistory": { + "crumbData": [ + { + "elevationOffset": 0.6, + "lonOffset": 0.0000387, + "timeOffset": 0.8, + "latOffset": -0.0001604 + }, + { + "elevationOffset": 4.3, + "lonOffset": 0.000231, + "timeOffset": 5.6, + "latOffset": -0.0011603 + }, + { + "elevationOffset": 9.1, + "lonOffset": 0.0003834, + "timeOffset": 11, + "latOffset": -0.0023425 + } + ] + }, + "pathPrediction": { + "confidence": 80, + "radiusOfCurve": -1622.5 + } + } + } + ], + "coreData": { + "secMark": 43237, + "transmission": "UNAVAILABLE", + "size": { + "width": 180, + "length": 480 + }, + "heading": 347.3, + "accelSet": { + "accelYaw": -0.65, + "accelLong": -0.05 + }, + "brakes": { + "scs": "unavailable", + "wheelBrakes": { + "leftFront": false, + "rightFront": false, + "unavailable": true, + "leftRear": false, + "rightRear": false + }, + "abs": "off", + "traction": "unavailable", + "brakeBoost": "unavailable", + "auxBrakes": "unavailable" + }, + "accuracy": { + "semiMinor": 2, + "orientation": 0, + "semiMajor": 2 + }, + "id": "D51E0ECA", + "position": { + "elevation": 1691.9, + "latitude": 39.587870522710325, + "longitude": -105.09078550125625 + }, + "msgCnt": 25, + "speed": 18.26 + } + }, + "dataType": "us.dot.its.jpo.ode.plugin.j2735.J2735Bsm" + }, + "recordGeneratedAt": { + "$date": { + "$numberLong": "1717518163250" + } + } +},{ + "_id": { + "$oid": "665f3fc08c25216ef4f53d7a" + }, + "metadata": { + "securityResultCode": "success", + "bsmSource": "RV", + "schemaVersion": 6, + "odePacketID": "", + "sanitized": false, + "recordType": "bsmTx", + "recordGeneratedAt": "", + "maxDurationTime": 0, + "odeTimStartDateTime": "", + "receivedMessageDetails": { + "locationData": { + "elevation": "", + "heading": "", + "latitude": "", + "speed": "", + "longitude": "" + }, + "rxSource": "RV" + }, + "payloadType": "us.dot.its.jpo.ode.model.OdeBsmPayload", + "serialId": { + "recordId": 0, + "serialNumber": 0, + "streamId": "849c3ac6-4747-426f-ae5e-28ac8531b32c", + "bundleSize": 1, + "bundleId": 0 + }, + "logFileName": "", + "odeReceivedAt": "2024-06-04T16:22:43.344Z", + "originIp": "10.11.81.12" + }, + "payload": { + "data": { + "partII": [ + { + "id": "VehicleSafetyExtensions", + "value": { + "pathHistory": { + "crumbData": [ + { + "elevationOffset": 0.6, + "lonOffset": 0.0000387, + "timeOffset": 0.8, + "latOffset": -0.0001604 + }, + { + "elevationOffset": 4.3, + "lonOffset": 0.000231, + "timeOffset": 5.6, + "latOffset": -0.0011603 + }, + { + "elevationOffset": 9.1, + "lonOffset": 0.0003834, + "timeOffset": 11, + "latOffset": -0.0023425 + } + ] + }, + "pathPrediction": { + "confidence": 80, + "radiusOfCurve": -1622.5 + } + } + } + ], + "coreData": { + "secMark": 43344, + "transmission": "UNAVAILABLE", + "size": { + "width": 180, + "length": 480 + }, + "heading": 344.9, + "accelSet": { + "accelYaw": -0.65, + "accelLong": -0.05 + }, + "brakes": { + "scs": "unavailable", + "wheelBrakes": { + "leftFront": false, + "rightFront": false, + "unavailable": true, + "leftRear": false, + "rightRear": false + }, + "abs": "off", + "traction": "unavailable", + "brakeBoost": "unavailable", + "auxBrakes": "unavailable" + }, + "accuracy": { + "semiMinor": 2, + "orientation": 0, + "semiMajor": 2 + }, + "id": "D51E0ECA", + "position": { + "elevation": 1691.9, + "latitude": 39.587883958465525, + "longitude": -105.09079019589092 + }, + "msgCnt": 26, + "speed": 15.47 + } + }, + "dataType": "us.dot.its.jpo.ode.plugin.j2735.J2735Bsm" + }, + "recordGeneratedAt": { + "$date": { + "$numberLong": "1717518163357" + } + } +},{ + "_id": { + "$oid": "665f3fc08c25216ef4f53d7b" + }, + "metadata": { + "securityResultCode": "success", + "bsmSource": "RV", + "schemaVersion": 6, + "odePacketID": "", + "sanitized": false, + "recordType": "bsmTx", + "recordGeneratedAt": "", + "maxDurationTime": 0, + "odeTimStartDateTime": "", + "receivedMessageDetails": { + "locationData": { + "elevation": "", + "heading": "", + "latitude": "", + "speed": "", + "longitude": "" + }, + "rxSource": "RV" + }, + "payloadType": "us.dot.its.jpo.ode.model.OdeBsmPayload", + "serialId": { + "recordId": 0, + "serialNumber": 0, + "streamId": "849c3ac6-4747-426f-ae5e-28ac8531b32c", + "bundleSize": 1, + "bundleId": 0 + }, + "logFileName": "", + "odeReceivedAt": "2024-06-04T16:22:43.452Z", + "originIp": "10.11.81.12" + }, + "payload": { + "data": { + "partII": [ + { + "id": "VehicleSafetyExtensions", + "value": { + "pathHistory": { + "crumbData": [ + { + "elevationOffset": 0.6, + "lonOffset": 0.0000387, + "timeOffset": 0.8, + "latOffset": -0.0001604 + }, + { + "elevationOffset": 4.3, + "lonOffset": 0.000231, + "timeOffset": 5.6, + "latOffset": -0.0011603 + }, + { + "elevationOffset": 9.1, + "lonOffset": 0.0003834, + "timeOffset": 11, + "latOffset": -0.0023425 + } + ] + }, + "pathPrediction": { + "confidence": 80, + "radiusOfCurve": -1622.5 + } + } + } + ], + "coreData": { + "secMark": 43452, + "transmission": "UNAVAILABLE", + "size": { + "width": 180, + "length": 480 + }, + "heading": 344.5, + "accelSet": { + "accelYaw": -0.65, + "accelLong": -0.05 + }, + "brakes": { + "scs": "unavailable", + "wheelBrakes": { + "leftFront": false, + "rightFront": false, + "unavailable": true, + "leftRear": false, + "rightRear": false + }, + "abs": "off", + "traction": "unavailable", + "brakeBoost": "unavailable", + "auxBrakes": "unavailable" + }, + "accuracy": { + "semiMinor": 2, + "orientation": 0, + "semiMajor": 2 + }, + "id": "D51E0ECA", + "position": { + "elevation": 1691.9, + "latitude": 39.58789326014069, + "longitude": -105.09079354920141 + }, + "msgCnt": 27, + "speed": 10.73 + } + }, + "dataType": "us.dot.its.jpo.ode.plugin.j2735.J2735Bsm" + }, + "recordGeneratedAt": { + "$date": { + "$numberLong": "1717518163464" + } + } +},{ + "_id": { + "$oid": "665f3fc08c25216ef4f53d7c" + }, + "metadata": { + "securityResultCode": "success", + "bsmSource": "RV", + "schemaVersion": 6, + "odePacketID": "", + "sanitized": false, + "recordType": "bsmTx", + "recordGeneratedAt": "", + "maxDurationTime": 0, + "odeTimStartDateTime": "", + "receivedMessageDetails": { + "locationData": { + "elevation": "", + "heading": "", + "latitude": "", + "speed": "", + "longitude": "" + }, + "rxSource": "RV" + }, + "payloadType": "us.dot.its.jpo.ode.model.OdeBsmPayload", + "serialId": { + "recordId": 0, + "serialNumber": 0, + "streamId": "849c3ac6-4747-426f-ae5e-28ac8531b32c", + "bundleSize": 1, + "bundleId": 0 + }, + "logFileName": "", + "odeReceivedAt": "2024-06-04T16:22:43.561Z", + "originIp": "10.11.81.12" + }, + "payload": { + "data": { + "partII": [ + { + "id": "VehicleSafetyExtensions", + "value": { + "pathHistory": { + "crumbData": [ + { + "elevationOffset": 0.6, + "lonOffset": 0.0000387, + "timeOffset": 0.8, + "latOffset": -0.0001604 + }, + { + "elevationOffset": 4.3, + "lonOffset": 0.000231, + "timeOffset": 5.6, + "latOffset": -0.0011603 + }, + { + "elevationOffset": 9.1, + "lonOffset": 0.0003834, + "timeOffset": 11, + "latOffset": -0.0023425 + } + ] + }, + "pathPrediction": { + "confidence": 80, + "radiusOfCurve": -1622.5 + } + } + } + ], + "coreData": { + "secMark": 43561, + "transmission": "UNAVAILABLE", + "size": { + "width": 180, + "length": 480 + }, + "heading": 344.5, + "accelSet": { + "accelYaw": -0.65, + "accelLong": -0.05 + }, + "brakes": { + "scs": "unavailable", + "wheelBrakes": { + "leftFront": false, + "rightFront": false, + "unavailable": true, + "leftRear": false, + "rightRear": false + }, + "abs": "off", + "traction": "unavailable", + "brakeBoost": "unavailable", + "auxBrakes": "unavailable" + }, + "accuracy": { + "semiMinor": 2, + "orientation": 0, + "semiMajor": 2 + }, + "id": "D51E0ECA", + "position": { + "elevation": 1691.9, + "latitude": 39.58790256181461, + "longitude": -105.09079690251188 + }, + "msgCnt": 28, + "speed": 10.73 + } + }, + "dataType": "us.dot.its.jpo.ode.plugin.j2735.J2735Bsm" + }, + "recordGeneratedAt": { + "$date": { + "$numberLong": "1717518163570" + } + } +},{ + "_id": { + "$oid": "665f3fc08c25216ef4f53d7d" + }, + "metadata": { + "securityResultCode": "success", + "bsmSource": "RV", + "schemaVersion": 6, + "odePacketID": "", + "sanitized": false, + "recordType": "bsmTx", + "recordGeneratedAt": "", + "maxDurationTime": 0, + "odeTimStartDateTime": "", + "receivedMessageDetails": { + "locationData": { + "elevation": "", + "heading": "", + "latitude": "", + "speed": "", + "longitude": "" + }, + "rxSource": "RV" + }, + "payloadType": "us.dot.its.jpo.ode.model.OdeBsmPayload", + "serialId": { + "recordId": 0, + "serialNumber": 0, + "streamId": "849c3ac6-4747-426f-ae5e-28ac8531b32c", + "bundleSize": 1, + "bundleId": 0 + }, + "logFileName": "", + "odeReceivedAt": "2024-06-04T16:22:43.668Z", + "originIp": "10.11.81.12" + }, + "payload": { + "data": { + "partII": [ + { + "id": "VehicleSafetyExtensions", + "value": { + "pathHistory": { + "crumbData": [ + { + "elevationOffset": 0.6, + "lonOffset": 0.0000387, + "timeOffset": 0.8, + "latOffset": -0.0001604 + }, + { + "elevationOffset": 4.3, + "lonOffset": 0.000231, + "timeOffset": 5.6, + "latOffset": -0.0011603 + }, + { + "elevationOffset": 9.1, + "lonOffset": 0.0003834, + "timeOffset": 11, + "latOffset": -0.0023425 + } + ] + }, + "pathPrediction": { + "confidence": 80, + "radiusOfCurve": -1622.5 + } + } + } + ], + "coreData": { + "secMark": 43668, + "transmission": "UNAVAILABLE", + "size": { + "width": 180, + "length": 480 + }, + "heading": 332.4, + "accelSet": { + "accelYaw": -0.65, + "accelLong": -0.05 + }, + "brakes": { + "scs": "unavailable", + "wheelBrakes": { + "leftFront": false, + "rightFront": false, + "unavailable": true, + "leftRear": false, + "rightRear": false + }, + "abs": "off", + "traction": "unavailable", + "brakeBoost": "unavailable", + "auxBrakes": "unavailable" + }, + "accuracy": { + "semiMinor": 2, + "orientation": 0, + "semiMajor": 2 + }, + "id": "D51E0ECA", + "position": { + "elevation": 1691.9, + "latitude": 39.587914447284994, + "longitude": -105.09080495045706 + }, + "msgCnt": 29, + "speed": 14.91 + } + }, + "dataType": "us.dot.its.jpo.ode.plugin.j2735.J2735Bsm" + }, + "recordGeneratedAt": { + "$date": { + "$numberLong": "1717518163675" + } + } +},{ + "_id": { + "$oid": "665f3fc08c25216ef4f53d7e" + }, + "metadata": { + "securityResultCode": "success", + "bsmSource": "RV", + "schemaVersion": 6, + "odePacketID": "", + "sanitized": false, + "recordType": "bsmTx", + "recordGeneratedAt": "", + "maxDurationTime": 0, + "odeTimStartDateTime": "", + "receivedMessageDetails": { + "locationData": { + "elevation": "", + "heading": "", + "latitude": "", + "speed": "", + "longitude": "" + }, + "rxSource": "RV" + }, + "payloadType": "us.dot.its.jpo.ode.model.OdeBsmPayload", + "serialId": { + "recordId": 0, + "serialNumber": 0, + "streamId": "849c3ac6-4747-426f-ae5e-28ac8531b32c", + "bundleSize": 1, + "bundleId": 0 + }, + "logFileName": "", + "odeReceivedAt": "2024-06-04T16:22:43.774Z", + "originIp": "10.11.81.12" + }, + "payload": { + "data": { + "partII": [ + { + "id": "VehicleSafetyExtensions", + "value": { + "pathHistory": { + "crumbData": [ + { + "elevationOffset": 0.6, + "lonOffset": 0.0000387, + "timeOffset": 0.8, + "latOffset": -0.0001604 + }, + { + "elevationOffset": 4.3, + "lonOffset": 0.000231, + "timeOffset": 5.6, + "latOffset": -0.0011603 + }, + { + "elevationOffset": 9.1, + "lonOffset": 0.0003834, + "timeOffset": 11, + "latOffset": -0.0023425 + } + ] + }, + "pathPrediction": { + "confidence": 80, + "radiusOfCurve": -1622.5 + } + } + } + ], + "coreData": { + "secMark": 43774, + "transmission": "UNAVAILABLE", + "size": { + "width": 180, + "length": 480 + }, + "heading": 326.3, + "accelSet": { + "accelYaw": -0.65, + "accelLong": -0.05 + }, + "brakes": { + "scs": "unavailable", + "wheelBrakes": { + "leftFront": false, + "rightFront": false, + "unavailable": true, + "leftRear": false, + "rightRear": false + }, + "abs": "off", + "traction": "unavailable", + "brakeBoost": "unavailable", + "auxBrakes": "unavailable" + }, + "accuracy": { + "semiMinor": 2, + "orientation": 0, + "semiMajor": 2 + }, + "id": "D51E0ECA", + "position": { + "elevation": 1691.9, + "latitude": 39.58792529923446, + "longitude": -105.09081433972638 + }, + "msgCnt": 30, + "speed": 14.5 + } + }, + "dataType": "us.dot.its.jpo.ode.plugin.j2735.J2735Bsm" + }, + "recordGeneratedAt": { + "$date": { + "$numberLong": "1717518163782" + } + } +},{ + "_id": { + "$oid": "665f3fc08c25216ef4f53d7f" + }, + "metadata": { + "securityResultCode": "success", + "bsmSource": "RV", + "schemaVersion": 6, + "odePacketID": "", + "sanitized": false, + "recordType": "bsmTx", + "recordGeneratedAt": "", + "maxDurationTime": 0, + "odeTimStartDateTime": "", + "receivedMessageDetails": { + "locationData": { + "elevation": "", + "heading": "", + "latitude": "", + "speed": "", + "longitude": "" + }, + "rxSource": "RV" + }, + "payloadType": "us.dot.its.jpo.ode.model.OdeBsmPayload", + "serialId": { + "recordId": 0, + "serialNumber": 0, + "streamId": "849c3ac6-4747-426f-ae5e-28ac8531b32c", + "bundleSize": 1, + "bundleId": 0 + }, + "logFileName": "", + "odeReceivedAt": "2024-06-04T16:22:43.882Z", + "originIp": "10.11.81.12" + }, + "payload": { + "data": { + "partII": [ + { + "id": "VehicleSafetyExtensions", + "value": { + "pathHistory": { + "crumbData": [ + { + "elevationOffset": 0.6, + "lonOffset": 0.0000387, + "timeOffset": 0.8, + "latOffset": -0.0001604 + }, + { + "elevationOffset": 4.3, + "lonOffset": 0.000231, + "timeOffset": 5.6, + "latOffset": -0.0011603 + }, + { + "elevationOffset": 9.1, + "lonOffset": 0.0003834, + "timeOffset": 11, + "latOffset": -0.0023425 + } + ] + }, + "pathPrediction": { + "confidence": 80, + "radiusOfCurve": -1622.5 + } + } + } + ], + "coreData": { + "secMark": 43882, + "transmission": "UNAVAILABLE", + "size": { + "width": 180, + "length": 480 + }, + "heading": 323.1, + "accelSet": { + "accelYaw": -0.65, + "accelLong": -0.05 + }, + "brakes": { + "scs": "unavailable", + "wheelBrakes": { + "leftFront": false, + "rightFront": false, + "unavailable": true, + "leftRear": false, + "rightRear": false + }, + "abs": "off", + "traction": "unavailable", + "brakeBoost": "unavailable", + "auxBrakes": "unavailable" + }, + "accuracy": { + "semiMinor": 2, + "orientation": 0, + "semiMajor": 2 + }, + "id": "D51E0ECA", + "position": { + "elevation": 1691.9, + "latitude": 39.587935634422834, + "longitude": -105.09082439965782 + }, + "msgCnt": 31, + "speed": 14.37 + } + }, + "dataType": "us.dot.its.jpo.ode.plugin.j2735.J2735Bsm" + }, + "recordGeneratedAt": { + "$date": { + "$numberLong": "1717518163888" + } + } +},{ + "_id": { + "$oid": "665f3fc08c25216ef4f53d80" + }, + "metadata": { + "securityResultCode": "success", + "bsmSource": "RV", + "schemaVersion": 6, + "odePacketID": "", + "sanitized": false, + "recordType": "bsmTx", + "recordGeneratedAt": "", + "maxDurationTime": 0, + "odeTimStartDateTime": "", + "receivedMessageDetails": { + "locationData": { + "elevation": "", + "heading": "", + "latitude": "", + "speed": "", + "longitude": "" + }, + "rxSource": "RV" + }, + "payloadType": "us.dot.its.jpo.ode.model.OdeBsmPayload", + "serialId": { + "recordId": 0, + "serialNumber": 0, + "streamId": "849c3ac6-4747-426f-ae5e-28ac8531b32c", + "bundleSize": 1, + "bundleId": 0 + }, + "logFileName": "", + "odeReceivedAt": "2024-06-04T16:22:43.989Z", + "originIp": "10.11.81.12" + }, + "payload": { + "data": { + "partII": [ + { + "id": "VehicleSafetyExtensions", + "value": { + "pathHistory": { + "crumbData": [ + { + "elevationOffset": 0.6, + "lonOffset": 0.0000387, + "timeOffset": 0.8, + "latOffset": -0.0001604 + }, + { + "elevationOffset": 4.3, + "lonOffset": 0.000231, + "timeOffset": 5.6, + "latOffset": -0.0011603 + }, + { + "elevationOffset": 9.1, + "lonOffset": 0.0003834, + "timeOffset": 11, + "latOffset": -0.0023425 + } + ] + }, + "pathPrediction": { + "confidence": 80, + "radiusOfCurve": -1622.5 + } + } + } + ], + "coreData": { + "secMark": 43989, + "transmission": "UNAVAILABLE", + "size": { + "width": 180, + "length": 480 + }, + "heading": 306.5, + "accelSet": { + "accelYaw": -0.65, + "accelLong": -0.05 + }, + "brakes": { + "scs": "unavailable", + "wheelBrakes": { + "leftFront": false, + "rightFront": false, + "unavailable": true, + "leftRear": false, + "rightRear": false + }, + "abs": "off", + "traction": "unavailable", + "brakeBoost": "unavailable", + "auxBrakes": "unavailable" + }, + "accuracy": { + "semiMinor": 2, + "orientation": 0, + "semiMajor": 2 + }, + "id": "D51E0ECA", + "position": { + "elevation": 1691.9, + "latitude": 39.58794441933173, + "longitude": -105.09083982488603 + }, + "msgCnt": 32, + "speed": 16.44 + } + }, + "dataType": "us.dot.its.jpo.ode.plugin.j2735.J2735Bsm" + }, + "recordGeneratedAt": { + "$date": { + "$numberLong": "1717518163995" + } + } +},{ + "_id": { + "$oid": "665f3fc08c25216ef4f53d81" + }, + "metadata": { + "securityResultCode": "success", + "bsmSource": "RV", + "schemaVersion": 6, + "odePacketID": "", + "sanitized": false, + "recordType": "bsmTx", + "recordGeneratedAt": "", + "maxDurationTime": 0, + "odeTimStartDateTime": "", + "receivedMessageDetails": { + "locationData": { + "elevation": "", + "heading": "", + "latitude": "", + "speed": "", + "longitude": "" + }, + "rxSource": "RV" + }, + "payloadType": "us.dot.its.jpo.ode.model.OdeBsmPayload", + "serialId": { + "recordId": 0, + "serialNumber": 0, + "streamId": "849c3ac6-4747-426f-ae5e-28ac8531b32c", + "bundleSize": 1, + "bundleId": 0 + }, + "logFileName": "", + "odeReceivedAt": "2024-06-04T16:22:44.096Z", + "originIp": "10.11.81.12" + }, + "payload": { + "data": { + "partII": [ + { + "id": "VehicleSafetyExtensions", + "value": { + "pathHistory": { + "crumbData": [ + { + "elevationOffset": 0.6, + "lonOffset": 0.0000387, + "timeOffset": 0.8, + "latOffset": -0.0001604 + }, + { + "elevationOffset": 4.3, + "lonOffset": 0.000231, + "timeOffset": 5.6, + "latOffset": -0.0011603 + }, + { + "elevationOffset": 9.1, + "lonOffset": 0.0003834, + "timeOffset": 11, + "latOffset": -0.0023425 + } + ] + }, + "pathPrediction": { + "confidence": 80, + "radiusOfCurve": -1622.5 + } + } + } + ], + "coreData": { + "secMark": 44096, + "transmission": "UNAVAILABLE", + "size": { + "width": 180, + "length": 480 + }, + "heading": 291.2, + "accelSet": { + "accelYaw": -0.65, + "accelLong": -0.05 + }, + "brakes": { + "scs": "unavailable", + "wheelBrakes": { + "leftFront": false, + "rightFront": false, + "unavailable": true, + "leftRear": false, + "rightRear": false + }, + "abs": "off", + "traction": "unavailable", + "brakeBoost": "unavailable", + "auxBrakes": "unavailable" + }, + "accuracy": { + "semiMinor": 2, + "orientation": 0, + "semiMajor": 2 + }, + "id": "D51E0ECA", + "position": { + "elevation": 1691.9, + "latitude": 39.58795062044324, + "longitude": -105.090860615411 + }, + "msgCnt": 33, + "speed": 19.1 + } + }, + "dataType": "us.dot.its.jpo.ode.plugin.j2735.J2735Bsm" + }, + "recordGeneratedAt": { + "$date": { + "$numberLong": "1717518164100" + } + } +},{ + "_id": { + "$oid": "665f3fc08c25216ef4f53d82" + }, + "metadata": { + "securityResultCode": "success", + "bsmSource": "RV", + "schemaVersion": 6, + "odePacketID": "", + "sanitized": false, + "recordType": "bsmTx", + "recordGeneratedAt": "", + "maxDurationTime": 0, + "odeTimStartDateTime": "", + "receivedMessageDetails": { + "locationData": { + "elevation": "", + "heading": "", + "latitude": "", + "speed": "", + "longitude": "" + }, + "rxSource": "RV" + }, + "payloadType": "us.dot.its.jpo.ode.model.OdeBsmPayload", + "serialId": { + "recordId": 0, + "serialNumber": 0, + "streamId": "849c3ac6-4747-426f-ae5e-28ac8531b32c", + "bundleSize": 1, + "bundleId": 0 + }, + "logFileName": "", + "odeReceivedAt": "2024-06-04T16:22:44.202Z", + "originIp": "10.11.81.12" + }, + "payload": { + "data": { + "partII": [ + { + "id": "VehicleSafetyExtensions", + "value": { + "pathHistory": { + "crumbData": [ + { + "elevationOffset": 0.6, + "lonOffset": 0.0000387, + "timeOffset": 0.8, + "latOffset": -0.0001604 + }, + { + "elevationOffset": 4.3, + "lonOffset": 0.000231, + "timeOffset": 5.6, + "latOffset": -0.0011603 + }, + { + "elevationOffset": 9.1, + "lonOffset": 0.0003834, + "timeOffset": 11, + "latOffset": -0.0023425 + } + ] + }, + "pathPrediction": { + "confidence": 80, + "radiusOfCurve": -1622.5 + } + } + } + ], + "coreData": { + "secMark": 44202, + "transmission": "UNAVAILABLE", + "size": { + "width": 180, + "length": 480 + }, + "heading": 261, + "accelSet": { + "accelYaw": -0.65, + "accelLong": -0.05 + }, + "brakes": { + "scs": "unavailable", + "wheelBrakes": { + "leftFront": false, + "rightFront": false, + "unavailable": true, + "leftRear": false, + "rightRear": false + }, + "abs": "off", + "traction": "unavailable", + "brakeBoost": "unavailable", + "auxBrakes": "unavailable" + }, + "accuracy": { + "semiMinor": 2, + "orientation": 0, + "semiMajor": 2 + }, + "id": "D51E0ECA", + "position": { + "elevation": 1691.9, + "latitude": 39.587949070165415, + "longitude": -105.09087335799083 + }, + "msgCnt": 34, + "speed": 11.05 + } + }, + "dataType": "us.dot.its.jpo.ode.plugin.j2735.J2735Bsm" + }, + "recordGeneratedAt": { + "$date": { + "$numberLong": "1717518164208" + } + } +},{ + "_id": { + "$oid": "665f3fc08c25216ef4f53d83" + }, + "metadata": { + "securityResultCode": "success", + "bsmSource": "RV", + "schemaVersion": 6, + "odePacketID": "", + "sanitized": false, + "recordType": "bsmTx", + "recordGeneratedAt": "", + "maxDurationTime": 0, + "odeTimStartDateTime": "", + "receivedMessageDetails": { + "locationData": { + "elevation": "", + "heading": "", + "latitude": "", + "speed": "", + "longitude": "" + }, + "rxSource": "RV" + }, + "payloadType": "us.dot.its.jpo.ode.model.OdeBsmPayload", + "serialId": { + "recordId": 0, + "serialNumber": 0, + "streamId": "849c3ac6-4747-426f-ae5e-28ac8531b32c", + "bundleSize": 1, + "bundleId": 0 + }, + "logFileName": "", + "odeReceivedAt": "2024-06-04T16:22:44.31Z", + "originIp": "10.11.81.12" + }, + "payload": { + "data": { + "partII": [ + { + "id": "VehicleSafetyExtensions", + "value": { + "pathHistory": { + "crumbData": [ + { + "elevationOffset": 0.6, + "lonOffset": 0.0000387, + "timeOffset": 0.8, + "latOffset": -0.0001604 + }, + { + "elevationOffset": 4.3, + "lonOffset": 0.000231, + "timeOffset": 5.6, + "latOffset": -0.0011603 + }, + { + "elevationOffset": 9.1, + "lonOffset": 0.0003834, + "timeOffset": 11, + "latOffset": -0.0023425 + } + ] + }, + "pathPrediction": { + "confidence": 80, + "radiusOfCurve": -1622.5 + } + } + } + ], + "coreData": { + "secMark": 44310, + "transmission": "UNAVAILABLE", + "size": { + "width": 180, + "length": 480 + }, + "heading": 274.8, + "accelSet": { + "accelYaw": -0.65, + "accelLong": -0.05 + }, + "brakes": { + "scs": "unavailable", + "wheelBrakes": { + "leftFront": false, + "rightFront": false, + "unavailable": true, + "leftRear": false, + "rightRear": false + }, + "abs": "off", + "traction": "unavailable", + "brakeBoost": "unavailable", + "auxBrakes": "unavailable" + }, + "accuracy": { + "semiMinor": 2, + "orientation": 0, + "semiMajor": 2 + }, + "id": "D51E0ECA", + "position": { + "elevation": 1691.9, + "latitude": 39.58795010368396, + "longitude": -105.09088945388118 + }, + "msgCnt": 35, + "speed": 13.84 + } + }, + "dataType": "us.dot.its.jpo.ode.plugin.j2735.J2735Bsm" + }, + "recordGeneratedAt": { + "$date": { + "$numberLong": "1717518164313" + } + } +},{ + "_id": { + "$oid": "665f3fc08c25216ef4f53d84" + }, + "metadata": { + "securityResultCode": "success", + "bsmSource": "RV", + "schemaVersion": 6, + "odePacketID": "", + "sanitized": false, + "recordType": "bsmTx", + "recordGeneratedAt": "", + "maxDurationTime": 0, + "odeTimStartDateTime": "", + "receivedMessageDetails": { + "locationData": { + "elevation": "", + "heading": "", + "latitude": "", + "speed": "", + "longitude": "" + }, + "rxSource": "RV" + }, + "payloadType": "us.dot.its.jpo.ode.model.OdeBsmPayload", + "serialId": { + "recordId": 0, + "serialNumber": 0, + "streamId": "849c3ac6-4747-426f-ae5e-28ac8531b32c", + "bundleSize": 1, + "bundleId": 0 + }, + "logFileName": "", + "odeReceivedAt": "2024-06-04T16:22:44.417Z", + "originIp": "10.11.81.12" + }, + "payload": { + "data": { + "partII": [ + { + "id": "VehicleSafetyExtensions", + "value": { + "pathHistory": { + "crumbData": [ + { + "elevationOffset": 0.6, + "lonOffset": 0.0000387, + "timeOffset": 0.8, + "latOffset": -0.0001604 + }, + { + "elevationOffset": 4.3, + "lonOffset": 0.000231, + "timeOffset": 5.6, + "latOffset": -0.0011603 + }, + { + "elevationOffset": 9.1, + "lonOffset": 0.0003834, + "timeOffset": 11, + "latOffset": -0.0023425 + } + ] + }, + "pathPrediction": { + "confidence": 80, + "radiusOfCurve": -1622.5 + } + } + } + ], + "coreData": { + "secMark": 44417, + "transmission": "UNAVAILABLE", + "size": { + "width": 180, + "length": 480 + }, + "heading": 259.7, + "accelSet": { + "accelYaw": -0.65, + "accelLong": -0.05 + }, + "brakes": { + "scs": "unavailable", + "wheelBrakes": { + "leftFront": false, + "rightFront": false, + "unavailable": true, + "leftRear": false, + "rightRear": false + }, + "abs": "off", + "traction": "unavailable", + "brakeBoost": "unavailable", + "auxBrakes": "unavailable" + }, + "accuracy": { + "semiMinor": 2, + "orientation": 0, + "semiMajor": 2 + }, + "id": "D51E0ECA", + "position": { + "elevation": 1691.9, + "latitude": 39.58794803664684, + "longitude": -105.09090420844727 + }, + "msgCnt": 36, + "speed": 12.85 + } + }, + "dataType": "us.dot.its.jpo.ode.plugin.j2735.J2735Bsm" + }, + "recordGeneratedAt": { + "$date": { + "$numberLong": "1717518164420" + } + } +},{ + "_id": { + "$oid": "665f3fc08c25216ef4f53d85" + }, + "metadata": { + "securityResultCode": "success", + "bsmSource": "RV", + "schemaVersion": 6, + "odePacketID": "", + "sanitized": false, + "recordType": "bsmTx", + "recordGeneratedAt": "", + "maxDurationTime": 0, + "odeTimStartDateTime": "", + "receivedMessageDetails": { + "locationData": { + "elevation": "", + "heading": "", + "latitude": "", + "speed": "", + "longitude": "" + }, + "rxSource": "RV" + }, + "payloadType": "us.dot.its.jpo.ode.model.OdeBsmPayload", + "serialId": { + "recordId": 0, + "serialNumber": 0, + "streamId": "849c3ac6-4747-426f-ae5e-28ac8531b32c", + "bundleSize": 1, + "bundleId": 0 + }, + "logFileName": "", + "odeReceivedAt": "2024-06-04T16:22:44.524Z", + "originIp": "10.11.81.12" + }, + "payload": { + "data": { + "partII": [ + { + "id": "VehicleSafetyExtensions", + "value": { + "pathHistory": { + "crumbData": [ + { + "elevationOffset": 0.6, + "lonOffset": 0.0000387, + "timeOffset": 0.8, + "latOffset": -0.0001604 + }, + { + "elevationOffset": 4.3, + "lonOffset": 0.000231, + "timeOffset": 5.6, + "latOffset": -0.0011603 + }, + { + "elevationOffset": 9.1, + "lonOffset": 0.0003834, + "timeOffset": 11, + "latOffset": -0.0023425 + } + ] + }, + "pathPrediction": { + "confidence": 80, + "radiusOfCurve": -1622.5 + } + } + } + ], + "coreData": { + "secMark": 44524, + "transmission": "UNAVAILABLE", + "size": { + "width": 180, + "length": 480 + }, + "heading": 270, + "accelSet": { + "accelYaw": -0.65, + "accelLong": -0.05 + }, + "brakes": { + "scs": "unavailable", + "wheelBrakes": { + "leftFront": false, + "rightFront": false, + "unavailable": true, + "leftRear": false, + "rightRear": false + }, + "abs": "off", + "traction": "unavailable", + "brakeBoost": "unavailable", + "auxBrakes": "unavailable" + }, + "accuracy": { + "semiMinor": 2, + "orientation": 0, + "semiMajor": 2 + }, + "id": "D51E0ECA", + "position": { + "elevation": 1691.9, + "latitude": 39.58794803664684, + "longitude": -105.0909189630134 + }, + "msgCnt": 37, + "speed": 12.64 + } + }, + "dataType": "us.dot.its.jpo.ode.plugin.j2735.J2735Bsm" + }, + "recordGeneratedAt": { + "$date": { + "$numberLong": "1717518164532" + } + } +},{ + "_id": { + "$oid": "665f3fc08c25216ef4f53d86" + }, + "metadata": { + "securityResultCode": "success", + "bsmSource": "RV", + "schemaVersion": 6, + "odePacketID": "", + "sanitized": false, + "recordType": "bsmTx", + "recordGeneratedAt": "", + "maxDurationTime": 0, + "odeTimStartDateTime": "", + "receivedMessageDetails": { + "locationData": { + "elevation": "", + "heading": "", + "latitude": "", + "speed": "", + "longitude": "" + }, + "rxSource": "RV" + }, + "payloadType": "us.dot.its.jpo.ode.model.OdeBsmPayload", + "serialId": { + "recordId": 0, + "serialNumber": 0, + "streamId": "849c3ac6-4747-426f-ae5e-28ac8531b32c", + "bundleSize": 1, + "bundleId": 0 + }, + "logFileName": "", + "odeReceivedAt": "2024-06-04T16:22:44.631Z", + "originIp": "10.11.81.12" + }, + "payload": { + "data": { + "partII": [ + { + "id": "VehicleSafetyExtensions", + "value": { + "pathHistory": { + "crumbData": [ + { + "elevationOffset": 0.6, + "lonOffset": 0.0000387, + "timeOffset": 0.8, + "latOffset": -0.0001604 + }, + { + "elevationOffset": 4.3, + "lonOffset": 0.000231, + "timeOffset": 5.6, + "latOffset": -0.0011603 + }, + { + "elevationOffset": 9.1, + "lonOffset": 0.0003834, + "timeOffset": 11, + "latOffset": -0.0023425 + } + ] + }, + "pathPrediction": { + "confidence": 80, + "radiusOfCurve": -1622.5 + } + } + } + ], + "coreData": { + "secMark": 44631, + "transmission": "UNAVAILABLE", + "size": { + "width": 180, + "length": 480 + }, + "heading": 258.3, + "accelSet": { + "accelYaw": -0.65, + "accelLong": -0.05 + }, + "brakes": { + "scs": "unavailable", + "wheelBrakes": { + "leftFront": false, + "rightFront": false, + "unavailable": true, + "leftRear": false, + "rightRear": false + }, + "abs": "off", + "traction": "unavailable", + "brakeBoost": "unavailable", + "auxBrakes": "unavailable" + }, + "accuracy": { + "semiMinor": 2, + "orientation": 0, + "semiMajor": 2 + }, + "id": "D51E0ECA", + "position": { + "elevation": 1691.9, + "latitude": 39.58794493609105, + "longitude": -105.09093841221416 + }, + "msgCnt": 38, + "speed": 17.02 + } + }, + "dataType": "us.dot.its.jpo.ode.plugin.j2735.J2735Bsm" + }, + "recordGeneratedAt": { + "$date": { + "$numberLong": "1717518164634" + } + } +},{ + "_id": { + "$oid": "665f3fc08c25216ef4f53d87" + }, + "metadata": { + "securityResultCode": "success", + "bsmSource": "RV", + "schemaVersion": 6, + "odePacketID": "", + "sanitized": false, + "recordType": "bsmTx", + "recordGeneratedAt": "", + "maxDurationTime": 0, + "odeTimStartDateTime": "", + "receivedMessageDetails": { + "locationData": { + "elevation": "", + "heading": "", + "latitude": "", + "speed": "", + "longitude": "" + }, + "rxSource": "RV" + }, + "payloadType": "us.dot.its.jpo.ode.model.OdeBsmPayload", + "serialId": { + "recordId": 0, + "serialNumber": 0, + "streamId": "849c3ac6-4747-426f-ae5e-28ac8531b32c", + "bundleSize": 1, + "bundleId": 0 + }, + "logFileName": "", + "odeReceivedAt": "2024-06-04T16:22:44.739Z", + "originIp": "10.11.81.12" + }, + "payload": { + "data": { + "partII": [ + { + "id": "VehicleSafetyExtensions", + "value": { + "pathHistory": { + "crumbData": [ + { + "elevationOffset": 0.6, + "lonOffset": 0.0000387, + "timeOffset": 0.8, + "latOffset": -0.0001604 + }, + { + "elevationOffset": 4.3, + "lonOffset": 0.000231, + "timeOffset": 5.6, + "latOffset": -0.0011603 + }, + { + "elevationOffset": 9.1, + "lonOffset": 0.0003834, + "timeOffset": 11, + "latOffset": -0.0023425 + } + ] + }, + "pathPrediction": { + "confidence": 80, + "radiusOfCurve": -1622.5 + } + } + } + ], + "coreData": { + "secMark": 44739, + "transmission": "UNAVAILABLE", + "size": { + "width": 180, + "length": 480 + }, + "heading": 225, + "accelSet": { + "accelYaw": -0.65, + "accelLong": -0.05 + }, + "brakes": { + "scs": "unavailable", + "wheelBrakes": { + "leftFront": false, + "rightFront": false, + "unavailable": true, + "leftRear": false, + "rightRear": false + }, + "abs": "off", + "traction": "unavailable", + "brakeBoost": "unavailable", + "auxBrakes": "unavailable" + }, + "accuracy": { + "semiMinor": 2, + "orientation": 0, + "semiMajor": 2 + }, + "id": "D51E0ECA", + "position": { + "elevation": 1691.9, + "latitude": 39.587933567385285, + "longitude": -105.0909531667803 + }, + "msgCnt": 39, + "speed": 17.88 + } + }, + "dataType": "us.dot.its.jpo.ode.plugin.j2735.J2735Bsm" + }, + "recordGeneratedAt": { + "$date": { + "$numberLong": "1717518164742" + } + } +},{ + "_id": { + "$oid": "665f3fc08c25216ef4f53d88" + }, + "metadata": { + "securityResultCode": "success", + "bsmSource": "RV", + "schemaVersion": 6, + "odePacketID": "", + "sanitized": false, + "recordType": "bsmTx", + "recordGeneratedAt": "", + "maxDurationTime": 0, + "odeTimStartDateTime": "", + "receivedMessageDetails": { + "locationData": { + "elevation": "", + "heading": "", + "latitude": "", + "speed": "", + "longitude": "" + }, + "rxSource": "RV" + }, + "payloadType": "us.dot.its.jpo.ode.model.OdeBsmPayload", + "serialId": { + "recordId": 0, + "serialNumber": 0, + "streamId": "849c3ac6-4747-426f-ae5e-28ac8531b32c", + "bundleSize": 1, + "bundleId": 0 + }, + "logFileName": "", + "odeReceivedAt": "2024-06-04T16:22:44.845Z", + "originIp": "10.11.81.12" + }, + "payload": { + "data": { + "partII": [ + { + "id": "VehicleSafetyExtensions", + "value": { + "pathHistory": { + "crumbData": [ + { + "elevationOffset": 0.6, + "lonOffset": 0.0000387, + "timeOffset": 0.8, + "latOffset": -0.0001604 + }, + { + "elevationOffset": 4.3, + "lonOffset": 0.000231, + "timeOffset": 5.6, + "latOffset": -0.0011603 + }, + { + "elevationOffset": 9.1, + "lonOffset": 0.0003834, + "timeOffset": 11, + "latOffset": -0.0023425 + } + ] + }, + "pathPrediction": { + "confidence": 80, + "radiusOfCurve": -1622.5 + } + } + } + ], + "coreData": { + "secMark": 44845, + "transmission": "UNAVAILABLE", + "size": { + "width": 180, + "length": 480 + }, + "heading": 211.3, + "accelSet": { + "accelYaw": -0.65, + "accelLong": -0.05 + }, + "brakes": { + "scs": "unavailable", + "wheelBrakes": { + "leftFront": false, + "rightFront": false, + "unavailable": true, + "leftRear": false, + "rightRear": false + }, + "abs": "off", + "traction": "unavailable", + "brakeBoost": "unavailable", + "auxBrakes": "unavailable" + }, + "accuracy": { + "semiMinor": 2, + "orientation": 0, + "semiMajor": 2 + }, + "id": "D51E0ECA", + "position": { + "elevation": 1691.9, + "latitude": 39.587921681918154, + "longitude": -105.09096255604963 + }, + "msgCnt": 40, + "speed": 15.47 + } + }, + "dataType": "us.dot.its.jpo.ode.plugin.j2735.J2735Bsm" + }, + "recordGeneratedAt": { + "$date": { + "$numberLong": "1717518164848" + } + } +},{ + "_id": { + "$oid": "665f3fc08c25216ef4f53d89" + }, + "metadata": { + "securityResultCode": "success", + "bsmSource": "RV", + "schemaVersion": 6, + "odePacketID": "", + "sanitized": false, + "recordType": "bsmTx", + "recordGeneratedAt": "", + "maxDurationTime": 0, + "odeTimStartDateTime": "", + "receivedMessageDetails": { + "locationData": { + "elevation": "", + "heading": "", + "latitude": "", + "speed": "", + "longitude": "" + }, + "rxSource": "RV" + }, + "payloadType": "us.dot.its.jpo.ode.model.OdeBsmPayload", + "serialId": { + "recordId": 0, + "serialNumber": 0, + "streamId": "849c3ac6-4747-426f-ae5e-28ac8531b32c", + "bundleSize": 1, + "bundleId": 0 + }, + "logFileName": "", + "odeReceivedAt": "2024-06-04T16:22:44.953Z", + "originIp": "10.11.81.12" + }, + "payload": { + "data": { + "partII": [ + { + "id": "VehicleSafetyExtensions", + "value": { + "pathHistory": { + "crumbData": [ + { + "elevationOffset": 0.6, + "lonOffset": 0.0000387, + "timeOffset": 0.8, + "latOffset": -0.0001604 + }, + { + "elevationOffset": 4.3, + "lonOffset": 0.000231, + "timeOffset": 5.6, + "latOffset": -0.0011603 + }, + { + "elevationOffset": 9.1, + "lonOffset": 0.0003834, + "timeOffset": 11, + "latOffset": -0.0023425 + } + ] + }, + "pathPrediction": { + "confidence": 80, + "radiusOfCurve": -1622.5 + } + } + } + ], + "coreData": { + "secMark": 44953, + "transmission": "UNAVAILABLE", + "size": { + "width": 180, + "length": 480 + }, + "heading": 185, + "accelSet": { + "accelYaw": -0.65, + "accelLong": -0.05 + }, + "brakes": { + "scs": "unavailable", + "wheelBrakes": { + "leftFront": false, + "rightFront": false, + "unavailable": true, + "leftRear": false, + "rightRear": false + }, + "abs": "off", + "traction": "unavailable", + "brakeBoost": "unavailable", + "auxBrakes": "unavailable" + }, + "accuracy": { + "semiMinor": 2, + "orientation": 0, + "semiMajor": 2 + }, + "id": "D51E0ECA", + "position": { + "elevation": 1691.9, + "latitude": 39.58790979644899, + "longitude": -105.09096389737384 + }, + "msgCnt": 41, + "speed": 13.27 + } + }, + "dataType": "us.dot.its.jpo.ode.plugin.j2735.J2735Bsm" + }, + "recordGeneratedAt": { + "$date": { + "$numberLong": "1717518164969" + } + } +},{ + "_id": { + "$oid": "665f3fc08c25216ef4f53d8a" + }, + "metadata": { + "securityResultCode": "success", + "bsmSource": "RV", + "schemaVersion": 6, + "odePacketID": "", + "sanitized": false, + "recordType": "bsmTx", + "recordGeneratedAt": "", + "maxDurationTime": 0, + "odeTimStartDateTime": "", + "receivedMessageDetails": { + "locationData": { + "elevation": "", + "heading": "", + "latitude": "", + "speed": "", + "longitude": "" + }, + "rxSource": "RV" + }, + "payloadType": "us.dot.its.jpo.ode.model.OdeBsmPayload", + "serialId": { + "recordId": 0, + "serialNumber": 0, + "streamId": "849c3ac6-4747-426f-ae5e-28ac8531b32c", + "bundleSize": 1, + "bundleId": 0 + }, + "logFileName": "", + "odeReceivedAt": "2024-06-04T16:22:45.061Z", + "originIp": "10.11.81.12" + }, + "payload": { + "data": { + "partII": [ + { + "id": "VehicleSafetyExtensions", + "value": { + "pathHistory": { + "crumbData": [ + { + "elevationOffset": 0.6, + "lonOffset": 0.0000387, + "timeOffset": 0.8, + "latOffset": -0.0001604 + }, + { + "elevationOffset": 4.3, + "lonOffset": 0.000231, + "timeOffset": 5.6, + "latOffset": -0.0011603 + }, + { + "elevationOffset": 9.1, + "lonOffset": 0.0003834, + "timeOffset": 11, + "latOffset": -0.0023425 + } + ] + }, + "pathPrediction": { + "confidence": 80, + "radiusOfCurve": -1622.5 + } + } + } + ], + "coreData": { + "secMark": 45061, + "transmission": "UNAVAILABLE", + "size": { + "width": 180, + "length": 480 + }, + "heading": 180, + "accelSet": { + "accelYaw": -0.65, + "accelLong": -0.05 + }, + "brakes": { + "scs": "unavailable", + "wheelBrakes": { + "leftFront": false, + "rightFront": false, + "unavailable": true, + "leftRear": false, + "rightRear": false + }, + "abs": "off", + "traction": "unavailable", + "brakeBoost": "unavailable", + "auxBrakes": "unavailable" + }, + "accuracy": { + "semiMinor": 2, + "orientation": 0, + "semiMajor": 2 + }, + "id": "D51E0ECA", + "position": { + "elevation": 1691.9, + "latitude": 39.58789946125678, + "longitude": -105.09096389737384 + }, + "msgCnt": 42, + "speed": 11.49 + } + }, + "dataType": "us.dot.its.jpo.ode.plugin.j2735.J2735Bsm" + }, + "recordGeneratedAt": { + "$date": { + "$numberLong": "1717518165075" + } + } +},{ + "_id": { + "$oid": "665f3fc08c25216ef4f53d8b" + }, + "metadata": { + "securityResultCode": "success", + "bsmSource": "RV", + "schemaVersion": 6, + "odePacketID": "", + "sanitized": false, + "recordType": "bsmTx", + "recordGeneratedAt": "", + "maxDurationTime": 0, + "odeTimStartDateTime": "", + "receivedMessageDetails": { + "locationData": { + "elevation": "", + "heading": "", + "latitude": "", + "speed": "", + "longitude": "" + }, + "rxSource": "RV" + }, + "payloadType": "us.dot.its.jpo.ode.model.OdeBsmPayload", + "serialId": { + "recordId": 0, + "serialNumber": 0, + "streamId": "849c3ac6-4747-426f-ae5e-28ac8531b32c", + "bundleSize": 1, + "bundleId": 0 + }, + "logFileName": "", + "odeReceivedAt": "2024-06-04T16:22:45.168Z", + "originIp": "10.11.81.12" + }, + "payload": { + "data": { + "partII": [ + { + "id": "VehicleSafetyExtensions", + "value": { + "pathHistory": { + "crumbData": [ + { + "elevationOffset": 0.6, + "lonOffset": 0.0000387, + "timeOffset": 0.8, + "latOffset": -0.0001604 + }, + { + "elevationOffset": 4.3, + "lonOffset": 0.000231, + "timeOffset": 5.6, + "latOffset": -0.0011603 + }, + { + "elevationOffset": 9.1, + "lonOffset": 0.0003834, + "timeOffset": 11, + "latOffset": -0.0023425 + } + ] + }, + "pathPrediction": { + "confidence": 80, + "radiusOfCurve": -1622.5 + } + } + } + ], + "coreData": { + "secMark": 45168, + "transmission": "UNAVAILABLE", + "size": { + "width": 180, + "length": 480 + }, + "heading": 178.1, + "accelSet": { + "accelYaw": -0.65, + "accelLong": -0.05 + }, + "brakes": { + "scs": "unavailable", + "wheelBrakes": { + "leftFront": false, + "rightFront": false, + "unavailable": true, + "leftRear": false, + "rightRear": false + }, + "abs": "off", + "traction": "unavailable", + "brakeBoost": "unavailable", + "auxBrakes": "unavailable" + }, + "accuracy": { + "semiMinor": 2, + "orientation": 0, + "semiMajor": 2 + }, + "id": "D51E0ECA", + "position": { + "elevation": 1691.9, + "latitude": 39.587883958465525, + "longitude": -105.09096322671174 + }, + "msgCnt": 43, + "speed": 17.25 + } + }, + "dataType": "us.dot.its.jpo.ode.plugin.j2735.J2735Bsm" + }, + "recordGeneratedAt": { + "$date": { + "$numberLong": "1717518165182" + } + } +},{ + "_id": { + "$oid": "665f3fc08c25216ef4f53d8c" + }, + "metadata": { + "securityResultCode": "success", + "bsmSource": "RV", + "schemaVersion": 6, + "odePacketID": "", + "sanitized": false, + "recordType": "bsmTx", + "recordGeneratedAt": "", + "maxDurationTime": 0, + "odeTimStartDateTime": "", + "receivedMessageDetails": { + "locationData": { + "elevation": "", + "heading": "", + "latitude": "", + "speed": "", + "longitude": "" + }, + "rxSource": "RV" + }, + "payloadType": "us.dot.its.jpo.ode.model.OdeBsmPayload", + "serialId": { + "recordId": 0, + "serialNumber": 0, + "streamId": "849c3ac6-4747-426f-ae5e-28ac8531b32c", + "bundleSize": 1, + "bundleId": 0 + }, + "logFileName": "", + "odeReceivedAt": "2024-06-04T16:22:45.273Z", + "originIp": "10.11.81.12" + }, + "payload": { + "data": { + "partII": [ + { + "id": "VehicleSafetyExtensions", + "value": { + "pathHistory": { + "crumbData": [ + { + "elevationOffset": 0.6, + "lonOffset": 0.0000387, + "timeOffset": 0.8, + "latOffset": -0.0001604 + }, + { + "elevationOffset": 4.3, + "lonOffset": 0.000231, + "timeOffset": 5.6, + "latOffset": -0.0011603 + }, + { + "elevationOffset": 9.1, + "lonOffset": 0.0003834, + "timeOffset": 11, + "latOffset": -0.0023425 + } + ] + }, + "pathPrediction": { + "confidence": 80, + "radiusOfCurve": -1622.5 + } + } + } + ], + "coreData": { + "secMark": 45273, + "transmission": "UNAVAILABLE", + "size": { + "width": 180, + "length": 480 + }, + "heading": 177.5, + "accelSet": { + "accelYaw": -0.65, + "accelLong": -0.05 + }, + "brakes": { + "scs": "unavailable", + "wheelBrakes": { + "leftFront": false, + "rightFront": false, + "unavailable": true, + "leftRear": false, + "rightRear": false + }, + "abs": "off", + "traction": "unavailable", + "brakeBoost": "unavailable", + "auxBrakes": "unavailable" + }, + "accuracy": { + "semiMinor": 2, + "orientation": 0, + "semiMajor": 2 + }, + "id": "D51E0ECA", + "position": { + "elevation": 1691.9, + "latitude": 39.587872072989924, + "longitude": -105.09096255604963 + }, + "msgCnt": 44, + "speed": 13.23 + } + }, + "dataType": "us.dot.its.jpo.ode.plugin.j2735.J2735Bsm" + }, + "recordGeneratedAt": { + "$date": { + "$numberLong": "1717518165275" + } + } +},{ + "_id": { + "$oid": "665f3fc08c25216ef4f53d8d" + }, + "metadata": { + "securityResultCode": "success", + "bsmSource": "RV", + "schemaVersion": 6, + "odePacketID": "", + "sanitized": false, + "recordType": "bsmTx", + "recordGeneratedAt": "", + "maxDurationTime": 0, + "odeTimStartDateTime": "", + "receivedMessageDetails": { + "locationData": { + "elevation": "", + "heading": "", + "latitude": "", + "speed": "", + "longitude": "" + }, + "rxSource": "RV" + }, + "payloadType": "us.dot.its.jpo.ode.model.OdeBsmPayload", + "serialId": { + "recordId": 0, + "serialNumber": 0, + "streamId": "849c3ac6-4747-426f-ae5e-28ac8531b32c", + "bundleSize": 1, + "bundleId": 0 + }, + "logFileName": "", + "odeReceivedAt": "2024-06-04T16:22:45.381Z", + "originIp": "10.11.81.12" + }, + "payload": { + "data": { + "partII": [ + { + "id": "VehicleSafetyExtensions", + "value": { + "pathHistory": { + "crumbData": [ + { + "elevationOffset": 0.6, + "lonOffset": 0.0000387, + "timeOffset": 0.8, + "latOffset": -0.0001604 + }, + { + "elevationOffset": 4.3, + "lonOffset": 0.000231, + "timeOffset": 5.6, + "latOffset": -0.0011603 + }, + { + "elevationOffset": 9.1, + "lonOffset": 0.0003834, + "timeOffset": 11, + "latOffset": -0.0023425 + } + ] + }, + "pathPrediction": { + "confidence": 80, + "radiusOfCurve": -1622.5 + } + } + } + ], + "coreData": { + "secMark": 45381, + "transmission": "UNAVAILABLE", + "size": { + "width": 180, + "length": 480 + }, + "heading": 166, + "accelSet": { + "accelYaw": -0.65, + "accelLong": -0.05 + }, + "brakes": { + "scs": "unavailable", + "wheelBrakes": { + "leftFront": false, + "rightFront": false, + "unavailable": true, + "leftRear": false, + "rightRear": false + }, + "abs": "off", + "traction": "unavailable", + "brakeBoost": "unavailable", + "auxBrakes": "unavailable" + }, + "accuracy": { + "semiMinor": 2, + "orientation": 0, + "semiMajor": 2 + }, + "id": "D51E0ECA", + "position": { + "elevation": 1691.9, + "latitude": 39.5878596707523, + "longitude": -105.09095853207707 + }, + "msgCnt": 45, + "speed": 14.22 + } + }, + "dataType": "us.dot.its.jpo.ode.plugin.j2735.J2735Bsm" + }, + "recordGeneratedAt": { + "$date": { + "$numberLong": "1717518165382" + } + } +},{ + "_id": { + "$oid": "665f3fc08c25216ef4f53d8e" + }, + "metadata": { + "securityResultCode": "success", + "bsmSource": "RV", + "schemaVersion": 6, + "odePacketID": "", + "sanitized": false, + "recordType": "bsmTx", + "recordGeneratedAt": "", + "maxDurationTime": 0, + "odeTimStartDateTime": "", + "receivedMessageDetails": { + "locationData": { + "elevation": "", + "heading": "", + "latitude": "", + "speed": "", + "longitude": "" + }, + "rxSource": "RV" + }, + "payloadType": "us.dot.its.jpo.ode.model.OdeBsmPayload", + "serialId": { + "recordId": 0, + "serialNumber": 0, + "streamId": "849c3ac6-4747-426f-ae5e-28ac8531b32c", + "bundleSize": 1, + "bundleId": 0 + }, + "logFileName": "", + "odeReceivedAt": "2024-06-04T16:22:45.488Z", + "originIp": "10.11.81.12" + }, + "payload": { + "data": { + "partII": [ + { + "id": "VehicleSafetyExtensions", + "value": { + "pathHistory": { + "crumbData": [ + { + "elevationOffset": 0.6, + "lonOffset": 0.0000387, + "timeOffset": 0.8, + "latOffset": -0.0001604 + }, + { + "elevationOffset": 4.3, + "lonOffset": 0.000231, + "timeOffset": 5.6, + "latOffset": -0.0011603 + }, + { + "elevationOffset": 9.1, + "lonOffset": 0.0003834, + "timeOffset": 11, + "latOffset": -0.0023425 + } + ] + }, + "pathPrediction": { + "confidence": 80, + "radiusOfCurve": -1622.5 + } + } + } + ], + "coreData": { + "secMark": 45488, + "transmission": "UNAVAILABLE", + "size": { + "width": 180, + "length": 480 + }, + "heading": 169.7, + "accelSet": { + "accelYaw": -0.65, + "accelLong": -0.05 + }, + "brakes": { + "scs": "unavailable", + "wheelBrakes": { + "leftFront": false, + "rightFront": false, + "unavailable": true, + "leftRear": false, + "rightRear": false + }, + "abs": "off", + "traction": "unavailable", + "brakeBoost": "unavailable", + "auxBrakes": "unavailable" + }, + "accuracy": { + "semiMinor": 2, + "orientation": 0, + "semiMajor": 2 + }, + "id": "D51E0ECA", + "position": { + "elevation": 1691.9, + "latitude": 39.58784261767199, + "longitude": -105.0909545081045 + }, + "msgCnt": 46, + "speed": 19.27 + } + }, + "dataType": "us.dot.its.jpo.ode.plugin.j2735.J2735Bsm" + }, + "recordGeneratedAt": { + "$date": { + "$numberLong": "1717518165504" + } + } +},{ + "_id": { + "$oid": "665f3fc08c25216ef4f53d8f" + }, + "metadata": { + "securityResultCode": "success", + "bsmSource": "RV", + "schemaVersion": 6, + "odePacketID": "", + "sanitized": false, + "recordType": "bsmTx", + "recordGeneratedAt": "", + "maxDurationTime": 0, + "odeTimStartDateTime": "", + "receivedMessageDetails": { + "locationData": { + "elevation": "", + "heading": "", + "latitude": "", + "speed": "", + "longitude": "" + }, + "rxSource": "RV" + }, + "payloadType": "us.dot.its.jpo.ode.model.OdeBsmPayload", + "serialId": { + "recordId": 0, + "serialNumber": 0, + "streamId": "849c3ac6-4747-426f-ae5e-28ac8531b32c", + "bundleSize": 1, + "bundleId": 0 + }, + "logFileName": "", + "odeReceivedAt": "2024-06-04T16:22:45.595Z", + "originIp": "10.11.81.12" + }, + "payload": { + "data": { + "partII": [ + { + "id": "VehicleSafetyExtensions", + "value": { + "pathHistory": { + "crumbData": [ + { + "elevationOffset": 0.6, + "lonOffset": 0.0000387, + "timeOffset": 0.8, + "latOffset": -0.0001604 + }, + { + "elevationOffset": 4.3, + "lonOffset": 0.000231, + "timeOffset": 5.6, + "latOffset": -0.0011603 + }, + { + "elevationOffset": 9.1, + "lonOffset": 0.0003834, + "timeOffset": 11, + "latOffset": -0.0023425 + } + ] + }, + "pathPrediction": { + "confidence": 80, + "radiusOfCurve": -1622.5 + } + } + } + ], + "coreData": { + "secMark": 45595, + "transmission": "UNAVAILABLE", + "size": { + "width": 180, + "length": 480 + }, + "heading": 168.7, + "accelSet": { + "accelYaw": -0.65, + "accelLong": -0.05 + }, + "brakes": { + "scs": "unavailable", + "wheelBrakes": { + "leftFront": false, + "rightFront": false, + "unavailable": true, + "leftRear": false, + "rightRear": false + }, + "abs": "off", + "traction": "unavailable", + "brakeBoost": "unavailable", + "auxBrakes": "unavailable" + }, + "accuracy": { + "semiMinor": 2, + "orientation": 0, + "semiMajor": 2 + }, + "id": "D51E0ECA", + "position": { + "elevation": 1691.9, + "latitude": 39.587829698668905, + "longitude": -105.090951154794 + }, + "msgCnt": 47, + "speed": 14.65 + } + }, + "dataType": "us.dot.its.jpo.ode.plugin.j2735.J2735Bsm" + }, + "recordGeneratedAt": { + "$date": { + "$numberLong": "1717518165611" + } + } +},{ + "_id": { + "$oid": "665f3fc08c25216ef4f53d90" + }, + "metadata": { + "securityResultCode": "success", + "bsmSource": "RV", + "schemaVersion": 6, + "odePacketID": "", + "sanitized": false, + "recordType": "bsmTx", + "recordGeneratedAt": "", + "maxDurationTime": 0, + "odeTimStartDateTime": "", + "receivedMessageDetails": { + "locationData": { + "elevation": "", + "heading": "", + "latitude": "", + "speed": "", + "longitude": "" + }, + "rxSource": "RV" + }, + "payloadType": "us.dot.its.jpo.ode.model.OdeBsmPayload", + "serialId": { + "recordId": 0, + "serialNumber": 0, + "streamId": "849c3ac6-4747-426f-ae5e-28ac8531b32c", + "bundleSize": 1, + "bundleId": 0 + }, + "logFileName": "", + "odeReceivedAt": "2024-06-04T16:22:45.704Z", + "originIp": "10.11.81.12" + }, + "payload": { + "data": { + "partII": [ + { + "id": "VehicleSafetyExtensions", + "value": { + "pathHistory": { + "crumbData": [ + { + "elevationOffset": 0.6, + "lonOffset": 0.0000387, + "timeOffset": 0.8, + "latOffset": -0.0001604 + }, + { + "elevationOffset": 4.3, + "lonOffset": 0.000231, + "timeOffset": 5.6, + "latOffset": -0.0011603 + }, + { + "elevationOffset": 9.1, + "lonOffset": 0.0003834, + "timeOffset": 11, + "latOffset": -0.0023425 + } + ] + }, + "pathPrediction": { + "confidence": 80, + "radiusOfCurve": -1622.5 + } + } + } + ], + "coreData": { + "secMark": 45704, + "transmission": "UNAVAILABLE", + "size": { + "width": 180, + "length": 480 + }, + "heading": 168, + "accelSet": { + "accelYaw": -0.65, + "accelLong": -0.05 + }, + "brakes": { + "scs": "unavailable", + "wheelBrakes": { + "leftFront": false, + "rightFront": false, + "unavailable": true, + "leftRear": false, + "rightRear": false + }, + "abs": "off", + "traction": "unavailable", + "brakeBoost": "unavailable", + "auxBrakes": "unavailable" + }, + "accuracy": { + "semiMinor": 2, + "orientation": 0, + "semiMajor": 2 + }, + "id": "D51E0ECA", + "position": { + "elevation": 1691.9, + "latitude": 39.58781264558121, + "longitude": -105.09094646015933 + }, + "msgCnt": 48, + "speed": 19.38 + } + }, + "dataType": "us.dot.its.jpo.ode.plugin.j2735.J2735Bsm" + }, + "recordGeneratedAt": { + "$date": { + "$numberLong": "1717518165718" + } + } +},{ + "_id": { + "$oid": "665f3fc08c25216ef4f53d91" + }, + "metadata": { + "securityResultCode": "success", + "bsmSource": "RV", + "schemaVersion": 6, + "odePacketID": "", + "sanitized": false, + "recordType": "bsmTx", + "recordGeneratedAt": "", + "maxDurationTime": 0, + "odeTimStartDateTime": "", + "receivedMessageDetails": { + "locationData": { + "elevation": "", + "heading": "", + "latitude": "", + "speed": "", + "longitude": "" + }, + "rxSource": "RV" + }, + "payloadType": "us.dot.its.jpo.ode.model.OdeBsmPayload", + "serialId": { + "recordId": 0, + "serialNumber": 0, + "streamId": "849c3ac6-4747-426f-ae5e-28ac8531b32c", + "bundleSize": 1, + "bundleId": 0 + }, + "logFileName": "", + "odeReceivedAt": "2024-06-04T16:22:45.813Z", + "originIp": "10.11.81.12" + }, + "payload": { + "data": { + "partII": [ + { + "id": "VehicleSafetyExtensions", + "value": { + "pathHistory": { + "crumbData": [ + { + "elevationOffset": 0.6, + "lonOffset": 0.0000387, + "timeOffset": 0.8, + "latOffset": -0.0001604 + }, + { + "elevationOffset": 4.3, + "lonOffset": 0.000231, + "timeOffset": 5.6, + "latOffset": -0.0011603 + }, + { + "elevationOffset": 9.1, + "lonOffset": 0.0003834, + "timeOffset": 11, + "latOffset": -0.0023425 + } + ] + }, + "pathPrediction": { + "confidence": 80, + "radiusOfCurve": -1622.5 + } + } + } + ], + "coreData": { + "secMark": 45813, + "transmission": "UNAVAILABLE", + "size": { + "width": 180, + "length": 480 + }, + "heading": 165.3, + "accelSet": { + "accelYaw": -0.65, + "accelLong": -0.05 + }, + "brakes": { + "scs": "unavailable", + "wheelBrakes": { + "leftFront": false, + "rightFront": false, + "unavailable": true, + "leftRear": false, + "rightRear": false + }, + "abs": "off", + "traction": "unavailable", + "brakeBoost": "unavailable", + "auxBrakes": "unavailable" + }, + "accuracy": { + "semiMinor": 2, + "orientation": 0, + "semiMajor": 2 + }, + "id": "D51E0ECA", + "position": { + "elevation": 1691.9, + "latitude": 39.58779300868712, + "longitude": -105.09093975353838 + }, + "msgCnt": 49, + "speed": 22.58 + } + }, + "dataType": "us.dot.its.jpo.ode.plugin.j2735.J2735Bsm" + }, + "recordGeneratedAt": { + "$date": { + "$numberLong": "1717518165823" + } + } +},{ + "_id": { + "$oid": "665f3fc08c25216ef4f53d92" + }, + "metadata": { + "securityResultCode": "success", + "bsmSource": "RV", + "schemaVersion": 6, + "odePacketID": "", + "sanitized": false, + "recordType": "bsmTx", + "recordGeneratedAt": "", + "maxDurationTime": 0, + "odeTimStartDateTime": "", + "receivedMessageDetails": { + "locationData": { + "elevation": "", + "heading": "", + "latitude": "", + "speed": "", + "longitude": "" + }, + "rxSource": "RV" + }, + "payloadType": "us.dot.its.jpo.ode.model.OdeBsmPayload", + "serialId": { + "recordId": 0, + "serialNumber": 0, + "streamId": "849c3ac6-4747-426f-ae5e-28ac8531b32c", + "bundleSize": 1, + "bundleId": 0 + }, + "logFileName": "", + "odeReceivedAt": "2024-06-04T16:22:45.918Z", + "originIp": "10.11.81.12" + }, + "payload": { + "data": { + "partII": [ + { + "id": "VehicleSafetyExtensions", + "value": { + "pathHistory": { + "crumbData": [ + { + "elevationOffset": 0.6, + "lonOffset": 0.0000387, + "timeOffset": 0.8, + "latOffset": -0.0001604 + }, + { + "elevationOffset": 4.3, + "lonOffset": 0.000231, + "timeOffset": 5.6, + "latOffset": -0.0011603 + }, + { + "elevationOffset": 9.1, + "lonOffset": 0.0003834, + "timeOffset": 11, + "latOffset": -0.0023425 + } + ] + }, + "pathPrediction": { + "confidence": 80, + "radiusOfCurve": -1622.5 + } + } + } + ], + "coreData": { + "secMark": 45918, + "transmission": "UNAVAILABLE", + "size": { + "width": 180, + "length": 480 + }, + "heading": 164.1, + "accelSet": { + "accelYaw": -0.65, + "accelLong": -0.05 + }, + "brakes": { + "scs": "unavailable", + "wheelBrakes": { + "leftFront": false, + "rightFront": false, + "unavailable": true, + "leftRear": false, + "rightRear": false + }, + "abs": "off", + "traction": "unavailable", + "brakeBoost": "unavailable", + "auxBrakes": "unavailable" + }, + "accuracy": { + "semiMinor": 2, + "orientation": 0, + "semiMajor": 2 + }, + "id": "D51E0ECA", + "position": { + "elevation": 1691.9, + "latitude": 39.58777853939319, + "longitude": -105.0909343882416 + }, + "msgCnt": 50, + "speed": 16.73 + } + }, + "dataType": "us.dot.its.jpo.ode.plugin.j2735.J2735Bsm" + }, + "recordGeneratedAt": { + "$date": { + "$numberLong": "1717518165930" + } + } +},{ + "_id": { + "$oid": "665f3fc08c25216ef4f53d93" + }, + "metadata": { + "securityResultCode": "success", + "bsmSource": "RV", + "schemaVersion": 6, + "odePacketID": "", + "sanitized": false, + "recordType": "bsmTx", + "recordGeneratedAt": "", + "maxDurationTime": 0, + "odeTimStartDateTime": "", + "receivedMessageDetails": { + "locationData": { + "elevation": "", + "heading": "", + "latitude": "", + "speed": "", + "longitude": "" + }, + "rxSource": "RV" + }, + "payloadType": "us.dot.its.jpo.ode.model.OdeBsmPayload", + "serialId": { + "recordId": 0, + "serialNumber": 0, + "streamId": "849c3ac6-4747-426f-ae5e-28ac8531b32c", + "bundleSize": 1, + "bundleId": 0 + }, + "logFileName": "", + "odeReceivedAt": "2024-06-04T16:22:55.253Z", + "originIp": "10.11.81.12" + }, + "payload": { + "data": { + "partII": [ + { + "id": "VehicleSafetyExtensions", + "value": { + "pathHistory": { + "crumbData": [ + { + "elevationOffset": 0.6, + "lonOffset": 0.0000387, + "timeOffset": 0.8, + "latOffset": -0.0001604 + }, + { + "elevationOffset": 4.3, + "lonOffset": 0.000231, + "timeOffset": 5.6, + "latOffset": -0.0011603 + }, + { + "elevationOffset": 9.1, + "lonOffset": 0.0003834, + "timeOffset": 11, + "latOffset": -0.0023425 + } + ] + }, + "pathPrediction": { + "confidence": 80, + "radiusOfCurve": -1622.5 + } + } + } + ], + "coreData": { + "secMark": 55253, + "transmission": "UNAVAILABLE", + "size": { + "width": 180, + "length": 480 + }, + "heading": 169.2, + "accelSet": { + "accelYaw": -0.65, + "accelLong": -0.05 + }, + "brakes": { + "scs": "unavailable", + "wheelBrakes": { + "leftFront": false, + "rightFront": false, + "unavailable": true, + "leftRear": false, + "rightRear": false + }, + "abs": "off", + "traction": "unavailable", + "brakeBoost": "unavailable", + "auxBrakes": "unavailable" + }, + "accuracy": { + "semiMinor": 2, + "orientation": 0, + "semiMajor": 2 + }, + "id": "DD2F5ECF", + "position": { + "elevation": 1691.9, + "latitude": 39.587279346902015, + "longitude": -105.09059802750382 + }, + "msgCnt": 1, + "speed": 22.62 + } + }, + "dataType": "us.dot.its.jpo.ode.plugin.j2735.J2735Bsm" + }, + "recordGeneratedAt": { + "$date": { + "$numberLong": "1717518175269" + } + } +},{ + "_id": { + "$oid": "665f3fc08c25216ef4f53d94" + }, + "metadata": { + "securityResultCode": "success", + "bsmSource": "RV", + "schemaVersion": 6, + "odePacketID": "", + "sanitized": false, + "recordType": "bsmTx", + "recordGeneratedAt": "", + "maxDurationTime": 0, + "odeTimStartDateTime": "", + "receivedMessageDetails": { + "locationData": { + "elevation": "", + "heading": "", + "latitude": "", + "speed": "", + "longitude": "" + }, + "rxSource": "RV" + }, + "payloadType": "us.dot.its.jpo.ode.model.OdeBsmPayload", + "serialId": { + "recordId": 0, + "serialNumber": 0, + "streamId": "849c3ac6-4747-426f-ae5e-28ac8531b32c", + "bundleSize": 1, + "bundleId": 0 + }, + "logFileName": "", + "odeReceivedAt": "2024-06-04T16:22:55.365Z", + "originIp": "10.11.81.12" + }, + "payload": { + "data": { + "partII": [ + { + "id": "VehicleSafetyExtensions", + "value": { + "pathHistory": { + "crumbData": [ + { + "elevationOffset": 0.6, + "lonOffset": 0.0000387, + "timeOffset": 0.8, + "latOffset": -0.0001604 + }, + { + "elevationOffset": 4.3, + "lonOffset": 0.000231, + "timeOffset": 5.6, + "latOffset": -0.0011603 + }, + { + "elevationOffset": 9.1, + "lonOffset": 0.0003834, + "timeOffset": 11, + "latOffset": -0.0023425 + } + ] + }, + "pathPrediction": { + "confidence": 80, + "radiusOfCurve": -1622.5 + } + } + } + ], + "coreData": { + "secMark": 55365, + "transmission": "UNAVAILABLE", + "size": { + "width": 180, + "length": 480 + }, + "heading": 342, + "accelSet": { + "accelYaw": -0.65, + "accelLong": -0.05 + }, + "brakes": { + "scs": "unavailable", + "wheelBrakes": { + "leftFront": false, + "rightFront": false, + "unavailable": true, + "leftRear": false, + "rightRear": false + }, + "abs": "off", + "traction": "unavailable", + "brakeBoost": "unavailable", + "auxBrakes": "unavailable" + }, + "accuracy": { + "semiMinor": 2, + "orientation": 0, + "semiMajor": 2 + }, + "id": "DD2F5ECF", + "position": { + "elevation": 1691.9, + "latitude": 39.58731758744779, + "longitude": -105.09061412339413 + }, + "msgCnt": 2, + "speed": 44.7 + } + }, + "dataType": "us.dot.its.jpo.ode.plugin.j2735.J2735Bsm" + }, + "recordGeneratedAt": { + "$date": { + "$numberLong": "1717518175372" + } + } +},{ + "_id": { + "$oid": "665f3fc08c25216ef4f53d95" + }, + "metadata": { + "securityResultCode": "success", + "bsmSource": "RV", + "schemaVersion": 6, + "odePacketID": "", + "sanitized": false, + "recordType": "bsmTx", + "recordGeneratedAt": "", + "maxDurationTime": 0, + "odeTimStartDateTime": "", + "receivedMessageDetails": { + "locationData": { + "elevation": "", + "heading": "", + "latitude": "", + "speed": "", + "longitude": "" + }, + "rxSource": "RV" + }, + "payloadType": "us.dot.its.jpo.ode.model.OdeBsmPayload", + "serialId": { + "recordId": 0, + "serialNumber": 0, + "streamId": "849c3ac6-4747-426f-ae5e-28ac8531b32c", + "bundleSize": 1, + "bundleId": 0 + }, + "logFileName": "", + "odeReceivedAt": "2024-06-04T16:22:55.472Z", + "originIp": "10.11.81.12" + }, + "payload": { + "data": { + "partII": [ + { + "id": "VehicleSafetyExtensions", + "value": { + "pathHistory": { + "crumbData": [ + { + "elevationOffset": 0.6, + "lonOffset": 0.0000387, + "timeOffset": 0.8, + "latOffset": -0.0001604 + }, + { + "elevationOffset": 4.3, + "lonOffset": 0.000231, + "timeOffset": 5.6, + "latOffset": -0.0011603 + }, + { + "elevationOffset": 9.1, + "lonOffset": 0.0003834, + "timeOffset": 11, + "latOffset": -0.0023425 + } + ] + }, + "pathPrediction": { + "confidence": 80, + "radiusOfCurve": -1622.5 + } + } + } + ], + "coreData": { + "secMark": 55472, + "transmission": "UNAVAILABLE", + "size": { + "width": 180, + "length": 480 + }, + "heading": 339.9, + "accelSet": { + "accelYaw": -0.65, + "accelLong": -0.05 + }, + "brakes": { + "scs": "unavailable", + "wheelBrakes": { + "leftFront": false, + "rightFront": false, + "unavailable": true, + "leftRear": false, + "rightRear": false + }, + "abs": "off", + "traction": "unavailable", + "brakeBoost": "unavailable", + "auxBrakes": "unavailable" + }, + "accuracy": { + "semiMinor": 2, + "orientation": 0, + "semiMajor": 2 + }, + "id": "DD2F5ECF", + "position": { + "elevation": 1691.9, + "latitude": 39.58734859328024, + "longitude": -105.09062887796024 + }, + "msgCnt": 3, + "speed": 36.72 + } + }, + "dataType": "us.dot.its.jpo.ode.plugin.j2735.J2735Bsm" + }, + "recordGeneratedAt": { + "$date": { + "$numberLong": "1717518175479" + } + } +},{ + "_id": { + "$oid": "665f3fc08c25216ef4f53d96" + }, + "metadata": { + "securityResultCode": "success", + "bsmSource": "RV", + "schemaVersion": 6, + "odePacketID": "", + "sanitized": false, + "recordType": "bsmTx", + "recordGeneratedAt": "", + "maxDurationTime": 0, + "odeTimStartDateTime": "", + "receivedMessageDetails": { + "locationData": { + "elevation": "", + "heading": "", + "latitude": "", + "speed": "", + "longitude": "" + }, + "rxSource": "RV" + }, + "payloadType": "us.dot.its.jpo.ode.model.OdeBsmPayload", + "serialId": { + "recordId": 0, + "serialNumber": 0, + "streamId": "849c3ac6-4747-426f-ae5e-28ac8531b32c", + "bundleSize": 1, + "bundleId": 0 + }, + "logFileName": "", + "odeReceivedAt": "2024-06-04T16:22:55.578Z", + "originIp": "10.11.81.12" + }, + "payload": { + "data": { + "partII": [ + { + "id": "VehicleSafetyExtensions", + "value": { + "pathHistory": { + "crumbData": [ + { + "elevationOffset": 0.6, + "lonOffset": 0.0000387, + "timeOffset": 0.8, + "latOffset": -0.0001604 + }, + { + "elevationOffset": 4.3, + "lonOffset": 0.000231, + "timeOffset": 5.6, + "latOffset": -0.0011603 + }, + { + "elevationOffset": 9.1, + "lonOffset": 0.0003834, + "timeOffset": 11, + "latOffset": -0.0023425 + } + ] + }, + "pathPrediction": { + "confidence": 80, + "radiusOfCurve": -1622.5 + } + } + } + ], + "coreData": { + "secMark": 55578, + "transmission": "UNAVAILABLE", + "size": { + "width": 180, + "length": 480 + }, + "heading": 347.9, + "accelSet": { + "accelYaw": -0.65, + "accelLong": -0.05 + }, + "brakes": { + "scs": "unavailable", + "wheelBrakes": { + "leftFront": false, + "rightFront": false, + "unavailable": true, + "leftRear": false, + "rightRear": false + }, + "abs": "off", + "traction": "unavailable", + "brakeBoost": "unavailable", + "auxBrakes": "unavailable" + }, + "accuracy": { + "semiMinor": 2, + "orientation": 0, + "semiMajor": 2 + }, + "id": "DD2F5ECF", + "position": { + "elevation": 1691.9, + "latitude": 39.587377532044655, + "longitude": -105.09063692590537 + }, + "msgCnt": 4, + "speed": 32.91 + } + }, + "dataType": "us.dot.its.jpo.ode.plugin.j2735.J2735Bsm" + }, + "recordGeneratedAt": { + "$date": { + "$numberLong": "1717518175586" + } + } +},{ + "_id": { + "$oid": "665f3fc08c25216ef4f53d97" + }, + "metadata": { + "securityResultCode": "success", + "bsmSource": "RV", + "schemaVersion": 6, + "odePacketID": "", + "sanitized": false, + "recordType": "bsmTx", + "recordGeneratedAt": "", + "maxDurationTime": 0, + "odeTimStartDateTime": "", + "receivedMessageDetails": { + "locationData": { + "elevation": "", + "heading": "", + "latitude": "", + "speed": "", + "longitude": "" + }, + "rxSource": "RV" + }, + "payloadType": "us.dot.its.jpo.ode.model.OdeBsmPayload", + "serialId": { + "recordId": 0, + "serialNumber": 0, + "streamId": "849c3ac6-4747-426f-ae5e-28ac8531b32c", + "bundleSize": 1, + "bundleId": 0 + }, + "logFileName": "", + "odeReceivedAt": "2024-06-04T16:22:55.686Z", + "originIp": "10.11.81.12" + }, + "payload": { + "data": { + "partII": [ + { + "id": "VehicleSafetyExtensions", + "value": { + "pathHistory": { + "crumbData": [ + { + "elevationOffset": 0.6, + "lonOffset": 0.0000387, + "timeOffset": 0.8, + "latOffset": -0.0001604 + }, + { + "elevationOffset": 4.3, + "lonOffset": 0.000231, + "timeOffset": 5.6, + "latOffset": -0.0011603 + }, + { + "elevationOffset": 9.1, + "lonOffset": 0.0003834, + "timeOffset": 11, + "latOffset": -0.0023425 + } + ] + }, + "pathPrediction": { + "confidence": 80, + "radiusOfCurve": -1622.5 + } + } + } + ], + "coreData": { + "secMark": 55686, + "transmission": "UNAVAILABLE", + "size": { + "width": 180, + "length": 480 + }, + "heading": 347.7, + "accelSet": { + "accelYaw": -0.65, + "accelLong": -0.05 + }, + "brakes": { + "scs": "unavailable", + "wheelBrakes": { + "leftFront": false, + "rightFront": false, + "unavailable": true, + "leftRear": false, + "rightRear": false + }, + "abs": "off", + "traction": "unavailable", + "brakeBoost": "unavailable", + "auxBrakes": "unavailable" + }, + "accuracy": { + "semiMinor": 2, + "orientation": 0, + "semiMajor": 2 + }, + "id": "DD2F5ECF", + "position": { + "elevation": 1691.9, + "latitude": 39.58741060490348, + "longitude": -105.09064631517474 + }, + "msgCnt": 5, + "speed": 37.65 + } + }, + "dataType": "us.dot.its.jpo.ode.plugin.j2735.J2735Bsm" + }, + "recordGeneratedAt": { + "$date": { + "$numberLong": "1717518175693" + } + } +},{ + "_id": { + "$oid": "665f3fc08c25216ef4f53d98" + }, + "metadata": { + "securityResultCode": "success", + "bsmSource": "RV", + "schemaVersion": 6, + "odePacketID": "", + "sanitized": false, + "recordType": "bsmTx", + "recordGeneratedAt": "", + "maxDurationTime": 0, + "odeTimStartDateTime": "", + "receivedMessageDetails": { + "locationData": { + "elevation": "", + "heading": "", + "latitude": "", + "speed": "", + "longitude": "" + }, + "rxSource": "RV" + }, + "payloadType": "us.dot.its.jpo.ode.model.OdeBsmPayload", + "serialId": { + "recordId": 0, + "serialNumber": 0, + "streamId": "849c3ac6-4747-426f-ae5e-28ac8531b32c", + "bundleSize": 1, + "bundleId": 0 + }, + "logFileName": "", + "odeReceivedAt": "2024-06-04T16:22:55.794Z", + "originIp": "10.11.81.12" + }, + "payload": { + "data": { + "partII": [ + { + "id": "VehicleSafetyExtensions", + "value": { + "pathHistory": { + "crumbData": [ + { + "elevationOffset": 0.6, + "lonOffset": 0.0000387, + "timeOffset": 0.8, + "latOffset": -0.0001604 + }, + { + "elevationOffset": 4.3, + "lonOffset": 0.000231, + "timeOffset": 5.6, + "latOffset": -0.0011603 + }, + { + "elevationOffset": 9.1, + "lonOffset": 0.0003834, + "timeOffset": 11, + "latOffset": -0.0023425 + } + ] + }, + "pathPrediction": { + "confidence": 80, + "radiusOfCurve": -1622.5 + } + } + } + ], + "coreData": { + "secMark": 55794, + "transmission": "UNAVAILABLE", + "size": { + "width": 180, + "length": 480 + }, + "heading": 343.3, + "accelSet": { + "accelYaw": -0.65, + "accelLong": -0.05 + }, + "brakes": { + "scs": "unavailable", + "wheelBrakes": { + "leftFront": false, + "rightFront": false, + "unavailable": true, + "leftRear": false, + "rightRear": false + }, + "abs": "off", + "traction": "unavailable", + "brakeBoost": "unavailable", + "auxBrakes": "unavailable" + }, + "accuracy": { + "semiMinor": 2, + "orientation": 0, + "semiMajor": 2 + }, + "id": "DD2F5ECF", + "position": { + "elevation": 1691.9, + "latitude": 39.58744161069427, + "longitude": -105.09065838709249 + }, + "msgCnt": 6, + "speed": 36 + } + }, + "dataType": "us.dot.its.jpo.ode.plugin.j2735.J2735Bsm" + }, + "recordGeneratedAt": { + "$date": { + "$numberLong": "1717518175800" + } + } +},{ + "_id": { + "$oid": "665f3fc08c25216ef4f53d99" + }, + "metadata": { + "securityResultCode": "success", + "bsmSource": "RV", + "schemaVersion": 6, + "odePacketID": "", + "sanitized": false, + "recordType": "bsmTx", + "recordGeneratedAt": "", + "maxDurationTime": 0, + "odeTimStartDateTime": "", + "receivedMessageDetails": { + "locationData": { + "elevation": "", + "heading": "", + "latitude": "", + "speed": "", + "longitude": "" + }, + "rxSource": "RV" + }, + "payloadType": "us.dot.its.jpo.ode.model.OdeBsmPayload", + "serialId": { + "recordId": 0, + "serialNumber": 0, + "streamId": "849c3ac6-4747-426f-ae5e-28ac8531b32c", + "bundleSize": 1, + "bundleId": 0 + }, + "logFileName": "", + "odeReceivedAt": "2024-06-04T16:22:55.902Z", + "originIp": "10.11.81.12" + }, + "payload": { + "data": { + "partII": [ + { + "id": "VehicleSafetyExtensions", + "value": { + "pathHistory": { + "crumbData": [ + { + "elevationOffset": 0.6, + "lonOffset": 0.0000387, + "timeOffset": 0.8, + "latOffset": -0.0001604 + }, + { + "elevationOffset": 4.3, + "lonOffset": 0.000231, + "timeOffset": 5.6, + "latOffset": -0.0011603 + }, + { + "elevationOffset": 9.1, + "lonOffset": 0.0003834, + "timeOffset": 11, + "latOffset": -0.0023425 + } + ] + }, + "pathPrediction": { + "confidence": 80, + "radiusOfCurve": -1622.5 + } + } + } + ], + "coreData": { + "secMark": 55902, + "transmission": "UNAVAILABLE", + "size": { + "width": 180, + "length": 480 + }, + "heading": 348, + "accelSet": { + "accelYaw": -0.65, + "accelLong": -0.05 + }, + "brakes": { + "scs": "unavailable", + "wheelBrakes": { + "leftFront": false, + "rightFront": false, + "unavailable": true, + "leftRear": false, + "rightRear": false + }, + "abs": "off", + "traction": "unavailable", + "brakeBoost": "unavailable", + "auxBrakes": "unavailable" + }, + "accuracy": { + "semiMinor": 2, + "orientation": 0, + "semiMajor": 2 + }, + "id": "DD2F5ECF", + "position": { + "elevation": 1691.9, + "latitude": 39.587475717048164, + "longitude": -105.09066777636183 + }, + "msgCnt": 7, + "speed": 38.77 + } + }, + "dataType": "us.dot.its.jpo.ode.plugin.j2735.J2735Bsm" + }, + "recordGeneratedAt": { + "$date": { + "$numberLong": "1717518175907" + } + } +},{ + "_id": { + "$oid": "665f3fc08c25216ef4f53d9a" + }, + "metadata": { + "securityResultCode": "success", + "bsmSource": "RV", + "schemaVersion": 6, + "odePacketID": "", + "sanitized": false, + "recordType": "bsmTx", + "recordGeneratedAt": "", + "maxDurationTime": 0, + "odeTimStartDateTime": "", + "receivedMessageDetails": { + "locationData": { + "elevation": "", + "heading": "", + "latitude": "", + "speed": "", + "longitude": "" + }, + "rxSource": "RV" + }, + "payloadType": "us.dot.its.jpo.ode.model.OdeBsmPayload", + "serialId": { + "recordId": 0, + "serialNumber": 0, + "streamId": "849c3ac6-4747-426f-ae5e-28ac8531b32c", + "bundleSize": 1, + "bundleId": 0 + }, + "logFileName": "", + "odeReceivedAt": "2024-06-04T16:22:56.007Z", + "originIp": "10.11.81.12" + }, + "payload": { + "data": { + "partII": [ + { + "id": "VehicleSafetyExtensions", + "value": { + "pathHistory": { + "crumbData": [ + { + "elevationOffset": 0.6, + "lonOffset": 0.0000387, + "timeOffset": 0.8, + "latOffset": -0.0001604 + }, + { + "elevationOffset": 4.3, + "lonOffset": 0.000231, + "timeOffset": 5.6, + "latOffset": -0.0011603 + }, + { + "elevationOffset": 9.1, + "lonOffset": 0.0003834, + "timeOffset": 11, + "latOffset": -0.0023425 + } + ] + }, + "pathPrediction": { + "confidence": 80, + "radiusOfCurve": -1622.5 + } + } + } + ], + "coreData": { + "secMark": 56007, + "transmission": "UNAVAILABLE", + "size": { + "width": 180, + "length": 480 + }, + "heading": 344.6, + "accelSet": { + "accelYaw": -0.65, + "accelLong": -0.05 + }, + "brakes": { + "scs": "unavailable", + "wheelBrakes": { + "leftFront": false, + "rightFront": false, + "unavailable": true, + "leftRear": false, + "rightRear": false + }, + "abs": "off", + "traction": "unavailable", + "brakeBoost": "unavailable", + "auxBrakes": "unavailable" + }, + "accuracy": { + "semiMinor": 2, + "orientation": 0, + "semiMajor": 2 + }, + "id": "DD2F5ECF", + "position": { + "elevation": 1691.9, + "latitude": 39.58750568928466, + "longitude": -105.09067850695536 + }, + "msgCnt": 8, + "speed": 34.57 + } + }, + "dataType": "us.dot.its.jpo.ode.plugin.j2735.J2735Bsm" + }, + "recordGeneratedAt": { + "$date": { + "$numberLong": "1717518176013" + } + } +},{ + "_id": { + "$oid": "665f3fc08c25216ef4f53d9b" + }, + "metadata": { + "securityResultCode": "success", + "bsmSource": "RV", + "schemaVersion": 6, + "odePacketID": "", + "sanitized": false, + "recordType": "bsmTx", + "recordGeneratedAt": "", + "maxDurationTime": 0, + "odeTimStartDateTime": "", + "receivedMessageDetails": { + "locationData": { + "elevation": "", + "heading": "", + "latitude": "", + "speed": "", + "longitude": "" + }, + "rxSource": "RV" + }, + "payloadType": "us.dot.its.jpo.ode.model.OdeBsmPayload", + "serialId": { + "recordId": 0, + "serialNumber": 0, + "streamId": "849c3ac6-4747-426f-ae5e-28ac8531b32c", + "bundleSize": 1, + "bundleId": 0 + }, + "logFileName": "", + "odeReceivedAt": "2024-06-04T16:22:56.116Z", + "originIp": "10.11.81.12" + }, + "payload": { + "data": { + "partII": [ + { + "id": "VehicleSafetyExtensions", + "value": { + "pathHistory": { + "crumbData": [ + { + "elevationOffset": 0.6, + "lonOffset": 0.0000387, + "timeOffset": 0.8, + "latOffset": -0.0001604 + }, + { + "elevationOffset": 4.3, + "lonOffset": 0.000231, + "timeOffset": 5.6, + "latOffset": -0.0011603 + }, + { + "elevationOffset": 9.1, + "lonOffset": 0.0003834, + "timeOffset": 11, + "latOffset": -0.0023425 + } + ] + }, + "pathPrediction": { + "confidence": 80, + "radiusOfCurve": -1622.5 + } + } + } + ], + "coreData": { + "secMark": 56116, + "transmission": "UNAVAILABLE", + "size": { + "width": 180, + "length": 480 + }, + "heading": 346, + "accelSet": { + "accelYaw": -0.65, + "accelLong": -0.05 + }, + "brakes": { + "scs": "unavailable", + "wheelBrakes": { + "leftFront": false, + "rightFront": false, + "unavailable": true, + "leftRear": false, + "rightRear": false + }, + "abs": "off", + "traction": "unavailable", + "brakeBoost": "unavailable", + "auxBrakes": "unavailable" + }, + "accuracy": { + "semiMinor": 2, + "orientation": 0, + "semiMajor": 2 + }, + "id": "DD2F5ECF", + "position": { + "elevation": 1691.9, + "latitude": 39.58753049388438, + "longitude": -105.09068655490051 + }, + "msgCnt": 9, + "speed": 28.43 + } + }, + "dataType": "us.dot.its.jpo.ode.plugin.j2735.J2735Bsm" + }, + "recordGeneratedAt": { + "$date": { + "$numberLong": "1717518176120" + } + } +},{ + "_id": { + "$oid": "665f3fc08c25216ef4f53d9c" + }, + "metadata": { + "securityResultCode": "success", + "bsmSource": "RV", + "schemaVersion": 6, + "odePacketID": "", + "sanitized": false, + "recordType": "bsmTx", + "recordGeneratedAt": "", + "maxDurationTime": 0, + "odeTimStartDateTime": "", + "receivedMessageDetails": { + "locationData": { + "elevation": "", + "heading": "", + "latitude": "", + "speed": "", + "longitude": "" + }, + "rxSource": "RV" + }, + "payloadType": "us.dot.its.jpo.ode.model.OdeBsmPayload", + "serialId": { + "recordId": 0, + "serialNumber": 0, + "streamId": "849c3ac6-4747-426f-ae5e-28ac8531b32c", + "bundleSize": 1, + "bundleId": 0 + }, + "logFileName": "", + "odeReceivedAt": "2024-06-04T16:22:56.223Z", + "originIp": "10.11.81.12" + }, + "payload": { + "data": { + "partII": [ + { + "id": "VehicleSafetyExtensions", + "value": { + "pathHistory": { + "crumbData": [ + { + "elevationOffset": 0.6, + "lonOffset": 0.0000387, + "timeOffset": 0.8, + "latOffset": -0.0001604 + }, + { + "elevationOffset": 4.3, + "lonOffset": 0.000231, + "timeOffset": 5.6, + "latOffset": -0.0011603 + }, + { + "elevationOffset": 9.1, + "lonOffset": 0.0003834, + "timeOffset": 11, + "latOffset": -0.0023425 + } + ] + }, + "pathPrediction": { + "confidence": 80, + "radiusOfCurve": -1622.5 + } + } + } + ], + "coreData": { + "secMark": 56223, + "transmission": "UNAVAILABLE", + "size": { + "width": 180, + "length": 480 + }, + "heading": 347, + "accelSet": { + "accelYaw": -0.65, + "accelLong": -0.05 + }, + "brakes": { + "scs": "unavailable", + "wheelBrakes": { + "leftFront": false, + "rightFront": false, + "unavailable": true, + "leftRear": false, + "rightRear": false + }, + "abs": "off", + "traction": "unavailable", + "brakeBoost": "unavailable", + "auxBrakes": "unavailable" + }, + "accuracy": { + "semiMinor": 2, + "orientation": 0, + "semiMajor": 2 + }, + "id": "DD2F5ECF", + "position": { + "elevation": 1691.9, + "latitude": 39.58755633199964, + "longitude": -105.09069429121115 + }, + "msgCnt": 10, + "speed": 29.49 + } + }, + "dataType": "us.dot.its.jpo.ode.plugin.j2735.J2735Bsm" + }, + "recordGeneratedAt": { + "$date": { + "$numberLong": "1717518176226" + } + } +},{ + "_id": { + "$oid": "665f3fc08c25216ef4f53d9d" + }, + "metadata": { + "securityResultCode": "success", + "bsmSource": "RV", + "schemaVersion": 6, + "odePacketID": "", + "sanitized": false, + "recordType": "bsmTx", + "recordGeneratedAt": "", + "maxDurationTime": 0, + "odeTimStartDateTime": "", + "receivedMessageDetails": { + "locationData": { + "elevation": "", + "heading": "", + "latitude": "", + "speed": "", + "longitude": "" + }, + "rxSource": "RV" + }, + "payloadType": "us.dot.its.jpo.ode.model.OdeBsmPayload", + "serialId": { + "recordId": 0, + "serialNumber": 0, + "streamId": "849c3ac6-4747-426f-ae5e-28ac8531b32c", + "bundleSize": 1, + "bundleId": 0 + }, + "logFileName": "", + "odeReceivedAt": "2024-06-04T16:22:56.329Z", + "originIp": "10.11.81.12" + }, + "payload": { + "data": { + "partII": [ + { + "id": "VehicleSafetyExtensions", + "value": { + "pathHistory": { + "crumbData": [ + { + "elevationOffset": 0.6, + "lonOffset": 0.0000387, + "timeOffset": 0.8, + "latOffset": -0.0001604 + }, + { + "elevationOffset": 4.3, + "lonOffset": 0.000231, + "timeOffset": 5.6, + "latOffset": -0.0011603 + }, + { + "elevationOffset": 9.1, + "lonOffset": 0.0003834, + "timeOffset": 11, + "latOffset": -0.0023425 + } + ] + }, + "pathPrediction": { + "confidence": 80, + "radiusOfCurve": -1622.5 + } + } + } + ], + "coreData": { + "secMark": 56329, + "transmission": "UNAVAILABLE", + "size": { + "width": 180, + "length": 480 + }, + "heading": 347.2, + "accelSet": { + "accelYaw": -0.65, + "accelLong": -0.05 + }, + "brakes": { + "scs": "unavailable", + "wheelBrakes": { + "leftFront": false, + "rightFront": false, + "unavailable": true, + "leftRear": false, + "rightRear": false + }, + "abs": "off", + "traction": "unavailable", + "brakeBoost": "unavailable", + "auxBrakes": "unavailable" + }, + "accuracy": { + "semiMinor": 2, + "orientation": 0, + "semiMajor": 2 + }, + "id": "DD2F5ECF", + "position": { + "elevation": 1691.9, + "latitude": 39.58758578743928, + "longitude": -105.09070300981843 + }, + "msgCnt": 11, + "speed": 33.59 + } + }, + "dataType": "us.dot.its.jpo.ode.plugin.j2735.J2735Bsm" + }, + "recordGeneratedAt": { + "$date": { + "$numberLong": "1717518176333" + } + } +},{ + "_id": { + "$oid": "665f3fc08c25216ef4f53d9e" + }, + "metadata": { + "securityResultCode": "success", + "bsmSource": "RV", + "schemaVersion": 6, + "odePacketID": "", + "sanitized": false, + "recordType": "bsmTx", + "recordGeneratedAt": "", + "maxDurationTime": 0, + "odeTimStartDateTime": "", + "receivedMessageDetails": { + "locationData": { + "elevation": "", + "heading": "", + "latitude": "", + "speed": "", + "longitude": "" + }, + "rxSource": "RV" + }, + "payloadType": "us.dot.its.jpo.ode.model.OdeBsmPayload", + "serialId": { + "recordId": 0, + "serialNumber": 0, + "streamId": "849c3ac6-4747-426f-ae5e-28ac8531b32c", + "bundleSize": 1, + "bundleId": 0 + }, + "logFileName": "", + "odeReceivedAt": "2024-06-04T16:22:56.437Z", + "originIp": "10.11.81.12" + }, + "payload": { + "data": { + "partII": [ + { + "id": "VehicleSafetyExtensions", + "value": { + "pathHistory": { + "crumbData": [ + { + "elevationOffset": 0.6, + "lonOffset": 0.0000387, + "timeOffset": 0.8, + "latOffset": -0.0001604 + }, + { + "elevationOffset": 4.3, + "lonOffset": 0.000231, + "timeOffset": 5.6, + "latOffset": -0.0011603 + }, + { + "elevationOffset": 9.1, + "lonOffset": 0.0003834, + "timeOffset": 11, + "latOffset": -0.0023425 + } + ] + }, + "pathPrediction": { + "confidence": 80, + "radiusOfCurve": -1622.5 + } + } + } + ], + "coreData": { + "secMark": 56437, + "transmission": "UNAVAILABLE", + "size": { + "width": 180, + "length": 480 + }, + "heading": 345.2, + "accelSet": { + "accelYaw": -0.65, + "accelLong": -0.05 + }, + "brakes": { + "scs": "unavailable", + "wheelBrakes": { + "leftFront": false, + "rightFront": false, + "unavailable": true, + "leftRear": false, + "rightRear": false + }, + "abs": "off", + "traction": "unavailable", + "brakeBoost": "unavailable", + "auxBrakes": "unavailable" + }, + "accuracy": { + "semiMinor": 2, + "orientation": 0, + "semiMajor": 2 + }, + "id": "DD2F5ECF", + "position": { + "elevation": 1691.9, + "latitude": 39.587613175819314, + "longitude": -105.09071239908775 + }, + "msgCnt": 12, + "speed": 31.5 + } + }, + "dataType": "us.dot.its.jpo.ode.plugin.j2735.J2735Bsm" + }, + "recordGeneratedAt": { + "$date": { + "$numberLong": "1717518176441" + } + } +},{ + "_id": { + "$oid": "665f3fc08c25216ef4f53d9f" + }, + "metadata": { + "securityResultCode": "success", + "bsmSource": "RV", + "schemaVersion": 6, + "odePacketID": "", + "sanitized": false, + "recordType": "bsmTx", + "recordGeneratedAt": "", + "maxDurationTime": 0, + "odeTimStartDateTime": "", + "receivedMessageDetails": { + "locationData": { + "elevation": "", + "heading": "", + "latitude": "", + "speed": "", + "longitude": "" + }, + "rxSource": "RV" + }, + "payloadType": "us.dot.its.jpo.ode.model.OdeBsmPayload", + "serialId": { + "recordId": 0, + "serialNumber": 0, + "streamId": "849c3ac6-4747-426f-ae5e-28ac8531b32c", + "bundleSize": 1, + "bundleId": 0 + }, + "logFileName": "", + "odeReceivedAt": "2024-06-04T16:22:56.544Z", + "originIp": "10.11.81.12" + }, + "payload": { + "data": { + "partII": [ + { + "id": "VehicleSafetyExtensions", + "value": { + "pathHistory": { + "crumbData": [ + { + "elevationOffset": 0.6, + "lonOffset": 0.0000387, + "timeOffset": 0.8, + "latOffset": -0.0001604 + }, + { + "elevationOffset": 4.3, + "lonOffset": 0.000231, + "timeOffset": 5.6, + "latOffset": -0.0011603 + }, + { + "elevationOffset": 9.1, + "lonOffset": 0.0003834, + "timeOffset": 11, + "latOffset": -0.0023425 + } + ] + }, + "pathPrediction": { + "confidence": 80, + "radiusOfCurve": -1622.5 + } + } + } + ], + "coreData": { + "secMark": 56544, + "transmission": "UNAVAILABLE", + "size": { + "width": 180, + "length": 480 + }, + "heading": 346.2, + "accelSet": { + "accelYaw": -0.65, + "accelLong": -0.05 + }, + "brakes": { + "scs": "unavailable", + "wheelBrakes": { + "leftFront": false, + "rightFront": false, + "unavailable": true, + "leftRear": false, + "rightRear": false + }, + "abs": "off", + "traction": "unavailable", + "brakeBoost": "unavailable", + "auxBrakes": "unavailable" + }, + "accuracy": { + "semiMinor": 2, + "orientation": 0, + "semiMajor": 2 + }, + "id": "DD2F5ECF", + "position": { + "elevation": 1691.9, + "latitude": 39.587642631234786, + "longitude": -105.09072178835711 + }, + "msgCnt": 13, + "speed": 33.73 + } + }, + "dataType": "us.dot.its.jpo.ode.plugin.j2735.J2735Bsm" + }, + "recordGeneratedAt": { + "$date": { + "$numberLong": "1717518176548" + } + } +},{ + "_id": { + "$oid": "665f3fc08c25216ef4f53da0" + }, + "metadata": { + "securityResultCode": "success", + "bsmSource": "RV", + "schemaVersion": 6, + "odePacketID": "", + "sanitized": false, + "recordType": "bsmTx", + "recordGeneratedAt": "", + "maxDurationTime": 0, + "odeTimStartDateTime": "", + "receivedMessageDetails": { + "locationData": { + "elevation": "", + "heading": "", + "latitude": "", + "speed": "", + "longitude": "" + }, + "rxSource": "RV" + }, + "payloadType": "us.dot.its.jpo.ode.model.OdeBsmPayload", + "serialId": { + "recordId": 0, + "serialNumber": 0, + "streamId": "849c3ac6-4747-426f-ae5e-28ac8531b32c", + "bundleSize": 1, + "bundleId": 0 + }, + "logFileName": "", + "odeReceivedAt": "2024-06-04T16:22:56.651Z", + "originIp": "10.11.81.12" + }, + "payload": { + "data": { + "partII": [ + { + "id": "VehicleSafetyExtensions", + "value": { + "pathHistory": { + "crumbData": [ + { + "elevationOffset": 0.6, + "lonOffset": 0.0000387, + "timeOffset": 0.8, + "latOffset": -0.0001604 + }, + { + "elevationOffset": 4.3, + "lonOffset": 0.000231, + "timeOffset": 5.6, + "latOffset": -0.0011603 + }, + { + "elevationOffset": 9.1, + "lonOffset": 0.0003834, + "timeOffset": 11, + "latOffset": -0.0023425 + } + ] + }, + "pathPrediction": { + "confidence": 80, + "radiusOfCurve": -1622.5 + } + } + } + ], + "coreData": { + "secMark": 56651, + "transmission": "UNAVAILABLE", + "size": { + "width": 180, + "length": 480 + }, + "heading": 345.6, + "accelSet": { + "accelYaw": -0.65, + "accelLong": -0.05 + }, + "brakes": { + "scs": "unavailable", + "wheelBrakes": { + "leftFront": false, + "rightFront": false, + "unavailable": true, + "leftRear": false, + "rightRear": false + }, + "abs": "off", + "traction": "unavailable", + "brakeBoost": "unavailable", + "auxBrakes": "unavailable" + }, + "accuracy": { + "semiMinor": 2, + "orientation": 0, + "semiMajor": 2 + }, + "id": "DD2F5ECF", + "position": { + "elevation": 1691.9, + "latitude": 39.58767673748972, + "longitude": -105.09073318961273 + }, + "msgCnt": 14, + "speed": 39.16 + } + }, + "dataType": "us.dot.its.jpo.ode.plugin.j2735.J2735Bsm" + }, + "recordGeneratedAt": { + "$date": { + "$numberLong": "1717518176658" + } + } +},{ + "_id": { + "$oid": "665f3fc08c25216ef4f53da1" + }, + "metadata": { + "securityResultCode": "success", + "bsmSource": "RV", + "schemaVersion": 6, + "odePacketID": "", + "sanitized": false, + "recordType": "bsmTx", + "recordGeneratedAt": "", + "maxDurationTime": 0, + "odeTimStartDateTime": "", + "receivedMessageDetails": { + "locationData": { + "elevation": "", + "heading": "", + "latitude": "", + "speed": "", + "longitude": "" + }, + "rxSource": "RV" + }, + "payloadType": "us.dot.its.jpo.ode.model.OdeBsmPayload", + "serialId": { + "recordId": 0, + "serialNumber": 0, + "streamId": "849c3ac6-4747-426f-ae5e-28ac8531b32c", + "bundleSize": 1, + "bundleId": 0 + }, + "logFileName": "", + "odeReceivedAt": "2024-06-04T16:22:56.758Z", + "originIp": "10.11.81.12" + }, + "payload": { + "data": { + "partII": [ + { + "id": "VehicleSafetyExtensions", + "value": { + "pathHistory": { + "crumbData": [ + { + "elevationOffset": 0.6, + "lonOffset": 0.0000387, + "timeOffset": 0.8, + "latOffset": -0.0001604 + }, + { + "elevationOffset": 4.3, + "lonOffset": 0.000231, + "timeOffset": 5.6, + "latOffset": -0.0011603 + }, + { + "elevationOffset": 9.1, + "lonOffset": 0.0003834, + "timeOffset": 11, + "latOffset": -0.0023425 + } + ] + }, + "pathPrediction": { + "confidence": 80, + "radiusOfCurve": -1622.5 + } + } + } + ], + "coreData": { + "secMark": 56758, + "transmission": "UNAVAILABLE", + "size": { + "width": 180, + "length": 480 + }, + "heading": 350, + "accelSet": { + "accelYaw": -0.65, + "accelLong": -0.05 + }, + "brakes": { + "scs": "unavailable", + "wheelBrakes": { + "leftFront": false, + "rightFront": false, + "unavailable": true, + "leftRear": false, + "rightRear": false + }, + "abs": "off", + "traction": "unavailable", + "brakeBoost": "unavailable", + "auxBrakes": "unavailable" + }, + "accuracy": { + "semiMinor": 2, + "orientation": 0, + "semiMajor": 2 + }, + "id": "DD2F5ECF", + "position": { + "elevation": 1691.9, + "latitude": 39.58769430737206, + "longitude": -105.09073721358531 + }, + "msgCnt": 15, + "speed": 19.84 + } + }, + "dataType": "us.dot.its.jpo.ode.plugin.j2735.J2735Bsm" + }, + "recordGeneratedAt": { + "$date": { + "$numberLong": "1717518176763" + } + } +},{ + "_id": { + "$oid": "665f3fc08c25216ef4f53da2" + }, + "metadata": { + "securityResultCode": "success", + "bsmSource": "RV", + "schemaVersion": 6, + "odePacketID": "", + "sanitized": false, + "recordType": "bsmTx", + "recordGeneratedAt": "", + "maxDurationTime": 0, + "odeTimStartDateTime": "", + "receivedMessageDetails": { + "locationData": { + "elevation": "", + "heading": "", + "latitude": "", + "speed": "", + "longitude": "" + }, + "rxSource": "RV" + }, + "payloadType": "us.dot.its.jpo.ode.model.OdeBsmPayload", + "serialId": { + "recordId": 0, + "serialNumber": 0, + "streamId": "849c3ac6-4747-426f-ae5e-28ac8531b32c", + "bundleSize": 1, + "bundleId": 0 + }, + "logFileName": "", + "odeReceivedAt": "2024-06-04T16:22:56.866Z", + "originIp": "10.11.81.12" + }, + "payload": { + "data": { + "partII": [ + { + "id": "VehicleSafetyExtensions", + "value": { + "pathHistory": { + "crumbData": [ + { + "elevationOffset": 0.6, + "lonOffset": 0.0000387, + "timeOffset": 0.8, + "latOffset": -0.0001604 + }, + { + "elevationOffset": 4.3, + "lonOffset": 0.000231, + "timeOffset": 5.6, + "latOffset": -0.0011603 + }, + { + "elevationOffset": 9.1, + "lonOffset": 0.0003834, + "timeOffset": 11, + "latOffset": -0.0023425 + } + ] + }, + "pathPrediction": { + "confidence": 80, + "radiusOfCurve": -1622.5 + } + } + } + ], + "coreData": { + "secMark": 56866, + "transmission": "UNAVAILABLE", + "size": { + "width": 180, + "length": 480 + }, + "heading": 349, + "accelSet": { + "accelYaw": -0.65, + "accelLong": -0.05 + }, + "brakes": { + "scs": "unavailable", + "wheelBrakes": { + "leftFront": false, + "rightFront": false, + "unavailable": true, + "leftRear": false, + "rightRear": false + }, + "abs": "off", + "traction": "unavailable", + "brakeBoost": "unavailable", + "auxBrakes": "unavailable" + }, + "accuracy": { + "semiMinor": 2, + "orientation": 0, + "semiMajor": 2 + }, + "id": "DD2F5ECF", + "position": { + "elevation": 1691.9, + "latitude": 39.58771032696678, + "longitude": -105.09074123755789 + }, + "msgCnt": 16, + "speed": 18.14 + } + }, + "dataType": "us.dot.its.jpo.ode.plugin.j2735.J2735Bsm" + }, + "recordGeneratedAt": { + "$date": { + "$numberLong": "1717518176870" + } + } +},{ + "_id": { + "$oid": "665f3fc08c25216ef4f53da3" + }, + "metadata": { + "securityResultCode": "success", + "bsmSource": "RV", + "schemaVersion": 6, + "odePacketID": "", + "sanitized": false, + "recordType": "bsmTx", + "recordGeneratedAt": "", + "maxDurationTime": 0, + "odeTimStartDateTime": "", + "receivedMessageDetails": { + "locationData": { + "elevation": "", + "heading": "", + "latitude": "", + "speed": "", + "longitude": "" + }, + "rxSource": "RV" + }, + "payloadType": "us.dot.its.jpo.ode.model.OdeBsmPayload", + "serialId": { + "recordId": 0, + "serialNumber": 0, + "streamId": "849c3ac6-4747-426f-ae5e-28ac8531b32c", + "bundleSize": 1, + "bundleId": 0 + }, + "logFileName": "", + "odeReceivedAt": "2024-06-04T16:22:56.973Z", + "originIp": "10.11.81.12" + }, + "payload": { + "data": { + "partII": [ + { + "id": "VehicleSafetyExtensions", + "value": { + "pathHistory": { + "crumbData": [ + { + "elevationOffset": 0.6, + "lonOffset": 0.0000387, + "timeOffset": 0.8, + "latOffset": -0.0001604 + }, + { + "elevationOffset": 4.3, + "lonOffset": 0.000231, + "timeOffset": 5.6, + "latOffset": -0.0011603 + }, + { + "elevationOffset": 9.1, + "lonOffset": 0.0003834, + "timeOffset": 11, + "latOffset": -0.0023425 + } + ] + }, + "pathPrediction": { + "confidence": 80, + "radiusOfCurve": -1622.5 + } + } + } + ], + "coreData": { + "secMark": 56973, + "transmission": "UNAVAILABLE", + "size": { + "width": 180, + "length": 480 + }, + "heading": 345.2, + "accelSet": { + "accelYaw": -0.65, + "accelLong": -0.05 + }, + "brakes": { + "scs": "unavailable", + "wheelBrakes": { + "leftFront": false, + "rightFront": false, + "unavailable": true, + "leftRear": false, + "rightRear": false + }, + "abs": "off", + "traction": "unavailable", + "brakeBoost": "unavailable", + "auxBrakes": "unavailable" + }, + "accuracy": { + "semiMinor": 2, + "orientation": 0, + "semiMajor": 2 + }, + "id": "DD2F5ECF", + "position": { + "elevation": 1691.9, + "latitude": 39.58772789684062, + "longitude": -105.09074727351678 + }, + "msgCnt": 17, + "speed": 20.21 + } + }, + "dataType": "us.dot.its.jpo.ode.plugin.j2735.J2735Bsm" + }, + "recordGeneratedAt": { + "$date": { + "$numberLong": "1717518176977" + } + } +},{ + "_id": { + "$oid": "665f3fc08c25216ef4f53da4" + }, + "metadata": { + "securityResultCode": "success", + "bsmSource": "RV", + "schemaVersion": 6, + "odePacketID": "", + "sanitized": false, + "recordType": "bsmTx", + "recordGeneratedAt": "", + "maxDurationTime": 0, + "odeTimStartDateTime": "", + "receivedMessageDetails": { + "locationData": { + "elevation": "", + "heading": "", + "latitude": "", + "speed": "", + "longitude": "" + }, + "rxSource": "RV" + }, + "payloadType": "us.dot.its.jpo.ode.model.OdeBsmPayload", + "serialId": { + "recordId": 0, + "serialNumber": 0, + "streamId": "849c3ac6-4747-426f-ae5e-28ac8531b32c", + "bundleSize": 1, + "bundleId": 0 + }, + "logFileName": "", + "odeReceivedAt": "2024-06-04T16:22:57.08Z", + "originIp": "10.11.81.12" + }, + "payload": { + "data": { + "partII": [ + { + "id": "VehicleSafetyExtensions", + "value": { + "pathHistory": { + "crumbData": [ + { + "elevationOffset": 0.6, + "lonOffset": 0.0000387, + "timeOffset": 0.8, + "latOffset": -0.0001604 + }, + { + "elevationOffset": 4.3, + "lonOffset": 0.000231, + "timeOffset": 5.6, + "latOffset": -0.0011603 + }, + { + "elevationOffset": 9.1, + "lonOffset": 0.0003834, + "timeOffset": 11, + "latOffset": -0.0023425 + } + ] + }, + "pathPrediction": { + "confidence": 80, + "radiusOfCurve": -1622.5 + } + } + } + ], + "coreData": { + "secMark": 57080, + "transmission": "UNAVAILABLE", + "size": { + "width": 180, + "length": 480 + }, + "heading": 346.3, + "accelSet": { + "accelYaw": -0.65, + "accelLong": -0.05 + }, + "brakes": { + "scs": "unavailable", + "wheelBrakes": { + "leftFront": false, + "rightFront": false, + "unavailable": true, + "leftRear": false, + "rightRear": false + }, + "abs": "off", + "traction": "unavailable", + "brakeBoost": "unavailable", + "auxBrakes": "unavailable" + }, + "accuracy": { + "semiMinor": 2, + "orientation": 0, + "semiMajor": 2 + }, + "id": "DD2F5ECF", + "position": { + "elevation": 1691.9, + "latitude": 39.58774908403548, + "longitude": -105.09075398013772 + }, + "msgCnt": 18, + "speed": 24.25 + } + }, + "dataType": "us.dot.its.jpo.ode.plugin.j2735.J2735Bsm" + }, + "recordGeneratedAt": { + "$date": { + "$numberLong": "1717518177083" + } + } +},{ + "_id": { + "$oid": "665f3fc08c25216ef4f53da5" + }, + "metadata": { + "securityResultCode": "success", + "bsmSource": "RV", + "schemaVersion": 6, + "odePacketID": "", + "sanitized": false, + "recordType": "bsmTx", + "recordGeneratedAt": "", + "maxDurationTime": 0, + "odeTimStartDateTime": "", + "receivedMessageDetails": { + "locationData": { + "elevation": "", + "heading": "", + "latitude": "", + "speed": "", + "longitude": "" + }, + "rxSource": "RV" + }, + "payloadType": "us.dot.its.jpo.ode.model.OdeBsmPayload", + "serialId": { + "recordId": 0, + "serialNumber": 0, + "streamId": "849c3ac6-4747-426f-ae5e-28ac8531b32c", + "bundleSize": 1, + "bundleId": 0 + }, + "logFileName": "", + "odeReceivedAt": "2024-06-04T16:22:57.188Z", + "originIp": "10.11.81.12" + }, + "payload": { + "data": { + "partII": [ + { + "id": "VehicleSafetyExtensions", + "value": { + "pathHistory": { + "crumbData": [ + { + "elevationOffset": 0.6, + "lonOffset": 0.0000387, + "timeOffset": 0.8, + "latOffset": -0.0001604 + }, + { + "elevationOffset": 4.3, + "lonOffset": 0.000231, + "timeOffset": 5.6, + "latOffset": -0.0011603 + }, + { + "elevationOffset": 9.1, + "lonOffset": 0.0003834, + "timeOffset": 11, + "latOffset": -0.0023425 + } + ] + }, + "pathPrediction": { + "confidence": 80, + "radiusOfCurve": -1622.5 + } + } + } + ], + "coreData": { + "secMark": 57188, + "transmission": "UNAVAILABLE", + "size": { + "width": 180, + "length": 480 + }, + "heading": 351.4, + "accelSet": { + "accelYaw": -0.65, + "accelLong": -0.05 + }, + "brakes": { + "scs": "unavailable", + "wheelBrakes": { + "leftFront": false, + "rightFront": false, + "unavailable": true, + "leftRear": false, + "rightRear": false + }, + "abs": "off", + "traction": "unavailable", + "brakeBoost": "unavailable", + "auxBrakes": "unavailable" + }, + "accuracy": { + "semiMinor": 2, + "orientation": 0, + "semiMajor": 2 + }, + "id": "DD2F5ECF", + "position": { + "elevation": 1691.9, + "latitude": 39.58776613713885, + "longitude": -105.0907573334482 + }, + "msgCnt": 19, + "speed": 19.18 + } + }, + "dataType": "us.dot.its.jpo.ode.plugin.j2735.J2735Bsm" + }, + "recordGeneratedAt": { + "$date": { + "$numberLong": "1717518177188" + } + } +},{ + "_id": { + "$oid": "665f3fc08c25216ef4f53da6" + }, + "metadata": { + "securityResultCode": "success", + "bsmSource": "RV", + "schemaVersion": 6, + "odePacketID": "", + "sanitized": false, + "recordType": "bsmTx", + "recordGeneratedAt": "", + "maxDurationTime": 0, + "odeTimStartDateTime": "", + "receivedMessageDetails": { + "locationData": { + "elevation": "", + "heading": "", + "latitude": "", + "speed": "", + "longitude": "" + }, + "rxSource": "RV" + }, + "payloadType": "us.dot.its.jpo.ode.model.OdeBsmPayload", + "serialId": { + "recordId": 0, + "serialNumber": 0, + "streamId": "849c3ac6-4747-426f-ae5e-28ac8531b32c", + "bundleSize": 1, + "bundleId": 0 + }, + "logFileName": "", + "odeReceivedAt": "2024-06-04T16:22:57.295Z", + "originIp": "10.11.81.12" + }, + "payload": { + "data": { + "partII": [ + { + "id": "VehicleSafetyExtensions", + "value": { + "pathHistory": { + "crumbData": [ + { + "elevationOffset": 0.6, + "lonOffset": 0.0000387, + "timeOffset": 0.8, + "latOffset": -0.0001604 + }, + { + "elevationOffset": 4.3, + "lonOffset": 0.000231, + "timeOffset": 5.6, + "latOffset": -0.0011603 + }, + { + "elevationOffset": 9.1, + "lonOffset": 0.0003834, + "timeOffset": 11, + "latOffset": -0.0023425 + } + ] + }, + "pathPrediction": { + "confidence": 80, + "radiusOfCurve": -1622.5 + } + } + } + ], + "coreData": { + "secMark": 57295, + "transmission": "UNAVAILABLE", + "size": { + "width": 180, + "length": 480 + }, + "heading": 346.7, + "accelSet": { + "accelYaw": -0.65, + "accelLong": -0.05 + }, + "brakes": { + "scs": "unavailable", + "wheelBrakes": { + "leftFront": false, + "rightFront": false, + "unavailable": true, + "leftRear": false, + "rightRear": false + }, + "abs": "off", + "traction": "unavailable", + "brakeBoost": "unavailable", + "auxBrakes": "unavailable" + }, + "accuracy": { + "semiMinor": 2, + "orientation": 0, + "semiMajor": 2 + }, + "id": "DD2F5ECF", + "position": { + "elevation": 1691.9, + "latitude": 39.587785774040526, + "longitude": -105.09076336940707 + }, + "msgCnt": 20, + "speed": 22.44 + } + }, + "dataType": "us.dot.its.jpo.ode.plugin.j2735.J2735Bsm" + }, + "recordGeneratedAt": { + "$date": { + "$numberLong": "1717518177297" + } + } +},{ + "_id": { + "$oid": "665f3fc08c25216ef4f53da7" + }, + "metadata": { + "securityResultCode": "success", + "bsmSource": "RV", + "schemaVersion": 6, + "odePacketID": "", + "sanitized": false, + "recordType": "bsmTx", + "recordGeneratedAt": "", + "maxDurationTime": 0, + "odeTimStartDateTime": "", + "receivedMessageDetails": { + "locationData": { + "elevation": "", + "heading": "", + "latitude": "", + "speed": "", + "longitude": "" + }, + "rxSource": "RV" + }, + "payloadType": "us.dot.its.jpo.ode.model.OdeBsmPayload", + "serialId": { + "recordId": 0, + "serialNumber": 0, + "streamId": "849c3ac6-4747-426f-ae5e-28ac8531b32c", + "bundleSize": 1, + "bundleId": 0 + }, + "logFileName": "", + "odeReceivedAt": "2024-06-04T16:22:57.402Z", + "originIp": "10.11.81.12" + }, + "payload": { + "data": { + "partII": [ + { + "id": "VehicleSafetyExtensions", + "value": { + "pathHistory": { + "crumbData": [ + { + "elevationOffset": 0.6, + "lonOffset": 0.0000387, + "timeOffset": 0.8, + "latOffset": -0.0001604 + }, + { + "elevationOffset": 4.3, + "lonOffset": 0.000231, + "timeOffset": 5.6, + "latOffset": -0.0011603 + }, + { + "elevationOffset": 9.1, + "lonOffset": 0.0003834, + "timeOffset": 11, + "latOffset": -0.0023425 + } + ] + }, + "pathPrediction": { + "confidence": 80, + "radiusOfCurve": -1622.5 + } + } + } + ], + "coreData": { + "secMark": 57402, + "transmission": "UNAVAILABLE", + "size": { + "width": 180, + "length": 480 + }, + "heading": 347.5, + "accelSet": { + "accelYaw": -0.65, + "accelLong": -0.05 + }, + "brakes": { + "scs": "unavailable", + "wheelBrakes": { + "leftFront": false, + "rightFront": false, + "unavailable": true, + "leftRear": false, + "rightRear": false + }, + "abs": "off", + "traction": "unavailable", + "brakeBoost": "unavailable", + "auxBrakes": "unavailable" + }, + "accuracy": { + "semiMinor": 2, + "orientation": 0, + "semiMajor": 2 + }, + "id": "DD2F5ECF", + "position": { + "elevation": 1691.9, + "latitude": 39.587804377415964, + "longitude": -105.09076873470384 + }, + "msgCnt": 21, + "speed": 21.19 + } + }, + "dataType": "us.dot.its.jpo.ode.plugin.j2735.J2735Bsm" + }, + "recordGeneratedAt": { + "$date": { + "$numberLong": "1717518177416" + } + } +},{ + "_id": { + "$oid": "665f3fc08c25216ef4f53da8" + }, + "metadata": { + "securityResultCode": "success", + "bsmSource": "RV", + "schemaVersion": 6, + "odePacketID": "", + "sanitized": false, + "recordType": "bsmTx", + "recordGeneratedAt": "", + "maxDurationTime": 0, + "odeTimStartDateTime": "", + "receivedMessageDetails": { + "locationData": { + "elevation": "", + "heading": "", + "latitude": "", + "speed": "", + "longitude": "" + }, + "rxSource": "RV" + }, + "payloadType": "us.dot.its.jpo.ode.model.OdeBsmPayload", + "serialId": { + "recordId": 0, + "serialNumber": 0, + "streamId": "849c3ac6-4747-426f-ae5e-28ac8531b32c", + "bundleSize": 1, + "bundleId": 0 + }, + "logFileName": "", + "odeReceivedAt": "2024-06-04T16:22:57.51Z", + "originIp": "10.11.81.12" + }, + "payload": { + "data": { + "partII": [ + { + "id": "VehicleSafetyExtensions", + "value": { + "pathHistory": { + "crumbData": [ + { + "elevationOffset": 0.6, + "lonOffset": 0.0000387, + "timeOffset": 0.8, + "latOffset": -0.0001604 + }, + { + "elevationOffset": 4.3, + "lonOffset": 0.000231, + "timeOffset": 5.6, + "latOffset": -0.0011603 + }, + { + "elevationOffset": 9.1, + "lonOffset": 0.0003834, + "timeOffset": 11, + "latOffset": -0.0023425 + } + ] + }, + "pathPrediction": { + "confidence": 80, + "radiusOfCurve": -1622.5 + } + } + } + ], + "coreData": { + "secMark": 57510, + "transmission": "UNAVAILABLE", + "size": { + "width": 180, + "length": 480 + }, + "heading": 349, + "accelSet": { + "accelYaw": -0.65, + "accelLong": -0.05 + }, + "brakes": { + "scs": "unavailable", + "wheelBrakes": { + "leftFront": false, + "rightFront": false, + "unavailable": true, + "leftRear": false, + "rightRear": false + }, + "abs": "off", + "traction": "unavailable", + "brakeBoost": "unavailable", + "auxBrakes": "unavailable" + }, + "accuracy": { + "semiMinor": 2, + "orientation": 0, + "semiMajor": 2 + }, + "id": "DD2F5ECF", + "position": { + "elevation": 1691.9, + "latitude": 39.587820396985244, + "longitude": -105.09077275867644 + }, + "msgCnt": 22, + "speed": 18.14 + } + }, + "dataType": "us.dot.its.jpo.ode.plugin.j2735.J2735Bsm" + }, + "recordGeneratedAt": { + "$date": { + "$numberLong": "1717518177523" + } + } +},{ + "_id": { + "$oid": "665f3fc08c25216ef4f53da9" + }, + "metadata": { + "securityResultCode": "success", + "bsmSource": "RV", + "schemaVersion": 6, + "odePacketID": "", + "sanitized": false, + "recordType": "bsmTx", + "recordGeneratedAt": "", + "maxDurationTime": 0, + "odeTimStartDateTime": "", + "receivedMessageDetails": { + "locationData": { + "elevation": "", + "heading": "", + "latitude": "", + "speed": "", + "longitude": "" + }, + "rxSource": "RV" + }, + "payloadType": "us.dot.its.jpo.ode.model.OdeBsmPayload", + "serialId": { + "recordId": 0, + "serialNumber": 0, + "streamId": "849c3ac6-4747-426f-ae5e-28ac8531b32c", + "bundleSize": 1, + "bundleId": 0 + }, + "logFileName": "", + "odeReceivedAt": "2024-06-04T16:22:57.618Z", + "originIp": "10.11.81.12" + }, + "payload": { + "data": { + "partII": [ + { + "id": "VehicleSafetyExtensions", + "value": { + "pathHistory": { + "crumbData": [ + { + "elevationOffset": 0.6, + "lonOffset": 0.0000387, + "timeOffset": 0.8, + "latOffset": -0.0001604 + }, + { + "elevationOffset": 4.3, + "lonOffset": 0.000231, + "timeOffset": 5.6, + "latOffset": -0.0011603 + }, + { + "elevationOffset": 9.1, + "lonOffset": 0.0003834, + "timeOffset": 11, + "latOffset": -0.0023425 + } + ] + }, + "pathPrediction": { + "confidence": 80, + "radiusOfCurve": -1622.5 + } + } + } + ], + "coreData": { + "secMark": 57618, + "transmission": "UNAVAILABLE", + "size": { + "width": 180, + "length": 480 + }, + "heading": 349.7, + "accelSet": { + "accelYaw": -0.65, + "accelLong": -0.05 + }, + "brakes": { + "scs": "unavailable", + "wheelBrakes": { + "leftFront": false, + "rightFront": false, + "unavailable": true, + "leftRear": false, + "rightRear": false + }, + "abs": "off", + "traction": "unavailable", + "brakeBoost": "unavailable", + "auxBrakes": "unavailable" + }, + "accuracy": { + "semiMinor": 2, + "orientation": 0, + "semiMajor": 2 + }, + "id": "DD2F5ECF", + "position": { + "elevation": 1691.9, + "latitude": 39.587837450071035, + "longitude": -105.09077678264897 + }, + "msgCnt": 23, + "speed": 19.27 + } + }, + "dataType": "us.dot.its.jpo.ode.plugin.j2735.J2735Bsm" + }, + "recordGeneratedAt": { + "$date": { + "$numberLong": "1717518177631" + } + } +},{ + "_id": { + "$oid": "665f3fc08c25216ef4f53daa" + }, + "metadata": { + "securityResultCode": "success", + "bsmSource": "RV", + "schemaVersion": 6, + "odePacketID": "", + "sanitized": false, + "recordType": "bsmTx", + "recordGeneratedAt": "", + "maxDurationTime": 0, + "odeTimStartDateTime": "", + "receivedMessageDetails": { + "locationData": { + "elevation": "", + "heading": "", + "latitude": "", + "speed": "", + "longitude": "" + }, + "rxSource": "RV" + }, + "payloadType": "us.dot.its.jpo.ode.model.OdeBsmPayload", + "serialId": { + "recordId": 0, + "serialNumber": 0, + "streamId": "849c3ac6-4747-426f-ae5e-28ac8531b32c", + "bundleSize": 1, + "bundleId": 0 + }, + "logFileName": "", + "odeReceivedAt": "2024-06-04T16:22:57.723Z", + "originIp": "10.11.81.12" + }, + "payload": { + "data": { + "partII": [ + { + "id": "VehicleSafetyExtensions", + "value": { + "pathHistory": { + "crumbData": [ + { + "elevationOffset": 0.6, + "lonOffset": 0.0000387, + "timeOffset": 0.8, + "latOffset": -0.0001604 + }, + { + "elevationOffset": 4.3, + "lonOffset": 0.000231, + "timeOffset": 5.6, + "latOffset": -0.0011603 + }, + { + "elevationOffset": 9.1, + "lonOffset": 0.0003834, + "timeOffset": 11, + "latOffset": -0.0023425 + } + ] + }, + "pathPrediction": { + "confidence": 80, + "radiusOfCurve": -1622.5 + } + } + } + ], + "coreData": { + "secMark": 57723, + "transmission": "UNAVAILABLE", + "size": { + "width": 180, + "length": 480 + }, + "heading": 349.7, + "accelSet": { + "accelYaw": -0.65, + "accelLong": -0.05 + }, + "brakes": { + "scs": "unavailable", + "wheelBrakes": { + "leftFront": false, + "rightFront": false, + "unavailable": true, + "leftRear": false, + "rightRear": false + }, + "abs": "off", + "traction": "unavailable", + "brakeBoost": "unavailable", + "auxBrakes": "unavailable" + }, + "accuracy": { + "semiMinor": 2, + "orientation": 0, + "semiMajor": 2 + }, + "id": "DD2F5ECF", + "position": { + "elevation": 1691.9, + "latitude": 39.58785450315265, + "longitude": -105.0907808066216 + }, + "msgCnt": 24, + "speed": 19.27 + } + }, + "dataType": "us.dot.its.jpo.ode.plugin.j2735.J2735Bsm" + }, + "recordGeneratedAt": { + "$date": { + "$numberLong": "1717518177737" + } + } +},{ + "_id": { + "$oid": "665f3fc08c25216ef4f53dab" + }, + "metadata": { + "securityResultCode": "success", + "bsmSource": "RV", + "schemaVersion": 6, + "odePacketID": "", + "sanitized": false, + "recordType": "bsmTx", + "recordGeneratedAt": "", + "maxDurationTime": 0, + "odeTimStartDateTime": "", + "receivedMessageDetails": { + "locationData": { + "elevation": "", + "heading": "", + "latitude": "", + "speed": "", + "longitude": "" + }, + "rxSource": "RV" + }, + "payloadType": "us.dot.its.jpo.ode.model.OdeBsmPayload", + "serialId": { + "recordId": 0, + "serialNumber": 0, + "streamId": "849c3ac6-4747-426f-ae5e-28ac8531b32c", + "bundleSize": 1, + "bundleId": 0 + }, + "logFileName": "", + "odeReceivedAt": "2024-06-04T16:22:57.832Z", + "originIp": "10.11.81.12" + }, + "payload": { + "data": { + "partII": [ + { + "id": "VehicleSafetyExtensions", + "value": { + "pathHistory": { + "crumbData": [ + { + "elevationOffset": 0.6, + "lonOffset": 0.0000387, + "timeOffset": 0.8, + "latOffset": -0.0001604 + }, + { + "elevationOffset": 4.3, + "lonOffset": 0.000231, + "timeOffset": 5.6, + "latOffset": -0.0011603 + }, + { + "elevationOffset": 9.1, + "lonOffset": 0.0003834, + "timeOffset": 11, + "latOffset": -0.0023425 + } + ] + }, + "pathPrediction": { + "confidence": 80, + "radiusOfCurve": -1622.5 + } + } + } + ], + "coreData": { + "secMark": 57832, + "transmission": "UNAVAILABLE", + "size": { + "width": 180, + "length": 480 + }, + "heading": 347.3, + "accelSet": { + "accelYaw": -0.65, + "accelLong": -0.05 + }, + "brakes": { + "scs": "unavailable", + "wheelBrakes": { + "leftFront": false, + "rightFront": false, + "unavailable": true, + "leftRear": false, + "rightRear": false + }, + "abs": "off", + "traction": "unavailable", + "brakeBoost": "unavailable", + "auxBrakes": "unavailable" + }, + "accuracy": { + "semiMinor": 2, + "orientation": 0, + "semiMajor": 2 + }, + "id": "DD2F5ECF", + "position": { + "elevation": 1691.9, + "latitude": 39.587870522710325, + "longitude": -105.09078550125625 + }, + "msgCnt": 25, + "speed": 18.26 + } + }, + "dataType": "us.dot.its.jpo.ode.plugin.j2735.J2735Bsm" + }, + "recordGeneratedAt": { + "$date": { + "$numberLong": "1717518177845" + } + } +},{ + "_id": { + "$oid": "665f3fc08c25216ef4f53dac" + }, + "metadata": { + "securityResultCode": "success", + "bsmSource": "RV", + "schemaVersion": 6, + "odePacketID": "", + "sanitized": false, + "recordType": "bsmTx", + "recordGeneratedAt": "", + "maxDurationTime": 0, + "odeTimStartDateTime": "", + "receivedMessageDetails": { + "locationData": { + "elevation": "", + "heading": "", + "latitude": "", + "speed": "", + "longitude": "" + }, + "rxSource": "RV" + }, + "payloadType": "us.dot.its.jpo.ode.model.OdeBsmPayload", + "serialId": { + "recordId": 0, + "serialNumber": 0, + "streamId": "849c3ac6-4747-426f-ae5e-28ac8531b32c", + "bundleSize": 1, + "bundleId": 0 + }, + "logFileName": "", + "odeReceivedAt": "2024-06-04T16:22:57.939Z", + "originIp": "10.11.81.12" + }, + "payload": { + "data": { + "partII": [ + { + "id": "VehicleSafetyExtensions", + "value": { + "pathHistory": { + "crumbData": [ + { + "elevationOffset": 0.6, + "lonOffset": 0.0000387, + "timeOffset": 0.8, + "latOffset": -0.0001604 + }, + { + "elevationOffset": 4.3, + "lonOffset": 0.000231, + "timeOffset": 5.6, + "latOffset": -0.0011603 + }, + { + "elevationOffset": 9.1, + "lonOffset": 0.0003834, + "timeOffset": 11, + "latOffset": -0.0023425 + } + ] + }, + "pathPrediction": { + "confidence": 80, + "radiusOfCurve": -1622.5 + } + } + } + ], + "coreData": { + "secMark": 57939, + "transmission": "UNAVAILABLE", + "size": { + "width": 180, + "length": 480 + }, + "heading": 344.9, + "accelSet": { + "accelYaw": -0.65, + "accelLong": -0.05 + }, + "brakes": { + "scs": "unavailable", + "wheelBrakes": { + "leftFront": false, + "rightFront": false, + "unavailable": true, + "leftRear": false, + "rightRear": false + }, + "abs": "off", + "traction": "unavailable", + "brakeBoost": "unavailable", + "auxBrakes": "unavailable" + }, + "accuracy": { + "semiMinor": 2, + "orientation": 0, + "semiMajor": 2 + }, + "id": "DD2F5ECF", + "position": { + "elevation": 1691.9, + "latitude": 39.587883958465525, + "longitude": -105.09079019589092 + }, + "msgCnt": 26, + "speed": 15.47 + } + }, + "dataType": "us.dot.its.jpo.ode.plugin.j2735.J2735Bsm" + }, + "recordGeneratedAt": { + "$date": { + "$numberLong": "1717518177952" + } + } +},{ + "_id": { + "$oid": "665f3fc08c25216ef4f53dad" + }, + "metadata": { + "securityResultCode": "success", + "bsmSource": "RV", + "schemaVersion": 6, + "odePacketID": "", + "sanitized": false, + "recordType": "bsmTx", + "recordGeneratedAt": "", + "maxDurationTime": 0, + "odeTimStartDateTime": "", + "receivedMessageDetails": { + "locationData": { + "elevation": "", + "heading": "", + "latitude": "", + "speed": "", + "longitude": "" + }, + "rxSource": "RV" + }, + "payloadType": "us.dot.its.jpo.ode.model.OdeBsmPayload", + "serialId": { + "recordId": 0, + "serialNumber": 0, + "streamId": "849c3ac6-4747-426f-ae5e-28ac8531b32c", + "bundleSize": 1, + "bundleId": 0 + }, + "logFileName": "", + "odeReceivedAt": "2024-06-04T16:22:58.047Z", + "originIp": "10.11.81.12" + }, + "payload": { + "data": { + "partII": [ + { + "id": "VehicleSafetyExtensions", + "value": { + "pathHistory": { + "crumbData": [ + { + "elevationOffset": 0.6, + "lonOffset": 0.0000387, + "timeOffset": 0.8, + "latOffset": -0.0001604 + }, + { + "elevationOffset": 4.3, + "lonOffset": 0.000231, + "timeOffset": 5.6, + "latOffset": -0.0011603 + }, + { + "elevationOffset": 9.1, + "lonOffset": 0.0003834, + "timeOffset": 11, + "latOffset": -0.0023425 + } + ] + }, + "pathPrediction": { + "confidence": 80, + "radiusOfCurve": -1622.5 + } + } + } + ], + "coreData": { + "secMark": 58047, + "transmission": "UNAVAILABLE", + "size": { + "width": 180, + "length": 480 + }, + "heading": 344.5, + "accelSet": { + "accelYaw": -0.65, + "accelLong": -0.05 + }, + "brakes": { + "scs": "unavailable", + "wheelBrakes": { + "leftFront": false, + "rightFront": false, + "unavailable": true, + "leftRear": false, + "rightRear": false + }, + "abs": "off", + "traction": "unavailable", + "brakeBoost": "unavailable", + "auxBrakes": "unavailable" + }, + "accuracy": { + "semiMinor": 2, + "orientation": 0, + "semiMajor": 2 + }, + "id": "DD2F5ECF", + "position": { + "elevation": 1691.9, + "latitude": 39.58789326014069, + "longitude": -105.09079354920141 + }, + "msgCnt": 27, + "speed": 10.73 + } + }, + "dataType": "us.dot.its.jpo.ode.plugin.j2735.J2735Bsm" + }, + "recordGeneratedAt": { + "$date": { + "$numberLong": "1717518178059" + } + } +},{ + "_id": { + "$oid": "665f3fc08c25216ef4f53dae" + }, + "metadata": { + "securityResultCode": "success", + "bsmSource": "RV", + "schemaVersion": 6, + "odePacketID": "", + "sanitized": false, + "recordType": "bsmTx", + "recordGeneratedAt": "", + "maxDurationTime": 0, + "odeTimStartDateTime": "", + "receivedMessageDetails": { + "locationData": { + "elevation": "", + "heading": "", + "latitude": "", + "speed": "", + "longitude": "" + }, + "rxSource": "RV" + }, + "payloadType": "us.dot.its.jpo.ode.model.OdeBsmPayload", + "serialId": { + "recordId": 0, + "serialNumber": 0, + "streamId": "849c3ac6-4747-426f-ae5e-28ac8531b32c", + "bundleSize": 1, + "bundleId": 0 + }, + "logFileName": "", + "odeReceivedAt": "2024-06-04T16:22:58.156Z", + "originIp": "10.11.81.12" + }, + "payload": { + "data": { + "partII": [ + { + "id": "VehicleSafetyExtensions", + "value": { + "pathHistory": { + "crumbData": [ + { + "elevationOffset": 0.6, + "lonOffset": 0.0000387, + "timeOffset": 0.8, + "latOffset": -0.0001604 + }, + { + "elevationOffset": 4.3, + "lonOffset": 0.000231, + "timeOffset": 5.6, + "latOffset": -0.0011603 + }, + { + "elevationOffset": 9.1, + "lonOffset": 0.0003834, + "timeOffset": 11, + "latOffset": -0.0023425 + } + ] + }, + "pathPrediction": { + "confidence": 80, + "radiusOfCurve": -1622.5 + } + } + } + ], + "coreData": { + "secMark": 58156, + "transmission": "UNAVAILABLE", + "size": { + "width": 180, + "length": 480 + }, + "heading": 344.5, + "accelSet": { + "accelYaw": -0.65, + "accelLong": -0.05 + }, + "brakes": { + "scs": "unavailable", + "wheelBrakes": { + "leftFront": false, + "rightFront": false, + "unavailable": true, + "leftRear": false, + "rightRear": false + }, + "abs": "off", + "traction": "unavailable", + "brakeBoost": "unavailable", + "auxBrakes": "unavailable" + }, + "accuracy": { + "semiMinor": 2, + "orientation": 0, + "semiMajor": 2 + }, + "id": "DD2F5ECF", + "position": { + "elevation": 1691.9, + "latitude": 39.58790256181461, + "longitude": -105.09079690251188 + }, + "msgCnt": 28, + "speed": 10.73 + } + }, + "dataType": "us.dot.its.jpo.ode.plugin.j2735.J2735Bsm" + }, + "recordGeneratedAt": { + "$date": { + "$numberLong": "1717518178166" + } + } +},{ + "_id": { + "$oid": "665f3fc08c25216ef4f53daf" + }, + "metadata": { + "securityResultCode": "success", + "bsmSource": "RV", + "schemaVersion": 6, + "odePacketID": "", + "sanitized": false, + "recordType": "bsmTx", + "recordGeneratedAt": "", + "maxDurationTime": 0, + "odeTimStartDateTime": "", + "receivedMessageDetails": { + "locationData": { + "elevation": "", + "heading": "", + "latitude": "", + "speed": "", + "longitude": "" + }, + "rxSource": "RV" + }, + "payloadType": "us.dot.its.jpo.ode.model.OdeBsmPayload", + "serialId": { + "recordId": 0, + "serialNumber": 0, + "streamId": "849c3ac6-4747-426f-ae5e-28ac8531b32c", + "bundleSize": 1, + "bundleId": 0 + }, + "logFileName": "", + "odeReceivedAt": "2024-06-04T16:22:58.263Z", + "originIp": "10.11.81.12" + }, + "payload": { + "data": { + "partII": [ + { + "id": "VehicleSafetyExtensions", + "value": { + "pathHistory": { + "crumbData": [ + { + "elevationOffset": 0.6, + "lonOffset": 0.0000387, + "timeOffset": 0.8, + "latOffset": -0.0001604 + }, + { + "elevationOffset": 4.3, + "lonOffset": 0.000231, + "timeOffset": 5.6, + "latOffset": -0.0011603 + }, + { + "elevationOffset": 9.1, + "lonOffset": 0.0003834, + "timeOffset": 11, + "latOffset": -0.0023425 + } + ] + }, + "pathPrediction": { + "confidence": 80, + "radiusOfCurve": -1622.5 + } + } + } + ], + "coreData": { + "secMark": 58263, + "transmission": "UNAVAILABLE", + "size": { + "width": 180, + "length": 480 + }, + "heading": 332.4, + "accelSet": { + "accelYaw": -0.65, + "accelLong": -0.05 + }, + "brakes": { + "scs": "unavailable", + "wheelBrakes": { + "leftFront": false, + "rightFront": false, + "unavailable": true, + "leftRear": false, + "rightRear": false + }, + "abs": "off", + "traction": "unavailable", + "brakeBoost": "unavailable", + "auxBrakes": "unavailable" + }, + "accuracy": { + "semiMinor": 2, + "orientation": 0, + "semiMajor": 2 + }, + "id": "DD2F5ECF", + "position": { + "elevation": 1691.9, + "latitude": 39.587914447284994, + "longitude": -105.09080495045706 + }, + "msgCnt": 29, + "speed": 14.91 + } + }, + "dataType": "us.dot.its.jpo.ode.plugin.j2735.J2735Bsm" + }, + "recordGeneratedAt": { + "$date": { + "$numberLong": "1717518178272" + } + } +},{ + "_id": { + "$oid": "665f3fc08c25216ef4f53db0" + }, + "metadata": { + "securityResultCode": "success", + "bsmSource": "RV", + "schemaVersion": 6, + "odePacketID": "", + "sanitized": false, + "recordType": "bsmTx", + "recordGeneratedAt": "", + "maxDurationTime": 0, + "odeTimStartDateTime": "", + "receivedMessageDetails": { + "locationData": { + "elevation": "", + "heading": "", + "latitude": "", + "speed": "", + "longitude": "" + }, + "rxSource": "RV" + }, + "payloadType": "us.dot.its.jpo.ode.model.OdeBsmPayload", + "serialId": { + "recordId": 0, + "serialNumber": 0, + "streamId": "849c3ac6-4747-426f-ae5e-28ac8531b32c", + "bundleSize": 1, + "bundleId": 0 + }, + "logFileName": "", + "odeReceivedAt": "2024-06-04T16:22:58.369Z", + "originIp": "10.11.81.12" + }, + "payload": { + "data": { + "partII": [ + { + "id": "VehicleSafetyExtensions", + "value": { + "pathHistory": { + "crumbData": [ + { + "elevationOffset": 0.6, + "lonOffset": 0.0000387, + "timeOffset": 0.8, + "latOffset": -0.0001604 + }, + { + "elevationOffset": 4.3, + "lonOffset": 0.000231, + "timeOffset": 5.6, + "latOffset": -0.0011603 + }, + { + "elevationOffset": 9.1, + "lonOffset": 0.0003834, + "timeOffset": 11, + "latOffset": -0.0023425 + } + ] + }, + "pathPrediction": { + "confidence": 80, + "radiusOfCurve": -1622.5 + } + } + } + ], + "coreData": { + "secMark": 58369, + "transmission": "UNAVAILABLE", + "size": { + "width": 180, + "length": 480 + }, + "heading": 326.3, + "accelSet": { + "accelYaw": -0.65, + "accelLong": -0.05 + }, + "brakes": { + "scs": "unavailable", + "wheelBrakes": { + "leftFront": false, + "rightFront": false, + "unavailable": true, + "leftRear": false, + "rightRear": false + }, + "abs": "off", + "traction": "unavailable", + "brakeBoost": "unavailable", + "auxBrakes": "unavailable" + }, + "accuracy": { + "semiMinor": 2, + "orientation": 0, + "semiMajor": 2 + }, + "id": "DD2F5ECF", + "position": { + "elevation": 1691.9, + "latitude": 39.58792529923446, + "longitude": -105.09081433972638 + }, + "msgCnt": 30, + "speed": 14.5 + } + }, + "dataType": "us.dot.its.jpo.ode.plugin.j2735.J2735Bsm" + }, + "recordGeneratedAt": { + "$date": { + "$numberLong": "1717518178380" + } + } +},{ + "_id": { + "$oid": "665f3fc08c25216ef4f53db1" + }, + "metadata": { + "securityResultCode": "success", + "bsmSource": "RV", + "schemaVersion": 6, + "odePacketID": "", + "sanitized": false, + "recordType": "bsmTx", + "recordGeneratedAt": "", + "maxDurationTime": 0, + "odeTimStartDateTime": "", + "receivedMessageDetails": { + "locationData": { + "elevation": "", + "heading": "", + "latitude": "", + "speed": "", + "longitude": "" + }, + "rxSource": "RV" + }, + "payloadType": "us.dot.its.jpo.ode.model.OdeBsmPayload", + "serialId": { + "recordId": 0, + "serialNumber": 0, + "streamId": "849c3ac6-4747-426f-ae5e-28ac8531b32c", + "bundleSize": 1, + "bundleId": 0 + }, + "logFileName": "", + "odeReceivedAt": "2024-06-04T16:22:58.477Z", + "originIp": "10.11.81.12" + }, + "payload": { + "data": { + "partII": [ + { + "id": "VehicleSafetyExtensions", + "value": { + "pathHistory": { + "crumbData": [ + { + "elevationOffset": 0.6, + "lonOffset": 0.0000387, + "timeOffset": 0.8, + "latOffset": -0.0001604 + }, + { + "elevationOffset": 4.3, + "lonOffset": 0.000231, + "timeOffset": 5.6, + "latOffset": -0.0011603 + }, + { + "elevationOffset": 9.1, + "lonOffset": 0.0003834, + "timeOffset": 11, + "latOffset": -0.0023425 + } + ] + }, + "pathPrediction": { + "confidence": 80, + "radiusOfCurve": -1622.5 + } + } + } + ], + "coreData": { + "secMark": 58477, + "transmission": "UNAVAILABLE", + "size": { + "width": 180, + "length": 480 + }, + "heading": 323.1, + "accelSet": { + "accelYaw": -0.65, + "accelLong": -0.05 + }, + "brakes": { + "scs": "unavailable", + "wheelBrakes": { + "leftFront": false, + "rightFront": false, + "unavailable": true, + "leftRear": false, + "rightRear": false + }, + "abs": "off", + "traction": "unavailable", + "brakeBoost": "unavailable", + "auxBrakes": "unavailable" + }, + "accuracy": { + "semiMinor": 2, + "orientation": 0, + "semiMajor": 2 + }, + "id": "DD2F5ECF", + "position": { + "elevation": 1691.9, + "latitude": 39.587935634422834, + "longitude": -105.09082439965782 + }, + "msgCnt": 31, + "speed": 14.37 + } + }, + "dataType": "us.dot.its.jpo.ode.plugin.j2735.J2735Bsm" + }, + "recordGeneratedAt": { + "$date": { + "$numberLong": "1717518178486" + } + } +},{ + "_id": { + "$oid": "665f3fc08c25216ef4f53db2" + }, + "metadata": { + "securityResultCode": "success", + "bsmSource": "RV", + "schemaVersion": 6, + "odePacketID": "", + "sanitized": false, + "recordType": "bsmTx", + "recordGeneratedAt": "", + "maxDurationTime": 0, + "odeTimStartDateTime": "", + "receivedMessageDetails": { + "locationData": { + "elevation": "", + "heading": "", + "latitude": "", + "speed": "", + "longitude": "" + }, + "rxSource": "RV" + }, + "payloadType": "us.dot.its.jpo.ode.model.OdeBsmPayload", + "serialId": { + "recordId": 0, + "serialNumber": 0, + "streamId": "849c3ac6-4747-426f-ae5e-28ac8531b32c", + "bundleSize": 1, + "bundleId": 0 + }, + "logFileName": "", + "odeReceivedAt": "2024-06-04T16:22:58.584Z", + "originIp": "10.11.81.12" + }, + "payload": { + "data": { + "partII": [ + { + "id": "VehicleSafetyExtensions", + "value": { + "pathHistory": { + "crumbData": [ + { + "elevationOffset": 0.6, + "lonOffset": 0.0000387, + "timeOffset": 0.8, + "latOffset": -0.0001604 + }, + { + "elevationOffset": 4.3, + "lonOffset": 0.000231, + "timeOffset": 5.6, + "latOffset": -0.0011603 + }, + { + "elevationOffset": 9.1, + "lonOffset": 0.0003834, + "timeOffset": 11, + "latOffset": -0.0023425 + } + ] + }, + "pathPrediction": { + "confidence": 80, + "radiusOfCurve": -1622.5 + } + } + } + ], + "coreData": { + "secMark": 58584, + "transmission": "UNAVAILABLE", + "size": { + "width": 180, + "length": 480 + }, + "heading": 306.5, + "accelSet": { + "accelYaw": -0.65, + "accelLong": -0.05 + }, + "brakes": { + "scs": "unavailable", + "wheelBrakes": { + "leftFront": false, + "rightFront": false, + "unavailable": true, + "leftRear": false, + "rightRear": false + }, + "abs": "off", + "traction": "unavailable", + "brakeBoost": "unavailable", + "auxBrakes": "unavailable" + }, + "accuracy": { + "semiMinor": 2, + "orientation": 0, + "semiMajor": 2 + }, + "id": "DD2F5ECF", + "position": { + "elevation": 1691.9, + "latitude": 39.58794441933173, + "longitude": -105.09083982488603 + }, + "msgCnt": 32, + "speed": 16.44 + } + }, + "dataType": "us.dot.its.jpo.ode.plugin.j2735.J2735Bsm" + }, + "recordGeneratedAt": { + "$date": { + "$numberLong": "1717518178594" + } + } +},{ + "_id": { + "$oid": "665f3fc08c25216ef4f53db3" + }, + "metadata": { + "securityResultCode": "success", + "bsmSource": "RV", + "schemaVersion": 6, + "odePacketID": "", + "sanitized": false, + "recordType": "bsmTx", + "recordGeneratedAt": "", + "maxDurationTime": 0, + "odeTimStartDateTime": "", + "receivedMessageDetails": { + "locationData": { + "elevation": "", + "heading": "", + "latitude": "", + "speed": "", + "longitude": "" + }, + "rxSource": "RV" + }, + "payloadType": "us.dot.its.jpo.ode.model.OdeBsmPayload", + "serialId": { + "recordId": 0, + "serialNumber": 0, + "streamId": "849c3ac6-4747-426f-ae5e-28ac8531b32c", + "bundleSize": 1, + "bundleId": 0 + }, + "logFileName": "", + "odeReceivedAt": "2024-06-04T16:22:58.691Z", + "originIp": "10.11.81.12" + }, + "payload": { + "data": { + "partII": [ + { + "id": "VehicleSafetyExtensions", + "value": { + "pathHistory": { + "crumbData": [ + { + "elevationOffset": 0.6, + "lonOffset": 0.0000387, + "timeOffset": 0.8, + "latOffset": -0.0001604 + }, + { + "elevationOffset": 4.3, + "lonOffset": 0.000231, + "timeOffset": 5.6, + "latOffset": -0.0011603 + }, + { + "elevationOffset": 9.1, + "lonOffset": 0.0003834, + "timeOffset": 11, + "latOffset": -0.0023425 + } + ] + }, + "pathPrediction": { + "confidence": 80, + "radiusOfCurve": -1622.5 + } + } + } + ], + "coreData": { + "secMark": 58691, + "transmission": "UNAVAILABLE", + "size": { + "width": 180, + "length": 480 + }, + "heading": 291.2, + "accelSet": { + "accelYaw": -0.65, + "accelLong": -0.05 + }, + "brakes": { + "scs": "unavailable", + "wheelBrakes": { + "leftFront": false, + "rightFront": false, + "unavailable": true, + "leftRear": false, + "rightRear": false + }, + "abs": "off", + "traction": "unavailable", + "brakeBoost": "unavailable", + "auxBrakes": "unavailable" + }, + "accuracy": { + "semiMinor": 2, + "orientation": 0, + "semiMajor": 2 + }, + "id": "DD2F5ECF", + "position": { + "elevation": 1691.9, + "latitude": 39.58795062044324, + "longitude": -105.090860615411 + }, + "msgCnt": 33, + "speed": 19.1 + } + }, + "dataType": "us.dot.its.jpo.ode.plugin.j2735.J2735Bsm" + }, + "recordGeneratedAt": { + "$date": { + "$numberLong": "1717518178701" + } + } +},{ + "_id": { + "$oid": "665f3fc08c25216ef4f53db4" + }, + "metadata": { + "securityResultCode": "success", + "bsmSource": "RV", + "schemaVersion": 6, + "odePacketID": "", + "sanitized": false, + "recordType": "bsmTx", + "recordGeneratedAt": "", + "maxDurationTime": 0, + "odeTimStartDateTime": "", + "receivedMessageDetails": { + "locationData": { + "elevation": "", + "heading": "", + "latitude": "", + "speed": "", + "longitude": "" + }, + "rxSource": "RV" + }, + "payloadType": "us.dot.its.jpo.ode.model.OdeBsmPayload", + "serialId": { + "recordId": 0, + "serialNumber": 0, + "streamId": "849c3ac6-4747-426f-ae5e-28ac8531b32c", + "bundleSize": 1, + "bundleId": 0 + }, + "logFileName": "", + "odeReceivedAt": "2024-06-04T16:22:58.797Z", + "originIp": "10.11.81.12" + }, + "payload": { + "data": { + "partII": [ + { + "id": "VehicleSafetyExtensions", + "value": { + "pathHistory": { + "crumbData": [ + { + "elevationOffset": 0.6, + "lonOffset": 0.0000387, + "timeOffset": 0.8, + "latOffset": -0.0001604 + }, + { + "elevationOffset": 4.3, + "lonOffset": 0.000231, + "timeOffset": 5.6, + "latOffset": -0.0011603 + }, + { + "elevationOffset": 9.1, + "lonOffset": 0.0003834, + "timeOffset": 11, + "latOffset": -0.0023425 + } + ] + }, + "pathPrediction": { + "confidence": 80, + "radiusOfCurve": -1622.5 + } + } + } + ], + "coreData": { + "secMark": 58797, + "transmission": "UNAVAILABLE", + "size": { + "width": 180, + "length": 480 + }, + "heading": 261, + "accelSet": { + "accelYaw": -0.65, + "accelLong": -0.05 + }, + "brakes": { + "scs": "unavailable", + "wheelBrakes": { + "leftFront": false, + "rightFront": false, + "unavailable": true, + "leftRear": false, + "rightRear": false + }, + "abs": "off", + "traction": "unavailable", + "brakeBoost": "unavailable", + "auxBrakes": "unavailable" + }, + "accuracy": { + "semiMinor": 2, + "orientation": 0, + "semiMajor": 2 + }, + "id": "DD2F5ECF", + "position": { + "elevation": 1691.9, + "latitude": 39.587949070165415, + "longitude": -105.09087335799083 + }, + "msgCnt": 34, + "speed": 11.05 + } + }, + "dataType": "us.dot.its.jpo.ode.plugin.j2735.J2735Bsm" + }, + "recordGeneratedAt": { + "$date": { + "$numberLong": "1717518178807" + } + } +},{ + "_id": { + "$oid": "665f3fc08c25216ef4f53db5" + }, + "metadata": { + "securityResultCode": "success", + "bsmSource": "RV", + "schemaVersion": 6, + "odePacketID": "", + "sanitized": false, + "recordType": "bsmTx", + "recordGeneratedAt": "", + "maxDurationTime": 0, + "odeTimStartDateTime": "", + "receivedMessageDetails": { + "locationData": { + "elevation": "", + "heading": "", + "latitude": "", + "speed": "", + "longitude": "" + }, + "rxSource": "RV" + }, + "payloadType": "us.dot.its.jpo.ode.model.OdeBsmPayload", + "serialId": { + "recordId": 0, + "serialNumber": 0, + "streamId": "849c3ac6-4747-426f-ae5e-28ac8531b32c", + "bundleSize": 1, + "bundleId": 0 + }, + "logFileName": "", + "odeReceivedAt": "2024-06-04T16:22:58.905Z", + "originIp": "10.11.81.12" + }, + "payload": { + "data": { + "partII": [ + { + "id": "VehicleSafetyExtensions", + "value": { + "pathHistory": { + "crumbData": [ + { + "elevationOffset": 0.6, + "lonOffset": 0.0000387, + "timeOffset": 0.8, + "latOffset": -0.0001604 + }, + { + "elevationOffset": 4.3, + "lonOffset": 0.000231, + "timeOffset": 5.6, + "latOffset": -0.0011603 + }, + { + "elevationOffset": 9.1, + "lonOffset": 0.0003834, + "timeOffset": 11, + "latOffset": -0.0023425 + } + ] + }, + "pathPrediction": { + "confidence": 80, + "radiusOfCurve": -1622.5 + } + } + } + ], + "coreData": { + "secMark": 58905, + "transmission": "UNAVAILABLE", + "size": { + "width": 180, + "length": 480 + }, + "heading": 274.8, + "accelSet": { + "accelYaw": -0.65, + "accelLong": -0.05 + }, + "brakes": { + "scs": "unavailable", + "wheelBrakes": { + "leftFront": false, + "rightFront": false, + "unavailable": true, + "leftRear": false, + "rightRear": false + }, + "abs": "off", + "traction": "unavailable", + "brakeBoost": "unavailable", + "auxBrakes": "unavailable" + }, + "accuracy": { + "semiMinor": 2, + "orientation": 0, + "semiMajor": 2 + }, + "id": "DD2F5ECF", + "position": { + "elevation": 1691.9, + "latitude": 39.58795010368396, + "longitude": -105.09088945388118 + }, + "msgCnt": 35, + "speed": 13.84 + } + }, + "dataType": "us.dot.its.jpo.ode.plugin.j2735.J2735Bsm" + }, + "recordGeneratedAt": { + "$date": { + "$numberLong": "1717518178914" + } + } +},{ + "_id": { + "$oid": "665f3fc08c25216ef4f53db6" + }, + "metadata": { + "securityResultCode": "success", + "bsmSource": "RV", + "schemaVersion": 6, + "odePacketID": "", + "sanitized": false, + "recordType": "bsmTx", + "recordGeneratedAt": "", + "maxDurationTime": 0, + "odeTimStartDateTime": "", + "receivedMessageDetails": { + "locationData": { + "elevation": "", + "heading": "", + "latitude": "", + "speed": "", + "longitude": "" + }, + "rxSource": "RV" + }, + "payloadType": "us.dot.its.jpo.ode.model.OdeBsmPayload", + "serialId": { + "recordId": 0, + "serialNumber": 0, + "streamId": "849c3ac6-4747-426f-ae5e-28ac8531b32c", + "bundleSize": 1, + "bundleId": 0 + }, + "logFileName": "", + "odeReceivedAt": "2024-06-04T16:22:59.012Z", + "originIp": "10.11.81.12" + }, + "payload": { + "data": { + "partII": [ + { + "id": "VehicleSafetyExtensions", + "value": { + "pathHistory": { + "crumbData": [ + { + "elevationOffset": 0.6, + "lonOffset": 0.0000387, + "timeOffset": 0.8, + "latOffset": -0.0001604 + }, + { + "elevationOffset": 4.3, + "lonOffset": 0.000231, + "timeOffset": 5.6, + "latOffset": -0.0011603 + }, + { + "elevationOffset": 9.1, + "lonOffset": 0.0003834, + "timeOffset": 11, + "latOffset": -0.0023425 + } + ] + }, + "pathPrediction": { + "confidence": 80, + "radiusOfCurve": -1622.5 + } + } + } + ], + "coreData": { + "secMark": 59012, + "transmission": "UNAVAILABLE", + "size": { + "width": 180, + "length": 480 + }, + "heading": 259.7, + "accelSet": { + "accelYaw": -0.65, + "accelLong": -0.05 + }, + "brakes": { + "scs": "unavailable", + "wheelBrakes": { + "leftFront": false, + "rightFront": false, + "unavailable": true, + "leftRear": false, + "rightRear": false + }, + "abs": "off", + "traction": "unavailable", + "brakeBoost": "unavailable", + "auxBrakes": "unavailable" + }, + "accuracy": { + "semiMinor": 2, + "orientation": 0, + "semiMajor": 2 + }, + "id": "DD2F5ECF", + "position": { + "elevation": 1691.9, + "latitude": 39.58794803664684, + "longitude": -105.09090420844727 + }, + "msgCnt": 36, + "speed": 12.85 + } + }, + "dataType": "us.dot.its.jpo.ode.plugin.j2735.J2735Bsm" + }, + "recordGeneratedAt": { + "$date": { + "$numberLong": "1717518179021" + } + } +},{ + "_id": { + "$oid": "665f3fc08c25216ef4f53db7" + }, + "metadata": { + "securityResultCode": "success", + "bsmSource": "RV", + "schemaVersion": 6, + "odePacketID": "", + "sanitized": false, + "recordType": "bsmTx", + "recordGeneratedAt": "", + "maxDurationTime": 0, + "odeTimStartDateTime": "", + "receivedMessageDetails": { + "locationData": { + "elevation": "", + "heading": "", + "latitude": "", + "speed": "", + "longitude": "" + }, + "rxSource": "RV" + }, + "payloadType": "us.dot.its.jpo.ode.model.OdeBsmPayload", + "serialId": { + "recordId": 0, + "serialNumber": 0, + "streamId": "849c3ac6-4747-426f-ae5e-28ac8531b32c", + "bundleSize": 1, + "bundleId": 0 + }, + "logFileName": "", + "odeReceivedAt": "2024-06-04T16:22:59.119Z", + "originIp": "10.11.81.12" + }, + "payload": { + "data": { + "partII": [ + { + "id": "VehicleSafetyExtensions", + "value": { + "pathHistory": { + "crumbData": [ + { + "elevationOffset": 0.6, + "lonOffset": 0.0000387, + "timeOffset": 0.8, + "latOffset": -0.0001604 + }, + { + "elevationOffset": 4.3, + "lonOffset": 0.000231, + "timeOffset": 5.6, + "latOffset": -0.0011603 + }, + { + "elevationOffset": 9.1, + "lonOffset": 0.0003834, + "timeOffset": 11, + "latOffset": -0.0023425 + } + ] + }, + "pathPrediction": { + "confidence": 80, + "radiusOfCurve": -1622.5 + } + } + } + ], + "coreData": { + "secMark": 59119, + "transmission": "UNAVAILABLE", + "size": { + "width": 180, + "length": 480 + }, + "heading": 270, + "accelSet": { + "accelYaw": -0.65, + "accelLong": -0.05 + }, + "brakes": { + "scs": "unavailable", + "wheelBrakes": { + "leftFront": false, + "rightFront": false, + "unavailable": true, + "leftRear": false, + "rightRear": false + }, + "abs": "off", + "traction": "unavailable", + "brakeBoost": "unavailable", + "auxBrakes": "unavailable" + }, + "accuracy": { + "semiMinor": 2, + "orientation": 0, + "semiMajor": 2 + }, + "id": "DD2F5ECF", + "position": { + "elevation": 1691.9, + "latitude": 39.58794803664684, + "longitude": -105.0909189630134 + }, + "msgCnt": 37, + "speed": 12.64 + } + }, + "dataType": "us.dot.its.jpo.ode.plugin.j2735.J2735Bsm" + }, + "recordGeneratedAt": { + "$date": { + "$numberLong": "1717518179129" + } + } +},{ + "_id": { + "$oid": "665f3fc08c25216ef4f53db8" + }, + "metadata": { + "securityResultCode": "success", + "bsmSource": "RV", + "schemaVersion": 6, + "odePacketID": "", + "sanitized": false, + "recordType": "bsmTx", + "recordGeneratedAt": "", + "maxDurationTime": 0, + "odeTimStartDateTime": "", + "receivedMessageDetails": { + "locationData": { + "elevation": "", + "heading": "", + "latitude": "", + "speed": "", + "longitude": "" + }, + "rxSource": "RV" + }, + "payloadType": "us.dot.its.jpo.ode.model.OdeBsmPayload", + "serialId": { + "recordId": 0, + "serialNumber": 0, + "streamId": "849c3ac6-4747-426f-ae5e-28ac8531b32c", + "bundleSize": 1, + "bundleId": 0 + }, + "logFileName": "", + "odeReceivedAt": "2024-06-04T16:22:59.226Z", + "originIp": "10.11.81.12" + }, + "payload": { + "data": { + "partII": [ + { + "id": "VehicleSafetyExtensions", + "value": { + "pathHistory": { + "crumbData": [ + { + "elevationOffset": 0.6, + "lonOffset": 0.0000387, + "timeOffset": 0.8, + "latOffset": -0.0001604 + }, + { + "elevationOffset": 4.3, + "lonOffset": 0.000231, + "timeOffset": 5.6, + "latOffset": -0.0011603 + }, + { + "elevationOffset": 9.1, + "lonOffset": 0.0003834, + "timeOffset": 11, + "latOffset": -0.0023425 + } + ] + }, + "pathPrediction": { + "confidence": 80, + "radiusOfCurve": -1622.5 + } + } + } + ], + "coreData": { + "secMark": 59226, + "transmission": "UNAVAILABLE", + "size": { + "width": 180, + "length": 480 + }, + "heading": 258.3, + "accelSet": { + "accelYaw": -0.65, + "accelLong": -0.05 + }, + "brakes": { + "scs": "unavailable", + "wheelBrakes": { + "leftFront": false, + "rightFront": false, + "unavailable": true, + "leftRear": false, + "rightRear": false + }, + "abs": "off", + "traction": "unavailable", + "brakeBoost": "unavailable", + "auxBrakes": "unavailable" + }, + "accuracy": { + "semiMinor": 2, + "orientation": 0, + "semiMajor": 2 + }, + "id": "DD2F5ECF", + "position": { + "elevation": 1691.9, + "latitude": 39.58794493609105, + "longitude": -105.09093841221416 + }, + "msgCnt": 38, + "speed": 17.02 + } + }, + "dataType": "us.dot.its.jpo.ode.plugin.j2735.J2735Bsm" + }, + "recordGeneratedAt": { + "$date": { + "$numberLong": "1717518179236" + } + } +},{ + "_id": { + "$oid": "665f3fc08c25216ef4f53db9" + }, + "metadata": { + "securityResultCode": "success", + "bsmSource": "RV", + "schemaVersion": 6, + "odePacketID": "", + "sanitized": false, + "recordType": "bsmTx", + "recordGeneratedAt": "", + "maxDurationTime": 0, + "odeTimStartDateTime": "", + "receivedMessageDetails": { + "locationData": { + "elevation": "", + "heading": "", + "latitude": "", + "speed": "", + "longitude": "" + }, + "rxSource": "RV" + }, + "payloadType": "us.dot.its.jpo.ode.model.OdeBsmPayload", + "serialId": { + "recordId": 0, + "serialNumber": 0, + "streamId": "849c3ac6-4747-426f-ae5e-28ac8531b32c", + "bundleSize": 1, + "bundleId": 0 + }, + "logFileName": "", + "odeReceivedAt": "2024-06-04T16:22:59.334Z", + "originIp": "10.11.81.12" + }, + "payload": { + "data": { + "partII": [ + { + "id": "VehicleSafetyExtensions", + "value": { + "pathHistory": { + "crumbData": [ + { + "elevationOffset": 0.6, + "lonOffset": 0.0000387, + "timeOffset": 0.8, + "latOffset": -0.0001604 + }, + { + "elevationOffset": 4.3, + "lonOffset": 0.000231, + "timeOffset": 5.6, + "latOffset": -0.0011603 + }, + { + "elevationOffset": 9.1, + "lonOffset": 0.0003834, + "timeOffset": 11, + "latOffset": -0.0023425 + } + ] + }, + "pathPrediction": { + "confidence": 80, + "radiusOfCurve": -1622.5 + } + } + } + ], + "coreData": { + "secMark": 59334, + "transmission": "UNAVAILABLE", + "size": { + "width": 180, + "length": 480 + }, + "heading": 225, + "accelSet": { + "accelYaw": -0.65, + "accelLong": -0.05 + }, + "brakes": { + "scs": "unavailable", + "wheelBrakes": { + "leftFront": false, + "rightFront": false, + "unavailable": true, + "leftRear": false, + "rightRear": false + }, + "abs": "off", + "traction": "unavailable", + "brakeBoost": "unavailable", + "auxBrakes": "unavailable" + }, + "accuracy": { + "semiMinor": 2, + "orientation": 0, + "semiMajor": 2 + }, + "id": "DD2F5ECF", + "position": { + "elevation": 1691.9, + "latitude": 39.587933567385285, + "longitude": -105.0909531667803 + }, + "msgCnt": 39, + "speed": 17.88 + } + }, + "dataType": "us.dot.its.jpo.ode.plugin.j2735.J2735Bsm" + }, + "recordGeneratedAt": { + "$date": { + "$numberLong": "1717518179343" + } + } +},{ + "_id": { + "$oid": "665f3fc08c25216ef4f53dba" + }, + "metadata": { + "securityResultCode": "success", + "bsmSource": "RV", + "schemaVersion": 6, + "odePacketID": "", + "sanitized": false, + "recordType": "bsmTx", + "recordGeneratedAt": "", + "maxDurationTime": 0, + "odeTimStartDateTime": "", + "receivedMessageDetails": { + "locationData": { + "elevation": "", + "heading": "", + "latitude": "", + "speed": "", + "longitude": "" + }, + "rxSource": "RV" + }, + "payloadType": "us.dot.its.jpo.ode.model.OdeBsmPayload", + "serialId": { + "recordId": 0, + "serialNumber": 0, + "streamId": "849c3ac6-4747-426f-ae5e-28ac8531b32c", + "bundleSize": 1, + "bundleId": 0 + }, + "logFileName": "", + "odeReceivedAt": "2024-06-04T16:22:59.44Z", + "originIp": "10.11.81.12" + }, + "payload": { + "data": { + "partII": [ + { + "id": "VehicleSafetyExtensions", + "value": { + "pathHistory": { + "crumbData": [ + { + "elevationOffset": 0.6, + "lonOffset": 0.0000387, + "timeOffset": 0.8, + "latOffset": -0.0001604 + }, + { + "elevationOffset": 4.3, + "lonOffset": 0.000231, + "timeOffset": 5.6, + "latOffset": -0.0011603 + }, + { + "elevationOffset": 9.1, + "lonOffset": 0.0003834, + "timeOffset": 11, + "latOffset": -0.0023425 + } + ] + }, + "pathPrediction": { + "confidence": 80, + "radiusOfCurve": -1622.5 + } + } + } + ], + "coreData": { + "secMark": 59440, + "transmission": "UNAVAILABLE", + "size": { + "width": 180, + "length": 480 + }, + "heading": 211.3, + "accelSet": { + "accelYaw": -0.65, + "accelLong": -0.05 + }, + "brakes": { + "scs": "unavailable", + "wheelBrakes": { + "leftFront": false, + "rightFront": false, + "unavailable": true, + "leftRear": false, + "rightRear": false + }, + "abs": "off", + "traction": "unavailable", + "brakeBoost": "unavailable", + "auxBrakes": "unavailable" + }, + "accuracy": { + "semiMinor": 2, + "orientation": 0, + "semiMajor": 2 + }, + "id": "DD2F5ECF", + "position": { + "elevation": 1691.9, + "latitude": 39.587921681918154, + "longitude": -105.09096255604963 + }, + "msgCnt": 40, + "speed": 15.47 + } + }, + "dataType": "us.dot.its.jpo.ode.plugin.j2735.J2735Bsm" + }, + "recordGeneratedAt": { + "$date": { + "$numberLong": "1717518179451" + } + } +},{ + "_id": { + "$oid": "665f3fc08c25216ef4f53dbb" + }, + "metadata": { + "securityResultCode": "success", + "bsmSource": "RV", + "schemaVersion": 6, + "odePacketID": "", + "sanitized": false, + "recordType": "bsmTx", + "recordGeneratedAt": "", + "maxDurationTime": 0, + "odeTimStartDateTime": "", + "receivedMessageDetails": { + "locationData": { + "elevation": "", + "heading": "", + "latitude": "", + "speed": "", + "longitude": "" + }, + "rxSource": "RV" + }, + "payloadType": "us.dot.its.jpo.ode.model.OdeBsmPayload", + "serialId": { + "recordId": 0, + "serialNumber": 0, + "streamId": "849c3ac6-4747-426f-ae5e-28ac8531b32c", + "bundleSize": 1, + "bundleId": 0 + }, + "logFileName": "", + "odeReceivedAt": "2024-06-04T16:22:59.548Z", + "originIp": "10.11.81.12" + }, + "payload": { + "data": { + "partII": [ + { + "id": "VehicleSafetyExtensions", + "value": { + "pathHistory": { + "crumbData": [ + { + "elevationOffset": 0.6, + "lonOffset": 0.0000387, + "timeOffset": 0.8, + "latOffset": -0.0001604 + }, + { + "elevationOffset": 4.3, + "lonOffset": 0.000231, + "timeOffset": 5.6, + "latOffset": -0.0011603 + }, + { + "elevationOffset": 9.1, + "lonOffset": 0.0003834, + "timeOffset": 11, + "latOffset": -0.0023425 + } + ] + }, + "pathPrediction": { + "confidence": 80, + "radiusOfCurve": -1622.5 + } + } + } + ], + "coreData": { + "secMark": 59548, + "transmission": "UNAVAILABLE", + "size": { + "width": 180, + "length": 480 + }, + "heading": 185, + "accelSet": { + "accelYaw": -0.65, + "accelLong": -0.05 + }, + "brakes": { + "scs": "unavailable", + "wheelBrakes": { + "leftFront": false, + "rightFront": false, + "unavailable": true, + "leftRear": false, + "rightRear": false + }, + "abs": "off", + "traction": "unavailable", + "brakeBoost": "unavailable", + "auxBrakes": "unavailable" + }, + "accuracy": { + "semiMinor": 2, + "orientation": 0, + "semiMajor": 2 + }, + "id": "DD2F5ECF", + "position": { + "elevation": 1691.9, + "latitude": 39.58790979644899, + "longitude": -105.09096389737384 + }, + "msgCnt": 41, + "speed": 13.27 + } + }, + "dataType": "us.dot.its.jpo.ode.plugin.j2735.J2735Bsm" + }, + "recordGeneratedAt": { + "$date": { + "$numberLong": "1717518179555" + } + } +},{ + "_id": { + "$oid": "665f3fc08c25216ef4f53dbc" + }, + "metadata": { + "securityResultCode": "success", + "bsmSource": "RV", + "schemaVersion": 6, + "odePacketID": "", + "sanitized": false, + "recordType": "bsmTx", + "recordGeneratedAt": "", + "maxDurationTime": 0, + "odeTimStartDateTime": "", + "receivedMessageDetails": { + "locationData": { + "elevation": "", + "heading": "", + "latitude": "", + "speed": "", + "longitude": "" + }, + "rxSource": "RV" + }, + "payloadType": "us.dot.its.jpo.ode.model.OdeBsmPayload", + "serialId": { + "recordId": 0, + "serialNumber": 0, + "streamId": "849c3ac6-4747-426f-ae5e-28ac8531b32c", + "bundleSize": 1, + "bundleId": 0 + }, + "logFileName": "", + "odeReceivedAt": "2024-06-04T16:22:59.656Z", + "originIp": "10.11.81.12" + }, + "payload": { + "data": { + "partII": [ + { + "id": "VehicleSafetyExtensions", + "value": { + "pathHistory": { + "crumbData": [ + { + "elevationOffset": 0.6, + "lonOffset": 0.0000387, + "timeOffset": 0.8, + "latOffset": -0.0001604 + }, + { + "elevationOffset": 4.3, + "lonOffset": 0.000231, + "timeOffset": 5.6, + "latOffset": -0.0011603 + }, + { + "elevationOffset": 9.1, + "lonOffset": 0.0003834, + "timeOffset": 11, + "latOffset": -0.0023425 + } + ] + }, + "pathPrediction": { + "confidence": 80, + "radiusOfCurve": -1622.5 + } + } + } + ], + "coreData": { + "secMark": 59656, + "transmission": "UNAVAILABLE", + "size": { + "width": 180, + "length": 480 + }, + "heading": 180, + "accelSet": { + "accelYaw": -0.65, + "accelLong": -0.05 + }, + "brakes": { + "scs": "unavailable", + "wheelBrakes": { + "leftFront": false, + "rightFront": false, + "unavailable": true, + "leftRear": false, + "rightRear": false + }, + "abs": "off", + "traction": "unavailable", + "brakeBoost": "unavailable", + "auxBrakes": "unavailable" + }, + "accuracy": { + "semiMinor": 2, + "orientation": 0, + "semiMajor": 2 + }, + "id": "DD2F5ECF", + "position": { + "elevation": 1691.9, + "latitude": 39.58789946125678, + "longitude": -105.09096389737384 + }, + "msgCnt": 42, + "speed": 11.49 + } + }, + "dataType": "us.dot.its.jpo.ode.plugin.j2735.J2735Bsm" + }, + "recordGeneratedAt": { + "$date": { + "$numberLong": "1717518179663" + } + } +},{ + "_id": { + "$oid": "665f3fc08c25216ef4f53dbd" + }, + "metadata": { + "securityResultCode": "success", + "bsmSource": "RV", + "schemaVersion": 6, + "odePacketID": "", + "sanitized": false, + "recordType": "bsmTx", + "recordGeneratedAt": "", + "maxDurationTime": 0, + "odeTimStartDateTime": "", + "receivedMessageDetails": { + "locationData": { + "elevation": "", + "heading": "", + "latitude": "", + "speed": "", + "longitude": "" + }, + "rxSource": "RV" + }, + "payloadType": "us.dot.its.jpo.ode.model.OdeBsmPayload", + "serialId": { + "recordId": 0, + "serialNumber": 0, + "streamId": "849c3ac6-4747-426f-ae5e-28ac8531b32c", + "bundleSize": 1, + "bundleId": 0 + }, + "logFileName": "", + "odeReceivedAt": "2024-06-04T16:22:59.763Z", + "originIp": "10.11.81.12" + }, + "payload": { + "data": { + "partII": [ + { + "id": "VehicleSafetyExtensions", + "value": { + "pathHistory": { + "crumbData": [ + { + "elevationOffset": 0.6, + "lonOffset": 0.0000387, + "timeOffset": 0.8, + "latOffset": -0.0001604 + }, + { + "elevationOffset": 4.3, + "lonOffset": 0.000231, + "timeOffset": 5.6, + "latOffset": -0.0011603 + }, + { + "elevationOffset": 9.1, + "lonOffset": 0.0003834, + "timeOffset": 11, + "latOffset": -0.0023425 + } + ] + }, + "pathPrediction": { + "confidence": 80, + "radiusOfCurve": -1622.5 + } + } + } + ], + "coreData": { + "secMark": 59763, + "transmission": "UNAVAILABLE", + "size": { + "width": 180, + "length": 480 + }, + "heading": 178.1, + "accelSet": { + "accelYaw": -0.65, + "accelLong": -0.05 + }, + "brakes": { + "scs": "unavailable", + "wheelBrakes": { + "leftFront": false, + "rightFront": false, + "unavailable": true, + "leftRear": false, + "rightRear": false + }, + "abs": "off", + "traction": "unavailable", + "brakeBoost": "unavailable", + "auxBrakes": "unavailable" + }, + "accuracy": { + "semiMinor": 2, + "orientation": 0, + "semiMajor": 2 + }, + "id": "DD2F5ECF", + "position": { + "elevation": 1691.9, + "latitude": 39.587883958465525, + "longitude": -105.09096322671174 + }, + "msgCnt": 43, + "speed": 17.25 + } + }, + "dataType": "us.dot.its.jpo.ode.plugin.j2735.J2735Bsm" + }, + "recordGeneratedAt": { + "$date": { + "$numberLong": "1717518179769" + } + } +},{ + "_id": { + "$oid": "665f3fc08c25216ef4f53dbe" + }, + "metadata": { + "securityResultCode": "success", + "bsmSource": "RV", + "schemaVersion": 6, + "odePacketID": "", + "sanitized": false, + "recordType": "bsmTx", + "recordGeneratedAt": "", + "maxDurationTime": 0, + "odeTimStartDateTime": "", + "receivedMessageDetails": { + "locationData": { + "elevation": "", + "heading": "", + "latitude": "", + "speed": "", + "longitude": "" + }, + "rxSource": "RV" + }, + "payloadType": "us.dot.its.jpo.ode.model.OdeBsmPayload", + "serialId": { + "recordId": 0, + "serialNumber": 0, + "streamId": "849c3ac6-4747-426f-ae5e-28ac8531b32c", + "bundleSize": 1, + "bundleId": 0 + }, + "logFileName": "", + "odeReceivedAt": "2024-06-04T16:22:59.868Z", + "originIp": "10.11.81.12" + }, + "payload": { + "data": { + "partII": [ + { + "id": "VehicleSafetyExtensions", + "value": { + "pathHistory": { + "crumbData": [ + { + "elevationOffset": 0.6, + "lonOffset": 0.0000387, + "timeOffset": 0.8, + "latOffset": -0.0001604 + }, + { + "elevationOffset": 4.3, + "lonOffset": 0.000231, + "timeOffset": 5.6, + "latOffset": -0.0011603 + }, + { + "elevationOffset": 9.1, + "lonOffset": 0.0003834, + "timeOffset": 11, + "latOffset": -0.0023425 + } + ] + }, + "pathPrediction": { + "confidence": 80, + "radiusOfCurve": -1622.5 + } + } + } + ], + "coreData": { + "secMark": 59868, + "transmission": "UNAVAILABLE", + "size": { + "width": 180, + "length": 480 + }, + "heading": 177.5, + "accelSet": { + "accelYaw": -0.65, + "accelLong": -0.05 + }, + "brakes": { + "scs": "unavailable", + "wheelBrakes": { + "leftFront": false, + "rightFront": false, + "unavailable": true, + "leftRear": false, + "rightRear": false + }, + "abs": "off", + "traction": "unavailable", + "brakeBoost": "unavailable", + "auxBrakes": "unavailable" + }, + "accuracy": { + "semiMinor": 2, + "orientation": 0, + "semiMajor": 2 + }, + "id": "DD2F5ECF", + "position": { + "elevation": 1691.9, + "latitude": 39.587872072989924, + "longitude": -105.09096255604963 + }, + "msgCnt": 44, + "speed": 13.23 + } + }, + "dataType": "us.dot.its.jpo.ode.plugin.j2735.J2735Bsm" + }, + "recordGeneratedAt": { + "$date": { + "$numberLong": "1717518179875" + } + } +},{ + "_id": { + "$oid": "665f3fc08c25216ef4f53dbf" + }, + "metadata": { + "securityResultCode": "success", + "bsmSource": "RV", + "schemaVersion": 6, + "odePacketID": "", + "sanitized": false, + "recordType": "bsmTx", + "recordGeneratedAt": "", + "maxDurationTime": 0, + "odeTimStartDateTime": "", + "receivedMessageDetails": { + "locationData": { + "elevation": "", + "heading": "", + "latitude": "", + "speed": "", + "longitude": "" + }, + "rxSource": "RV" + }, + "payloadType": "us.dot.its.jpo.ode.model.OdeBsmPayload", + "serialId": { + "recordId": 0, + "serialNumber": 0, + "streamId": "849c3ac6-4747-426f-ae5e-28ac8531b32c", + "bundleSize": 1, + "bundleId": 0 + }, + "logFileName": "", + "odeReceivedAt": "2024-06-04T16:22:59.976Z", + "originIp": "10.11.81.12" + }, + "payload": { + "data": { + "partII": [ + { + "id": "VehicleSafetyExtensions", + "value": { + "pathHistory": { + "crumbData": [ + { + "elevationOffset": 0.6, + "lonOffset": 0.0000387, + "timeOffset": 0.8, + "latOffset": -0.0001604 + }, + { + "elevationOffset": 4.3, + "lonOffset": 0.000231, + "timeOffset": 5.6, + "latOffset": -0.0011603 + }, + { + "elevationOffset": 9.1, + "lonOffset": 0.0003834, + "timeOffset": 11, + "latOffset": -0.0023425 + } + ] + }, + "pathPrediction": { + "confidence": 80, + "radiusOfCurve": -1622.5 + } + } + } + ], + "coreData": { + "secMark": 59976, + "transmission": "UNAVAILABLE", + "size": { + "width": 180, + "length": 480 + }, + "heading": 166, + "accelSet": { + "accelYaw": -0.65, + "accelLong": -0.05 + }, + "brakes": { + "scs": "unavailable", + "wheelBrakes": { + "leftFront": false, + "rightFront": false, + "unavailable": true, + "leftRear": false, + "rightRear": false + }, + "abs": "off", + "traction": "unavailable", + "brakeBoost": "unavailable", + "auxBrakes": "unavailable" + }, + "accuracy": { + "semiMinor": 2, + "orientation": 0, + "semiMajor": 2 + }, + "id": "DD2F5ECF", + "position": { + "elevation": 1691.9, + "latitude": 39.5878596707523, + "longitude": -105.09095853207707 + }, + "msgCnt": 45, + "speed": 14.22 + } + }, + "dataType": "us.dot.its.jpo.ode.plugin.j2735.J2735Bsm" + }, + "recordGeneratedAt": { + "$date": { + "$numberLong": "1717518179982" + } + } +},{ + "_id": { + "$oid": "665f3fc08c25216ef4f53dc0" + }, + "metadata": { + "securityResultCode": "success", + "bsmSource": "RV", + "schemaVersion": 6, + "odePacketID": "", + "sanitized": false, + "recordType": "bsmTx", + "recordGeneratedAt": "", + "maxDurationTime": 0, + "odeTimStartDateTime": "", + "receivedMessageDetails": { + "locationData": { + "elevation": "", + "heading": "", + "latitude": "", + "speed": "", + "longitude": "" + }, + "rxSource": "RV" + }, + "payloadType": "us.dot.its.jpo.ode.model.OdeBsmPayload", + "serialId": { + "recordId": 0, + "serialNumber": 0, + "streamId": "849c3ac6-4747-426f-ae5e-28ac8531b32c", + "bundleSize": 1, + "bundleId": 0 + }, + "logFileName": "", + "odeReceivedAt": "2024-06-04T16:23:00.083Z", + "originIp": "10.11.81.12" + }, + "payload": { + "data": { + "partII": [ + { + "id": "VehicleSafetyExtensions", + "value": { + "pathHistory": { + "crumbData": [ + { + "elevationOffset": 0.6, + "lonOffset": 0.0000387, + "timeOffset": 0.8, + "latOffset": -0.0001604 + }, + { + "elevationOffset": 4.3, + "lonOffset": 0.000231, + "timeOffset": 5.6, + "latOffset": -0.0011603 + }, + { + "elevationOffset": 9.1, + "lonOffset": 0.0003834, + "timeOffset": 11, + "latOffset": -0.0023425 + } + ] + }, + "pathPrediction": { + "confidence": 80, + "radiusOfCurve": -1622.5 + } + } + } + ], + "coreData": { + "secMark": 83, + "transmission": "UNAVAILABLE", + "size": { + "width": 180, + "length": 480 + }, + "heading": 169.7, + "accelSet": { + "accelYaw": -0.65, + "accelLong": -0.05 + }, + "brakes": { + "scs": "unavailable", + "wheelBrakes": { + "leftFront": false, + "rightFront": false, + "unavailable": true, + "leftRear": false, + "rightRear": false + }, + "abs": "off", + "traction": "unavailable", + "brakeBoost": "unavailable", + "auxBrakes": "unavailable" + }, + "accuracy": { + "semiMinor": 2, + "orientation": 0, + "semiMajor": 2 + }, + "id": "DD2F5ECF", + "position": { + "elevation": 1691.9, + "latitude": 39.58784261767199, + "longitude": -105.0909545081045 + }, + "msgCnt": 46, + "speed": 19.27 + } + }, + "dataType": "us.dot.its.jpo.ode.plugin.j2735.J2735Bsm" + }, + "recordGeneratedAt": { + "$date": { + "$numberLong": "1717518180090" + } + } +},{ + "_id": { + "$oid": "665f3fc08c25216ef4f53dc1" + }, + "metadata": { + "securityResultCode": "success", + "bsmSource": "RV", + "schemaVersion": 6, + "odePacketID": "", + "sanitized": false, + "recordType": "bsmTx", + "recordGeneratedAt": "", + "maxDurationTime": 0, + "odeTimStartDateTime": "", + "receivedMessageDetails": { + "locationData": { + "elevation": "", + "heading": "", + "latitude": "", + "speed": "", + "longitude": "" + }, + "rxSource": "RV" + }, + "payloadType": "us.dot.its.jpo.ode.model.OdeBsmPayload", + "serialId": { + "recordId": 0, + "serialNumber": 0, + "streamId": "849c3ac6-4747-426f-ae5e-28ac8531b32c", + "bundleSize": 1, + "bundleId": 0 + }, + "logFileName": "", + "odeReceivedAt": "2024-06-04T16:23:00.19Z", + "originIp": "10.11.81.12" + }, + "payload": { + "data": { + "partII": [ + { + "id": "VehicleSafetyExtensions", + "value": { + "pathHistory": { + "crumbData": [ + { + "elevationOffset": 0.6, + "lonOffset": 0.0000387, + "timeOffset": 0.8, + "latOffset": -0.0001604 + }, + { + "elevationOffset": 4.3, + "lonOffset": 0.000231, + "timeOffset": 5.6, + "latOffset": -0.0011603 + }, + { + "elevationOffset": 9.1, + "lonOffset": 0.0003834, + "timeOffset": 11, + "latOffset": -0.0023425 + } + ] + }, + "pathPrediction": { + "confidence": 80, + "radiusOfCurve": -1622.5 + } + } + } + ], + "coreData": { + "secMark": 190, + "transmission": "UNAVAILABLE", + "size": { + "width": 180, + "length": 480 + }, + "heading": 168.7, + "accelSet": { + "accelYaw": -0.65, + "accelLong": -0.05 + }, + "brakes": { + "scs": "unavailable", + "wheelBrakes": { + "leftFront": false, + "rightFront": false, + "unavailable": true, + "leftRear": false, + "rightRear": false + }, + "abs": "off", + "traction": "unavailable", + "brakeBoost": "unavailable", + "auxBrakes": "unavailable" + }, + "accuracy": { + "semiMinor": 2, + "orientation": 0, + "semiMajor": 2 + }, + "id": "DD2F5ECF", + "position": { + "elevation": 1691.9, + "latitude": 39.587829698668905, + "longitude": -105.090951154794 + }, + "msgCnt": 47, + "speed": 14.65 + } + }, + "dataType": "us.dot.its.jpo.ode.plugin.j2735.J2735Bsm" + }, + "recordGeneratedAt": { + "$date": { + "$numberLong": "1717518180198" + } + } +},{ + "_id": { + "$oid": "665f3fc08c25216ef4f53dc2" + }, + "metadata": { + "securityResultCode": "success", + "bsmSource": "RV", + "schemaVersion": 6, + "odePacketID": "", + "sanitized": false, + "recordType": "bsmTx", + "recordGeneratedAt": "", + "maxDurationTime": 0, + "odeTimStartDateTime": "", + "receivedMessageDetails": { + "locationData": { + "elevation": "", + "heading": "", + "latitude": "", + "speed": "", + "longitude": "" + }, + "rxSource": "RV" + }, + "payloadType": "us.dot.its.jpo.ode.model.OdeBsmPayload", + "serialId": { + "recordId": 0, + "serialNumber": 0, + "streamId": "849c3ac6-4747-426f-ae5e-28ac8531b32c", + "bundleSize": 1, + "bundleId": 0 + }, + "logFileName": "", + "odeReceivedAt": "2024-06-04T16:23:00.299Z", + "originIp": "10.11.81.12" + }, + "payload": { + "data": { + "partII": [ + { + "id": "VehicleSafetyExtensions", + "value": { + "pathHistory": { + "crumbData": [ + { + "elevationOffset": 0.6, + "lonOffset": 0.0000387, + "timeOffset": 0.8, + "latOffset": -0.0001604 + }, + { + "elevationOffset": 4.3, + "lonOffset": 0.000231, + "timeOffset": 5.6, + "latOffset": -0.0011603 + }, + { + "elevationOffset": 9.1, + "lonOffset": 0.0003834, + "timeOffset": 11, + "latOffset": -0.0023425 + } + ] + }, + "pathPrediction": { + "confidence": 80, + "radiusOfCurve": -1622.5 + } + } + } + ], + "coreData": { + "secMark": 299, + "transmission": "UNAVAILABLE", + "size": { + "width": 180, + "length": 480 + }, + "heading": 168, + "accelSet": { + "accelYaw": -0.65, + "accelLong": -0.05 + }, + "brakes": { + "scs": "unavailable", + "wheelBrakes": { + "leftFront": false, + "rightFront": false, + "unavailable": true, + "leftRear": false, + "rightRear": false + }, + "abs": "off", + "traction": "unavailable", + "brakeBoost": "unavailable", + "auxBrakes": "unavailable" + }, + "accuracy": { + "semiMinor": 2, + "orientation": 0, + "semiMajor": 2 + }, + "id": "DD2F5ECF", + "position": { + "elevation": 1691.9, + "latitude": 39.58781264558121, + "longitude": -105.09094646015933 + }, + "msgCnt": 48, + "speed": 19.38 + } + }, + "dataType": "us.dot.its.jpo.ode.plugin.j2735.J2735Bsm" + }, + "recordGeneratedAt": { + "$date": { + "$numberLong": "1717518180305" + } + } +},{ + "_id": { + "$oid": "665f3fc08c25216ef4f53dc3" + }, + "metadata": { + "securityResultCode": "success", + "bsmSource": "RV", + "schemaVersion": 6, + "odePacketID": "", + "sanitized": false, + "recordType": "bsmTx", + "recordGeneratedAt": "", + "maxDurationTime": 0, + "odeTimStartDateTime": "", + "receivedMessageDetails": { + "locationData": { + "elevation": "", + "heading": "", + "latitude": "", + "speed": "", + "longitude": "" + }, + "rxSource": "RV" + }, + "payloadType": "us.dot.its.jpo.ode.model.OdeBsmPayload", + "serialId": { + "recordId": 0, + "serialNumber": 0, + "streamId": "849c3ac6-4747-426f-ae5e-28ac8531b32c", + "bundleSize": 1, + "bundleId": 0 + }, + "logFileName": "", + "odeReceivedAt": "2024-06-04T16:23:00.408Z", + "originIp": "10.11.81.12" + }, + "payload": { + "data": { + "partII": [ + { + "id": "VehicleSafetyExtensions", + "value": { + "pathHistory": { + "crumbData": [ + { + "elevationOffset": 0.6, + "lonOffset": 0.0000387, + "timeOffset": 0.8, + "latOffset": -0.0001604 + }, + { + "elevationOffset": 4.3, + "lonOffset": 0.000231, + "timeOffset": 5.6, + "latOffset": -0.0011603 + }, + { + "elevationOffset": 9.1, + "lonOffset": 0.0003834, + "timeOffset": 11, + "latOffset": -0.0023425 + } + ] + }, + "pathPrediction": { + "confidence": 80, + "radiusOfCurve": -1622.5 + } + } + } + ], + "coreData": { + "secMark": 408, + "transmission": "UNAVAILABLE", + "size": { + "width": 180, + "length": 480 + }, + "heading": 165.3, + "accelSet": { + "accelYaw": -0.65, + "accelLong": -0.05 + }, + "brakes": { + "scs": "unavailable", + "wheelBrakes": { + "leftFront": false, + "rightFront": false, + "unavailable": true, + "leftRear": false, + "rightRear": false + }, + "abs": "off", + "traction": "unavailable", + "brakeBoost": "unavailable", + "auxBrakes": "unavailable" + }, + "accuracy": { + "semiMinor": 2, + "orientation": 0, + "semiMajor": 2 + }, + "id": "DD2F5ECF", + "position": { + "elevation": 1691.9, + "latitude": 39.58779300868712, + "longitude": -105.09093975353838 + }, + "msgCnt": 49, + "speed": 22.58 + } + }, + "dataType": "us.dot.its.jpo.ode.plugin.j2735.J2735Bsm" + }, + "recordGeneratedAt": { + "$date": { + "$numberLong": "1717518180412" + } + } +},{ + "_id": { + "$oid": "665f3fc08c25216ef4f53dc4" + }, + "metadata": { + "securityResultCode": "success", + "bsmSource": "RV", + "schemaVersion": 6, + "odePacketID": "", + "sanitized": false, + "recordType": "bsmTx", + "recordGeneratedAt": "", + "maxDurationTime": 0, + "odeTimStartDateTime": "", + "receivedMessageDetails": { + "locationData": { + "elevation": "", + "heading": "", + "latitude": "", + "speed": "", + "longitude": "" + }, + "rxSource": "RV" + }, + "payloadType": "us.dot.its.jpo.ode.model.OdeBsmPayload", + "serialId": { + "recordId": 0, + "serialNumber": 0, + "streamId": "849c3ac6-4747-426f-ae5e-28ac8531b32c", + "bundleSize": 1, + "bundleId": 0 + }, + "logFileName": "", + "odeReceivedAt": "2024-06-04T16:23:00.513Z", + "originIp": "10.11.81.12" + }, + "payload": { + "data": { + "partII": [ + { + "id": "VehicleSafetyExtensions", + "value": { + "pathHistory": { + "crumbData": [ + { + "elevationOffset": 0.6, + "lonOffset": 0.0000387, + "timeOffset": 0.8, + "latOffset": -0.0001604 + }, + { + "elevationOffset": 4.3, + "lonOffset": 0.000231, + "timeOffset": 5.6, + "latOffset": -0.0011603 + }, + { + "elevationOffset": 9.1, + "lonOffset": 0.0003834, + "timeOffset": 11, + "latOffset": -0.0023425 + } + ] + }, + "pathPrediction": { + "confidence": 80, + "radiusOfCurve": -1622.5 + } + } + } + ], + "coreData": { + "secMark": 513, + "transmission": "UNAVAILABLE", + "size": { + "width": 180, + "length": 480 + }, + "heading": 164.1, + "accelSet": { + "accelYaw": -0.65, + "accelLong": -0.05 + }, + "brakes": { + "scs": "unavailable", + "wheelBrakes": { + "leftFront": false, + "rightFront": false, + "unavailable": true, + "leftRear": false, + "rightRear": false + }, + "abs": "off", + "traction": "unavailable", + "brakeBoost": "unavailable", + "auxBrakes": "unavailable" + }, + "accuracy": { + "semiMinor": 2, + "orientation": 0, + "semiMajor": 2 + }, + "id": "DD2F5ECF", + "position": { + "elevation": 1691.9, + "latitude": 39.58777853939319, + "longitude": -105.0909343882416 + }, + "msgCnt": 50, + "speed": 16.73 + } + }, + "dataType": "us.dot.its.jpo.ode.plugin.j2735.J2735Bsm" + }, + "recordGeneratedAt": { + "$date": { + "$numberLong": "1717518180520" + } + } +},{ + "_id": { + "$oid": "665f3fc08c25216ef4f53dc5" + }, + "metadata": { + "securityResultCode": "success", + "bsmSource": "RV", + "schemaVersion": 6, + "odePacketID": "", + "sanitized": false, + "recordType": "bsmTx", + "recordGeneratedAt": "", + "maxDurationTime": 0, + "odeTimStartDateTime": "", + "receivedMessageDetails": { + "locationData": { + "elevation": "", + "heading": "", + "latitude": "", + "speed": "", + "longitude": "" + }, + "rxSource": "RV" + }, + "payloadType": "us.dot.its.jpo.ode.model.OdeBsmPayload", + "serialId": { + "recordId": 0, + "serialNumber": 0, + "streamId": "849c3ac6-4747-426f-ae5e-28ac8531b32c", + "bundleSize": 1, + "bundleId": 0 + }, + "logFileName": "", + "odeReceivedAt": "2024-06-04T16:23:12.283Z", + "originIp": "10.11.81.12" + }, + "payload": { + "data": { + "partII": [ + { + "id": "VehicleSafetyExtensions", + "value": { + "pathHistory": { + "crumbData": [ + { + "elevationOffset": 0.6, + "lonOffset": 0.0000387, + "timeOffset": 0.8, + "latOffset": -0.0001604 + }, + { + "elevationOffset": 4.3, + "lonOffset": 0.000231, + "timeOffset": 5.6, + "latOffset": -0.0011603 + }, + { + "elevationOffset": 9.1, + "lonOffset": 0.0003834, + "timeOffset": 11, + "latOffset": -0.0023425 + } + ] + }, + "pathPrediction": { + "confidence": 80, + "radiusOfCurve": -1622.5 + } + } + } + ], + "coreData": { + "secMark": 12283, + "transmission": "UNAVAILABLE", + "size": { + "width": 180, + "length": 480 + }, + "heading": 169.2, + "accelSet": { + "accelYaw": -0.65, + "accelLong": -0.05 + }, + "brakes": { + "scs": "unavailable", + "wheelBrakes": { + "leftFront": false, + "rightFront": false, + "unavailable": true, + "leftRear": false, + "rightRear": false + }, + "abs": "off", + "traction": "unavailable", + "brakeBoost": "unavailable", + "auxBrakes": "unavailable" + }, + "accuracy": { + "semiMinor": 2, + "orientation": 0, + "semiMajor": 2 + }, + "id": "11AF9993", + "position": { + "elevation": 1691.9, + "latitude": 39.587279346902015, + "longitude": -105.09059802750382 + }, + "msgCnt": 1, + "speed": 22.62 + } + }, + "dataType": "us.dot.its.jpo.ode.plugin.j2735.J2735Bsm" + }, + "recordGeneratedAt": { + "$date": { + "$numberLong": "1717518192299" + } + } +},{ + "_id": { + "$oid": "665f3fc08c25216ef4f53dc6" + }, + "metadata": { + "securityResultCode": "success", + "bsmSource": "RV", + "schemaVersion": 6, + "odePacketID": "", + "sanitized": false, + "recordType": "bsmTx", + "recordGeneratedAt": "", + "maxDurationTime": 0, + "odeTimStartDateTime": "", + "receivedMessageDetails": { + "locationData": { + "elevation": "", + "heading": "", + "latitude": "", + "speed": "", + "longitude": "" + }, + "rxSource": "RV" + }, + "payloadType": "us.dot.its.jpo.ode.model.OdeBsmPayload", + "serialId": { + "recordId": 0, + "serialNumber": 0, + "streamId": "849c3ac6-4747-426f-ae5e-28ac8531b32c", + "bundleSize": 1, + "bundleId": 0 + }, + "logFileName": "", + "odeReceivedAt": "2024-06-04T16:23:12.395Z", + "originIp": "10.11.81.12" + }, + "payload": { + "data": { + "partII": [ + { + "id": "VehicleSafetyExtensions", + "value": { + "pathHistory": { + "crumbData": [ + { + "elevationOffset": 0.6, + "lonOffset": 0.0000387, + "timeOffset": 0.8, + "latOffset": -0.0001604 + }, + { + "elevationOffset": 4.3, + "lonOffset": 0.000231, + "timeOffset": 5.6, + "latOffset": -0.0011603 + }, + { + "elevationOffset": 9.1, + "lonOffset": 0.0003834, + "timeOffset": 11, + "latOffset": -0.0023425 + } + ] + }, + "pathPrediction": { + "confidence": 80, + "radiusOfCurve": -1622.5 + } + } + } + ], + "coreData": { + "secMark": 12395, + "transmission": "UNAVAILABLE", + "size": { + "width": 180, + "length": 480 + }, + "heading": 342, + "accelSet": { + "accelYaw": -0.65, + "accelLong": -0.05 + }, + "brakes": { + "scs": "unavailable", + "wheelBrakes": { + "leftFront": false, + "rightFront": false, + "unavailable": true, + "leftRear": false, + "rightRear": false + }, + "abs": "off", + "traction": "unavailable", + "brakeBoost": "unavailable", + "auxBrakes": "unavailable" + }, + "accuracy": { + "semiMinor": 2, + "orientation": 0, + "semiMajor": 2 + }, + "id": "11AF9993", + "position": { + "elevation": 1691.9, + "latitude": 39.58731758744779, + "longitude": -105.09061412339413 + }, + "msgCnt": 2, + "speed": 44.7 + } + }, + "dataType": "us.dot.its.jpo.ode.plugin.j2735.J2735Bsm" + }, + "recordGeneratedAt": { + "$date": { + "$numberLong": "1717518192403" + } + } +},{ + "_id": { + "$oid": "665f3fc08c25216ef4f53dc7" + }, + "metadata": { + "securityResultCode": "success", + "bsmSource": "RV", + "schemaVersion": 6, + "odePacketID": "", + "sanitized": false, + "recordType": "bsmTx", + "recordGeneratedAt": "", + "maxDurationTime": 0, + "odeTimStartDateTime": "", + "receivedMessageDetails": { + "locationData": { + "elevation": "", + "heading": "", + "latitude": "", + "speed": "", + "longitude": "" + }, + "rxSource": "RV" + }, + "payloadType": "us.dot.its.jpo.ode.model.OdeBsmPayload", + "serialId": { + "recordId": 0, + "serialNumber": 0, + "streamId": "849c3ac6-4747-426f-ae5e-28ac8531b32c", + "bundleSize": 1, + "bundleId": 0 + }, + "logFileName": "", + "odeReceivedAt": "2024-06-04T16:23:12.502Z", + "originIp": "10.11.81.12" + }, + "payload": { + "data": { + "partII": [ + { + "id": "VehicleSafetyExtensions", + "value": { + "pathHistory": { + "crumbData": [ + { + "elevationOffset": 0.6, + "lonOffset": 0.0000387, + "timeOffset": 0.8, + "latOffset": -0.0001604 + }, + { + "elevationOffset": 4.3, + "lonOffset": 0.000231, + "timeOffset": 5.6, + "latOffset": -0.0011603 + }, + { + "elevationOffset": 9.1, + "lonOffset": 0.0003834, + "timeOffset": 11, + "latOffset": -0.0023425 + } + ] + }, + "pathPrediction": { + "confidence": 80, + "radiusOfCurve": -1622.5 + } + } + } + ], + "coreData": { + "secMark": 12502, + "transmission": "UNAVAILABLE", + "size": { + "width": 180, + "length": 480 + }, + "heading": 339.9, + "accelSet": { + "accelYaw": -0.65, + "accelLong": -0.05 + }, + "brakes": { + "scs": "unavailable", + "wheelBrakes": { + "leftFront": false, + "rightFront": false, + "unavailable": true, + "leftRear": false, + "rightRear": false + }, + "abs": "off", + "traction": "unavailable", + "brakeBoost": "unavailable", + "auxBrakes": "unavailable" + }, + "accuracy": { + "semiMinor": 2, + "orientation": 0, + "semiMajor": 2 + }, + "id": "11AF9993", + "position": { + "elevation": 1691.9, + "latitude": 39.58734859328024, + "longitude": -105.09062887796024 + }, + "msgCnt": 3, + "speed": 36.72 + } + }, + "dataType": "us.dot.its.jpo.ode.plugin.j2735.J2735Bsm" + }, + "recordGeneratedAt": { + "$date": { + "$numberLong": "1717518192510" + } + } +},{ + "_id": { + "$oid": "665f3fc08c25216ef4f53dc8" + }, + "metadata": { + "securityResultCode": "success", + "bsmSource": "RV", + "schemaVersion": 6, + "odePacketID": "", + "sanitized": false, + "recordType": "bsmTx", + "recordGeneratedAt": "", + "maxDurationTime": 0, + "odeTimStartDateTime": "", + "receivedMessageDetails": { + "locationData": { + "elevation": "", + "heading": "", + "latitude": "", + "speed": "", + "longitude": "" + }, + "rxSource": "RV" + }, + "payloadType": "us.dot.its.jpo.ode.model.OdeBsmPayload", + "serialId": { + "recordId": 0, + "serialNumber": 0, + "streamId": "849c3ac6-4747-426f-ae5e-28ac8531b32c", + "bundleSize": 1, + "bundleId": 0 + }, + "logFileName": "", + "odeReceivedAt": "2024-06-04T16:23:12.608Z", + "originIp": "10.11.81.12" + }, + "payload": { + "data": { + "partII": [ + { + "id": "VehicleSafetyExtensions", + "value": { + "pathHistory": { + "crumbData": [ + { + "elevationOffset": 0.6, + "lonOffset": 0.0000387, + "timeOffset": 0.8, + "latOffset": -0.0001604 + }, + { + "elevationOffset": 4.3, + "lonOffset": 0.000231, + "timeOffset": 5.6, + "latOffset": -0.0011603 + }, + { + "elevationOffset": 9.1, + "lonOffset": 0.0003834, + "timeOffset": 11, + "latOffset": -0.0023425 + } + ] + }, + "pathPrediction": { + "confidence": 80, + "radiusOfCurve": -1622.5 + } + } + } + ], + "coreData": { + "secMark": 12608, + "transmission": "UNAVAILABLE", + "size": { + "width": 180, + "length": 480 + }, + "heading": 347.9, + "accelSet": { + "accelYaw": -0.65, + "accelLong": -0.05 + }, + "brakes": { + "scs": "unavailable", + "wheelBrakes": { + "leftFront": false, + "rightFront": false, + "unavailable": true, + "leftRear": false, + "rightRear": false + }, + "abs": "off", + "traction": "unavailable", + "brakeBoost": "unavailable", + "auxBrakes": "unavailable" + }, + "accuracy": { + "semiMinor": 2, + "orientation": 0, + "semiMajor": 2 + }, + "id": "11AF9993", + "position": { + "elevation": 1691.9, + "latitude": 39.587377532044655, + "longitude": -105.09063692590537 + }, + "msgCnt": 4, + "speed": 32.91 + } + }, + "dataType": "us.dot.its.jpo.ode.plugin.j2735.J2735Bsm" + }, + "recordGeneratedAt": { + "$date": { + "$numberLong": "1717518192617" + } + } +},{ + "_id": { + "$oid": "665f3fc08c25216ef4f53dc9" + }, + "metadata": { + "securityResultCode": "success", + "bsmSource": "RV", + "schemaVersion": 6, + "odePacketID": "", + "sanitized": false, + "recordType": "bsmTx", + "recordGeneratedAt": "", + "maxDurationTime": 0, + "odeTimStartDateTime": "", + "receivedMessageDetails": { + "locationData": { + "elevation": "", + "heading": "", + "latitude": "", + "speed": "", + "longitude": "" + }, + "rxSource": "RV" + }, + "payloadType": "us.dot.its.jpo.ode.model.OdeBsmPayload", + "serialId": { + "recordId": 0, + "serialNumber": 0, + "streamId": "849c3ac6-4747-426f-ae5e-28ac8531b32c", + "bundleSize": 1, + "bundleId": 0 + }, + "logFileName": "", + "odeReceivedAt": "2024-06-04T16:23:12.716Z", + "originIp": "10.11.81.12" + }, + "payload": { + "data": { + "partII": [ + { + "id": "VehicleSafetyExtensions", + "value": { + "pathHistory": { + "crumbData": [ + { + "elevationOffset": 0.6, + "lonOffset": 0.0000387, + "timeOffset": 0.8, + "latOffset": -0.0001604 + }, + { + "elevationOffset": 4.3, + "lonOffset": 0.000231, + "timeOffset": 5.6, + "latOffset": -0.0011603 + }, + { + "elevationOffset": 9.1, + "lonOffset": 0.0003834, + "timeOffset": 11, + "latOffset": -0.0023425 + } + ] + }, + "pathPrediction": { + "confidence": 80, + "radiusOfCurve": -1622.5 + } + } + } + ], + "coreData": { + "secMark": 12716, + "transmission": "UNAVAILABLE", + "size": { + "width": 180, + "length": 480 + }, + "heading": 347.7, + "accelSet": { + "accelYaw": -0.65, + "accelLong": -0.05 + }, + "brakes": { + "scs": "unavailable", + "wheelBrakes": { + "leftFront": false, + "rightFront": false, + "unavailable": true, + "leftRear": false, + "rightRear": false + }, + "abs": "off", + "traction": "unavailable", + "brakeBoost": "unavailable", + "auxBrakes": "unavailable" + }, + "accuracy": { + "semiMinor": 2, + "orientation": 0, + "semiMajor": 2 + }, + "id": "11AF9993", + "position": { + "elevation": 1691.9, + "latitude": 39.58741060490348, + "longitude": -105.09064631517474 + }, + "msgCnt": 5, + "speed": 37.65 + } + }, + "dataType": "us.dot.its.jpo.ode.plugin.j2735.J2735Bsm" + }, + "recordGeneratedAt": { + "$date": { + "$numberLong": "1717518192724" + } + } +},{ + "_id": { + "$oid": "665f3fc08c25216ef4f53dca" + }, + "metadata": { + "securityResultCode": "success", + "bsmSource": "RV", + "schemaVersion": 6, + "odePacketID": "", + "sanitized": false, + "recordType": "bsmTx", + "recordGeneratedAt": "", + "maxDurationTime": 0, + "odeTimStartDateTime": "", + "receivedMessageDetails": { + "locationData": { + "elevation": "", + "heading": "", + "latitude": "", + "speed": "", + "longitude": "" + }, + "rxSource": "RV" + }, + "payloadType": "us.dot.its.jpo.ode.model.OdeBsmPayload", + "serialId": { + "recordId": 0, + "serialNumber": 0, + "streamId": "849c3ac6-4747-426f-ae5e-28ac8531b32c", + "bundleSize": 1, + "bundleId": 0 + }, + "logFileName": "", + "odeReceivedAt": "2024-06-04T16:23:12.824Z", + "originIp": "10.11.81.12" + }, + "payload": { + "data": { + "partII": [ + { + "id": "VehicleSafetyExtensions", + "value": { + "pathHistory": { + "crumbData": [ + { + "elevationOffset": 0.6, + "lonOffset": 0.0000387, + "timeOffset": 0.8, + "latOffset": -0.0001604 + }, + { + "elevationOffset": 4.3, + "lonOffset": 0.000231, + "timeOffset": 5.6, + "latOffset": -0.0011603 + }, + { + "elevationOffset": 9.1, + "lonOffset": 0.0003834, + "timeOffset": 11, + "latOffset": -0.0023425 + } + ] + }, + "pathPrediction": { + "confidence": 80, + "radiusOfCurve": -1622.5 + } + } + } + ], + "coreData": { + "secMark": 12824, + "transmission": "UNAVAILABLE", + "size": { + "width": 180, + "length": 480 + }, + "heading": 343.3, + "accelSet": { + "accelYaw": -0.65, + "accelLong": -0.05 + }, + "brakes": { + "scs": "unavailable", + "wheelBrakes": { + "leftFront": false, + "rightFront": false, + "unavailable": true, + "leftRear": false, + "rightRear": false + }, + "abs": "off", + "traction": "unavailable", + "brakeBoost": "unavailable", + "auxBrakes": "unavailable" + }, + "accuracy": { + "semiMinor": 2, + "orientation": 0, + "semiMajor": 2 + }, + "id": "11AF9993", + "position": { + "elevation": 1691.9, + "latitude": 39.58744161069427, + "longitude": -105.09065838709249 + }, + "msgCnt": 6, + "speed": 36 + } + }, + "dataType": "us.dot.its.jpo.ode.plugin.j2735.J2735Bsm" + }, + "recordGeneratedAt": { + "$date": { + "$numberLong": "1717518192831" + } + } +},{ + "_id": { + "$oid": "665f3fc08c25216ef4f53dcb" + }, + "metadata": { + "securityResultCode": "success", + "bsmSource": "RV", + "schemaVersion": 6, + "odePacketID": "", + "sanitized": false, + "recordType": "bsmTx", + "recordGeneratedAt": "", + "maxDurationTime": 0, + "odeTimStartDateTime": "", + "receivedMessageDetails": { + "locationData": { + "elevation": "", + "heading": "", + "latitude": "", + "speed": "", + "longitude": "" + }, + "rxSource": "RV" + }, + "payloadType": "us.dot.its.jpo.ode.model.OdeBsmPayload", + "serialId": { + "recordId": 0, + "serialNumber": 0, + "streamId": "849c3ac6-4747-426f-ae5e-28ac8531b32c", + "bundleSize": 1, + "bundleId": 0 + }, + "logFileName": "", + "odeReceivedAt": "2024-06-04T16:23:12.932Z", + "originIp": "10.11.81.12" + }, + "payload": { + "data": { + "partII": [ + { + "id": "VehicleSafetyExtensions", + "value": { + "pathHistory": { + "crumbData": [ + { + "elevationOffset": 0.6, + "lonOffset": 0.0000387, + "timeOffset": 0.8, + "latOffset": -0.0001604 + }, + { + "elevationOffset": 4.3, + "lonOffset": 0.000231, + "timeOffset": 5.6, + "latOffset": -0.0011603 + }, + { + "elevationOffset": 9.1, + "lonOffset": 0.0003834, + "timeOffset": 11, + "latOffset": -0.0023425 + } + ] + }, + "pathPrediction": { + "confidence": 80, + "radiusOfCurve": -1622.5 + } + } + } + ], + "coreData": { + "secMark": 12932, + "transmission": "UNAVAILABLE", + "size": { + "width": 180, + "length": 480 + }, + "heading": 348, + "accelSet": { + "accelYaw": -0.65, + "accelLong": -0.05 + }, + "brakes": { + "scs": "unavailable", + "wheelBrakes": { + "leftFront": false, + "rightFront": false, + "unavailable": true, + "leftRear": false, + "rightRear": false + }, + "abs": "off", + "traction": "unavailable", + "brakeBoost": "unavailable", + "auxBrakes": "unavailable" + }, + "accuracy": { + "semiMinor": 2, + "orientation": 0, + "semiMajor": 2 + }, + "id": "11AF9993", + "position": { + "elevation": 1691.9, + "latitude": 39.587475717048164, + "longitude": -105.09066777636183 + }, + "msgCnt": 7, + "speed": 38.77 + } + }, + "dataType": "us.dot.its.jpo.ode.plugin.j2735.J2735Bsm" + }, + "recordGeneratedAt": { + "$date": { + "$numberLong": "1717518192938" + } + } +},{ + "_id": { + "$oid": "665f3fc08c25216ef4f53dcc" + }, + "metadata": { + "securityResultCode": "success", + "bsmSource": "RV", + "schemaVersion": 6, + "odePacketID": "", + "sanitized": false, + "recordType": "bsmTx", + "recordGeneratedAt": "", + "maxDurationTime": 0, + "odeTimStartDateTime": "", + "receivedMessageDetails": { + "locationData": { + "elevation": "", + "heading": "", + "latitude": "", + "speed": "", + "longitude": "" + }, + "rxSource": "RV" + }, + "payloadType": "us.dot.its.jpo.ode.model.OdeBsmPayload", + "serialId": { + "recordId": 0, + "serialNumber": 0, + "streamId": "849c3ac6-4747-426f-ae5e-28ac8531b32c", + "bundleSize": 1, + "bundleId": 0 + }, + "logFileName": "", + "odeReceivedAt": "2024-06-04T16:23:13.037Z", + "originIp": "10.11.81.12" + }, + "payload": { + "data": { + "partII": [ + { + "id": "VehicleSafetyExtensions", + "value": { + "pathHistory": { + "crumbData": [ + { + "elevationOffset": 0.6, + "lonOffset": 0.0000387, + "timeOffset": 0.8, + "latOffset": -0.0001604 + }, + { + "elevationOffset": 4.3, + "lonOffset": 0.000231, + "timeOffset": 5.6, + "latOffset": -0.0011603 + }, + { + "elevationOffset": 9.1, + "lonOffset": 0.0003834, + "timeOffset": 11, + "latOffset": -0.0023425 + } + ] + }, + "pathPrediction": { + "confidence": 80, + "radiusOfCurve": -1622.5 + } + } + } + ], + "coreData": { + "secMark": 13037, + "transmission": "UNAVAILABLE", + "size": { + "width": 180, + "length": 480 + }, + "heading": 344.6, + "accelSet": { + "accelYaw": -0.65, + "accelLong": -0.05 + }, + "brakes": { + "scs": "unavailable", + "wheelBrakes": { + "leftFront": false, + "rightFront": false, + "unavailable": true, + "leftRear": false, + "rightRear": false + }, + "abs": "off", + "traction": "unavailable", + "brakeBoost": "unavailable", + "auxBrakes": "unavailable" + }, + "accuracy": { + "semiMinor": 2, + "orientation": 0, + "semiMajor": 2 + }, + "id": "11AF9993", + "position": { + "elevation": 1691.9, + "latitude": 39.58750568928466, + "longitude": -105.09067850695536 + }, + "msgCnt": 8, + "speed": 34.57 + } + }, + "dataType": "us.dot.its.jpo.ode.plugin.j2735.J2735Bsm" + }, + "recordGeneratedAt": { + "$date": { + "$numberLong": "1717518193045" + } + } +},{ + "_id": { + "$oid": "665f3fc08c25216ef4f53dcd" + }, + "metadata": { + "securityResultCode": "success", + "bsmSource": "RV", + "schemaVersion": 6, + "odePacketID": "", + "sanitized": false, + "recordType": "bsmTx", + "recordGeneratedAt": "", + "maxDurationTime": 0, + "odeTimStartDateTime": "", + "receivedMessageDetails": { + "locationData": { + "elevation": "", + "heading": "", + "latitude": "", + "speed": "", + "longitude": "" + }, + "rxSource": "RV" + }, + "payloadType": "us.dot.its.jpo.ode.model.OdeBsmPayload", + "serialId": { + "recordId": 0, + "serialNumber": 0, + "streamId": "849c3ac6-4747-426f-ae5e-28ac8531b32c", + "bundleSize": 1, + "bundleId": 0 + }, + "logFileName": "", + "odeReceivedAt": "2024-06-04T16:23:13.146Z", + "originIp": "10.11.81.12" + }, + "payload": { + "data": { + "partII": [ + { + "id": "VehicleSafetyExtensions", + "value": { + "pathHistory": { + "crumbData": [ + { + "elevationOffset": 0.6, + "lonOffset": 0.0000387, + "timeOffset": 0.8, + "latOffset": -0.0001604 + }, + { + "elevationOffset": 4.3, + "lonOffset": 0.000231, + "timeOffset": 5.6, + "latOffset": -0.0011603 + }, + { + "elevationOffset": 9.1, + "lonOffset": 0.0003834, + "timeOffset": 11, + "latOffset": -0.0023425 + } + ] + }, + "pathPrediction": { + "confidence": 80, + "radiusOfCurve": -1622.5 + } + } + } + ], + "coreData": { + "secMark": 13146, + "transmission": "UNAVAILABLE", + "size": { + "width": 180, + "length": 480 + }, + "heading": 346, + "accelSet": { + "accelYaw": -0.65, + "accelLong": -0.05 + }, + "brakes": { + "scs": "unavailable", + "wheelBrakes": { + "leftFront": false, + "rightFront": false, + "unavailable": true, + "leftRear": false, + "rightRear": false + }, + "abs": "off", + "traction": "unavailable", + "brakeBoost": "unavailable", + "auxBrakes": "unavailable" + }, + "accuracy": { + "semiMinor": 2, + "orientation": 0, + "semiMajor": 2 + }, + "id": "11AF9993", + "position": { + "elevation": 1691.9, + "latitude": 39.58753049388438, + "longitude": -105.09068655490051 + }, + "msgCnt": 9, + "speed": 28.43 + } + }, + "dataType": "us.dot.its.jpo.ode.plugin.j2735.J2735Bsm" + }, + "recordGeneratedAt": { + "$date": { + "$numberLong": "1717518193151" + } + } +},{ + "_id": { + "$oid": "665f3fc08c25216ef4f53dce" + }, + "metadata": { + "securityResultCode": "success", + "bsmSource": "RV", + "schemaVersion": 6, + "odePacketID": "", + "sanitized": false, + "recordType": "bsmTx", + "recordGeneratedAt": "", + "maxDurationTime": 0, + "odeTimStartDateTime": "", + "receivedMessageDetails": { + "locationData": { + "elevation": "", + "heading": "", + "latitude": "", + "speed": "", + "longitude": "" + }, + "rxSource": "RV" + }, + "payloadType": "us.dot.its.jpo.ode.model.OdeBsmPayload", + "serialId": { + "recordId": 0, + "serialNumber": 0, + "streamId": "849c3ac6-4747-426f-ae5e-28ac8531b32c", + "bundleSize": 1, + "bundleId": 0 + }, + "logFileName": "", + "odeReceivedAt": "2024-06-04T16:23:13.253Z", + "originIp": "10.11.81.12" + }, + "payload": { + "data": { + "partII": [ + { + "id": "VehicleSafetyExtensions", + "value": { + "pathHistory": { + "crumbData": [ + { + "elevationOffset": 0.6, + "lonOffset": 0.0000387, + "timeOffset": 0.8, + "latOffset": -0.0001604 + }, + { + "elevationOffset": 4.3, + "lonOffset": 0.000231, + "timeOffset": 5.6, + "latOffset": -0.0011603 + }, + { + "elevationOffset": 9.1, + "lonOffset": 0.0003834, + "timeOffset": 11, + "latOffset": -0.0023425 + } + ] + }, + "pathPrediction": { + "confidence": 80, + "radiusOfCurve": -1622.5 + } + } + } + ], + "coreData": { + "secMark": 13253, + "transmission": "UNAVAILABLE", + "size": { + "width": 180, + "length": 480 + }, + "heading": 347, + "accelSet": { + "accelYaw": -0.65, + "accelLong": -0.05 + }, + "brakes": { + "scs": "unavailable", + "wheelBrakes": { + "leftFront": false, + "rightFront": false, + "unavailable": true, + "leftRear": false, + "rightRear": false + }, + "abs": "off", + "traction": "unavailable", + "brakeBoost": "unavailable", + "auxBrakes": "unavailable" + }, + "accuracy": { + "semiMinor": 2, + "orientation": 0, + "semiMajor": 2 + }, + "id": "11AF9993", + "position": { + "elevation": 1691.9, + "latitude": 39.58755633199964, + "longitude": -105.09069429121115 + }, + "msgCnt": 10, + "speed": 29.49 + } + }, + "dataType": "us.dot.its.jpo.ode.plugin.j2735.J2735Bsm" + }, + "recordGeneratedAt": { + "$date": { + "$numberLong": "1717518193257" + } + } +},{ + "_id": { + "$oid": "665f3fc08c25216ef4f53dcf" + }, + "metadata": { + "securityResultCode": "success", + "bsmSource": "RV", + "schemaVersion": 6, + "odePacketID": "", + "sanitized": false, + "recordType": "bsmTx", + "recordGeneratedAt": "", + "maxDurationTime": 0, + "odeTimStartDateTime": "", + "receivedMessageDetails": { + "locationData": { + "elevation": "", + "heading": "", + "latitude": "", + "speed": "", + "longitude": "" + }, + "rxSource": "RV" + }, + "payloadType": "us.dot.its.jpo.ode.model.OdeBsmPayload", + "serialId": { + "recordId": 0, + "serialNumber": 0, + "streamId": "849c3ac6-4747-426f-ae5e-28ac8531b32c", + "bundleSize": 1, + "bundleId": 0 + }, + "logFileName": "", + "odeReceivedAt": "2024-06-04T16:23:13.359Z", + "originIp": "10.11.81.12" + }, + "payload": { + "data": { + "partII": [ + { + "id": "VehicleSafetyExtensions", + "value": { + "pathHistory": { + "crumbData": [ + { + "elevationOffset": 0.6, + "lonOffset": 0.0000387, + "timeOffset": 0.8, + "latOffset": -0.0001604 + }, + { + "elevationOffset": 4.3, + "lonOffset": 0.000231, + "timeOffset": 5.6, + "latOffset": -0.0011603 + }, + { + "elevationOffset": 9.1, + "lonOffset": 0.0003834, + "timeOffset": 11, + "latOffset": -0.0023425 + } + ] + }, + "pathPrediction": { + "confidence": 80, + "radiusOfCurve": -1622.5 + } + } + } + ], + "coreData": { + "secMark": 13359, + "transmission": "UNAVAILABLE", + "size": { + "width": 180, + "length": 480 + }, + "heading": 347.2, + "accelSet": { + "accelYaw": -0.65, + "accelLong": -0.05 + }, + "brakes": { + "scs": "unavailable", + "wheelBrakes": { + "leftFront": false, + "rightFront": false, + "unavailable": true, + "leftRear": false, + "rightRear": false + }, + "abs": "off", + "traction": "unavailable", + "brakeBoost": "unavailable", + "auxBrakes": "unavailable" + }, + "accuracy": { + "semiMinor": 2, + "orientation": 0, + "semiMajor": 2 + }, + "id": "11AF9993", + "position": { + "elevation": 1691.9, + "latitude": 39.58758578743928, + "longitude": -105.09070300981843 + }, + "msgCnt": 11, + "speed": 33.59 + } + }, + "dataType": "us.dot.its.jpo.ode.plugin.j2735.J2735Bsm" + }, + "recordGeneratedAt": { + "$date": { + "$numberLong": "1717518193365" + } + } +},{ + "_id": { + "$oid": "665f3fc08c25216ef4f53dd0" + }, + "metadata": { + "securityResultCode": "success", + "bsmSource": "RV", + "schemaVersion": 6, + "odePacketID": "", + "sanitized": false, + "recordType": "bsmTx", + "recordGeneratedAt": "", + "maxDurationTime": 0, + "odeTimStartDateTime": "", + "receivedMessageDetails": { + "locationData": { + "elevation": "", + "heading": "", + "latitude": "", + "speed": "", + "longitude": "" + }, + "rxSource": "RV" + }, + "payloadType": "us.dot.its.jpo.ode.model.OdeBsmPayload", + "serialId": { + "recordId": 0, + "serialNumber": 0, + "streamId": "849c3ac6-4747-426f-ae5e-28ac8531b32c", + "bundleSize": 1, + "bundleId": 0 + }, + "logFileName": "", + "odeReceivedAt": "2024-06-04T16:23:13.467Z", + "originIp": "10.11.81.12" + }, + "payload": { + "data": { + "partII": [ + { + "id": "VehicleSafetyExtensions", + "value": { + "pathHistory": { + "crumbData": [ + { + "elevationOffset": 0.6, + "lonOffset": 0.0000387, + "timeOffset": 0.8, + "latOffset": -0.0001604 + }, + { + "elevationOffset": 4.3, + "lonOffset": 0.000231, + "timeOffset": 5.6, + "latOffset": -0.0011603 + }, + { + "elevationOffset": 9.1, + "lonOffset": 0.0003834, + "timeOffset": 11, + "latOffset": -0.0023425 + } + ] + }, + "pathPrediction": { + "confidence": 80, + "radiusOfCurve": -1622.5 + } + } + } + ], + "coreData": { + "secMark": 13467, + "transmission": "UNAVAILABLE", + "size": { + "width": 180, + "length": 480 + }, + "heading": 345.2, + "accelSet": { + "accelYaw": -0.65, + "accelLong": -0.05 + }, + "brakes": { + "scs": "unavailable", + "wheelBrakes": { + "leftFront": false, + "rightFront": false, + "unavailable": true, + "leftRear": false, + "rightRear": false + }, + "abs": "off", + "traction": "unavailable", + "brakeBoost": "unavailable", + "auxBrakes": "unavailable" + }, + "accuracy": { + "semiMinor": 2, + "orientation": 0, + "semiMajor": 2 + }, + "id": "11AF9993", + "position": { + "elevation": 1691.9, + "latitude": 39.587613175819314, + "longitude": -105.09071239908775 + }, + "msgCnt": 12, + "speed": 31.5 + } + }, + "dataType": "us.dot.its.jpo.ode.plugin.j2735.J2735Bsm" + }, + "recordGeneratedAt": { + "$date": { + "$numberLong": "1717518193473" + } + } +},{ + "_id": { + "$oid": "665f3fc08c25216ef4f53dd1" + }, + "metadata": { + "securityResultCode": "success", + "bsmSource": "RV", + "schemaVersion": 6, + "odePacketID": "", + "sanitized": false, + "recordType": "bsmTx", + "recordGeneratedAt": "", + "maxDurationTime": 0, + "odeTimStartDateTime": "", + "receivedMessageDetails": { + "locationData": { + "elevation": "", + "heading": "", + "latitude": "", + "speed": "", + "longitude": "" + }, + "rxSource": "RV" + }, + "payloadType": "us.dot.its.jpo.ode.model.OdeBsmPayload", + "serialId": { + "recordId": 0, + "serialNumber": 0, + "streamId": "849c3ac6-4747-426f-ae5e-28ac8531b32c", + "bundleSize": 1, + "bundleId": 0 + }, + "logFileName": "", + "odeReceivedAt": "2024-06-04T16:23:13.574Z", + "originIp": "10.11.81.12" + }, + "payload": { + "data": { + "partII": [ + { + "id": "VehicleSafetyExtensions", + "value": { + "pathHistory": { + "crumbData": [ + { + "elevationOffset": 0.6, + "lonOffset": 0.0000387, + "timeOffset": 0.8, + "latOffset": -0.0001604 + }, + { + "elevationOffset": 4.3, + "lonOffset": 0.000231, + "timeOffset": 5.6, + "latOffset": -0.0011603 + }, + { + "elevationOffset": 9.1, + "lonOffset": 0.0003834, + "timeOffset": 11, + "latOffset": -0.0023425 + } + ] + }, + "pathPrediction": { + "confidence": 80, + "radiusOfCurve": -1622.5 + } + } + } + ], + "coreData": { + "secMark": 13574, + "transmission": "UNAVAILABLE", + "size": { + "width": 180, + "length": 480 + }, + "heading": 346.2, + "accelSet": { + "accelYaw": -0.65, + "accelLong": -0.05 + }, + "brakes": { + "scs": "unavailable", + "wheelBrakes": { + "leftFront": false, + "rightFront": false, + "unavailable": true, + "leftRear": false, + "rightRear": false + }, + "abs": "off", + "traction": "unavailable", + "brakeBoost": "unavailable", + "auxBrakes": "unavailable" + }, + "accuracy": { + "semiMinor": 2, + "orientation": 0, + "semiMajor": 2 + }, + "id": "11AF9993", + "position": { + "elevation": 1691.9, + "latitude": 39.587642631234786, + "longitude": -105.09072178835711 + }, + "msgCnt": 13, + "speed": 33.73 + } + }, + "dataType": "us.dot.its.jpo.ode.plugin.j2735.J2735Bsm" + }, + "recordGeneratedAt": { + "$date": { + "$numberLong": "1717518193580" + } + } +},{ + "_id": { + "$oid": "665f3fc08c25216ef4f53dd2" + }, + "metadata": { + "securityResultCode": "success", + "bsmSource": "RV", + "schemaVersion": 6, + "odePacketID": "", + "sanitized": false, + "recordType": "bsmTx", + "recordGeneratedAt": "", + "maxDurationTime": 0, + "odeTimStartDateTime": "", + "receivedMessageDetails": { + "locationData": { + "elevation": "", + "heading": "", + "latitude": "", + "speed": "", + "longitude": "" + }, + "rxSource": "RV" + }, + "payloadType": "us.dot.its.jpo.ode.model.OdeBsmPayload", + "serialId": { + "recordId": 0, + "serialNumber": 0, + "streamId": "849c3ac6-4747-426f-ae5e-28ac8531b32c", + "bundleSize": 1, + "bundleId": 0 + }, + "logFileName": "", + "odeReceivedAt": "2024-06-04T16:23:13.681Z", + "originIp": "10.11.81.12" + }, + "payload": { + "data": { + "partII": [ + { + "id": "VehicleSafetyExtensions", + "value": { + "pathHistory": { + "crumbData": [ + { + "elevationOffset": 0.6, + "lonOffset": 0.0000387, + "timeOffset": 0.8, + "latOffset": -0.0001604 + }, + { + "elevationOffset": 4.3, + "lonOffset": 0.000231, + "timeOffset": 5.6, + "latOffset": -0.0011603 + }, + { + "elevationOffset": 9.1, + "lonOffset": 0.0003834, + "timeOffset": 11, + "latOffset": -0.0023425 + } + ] + }, + "pathPrediction": { + "confidence": 80, + "radiusOfCurve": -1622.5 + } + } + } + ], + "coreData": { + "secMark": 13681, + "transmission": "UNAVAILABLE", + "size": { + "width": 180, + "length": 480 + }, + "heading": 345.6, + "accelSet": { + "accelYaw": -0.65, + "accelLong": -0.05 + }, + "brakes": { + "scs": "unavailable", + "wheelBrakes": { + "leftFront": false, + "rightFront": false, + "unavailable": true, + "leftRear": false, + "rightRear": false + }, + "abs": "off", + "traction": "unavailable", + "brakeBoost": "unavailable", + "auxBrakes": "unavailable" + }, + "accuracy": { + "semiMinor": 2, + "orientation": 0, + "semiMajor": 2 + }, + "id": "11AF9993", + "position": { + "elevation": 1691.9, + "latitude": 39.58767673748972, + "longitude": -105.09073318961273 + }, + "msgCnt": 14, + "speed": 39.16 + } + }, + "dataType": "us.dot.its.jpo.ode.plugin.j2735.J2735Bsm" + }, + "recordGeneratedAt": { + "$date": { + "$numberLong": "1717518193687" + } + } +},{ + "_id": { + "$oid": "665f3fc08c25216ef4f53dd3" + }, + "metadata": { + "securityResultCode": "success", + "bsmSource": "RV", + "schemaVersion": 6, + "odePacketID": "", + "sanitized": false, + "recordType": "bsmTx", + "recordGeneratedAt": "", + "maxDurationTime": 0, + "odeTimStartDateTime": "", + "receivedMessageDetails": { + "locationData": { + "elevation": "", + "heading": "", + "latitude": "", + "speed": "", + "longitude": "" + }, + "rxSource": "RV" + }, + "payloadType": "us.dot.its.jpo.ode.model.OdeBsmPayload", + "serialId": { + "recordId": 0, + "serialNumber": 0, + "streamId": "849c3ac6-4747-426f-ae5e-28ac8531b32c", + "bundleSize": 1, + "bundleId": 0 + }, + "logFileName": "", + "odeReceivedAt": "2024-06-04T16:23:13.788Z", + "originIp": "10.11.81.12" + }, + "payload": { + "data": { + "partII": [ + { + "id": "VehicleSafetyExtensions", + "value": { + "pathHistory": { + "crumbData": [ + { + "elevationOffset": 0.6, + "lonOffset": 0.0000387, + "timeOffset": 0.8, + "latOffset": -0.0001604 + }, + { + "elevationOffset": 4.3, + "lonOffset": 0.000231, + "timeOffset": 5.6, + "latOffset": -0.0011603 + }, + { + "elevationOffset": 9.1, + "lonOffset": 0.0003834, + "timeOffset": 11, + "latOffset": -0.0023425 + } + ] + }, + "pathPrediction": { + "confidence": 80, + "radiusOfCurve": -1622.5 + } + } + } + ], + "coreData": { + "secMark": 13788, + "transmission": "UNAVAILABLE", + "size": { + "width": 180, + "length": 480 + }, + "heading": 350, + "accelSet": { + "accelYaw": -0.65, + "accelLong": -0.05 + }, + "brakes": { + "scs": "unavailable", + "wheelBrakes": { + "leftFront": false, + "rightFront": false, + "unavailable": true, + "leftRear": false, + "rightRear": false + }, + "abs": "off", + "traction": "unavailable", + "brakeBoost": "unavailable", + "auxBrakes": "unavailable" + }, + "accuracy": { + "semiMinor": 2, + "orientation": 0, + "semiMajor": 2 + }, + "id": "11AF9993", + "position": { + "elevation": 1691.9, + "latitude": 39.58769430737206, + "longitude": -105.09073721358531 + }, + "msgCnt": 15, + "speed": 19.84 + } + }, + "dataType": "us.dot.its.jpo.ode.plugin.j2735.J2735Bsm" + }, + "recordGeneratedAt": { + "$date": { + "$numberLong": "1717518193795" + } + } +},{ + "_id": { + "$oid": "665f3fc08c25216ef4f53dd4" + }, + "metadata": { + "securityResultCode": "success", + "bsmSource": "RV", + "schemaVersion": 6, + "odePacketID": "", + "sanitized": false, + "recordType": "bsmTx", + "recordGeneratedAt": "", + "maxDurationTime": 0, + "odeTimStartDateTime": "", + "receivedMessageDetails": { + "locationData": { + "elevation": "", + "heading": "", + "latitude": "", + "speed": "", + "longitude": "" + }, + "rxSource": "RV" + }, + "payloadType": "us.dot.its.jpo.ode.model.OdeBsmPayload", + "serialId": { + "recordId": 0, + "serialNumber": 0, + "streamId": "849c3ac6-4747-426f-ae5e-28ac8531b32c", + "bundleSize": 1, + "bundleId": 0 + }, + "logFileName": "", + "odeReceivedAt": "2024-06-04T16:23:13.896Z", + "originIp": "10.11.81.12" + }, + "payload": { + "data": { + "partII": [ + { + "id": "VehicleSafetyExtensions", + "value": { + "pathHistory": { + "crumbData": [ + { + "elevationOffset": 0.6, + "lonOffset": 0.0000387, + "timeOffset": 0.8, + "latOffset": -0.0001604 + }, + { + "elevationOffset": 4.3, + "lonOffset": 0.000231, + "timeOffset": 5.6, + "latOffset": -0.0011603 + }, + { + "elevationOffset": 9.1, + "lonOffset": 0.0003834, + "timeOffset": 11, + "latOffset": -0.0023425 + } + ] + }, + "pathPrediction": { + "confidence": 80, + "radiusOfCurve": -1622.5 + } + } + } + ], + "coreData": { + "secMark": 13896, + "transmission": "UNAVAILABLE", + "size": { + "width": 180, + "length": 480 + }, + "heading": 349, + "accelSet": { + "accelYaw": -0.65, + "accelLong": -0.05 + }, + "brakes": { + "scs": "unavailable", + "wheelBrakes": { + "leftFront": false, + "rightFront": false, + "unavailable": true, + "leftRear": false, + "rightRear": false + }, + "abs": "off", + "traction": "unavailable", + "brakeBoost": "unavailable", + "auxBrakes": "unavailable" + }, + "accuracy": { + "semiMinor": 2, + "orientation": 0, + "semiMajor": 2 + }, + "id": "11AF9993", + "position": { + "elevation": 1691.9, + "latitude": 39.58771032696678, + "longitude": -105.09074123755789 + }, + "msgCnt": 16, + "speed": 18.14 + } + }, + "dataType": "us.dot.its.jpo.ode.plugin.j2735.J2735Bsm" + }, + "recordGeneratedAt": { + "$date": { + "$numberLong": "1717518193901" + } + } +},{ + "_id": { + "$oid": "665f3fc08c25216ef4f53dd5" + }, + "metadata": { + "securityResultCode": "success", + "bsmSource": "RV", + "schemaVersion": 6, + "odePacketID": "", + "sanitized": false, + "recordType": "bsmTx", + "recordGeneratedAt": "", + "maxDurationTime": 0, + "odeTimStartDateTime": "", + "receivedMessageDetails": { + "locationData": { + "elevation": "", + "heading": "", + "latitude": "", + "speed": "", + "longitude": "" + }, + "rxSource": "RV" + }, + "payloadType": "us.dot.its.jpo.ode.model.OdeBsmPayload", + "serialId": { + "recordId": 0, + "serialNumber": 0, + "streamId": "849c3ac6-4747-426f-ae5e-28ac8531b32c", + "bundleSize": 1, + "bundleId": 0 + }, + "logFileName": "", + "odeReceivedAt": "2024-06-04T16:23:14.003Z", + "originIp": "10.11.81.12" + }, + "payload": { + "data": { + "partII": [ + { + "id": "VehicleSafetyExtensions", + "value": { + "pathHistory": { + "crumbData": [ + { + "elevationOffset": 0.6, + "lonOffset": 0.0000387, + "timeOffset": 0.8, + "latOffset": -0.0001604 + }, + { + "elevationOffset": 4.3, + "lonOffset": 0.000231, + "timeOffset": 5.6, + "latOffset": -0.0011603 + }, + { + "elevationOffset": 9.1, + "lonOffset": 0.0003834, + "timeOffset": 11, + "latOffset": -0.0023425 + } + ] + }, + "pathPrediction": { + "confidence": 80, + "radiusOfCurve": -1622.5 + } + } + } + ], + "coreData": { + "secMark": 14003, + "transmission": "UNAVAILABLE", + "size": { + "width": 180, + "length": 480 + }, + "heading": 345.2, + "accelSet": { + "accelYaw": -0.65, + "accelLong": -0.05 + }, + "brakes": { + "scs": "unavailable", + "wheelBrakes": { + "leftFront": false, + "rightFront": false, + "unavailable": true, + "leftRear": false, + "rightRear": false + }, + "abs": "off", + "traction": "unavailable", + "brakeBoost": "unavailable", + "auxBrakes": "unavailable" + }, + "accuracy": { + "semiMinor": 2, + "orientation": 0, + "semiMajor": 2 + }, + "id": "11AF9993", + "position": { + "elevation": 1691.9, + "latitude": 39.58772789684062, + "longitude": -105.09074727351678 + }, + "msgCnt": 17, + "speed": 20.21 + } + }, + "dataType": "us.dot.its.jpo.ode.plugin.j2735.J2735Bsm" + }, + "recordGeneratedAt": { + "$date": { + "$numberLong": "1717518194009" + } + } +},{ + "_id": { + "$oid": "665f3fc08c25216ef4f53dd6" + }, + "metadata": { + "securityResultCode": "success", + "bsmSource": "RV", + "schemaVersion": 6, + "odePacketID": "", + "sanitized": false, + "recordType": "bsmTx", + "recordGeneratedAt": "", + "maxDurationTime": 0, + "odeTimStartDateTime": "", + "receivedMessageDetails": { + "locationData": { + "elevation": "", + "heading": "", + "latitude": "", + "speed": "", + "longitude": "" + }, + "rxSource": "RV" + }, + "payloadType": "us.dot.its.jpo.ode.model.OdeBsmPayload", + "serialId": { + "recordId": 0, + "serialNumber": 0, + "streamId": "849c3ac6-4747-426f-ae5e-28ac8531b32c", + "bundleSize": 1, + "bundleId": 0 + }, + "logFileName": "", + "odeReceivedAt": "2024-06-04T16:23:14.11Z", + "originIp": "10.11.81.12" + }, + "payload": { + "data": { + "partII": [ + { + "id": "VehicleSafetyExtensions", + "value": { + "pathHistory": { + "crumbData": [ + { + "elevationOffset": 0.6, + "lonOffset": 0.0000387, + "timeOffset": 0.8, + "latOffset": -0.0001604 + }, + { + "elevationOffset": 4.3, + "lonOffset": 0.000231, + "timeOffset": 5.6, + "latOffset": -0.0011603 + }, + { + "elevationOffset": 9.1, + "lonOffset": 0.0003834, + "timeOffset": 11, + "latOffset": -0.0023425 + } + ] + }, + "pathPrediction": { + "confidence": 80, + "radiusOfCurve": -1622.5 + } + } + } + ], + "coreData": { + "secMark": 14110, + "transmission": "UNAVAILABLE", + "size": { + "width": 180, + "length": 480 + }, + "heading": 346.3, + "accelSet": { + "accelYaw": -0.65, + "accelLong": -0.05 + }, + "brakes": { + "scs": "unavailable", + "wheelBrakes": { + "leftFront": false, + "rightFront": false, + "unavailable": true, + "leftRear": false, + "rightRear": false + }, + "abs": "off", + "traction": "unavailable", + "brakeBoost": "unavailable", + "auxBrakes": "unavailable" + }, + "accuracy": { + "semiMinor": 2, + "orientation": 0, + "semiMajor": 2 + }, + "id": "11AF9993", + "position": { + "elevation": 1691.9, + "latitude": 39.58774908403548, + "longitude": -105.09075398013772 + }, + "msgCnt": 18, + "speed": 24.25 + } + }, + "dataType": "us.dot.its.jpo.ode.plugin.j2735.J2735Bsm" + }, + "recordGeneratedAt": { + "$date": { + "$numberLong": "1717518194116" + } + } +},{ + "_id": { + "$oid": "665f3fc08c25216ef4f53dd7" + }, + "metadata": { + "securityResultCode": "success", + "bsmSource": "RV", + "schemaVersion": 6, + "odePacketID": "", + "sanitized": false, + "recordType": "bsmTx", + "recordGeneratedAt": "", + "maxDurationTime": 0, + "odeTimStartDateTime": "", + "receivedMessageDetails": { + "locationData": { + "elevation": "", + "heading": "", + "latitude": "", + "speed": "", + "longitude": "" + }, + "rxSource": "RV" + }, + "payloadType": "us.dot.its.jpo.ode.model.OdeBsmPayload", + "serialId": { + "recordId": 0, + "serialNumber": 0, + "streamId": "849c3ac6-4747-426f-ae5e-28ac8531b32c", + "bundleSize": 1, + "bundleId": 0 + }, + "logFileName": "", + "odeReceivedAt": "2024-06-04T16:23:14.218Z", + "originIp": "10.11.81.12" + }, + "payload": { + "data": { + "partII": [ + { + "id": "VehicleSafetyExtensions", + "value": { + "pathHistory": { + "crumbData": [ + { + "elevationOffset": 0.6, + "lonOffset": 0.0000387, + "timeOffset": 0.8, + "latOffset": -0.0001604 + }, + { + "elevationOffset": 4.3, + "lonOffset": 0.000231, + "timeOffset": 5.6, + "latOffset": -0.0011603 + }, + { + "elevationOffset": 9.1, + "lonOffset": 0.0003834, + "timeOffset": 11, + "latOffset": -0.0023425 + } + ] + }, + "pathPrediction": { + "confidence": 80, + "radiusOfCurve": -1622.5 + } + } + } + ], + "coreData": { + "secMark": 14218, + "transmission": "UNAVAILABLE", + "size": { + "width": 180, + "length": 480 + }, + "heading": 351.4, + "accelSet": { + "accelYaw": -0.65, + "accelLong": -0.05 + }, + "brakes": { + "scs": "unavailable", + "wheelBrakes": { + "leftFront": false, + "rightFront": false, + "unavailable": true, + "leftRear": false, + "rightRear": false + }, + "abs": "off", + "traction": "unavailable", + "brakeBoost": "unavailable", + "auxBrakes": "unavailable" + }, + "accuracy": { + "semiMinor": 2, + "orientation": 0, + "semiMajor": 2 + }, + "id": "11AF9993", + "position": { + "elevation": 1691.9, + "latitude": 39.58776613713885, + "longitude": -105.0907573334482 + }, + "msgCnt": 19, + "speed": 19.18 + } + }, + "dataType": "us.dot.its.jpo.ode.plugin.j2735.J2735Bsm" + }, + "recordGeneratedAt": { + "$date": { + "$numberLong": "1717518194220" + } + } +},{ + "_id": { + "$oid": "665f3fc08c25216ef4f53dd8" + }, + "metadata": { + "securityResultCode": "success", + "bsmSource": "RV", + "schemaVersion": 6, + "odePacketID": "", + "sanitized": false, + "recordType": "bsmTx", + "recordGeneratedAt": "", + "maxDurationTime": 0, + "odeTimStartDateTime": "", + "receivedMessageDetails": { + "locationData": { + "elevation": "", + "heading": "", + "latitude": "", + "speed": "", + "longitude": "" + }, + "rxSource": "RV" + }, + "payloadType": "us.dot.its.jpo.ode.model.OdeBsmPayload", + "serialId": { + "recordId": 0, + "serialNumber": 0, + "streamId": "849c3ac6-4747-426f-ae5e-28ac8531b32c", + "bundleSize": 1, + "bundleId": 0 + }, + "logFileName": "", + "odeReceivedAt": "2024-06-04T16:23:14.325Z", + "originIp": "10.11.81.12" + }, + "payload": { + "data": { + "partII": [ + { + "id": "VehicleSafetyExtensions", + "value": { + "pathHistory": { + "crumbData": [ + { + "elevationOffset": 0.6, + "lonOffset": 0.0000387, + "timeOffset": 0.8, + "latOffset": -0.0001604 + }, + { + "elevationOffset": 4.3, + "lonOffset": 0.000231, + "timeOffset": 5.6, + "latOffset": -0.0011603 + }, + { + "elevationOffset": 9.1, + "lonOffset": 0.0003834, + "timeOffset": 11, + "latOffset": -0.0023425 + } + ] + }, + "pathPrediction": { + "confidence": 80, + "radiusOfCurve": -1622.5 + } + } + } + ], + "coreData": { + "secMark": 14325, + "transmission": "UNAVAILABLE", + "size": { + "width": 180, + "length": 480 + }, + "heading": 346.7, + "accelSet": { + "accelYaw": -0.65, + "accelLong": -0.05 + }, + "brakes": { + "scs": "unavailable", + "wheelBrakes": { + "leftFront": false, + "rightFront": false, + "unavailable": true, + "leftRear": false, + "rightRear": false + }, + "abs": "off", + "traction": "unavailable", + "brakeBoost": "unavailable", + "auxBrakes": "unavailable" + }, + "accuracy": { + "semiMinor": 2, + "orientation": 0, + "semiMajor": 2 + }, + "id": "11AF9993", + "position": { + "elevation": 1691.9, + "latitude": 39.587785774040526, + "longitude": -105.09076336940707 + }, + "msgCnt": 20, + "speed": 22.44 + } + }, + "dataType": "us.dot.its.jpo.ode.plugin.j2735.J2735Bsm" + }, + "recordGeneratedAt": { + "$date": { + "$numberLong": "1717518194328" + } + } +},{ + "_id": { + "$oid": "665f3fc08c25216ef4f53dd9" + }, + "metadata": { + "securityResultCode": "success", + "bsmSource": "RV", + "schemaVersion": 6, + "odePacketID": "", + "sanitized": false, + "recordType": "bsmTx", + "recordGeneratedAt": "", + "maxDurationTime": 0, + "odeTimStartDateTime": "", + "receivedMessageDetails": { + "locationData": { + "elevation": "", + "heading": "", + "latitude": "", + "speed": "", + "longitude": "" + }, + "rxSource": "RV" + }, + "payloadType": "us.dot.its.jpo.ode.model.OdeBsmPayload", + "serialId": { + "recordId": 0, + "serialNumber": 0, + "streamId": "849c3ac6-4747-426f-ae5e-28ac8531b32c", + "bundleSize": 1, + "bundleId": 0 + }, + "logFileName": "", + "odeReceivedAt": "2024-06-04T16:23:14.432Z", + "originIp": "10.11.81.12" + }, + "payload": { + "data": { + "partII": [ + { + "id": "VehicleSafetyExtensions", + "value": { + "pathHistory": { + "crumbData": [ + { + "elevationOffset": 0.6, + "lonOffset": 0.0000387, + "timeOffset": 0.8, + "latOffset": -0.0001604 + }, + { + "elevationOffset": 4.3, + "lonOffset": 0.000231, + "timeOffset": 5.6, + "latOffset": -0.0011603 + }, + { + "elevationOffset": 9.1, + "lonOffset": 0.0003834, + "timeOffset": 11, + "latOffset": -0.0023425 + } + ] + }, + "pathPrediction": { + "confidence": 80, + "radiusOfCurve": -1622.5 + } + } + } + ], + "coreData": { + "secMark": 14432, + "transmission": "UNAVAILABLE", + "size": { + "width": 180, + "length": 480 + }, + "heading": 347.5, + "accelSet": { + "accelYaw": -0.65, + "accelLong": -0.05 + }, + "brakes": { + "scs": "unavailable", + "wheelBrakes": { + "leftFront": false, + "rightFront": false, + "unavailable": true, + "leftRear": false, + "rightRear": false + }, + "abs": "off", + "traction": "unavailable", + "brakeBoost": "unavailable", + "auxBrakes": "unavailable" + }, + "accuracy": { + "semiMinor": 2, + "orientation": 0, + "semiMajor": 2 + }, + "id": "11AF9993", + "position": { + "elevation": 1691.9, + "latitude": 39.587804377415964, + "longitude": -105.09076873470384 + }, + "msgCnt": 21, + "speed": 21.19 + } + }, + "dataType": "us.dot.its.jpo.ode.plugin.j2735.J2735Bsm" + }, + "recordGeneratedAt": { + "$date": { + "$numberLong": "1717518194435" + } + } +},{ + "_id": { + "$oid": "665f3fc08c25216ef4f53dda" + }, + "metadata": { + "securityResultCode": "success", + "bsmSource": "RV", + "schemaVersion": 6, + "odePacketID": "", + "sanitized": false, + "recordType": "bsmTx", + "recordGeneratedAt": "", + "maxDurationTime": 0, + "odeTimStartDateTime": "", + "receivedMessageDetails": { + "locationData": { + "elevation": "", + "heading": "", + "latitude": "", + "speed": "", + "longitude": "" + }, + "rxSource": "RV" + }, + "payloadType": "us.dot.its.jpo.ode.model.OdeBsmPayload", + "serialId": { + "recordId": 0, + "serialNumber": 0, + "streamId": "849c3ac6-4747-426f-ae5e-28ac8531b32c", + "bundleSize": 1, + "bundleId": 0 + }, + "logFileName": "", + "odeReceivedAt": "2024-06-04T16:23:14.54Z", + "originIp": "10.11.81.12" + }, + "payload": { + "data": { + "partII": [ + { + "id": "VehicleSafetyExtensions", + "value": { + "pathHistory": { + "crumbData": [ + { + "elevationOffset": 0.6, + "lonOffset": 0.0000387, + "timeOffset": 0.8, + "latOffset": -0.0001604 + }, + { + "elevationOffset": 4.3, + "lonOffset": 0.000231, + "timeOffset": 5.6, + "latOffset": -0.0011603 + }, + { + "elevationOffset": 9.1, + "lonOffset": 0.0003834, + "timeOffset": 11, + "latOffset": -0.0023425 + } + ] + }, + "pathPrediction": { + "confidence": 80, + "radiusOfCurve": -1622.5 + } + } + } + ], + "coreData": { + "secMark": 14540, + "transmission": "UNAVAILABLE", + "size": { + "width": 180, + "length": 480 + }, + "heading": 349, + "accelSet": { + "accelYaw": -0.65, + "accelLong": -0.05 + }, + "brakes": { + "scs": "unavailable", + "wheelBrakes": { + "leftFront": false, + "rightFront": false, + "unavailable": true, + "leftRear": false, + "rightRear": false + }, + "abs": "off", + "traction": "unavailable", + "brakeBoost": "unavailable", + "auxBrakes": "unavailable" + }, + "accuracy": { + "semiMinor": 2, + "orientation": 0, + "semiMajor": 2 + }, + "id": "11AF9993", + "position": { + "elevation": 1691.9, + "latitude": 39.587820396985244, + "longitude": -105.09077275867644 + }, + "msgCnt": 22, + "speed": 18.14 + } + }, + "dataType": "us.dot.its.jpo.ode.plugin.j2735.J2735Bsm" + }, + "recordGeneratedAt": { + "$date": { + "$numberLong": "1717518194542" + } + } +},{ + "_id": { + "$oid": "665f3fc08c25216ef4f53ddb" + }, + "metadata": { + "securityResultCode": "success", + "bsmSource": "RV", + "schemaVersion": 6, + "odePacketID": "", + "sanitized": false, + "recordType": "bsmTx", + "recordGeneratedAt": "", + "maxDurationTime": 0, + "odeTimStartDateTime": "", + "receivedMessageDetails": { + "locationData": { + "elevation": "", + "heading": "", + "latitude": "", + "speed": "", + "longitude": "" + }, + "rxSource": "RV" + }, + "payloadType": "us.dot.its.jpo.ode.model.OdeBsmPayload", + "serialId": { + "recordId": 0, + "serialNumber": 0, + "streamId": "849c3ac6-4747-426f-ae5e-28ac8531b32c", + "bundleSize": 1, + "bundleId": 0 + }, + "logFileName": "", + "odeReceivedAt": "2024-06-04T16:23:14.648Z", + "originIp": "10.11.81.12" + }, + "payload": { + "data": { + "partII": [ + { + "id": "VehicleSafetyExtensions", + "value": { + "pathHistory": { + "crumbData": [ + { + "elevationOffset": 0.6, + "lonOffset": 0.0000387, + "timeOffset": 0.8, + "latOffset": -0.0001604 + }, + { + "elevationOffset": 4.3, + "lonOffset": 0.000231, + "timeOffset": 5.6, + "latOffset": -0.0011603 + }, + { + "elevationOffset": 9.1, + "lonOffset": 0.0003834, + "timeOffset": 11, + "latOffset": -0.0023425 + } + ] + }, + "pathPrediction": { + "confidence": 80, + "radiusOfCurve": -1622.5 + } + } + } + ], + "coreData": { + "secMark": 14648, + "transmission": "UNAVAILABLE", + "size": { + "width": 180, + "length": 480 + }, + "heading": 349.7, + "accelSet": { + "accelYaw": -0.65, + "accelLong": -0.05 + }, + "brakes": { + "scs": "unavailable", + "wheelBrakes": { + "leftFront": false, + "rightFront": false, + "unavailable": true, + "leftRear": false, + "rightRear": false + }, + "abs": "off", + "traction": "unavailable", + "brakeBoost": "unavailable", + "auxBrakes": "unavailable" + }, + "accuracy": { + "semiMinor": 2, + "orientation": 0, + "semiMajor": 2 + }, + "id": "11AF9993", + "position": { + "elevation": 1691.9, + "latitude": 39.587837450071035, + "longitude": -105.09077678264897 + }, + "msgCnt": 23, + "speed": 19.27 + } + }, + "dataType": "us.dot.its.jpo.ode.plugin.j2735.J2735Bsm" + }, + "recordGeneratedAt": { + "$date": { + "$numberLong": "1717518194650" + } + } +},{ + "_id": { + "$oid": "665f3fc08c25216ef4f53ddc" + }, + "metadata": { + "securityResultCode": "success", + "bsmSource": "RV", + "schemaVersion": 6, + "odePacketID": "", + "sanitized": false, + "recordType": "bsmTx", + "recordGeneratedAt": "", + "maxDurationTime": 0, + "odeTimStartDateTime": "", + "receivedMessageDetails": { + "locationData": { + "elevation": "", + "heading": "", + "latitude": "", + "speed": "", + "longitude": "" + }, + "rxSource": "RV" + }, + "payloadType": "us.dot.its.jpo.ode.model.OdeBsmPayload", + "serialId": { + "recordId": 0, + "serialNumber": 0, + "streamId": "849c3ac6-4747-426f-ae5e-28ac8531b32c", + "bundleSize": 1, + "bundleId": 0 + }, + "logFileName": "", + "odeReceivedAt": "2024-06-04T16:23:14.753Z", + "originIp": "10.11.81.12" + }, + "payload": { + "data": { + "partII": [ + { + "id": "VehicleSafetyExtensions", + "value": { + "pathHistory": { + "crumbData": [ + { + "elevationOffset": 0.6, + "lonOffset": 0.0000387, + "timeOffset": 0.8, + "latOffset": -0.0001604 + }, + { + "elevationOffset": 4.3, + "lonOffset": 0.000231, + "timeOffset": 5.6, + "latOffset": -0.0011603 + }, + { + "elevationOffset": 9.1, + "lonOffset": 0.0003834, + "timeOffset": 11, + "latOffset": -0.0023425 + } + ] + }, + "pathPrediction": { + "confidence": 80, + "radiusOfCurve": -1622.5 + } + } + } + ], + "coreData": { + "secMark": 14753, + "transmission": "UNAVAILABLE", + "size": { + "width": 180, + "length": 480 + }, + "heading": 349.7, + "accelSet": { + "accelYaw": -0.65, + "accelLong": -0.05 + }, + "brakes": { + "scs": "unavailable", + "wheelBrakes": { + "leftFront": false, + "rightFront": false, + "unavailable": true, + "leftRear": false, + "rightRear": false + }, + "abs": "off", + "traction": "unavailable", + "brakeBoost": "unavailable", + "auxBrakes": "unavailable" + }, + "accuracy": { + "semiMinor": 2, + "orientation": 0, + "semiMajor": 2 + }, + "id": "11AF9993", + "position": { + "elevation": 1691.9, + "latitude": 39.58785450315265, + "longitude": -105.0907808066216 + }, + "msgCnt": 24, + "speed": 19.27 + } + }, + "dataType": "us.dot.its.jpo.ode.plugin.j2735.J2735Bsm" + }, + "recordGeneratedAt": { + "$date": { + "$numberLong": "1717518194756" + } + } +},{ + "_id": { + "$oid": "665f3fc08c25216ef4f53ddd" + }, + "metadata": { + "securityResultCode": "success", + "bsmSource": "RV", + "schemaVersion": 6, + "odePacketID": "", + "sanitized": false, + "recordType": "bsmTx", + "recordGeneratedAt": "", + "maxDurationTime": 0, + "odeTimStartDateTime": "", + "receivedMessageDetails": { + "locationData": { + "elevation": "", + "heading": "", + "latitude": "", + "speed": "", + "longitude": "" + }, + "rxSource": "RV" + }, + "payloadType": "us.dot.its.jpo.ode.model.OdeBsmPayload", + "serialId": { + "recordId": 0, + "serialNumber": 0, + "streamId": "849c3ac6-4747-426f-ae5e-28ac8531b32c", + "bundleSize": 1, + "bundleId": 0 + }, + "logFileName": "", + "odeReceivedAt": "2024-06-04T16:23:14.862Z", + "originIp": "10.11.81.12" + }, + "payload": { + "data": { + "partII": [ + { + "id": "VehicleSafetyExtensions", + "value": { + "pathHistory": { + "crumbData": [ + { + "elevationOffset": 0.6, + "lonOffset": 0.0000387, + "timeOffset": 0.8, + "latOffset": -0.0001604 + }, + { + "elevationOffset": 4.3, + "lonOffset": 0.000231, + "timeOffset": 5.6, + "latOffset": -0.0011603 + }, + { + "elevationOffset": 9.1, + "lonOffset": 0.0003834, + "timeOffset": 11, + "latOffset": -0.0023425 + } + ] + }, + "pathPrediction": { + "confidence": 80, + "radiusOfCurve": -1622.5 + } + } + } + ], + "coreData": { + "secMark": 14862, + "transmission": "UNAVAILABLE", + "size": { + "width": 180, + "length": 480 + }, + "heading": 347.3, + "accelSet": { + "accelYaw": -0.65, + "accelLong": -0.05 + }, + "brakes": { + "scs": "unavailable", + "wheelBrakes": { + "leftFront": false, + "rightFront": false, + "unavailable": true, + "leftRear": false, + "rightRear": false + }, + "abs": "off", + "traction": "unavailable", + "brakeBoost": "unavailable", + "auxBrakes": "unavailable" + }, + "accuracy": { + "semiMinor": 2, + "orientation": 0, + "semiMajor": 2 + }, + "id": "11AF9993", + "position": { + "elevation": 1691.9, + "latitude": 39.587870522710325, + "longitude": -105.09078550125625 + }, + "msgCnt": 25, + "speed": 18.26 + } + }, + "dataType": "us.dot.its.jpo.ode.plugin.j2735.J2735Bsm" + }, + "recordGeneratedAt": { + "$date": { + "$numberLong": "1717518194863" + } + } +},{ + "_id": { + "$oid": "665f3fc08c25216ef4f53dde" + }, + "metadata": { + "securityResultCode": "success", + "bsmSource": "RV", + "schemaVersion": 6, + "odePacketID": "", + "sanitized": false, + "recordType": "bsmTx", + "recordGeneratedAt": "", + "maxDurationTime": 0, + "odeTimStartDateTime": "", + "receivedMessageDetails": { + "locationData": { + "elevation": "", + "heading": "", + "latitude": "", + "speed": "", + "longitude": "" + }, + "rxSource": "RV" + }, + "payloadType": "us.dot.its.jpo.ode.model.OdeBsmPayload", + "serialId": { + "recordId": 0, + "serialNumber": 0, + "streamId": "849c3ac6-4747-426f-ae5e-28ac8531b32c", + "bundleSize": 1, + "bundleId": 0 + }, + "logFileName": "", + "odeReceivedAt": "2024-06-04T16:23:14.969Z", + "originIp": "10.11.81.12" + }, + "payload": { + "data": { + "partII": [ + { + "id": "VehicleSafetyExtensions", + "value": { + "pathHistory": { + "crumbData": [ + { + "elevationOffset": 0.6, + "lonOffset": 0.0000387, + "timeOffset": 0.8, + "latOffset": -0.0001604 + }, + { + "elevationOffset": 4.3, + "lonOffset": 0.000231, + "timeOffset": 5.6, + "latOffset": -0.0011603 + }, + { + "elevationOffset": 9.1, + "lonOffset": 0.0003834, + "timeOffset": 11, + "latOffset": -0.0023425 + } + ] + }, + "pathPrediction": { + "confidence": 80, + "radiusOfCurve": -1622.5 + } + } + } + ], + "coreData": { + "secMark": 14969, + "transmission": "UNAVAILABLE", + "size": { + "width": 180, + "length": 480 + }, + "heading": 344.9, + "accelSet": { + "accelYaw": -0.65, + "accelLong": -0.05 + }, + "brakes": { + "scs": "unavailable", + "wheelBrakes": { + "leftFront": false, + "rightFront": false, + "unavailable": true, + "leftRear": false, + "rightRear": false + }, + "abs": "off", + "traction": "unavailable", + "brakeBoost": "unavailable", + "auxBrakes": "unavailable" + }, + "accuracy": { + "semiMinor": 2, + "orientation": 0, + "semiMajor": 2 + }, + "id": "11AF9993", + "position": { + "elevation": 1691.9, + "latitude": 39.587883958465525, + "longitude": -105.09079019589092 + }, + "msgCnt": 26, + "speed": 15.47 + } + }, + "dataType": "us.dot.its.jpo.ode.plugin.j2735.J2735Bsm" + }, + "recordGeneratedAt": { + "$date": { + "$numberLong": "1717518194984" + } + } +},{ + "_id": { + "$oid": "665f3fc08c25216ef4f53ddf" + }, + "metadata": { + "securityResultCode": "success", + "bsmSource": "RV", + "schemaVersion": 6, + "odePacketID": "", + "sanitized": false, + "recordType": "bsmTx", + "recordGeneratedAt": "", + "maxDurationTime": 0, + "odeTimStartDateTime": "", + "receivedMessageDetails": { + "locationData": { + "elevation": "", + "heading": "", + "latitude": "", + "speed": "", + "longitude": "" + }, + "rxSource": "RV" + }, + "payloadType": "us.dot.its.jpo.ode.model.OdeBsmPayload", + "serialId": { + "recordId": 0, + "serialNumber": 0, + "streamId": "849c3ac6-4747-426f-ae5e-28ac8531b32c", + "bundleSize": 1, + "bundleId": 0 + }, + "logFileName": "", + "odeReceivedAt": "2024-06-04T16:23:15.077Z", + "originIp": "10.11.81.12" + }, + "payload": { + "data": { + "partII": [ + { + "id": "VehicleSafetyExtensions", + "value": { + "pathHistory": { + "crumbData": [ + { + "elevationOffset": 0.6, + "lonOffset": 0.0000387, + "timeOffset": 0.8, + "latOffset": -0.0001604 + }, + { + "elevationOffset": 4.3, + "lonOffset": 0.000231, + "timeOffset": 5.6, + "latOffset": -0.0011603 + }, + { + "elevationOffset": 9.1, + "lonOffset": 0.0003834, + "timeOffset": 11, + "latOffset": -0.0023425 + } + ] + }, + "pathPrediction": { + "confidence": 80, + "radiusOfCurve": -1622.5 + } + } + } + ], + "coreData": { + "secMark": 15077, + "transmission": "UNAVAILABLE", + "size": { + "width": 180, + "length": 480 + }, + "heading": 344.5, + "accelSet": { + "accelYaw": -0.65, + "accelLong": -0.05 + }, + "brakes": { + "scs": "unavailable", + "wheelBrakes": { + "leftFront": false, + "rightFront": false, + "unavailable": true, + "leftRear": false, + "rightRear": false + }, + "abs": "off", + "traction": "unavailable", + "brakeBoost": "unavailable", + "auxBrakes": "unavailable" + }, + "accuracy": { + "semiMinor": 2, + "orientation": 0, + "semiMajor": 2 + }, + "id": "11AF9993", + "position": { + "elevation": 1691.9, + "latitude": 39.58789326014069, + "longitude": -105.09079354920141 + }, + "msgCnt": 27, + "speed": 10.73 + } + }, + "dataType": "us.dot.its.jpo.ode.plugin.j2735.J2735Bsm" + }, + "recordGeneratedAt": { + "$date": { + "$numberLong": "1717518195090" + } + } +},{ + "_id": { + "$oid": "665f3fc08c25216ef4f53de0" + }, + "metadata": { + "securityResultCode": "success", + "bsmSource": "RV", + "schemaVersion": 6, + "odePacketID": "", + "sanitized": false, + "recordType": "bsmTx", + "recordGeneratedAt": "", + "maxDurationTime": 0, + "odeTimStartDateTime": "", + "receivedMessageDetails": { + "locationData": { + "elevation": "", + "heading": "", + "latitude": "", + "speed": "", + "longitude": "" + }, + "rxSource": "RV" + }, + "payloadType": "us.dot.its.jpo.ode.model.OdeBsmPayload", + "serialId": { + "recordId": 0, + "serialNumber": 0, + "streamId": "849c3ac6-4747-426f-ae5e-28ac8531b32c", + "bundleSize": 1, + "bundleId": 0 + }, + "logFileName": "", + "odeReceivedAt": "2024-06-04T16:23:15.186Z", + "originIp": "10.11.81.12" + }, + "payload": { + "data": { + "partII": [ + { + "id": "VehicleSafetyExtensions", + "value": { + "pathHistory": { + "crumbData": [ + { + "elevationOffset": 0.6, + "lonOffset": 0.0000387, + "timeOffset": 0.8, + "latOffset": -0.0001604 + }, + { + "elevationOffset": 4.3, + "lonOffset": 0.000231, + "timeOffset": 5.6, + "latOffset": -0.0011603 + }, + { + "elevationOffset": 9.1, + "lonOffset": 0.0003834, + "timeOffset": 11, + "latOffset": -0.0023425 + } + ] + }, + "pathPrediction": { + "confidence": 80, + "radiusOfCurve": -1622.5 + } + } + } + ], + "coreData": { + "secMark": 15186, + "transmission": "UNAVAILABLE", + "size": { + "width": 180, + "length": 480 + }, + "heading": 344.5, + "accelSet": { + "accelYaw": -0.65, + "accelLong": -0.05 + }, + "brakes": { + "scs": "unavailable", + "wheelBrakes": { + "leftFront": false, + "rightFront": false, + "unavailable": true, + "leftRear": false, + "rightRear": false + }, + "abs": "off", + "traction": "unavailable", + "brakeBoost": "unavailable", + "auxBrakes": "unavailable" + }, + "accuracy": { + "semiMinor": 2, + "orientation": 0, + "semiMajor": 2 + }, + "id": "11AF9993", + "position": { + "elevation": 1691.9, + "latitude": 39.58790256181461, + "longitude": -105.09079690251188 + }, + "msgCnt": 28, + "speed": 10.73 + } + }, + "dataType": "us.dot.its.jpo.ode.plugin.j2735.J2735Bsm" + }, + "recordGeneratedAt": { + "$date": { + "$numberLong": "1717518195198" + } + } +},{ + "_id": { + "$oid": "665f3fc08c25216ef4f53de1" + }, + "metadata": { + "securityResultCode": "success", + "bsmSource": "RV", + "schemaVersion": 6, + "odePacketID": "", + "sanitized": false, + "recordType": "bsmTx", + "recordGeneratedAt": "", + "maxDurationTime": 0, + "odeTimStartDateTime": "", + "receivedMessageDetails": { + "locationData": { + "elevation": "", + "heading": "", + "latitude": "", + "speed": "", + "longitude": "" + }, + "rxSource": "RV" + }, + "payloadType": "us.dot.its.jpo.ode.model.OdeBsmPayload", + "serialId": { + "recordId": 0, + "serialNumber": 0, + "streamId": "849c3ac6-4747-426f-ae5e-28ac8531b32c", + "bundleSize": 1, + "bundleId": 0 + }, + "logFileName": "", + "odeReceivedAt": "2024-06-04T16:23:15.293Z", + "originIp": "10.11.81.12" + }, + "payload": { + "data": { + "partII": [ + { + "id": "VehicleSafetyExtensions", + "value": { + "pathHistory": { + "crumbData": [ + { + "elevationOffset": 0.6, + "lonOffset": 0.0000387, + "timeOffset": 0.8, + "latOffset": -0.0001604 + }, + { + "elevationOffset": 4.3, + "lonOffset": 0.000231, + "timeOffset": 5.6, + "latOffset": -0.0011603 + }, + { + "elevationOffset": 9.1, + "lonOffset": 0.0003834, + "timeOffset": 11, + "latOffset": -0.0023425 + } + ] + }, + "pathPrediction": { + "confidence": 80, + "radiusOfCurve": -1622.5 + } + } + } + ], + "coreData": { + "secMark": 15293, + "transmission": "UNAVAILABLE", + "size": { + "width": 180, + "length": 480 + }, + "heading": 332.4, + "accelSet": { + "accelYaw": -0.65, + "accelLong": -0.05 + }, + "brakes": { + "scs": "unavailable", + "wheelBrakes": { + "leftFront": false, + "rightFront": false, + "unavailable": true, + "leftRear": false, + "rightRear": false + }, + "abs": "off", + "traction": "unavailable", + "brakeBoost": "unavailable", + "auxBrakes": "unavailable" + }, + "accuracy": { + "semiMinor": 2, + "orientation": 0, + "semiMajor": 2 + }, + "id": "11AF9993", + "position": { + "elevation": 1691.9, + "latitude": 39.587914447284994, + "longitude": -105.09080495045706 + }, + "msgCnt": 29, + "speed": 14.91 + } + }, + "dataType": "us.dot.its.jpo.ode.plugin.j2735.J2735Bsm" + }, + "recordGeneratedAt": { + "$date": { + "$numberLong": "1717518195304" + } + } +},{ + "_id": { + "$oid": "665f3fc08c25216ef4f53de2" + }, + "metadata": { + "securityResultCode": "success", + "bsmSource": "RV", + "schemaVersion": 6, + "odePacketID": "", + "sanitized": false, + "recordType": "bsmTx", + "recordGeneratedAt": "", + "maxDurationTime": 0, + "odeTimStartDateTime": "", + "receivedMessageDetails": { + "locationData": { + "elevation": "", + "heading": "", + "latitude": "", + "speed": "", + "longitude": "" + }, + "rxSource": "RV" + }, + "payloadType": "us.dot.its.jpo.ode.model.OdeBsmPayload", + "serialId": { + "recordId": 0, + "serialNumber": 0, + "streamId": "849c3ac6-4747-426f-ae5e-28ac8531b32c", + "bundleSize": 1, + "bundleId": 0 + }, + "logFileName": "", + "odeReceivedAt": "2024-06-04T16:23:15.399Z", + "originIp": "10.11.81.12" + }, + "payload": { + "data": { + "partII": [ + { + "id": "VehicleSafetyExtensions", + "value": { + "pathHistory": { + "crumbData": [ + { + "elevationOffset": 0.6, + "lonOffset": 0.0000387, + "timeOffset": 0.8, + "latOffset": -0.0001604 + }, + { + "elevationOffset": 4.3, + "lonOffset": 0.000231, + "timeOffset": 5.6, + "latOffset": -0.0011603 + }, + { + "elevationOffset": 9.1, + "lonOffset": 0.0003834, + "timeOffset": 11, + "latOffset": -0.0023425 + } + ] + }, + "pathPrediction": { + "confidence": 80, + "radiusOfCurve": -1622.5 + } + } + } + ], + "coreData": { + "secMark": 15399, + "transmission": "UNAVAILABLE", + "size": { + "width": 180, + "length": 480 + }, + "heading": 326.3, + "accelSet": { + "accelYaw": -0.65, + "accelLong": -0.05 + }, + "brakes": { + "scs": "unavailable", + "wheelBrakes": { + "leftFront": false, + "rightFront": false, + "unavailable": true, + "leftRear": false, + "rightRear": false + }, + "abs": "off", + "traction": "unavailable", + "brakeBoost": "unavailable", + "auxBrakes": "unavailable" + }, + "accuracy": { + "semiMinor": 2, + "orientation": 0, + "semiMajor": 2 + }, + "id": "11AF9993", + "position": { + "elevation": 1691.9, + "latitude": 39.58792529923446, + "longitude": -105.09081433972638 + }, + "msgCnt": 30, + "speed": 14.5 + } + }, + "dataType": "us.dot.its.jpo.ode.plugin.j2735.J2735Bsm" + }, + "recordGeneratedAt": { + "$date": { + "$numberLong": "1717518195410" + } + } +},{ + "_id": { + "$oid": "665f3fc08c25216ef4f53de3" + }, + "metadata": { + "securityResultCode": "success", + "bsmSource": "RV", + "schemaVersion": 6, + "odePacketID": "", + "sanitized": false, + "recordType": "bsmTx", + "recordGeneratedAt": "", + "maxDurationTime": 0, + "odeTimStartDateTime": "", + "receivedMessageDetails": { + "locationData": { + "elevation": "", + "heading": "", + "latitude": "", + "speed": "", + "longitude": "" + }, + "rxSource": "RV" + }, + "payloadType": "us.dot.its.jpo.ode.model.OdeBsmPayload", + "serialId": { + "recordId": 0, + "serialNumber": 0, + "streamId": "849c3ac6-4747-426f-ae5e-28ac8531b32c", + "bundleSize": 1, + "bundleId": 0 + }, + "logFileName": "", + "odeReceivedAt": "2024-06-04T16:23:15.507Z", + "originIp": "10.11.81.12" + }, + "payload": { + "data": { + "partII": [ + { + "id": "VehicleSafetyExtensions", + "value": { + "pathHistory": { + "crumbData": [ + { + "elevationOffset": 0.6, + "lonOffset": 0.0000387, + "timeOffset": 0.8, + "latOffset": -0.0001604 + }, + { + "elevationOffset": 4.3, + "lonOffset": 0.000231, + "timeOffset": 5.6, + "latOffset": -0.0011603 + }, + { + "elevationOffset": 9.1, + "lonOffset": 0.0003834, + "timeOffset": 11, + "latOffset": -0.0023425 + } + ] + }, + "pathPrediction": { + "confidence": 80, + "radiusOfCurve": -1622.5 + } + } + } + ], + "coreData": { + "secMark": 15507, + "transmission": "UNAVAILABLE", + "size": { + "width": 180, + "length": 480 + }, + "heading": 323.1, + "accelSet": { + "accelYaw": -0.65, + "accelLong": -0.05 + }, + "brakes": { + "scs": "unavailable", + "wheelBrakes": { + "leftFront": false, + "rightFront": false, + "unavailable": true, + "leftRear": false, + "rightRear": false + }, + "abs": "off", + "traction": "unavailable", + "brakeBoost": "unavailable", + "auxBrakes": "unavailable" + }, + "accuracy": { + "semiMinor": 2, + "orientation": 0, + "semiMajor": 2 + }, + "id": "11AF9993", + "position": { + "elevation": 1691.9, + "latitude": 39.587935634422834, + "longitude": -105.09082439965782 + }, + "msgCnt": 31, + "speed": 14.37 + } + }, + "dataType": "us.dot.its.jpo.ode.plugin.j2735.J2735Bsm" + }, + "recordGeneratedAt": { + "$date": { + "$numberLong": "1717518195518" + } + } +},{ + "_id": { + "$oid": "665f3fc08c25216ef4f53de4" + }, + "metadata": { + "securityResultCode": "success", + "bsmSource": "RV", + "schemaVersion": 6, + "odePacketID": "", + "sanitized": false, + "recordType": "bsmTx", + "recordGeneratedAt": "", + "maxDurationTime": 0, + "odeTimStartDateTime": "", + "receivedMessageDetails": { + "locationData": { + "elevation": "", + "heading": "", + "latitude": "", + "speed": "", + "longitude": "" + }, + "rxSource": "RV" + }, + "payloadType": "us.dot.its.jpo.ode.model.OdeBsmPayload", + "serialId": { + "recordId": 0, + "serialNumber": 0, + "streamId": "849c3ac6-4747-426f-ae5e-28ac8531b32c", + "bundleSize": 1, + "bundleId": 0 + }, + "logFileName": "", + "odeReceivedAt": "2024-06-04T16:23:15.614Z", + "originIp": "10.11.81.12" + }, + "payload": { + "data": { + "partII": [ + { + "id": "VehicleSafetyExtensions", + "value": { + "pathHistory": { + "crumbData": [ + { + "elevationOffset": 0.6, + "lonOffset": 0.0000387, + "timeOffset": 0.8, + "latOffset": -0.0001604 + }, + { + "elevationOffset": 4.3, + "lonOffset": 0.000231, + "timeOffset": 5.6, + "latOffset": -0.0011603 + }, + { + "elevationOffset": 9.1, + "lonOffset": 0.0003834, + "timeOffset": 11, + "latOffset": -0.0023425 + } + ] + }, + "pathPrediction": { + "confidence": 80, + "radiusOfCurve": -1622.5 + } + } + } + ], + "coreData": { + "secMark": 15614, + "transmission": "UNAVAILABLE", + "size": { + "width": 180, + "length": 480 + }, + "heading": 306.5, + "accelSet": { + "accelYaw": -0.65, + "accelLong": -0.05 + }, + "brakes": { + "scs": "unavailable", + "wheelBrakes": { + "leftFront": false, + "rightFront": false, + "unavailable": true, + "leftRear": false, + "rightRear": false + }, + "abs": "off", + "traction": "unavailable", + "brakeBoost": "unavailable", + "auxBrakes": "unavailable" + }, + "accuracy": { + "semiMinor": 2, + "orientation": 0, + "semiMajor": 2 + }, + "id": "11AF9993", + "position": { + "elevation": 1691.9, + "latitude": 39.58794441933173, + "longitude": -105.09083982488603 + }, + "msgCnt": 32, + "speed": 16.44 + } + }, + "dataType": "us.dot.its.jpo.ode.plugin.j2735.J2735Bsm" + }, + "recordGeneratedAt": { + "$date": { + "$numberLong": "1717518195624" + } + } +},{ + "_id": { + "$oid": "665f3fc08c25216ef4f53de5" + }, + "metadata": { + "securityResultCode": "success", + "bsmSource": "RV", + "schemaVersion": 6, + "odePacketID": "", + "sanitized": false, + "recordType": "bsmTx", + "recordGeneratedAt": "", + "maxDurationTime": 0, + "odeTimStartDateTime": "", + "receivedMessageDetails": { + "locationData": { + "elevation": "", + "heading": "", + "latitude": "", + "speed": "", + "longitude": "" + }, + "rxSource": "RV" + }, + "payloadType": "us.dot.its.jpo.ode.model.OdeBsmPayload", + "serialId": { + "recordId": 0, + "serialNumber": 0, + "streamId": "849c3ac6-4747-426f-ae5e-28ac8531b32c", + "bundleSize": 1, + "bundleId": 0 + }, + "logFileName": "", + "odeReceivedAt": "2024-06-04T16:23:15.721Z", + "originIp": "10.11.81.12" + }, + "payload": { + "data": { + "partII": [ + { + "id": "VehicleSafetyExtensions", + "value": { + "pathHistory": { + "crumbData": [ + { + "elevationOffset": 0.6, + "lonOffset": 0.0000387, + "timeOffset": 0.8, + "latOffset": -0.0001604 + }, + { + "elevationOffset": 4.3, + "lonOffset": 0.000231, + "timeOffset": 5.6, + "latOffset": -0.0011603 + }, + { + "elevationOffset": 9.1, + "lonOffset": 0.0003834, + "timeOffset": 11, + "latOffset": -0.0023425 + } + ] + }, + "pathPrediction": { + "confidence": 80, + "radiusOfCurve": -1622.5 + } + } + } + ], + "coreData": { + "secMark": 15721, + "transmission": "UNAVAILABLE", + "size": { + "width": 180, + "length": 480 + }, + "heading": 291.2, + "accelSet": { + "accelYaw": -0.65, + "accelLong": -0.05 + }, + "brakes": { + "scs": "unavailable", + "wheelBrakes": { + "leftFront": false, + "rightFront": false, + "unavailable": true, + "leftRear": false, + "rightRear": false + }, + "abs": "off", + "traction": "unavailable", + "brakeBoost": "unavailable", + "auxBrakes": "unavailable" + }, + "accuracy": { + "semiMinor": 2, + "orientation": 0, + "semiMajor": 2 + }, + "id": "11AF9993", + "position": { + "elevation": 1691.9, + "latitude": 39.58795062044324, + "longitude": -105.090860615411 + }, + "msgCnt": 33, + "speed": 19.1 + } + }, + "dataType": "us.dot.its.jpo.ode.plugin.j2735.J2735Bsm" + }, + "recordGeneratedAt": { + "$date": { + "$numberLong": "1717518195731" + } + } +},{ + "_id": { + "$oid": "665f3fc08c25216ef4f53de6" + }, + "metadata": { + "securityResultCode": "success", + "bsmSource": "RV", + "schemaVersion": 6, + "odePacketID": "", + "sanitized": false, + "recordType": "bsmTx", + "recordGeneratedAt": "", + "maxDurationTime": 0, + "odeTimStartDateTime": "", + "receivedMessageDetails": { + "locationData": { + "elevation": "", + "heading": "", + "latitude": "", + "speed": "", + "longitude": "" + }, + "rxSource": "RV" + }, + "payloadType": "us.dot.its.jpo.ode.model.OdeBsmPayload", + "serialId": { + "recordId": 0, + "serialNumber": 0, + "streamId": "849c3ac6-4747-426f-ae5e-28ac8531b32c", + "bundleSize": 1, + "bundleId": 0 + }, + "logFileName": "", + "odeReceivedAt": "2024-06-04T16:23:15.827Z", + "originIp": "10.11.81.12" + }, + "payload": { + "data": { + "partII": [ + { + "id": "VehicleSafetyExtensions", + "value": { + "pathHistory": { + "crumbData": [ + { + "elevationOffset": 0.6, + "lonOffset": 0.0000387, + "timeOffset": 0.8, + "latOffset": -0.0001604 + }, + { + "elevationOffset": 4.3, + "lonOffset": 0.000231, + "timeOffset": 5.6, + "latOffset": -0.0011603 + }, + { + "elevationOffset": 9.1, + "lonOffset": 0.0003834, + "timeOffset": 11, + "latOffset": -0.0023425 + } + ] + }, + "pathPrediction": { + "confidence": 80, + "radiusOfCurve": -1622.5 + } + } + } + ], + "coreData": { + "secMark": 15827, + "transmission": "UNAVAILABLE", + "size": { + "width": 180, + "length": 480 + }, + "heading": 261, + "accelSet": { + "accelYaw": -0.65, + "accelLong": -0.05 + }, + "brakes": { + "scs": "unavailable", + "wheelBrakes": { + "leftFront": false, + "rightFront": false, + "unavailable": true, + "leftRear": false, + "rightRear": false + }, + "abs": "off", + "traction": "unavailable", + "brakeBoost": "unavailable", + "auxBrakes": "unavailable" + }, + "accuracy": { + "semiMinor": 2, + "orientation": 0, + "semiMajor": 2 + }, + "id": "11AF9993", + "position": { + "elevation": 1691.9, + "latitude": 39.587949070165415, + "longitude": -105.09087335799083 + }, + "msgCnt": 34, + "speed": 11.05 + } + }, + "dataType": "us.dot.its.jpo.ode.plugin.j2735.J2735Bsm" + }, + "recordGeneratedAt": { + "$date": { + "$numberLong": "1717518195839" + } + } +},{ + "_id": { + "$oid": "665f3fc08c25216ef4f53de7" + }, + "metadata": { + "securityResultCode": "success", + "bsmSource": "RV", + "schemaVersion": 6, + "odePacketID": "", + "sanitized": false, + "recordType": "bsmTx", + "recordGeneratedAt": "", + "maxDurationTime": 0, + "odeTimStartDateTime": "", + "receivedMessageDetails": { + "locationData": { + "elevation": "", + "heading": "", + "latitude": "", + "speed": "", + "longitude": "" + }, + "rxSource": "RV" + }, + "payloadType": "us.dot.its.jpo.ode.model.OdeBsmPayload", + "serialId": { + "recordId": 0, + "serialNumber": 0, + "streamId": "849c3ac6-4747-426f-ae5e-28ac8531b32c", + "bundleSize": 1, + "bundleId": 0 + }, + "logFileName": "", + "odeReceivedAt": "2024-06-04T16:23:15.935Z", + "originIp": "10.11.81.12" + }, + "payload": { + "data": { + "partII": [ + { + "id": "VehicleSafetyExtensions", + "value": { + "pathHistory": { + "crumbData": [ + { + "elevationOffset": 0.6, + "lonOffset": 0.0000387, + "timeOffset": 0.8, + "latOffset": -0.0001604 + }, + { + "elevationOffset": 4.3, + "lonOffset": 0.000231, + "timeOffset": 5.6, + "latOffset": -0.0011603 + }, + { + "elevationOffset": 9.1, + "lonOffset": 0.0003834, + "timeOffset": 11, + "latOffset": -0.0023425 + } + ] + }, + "pathPrediction": { + "confidence": 80, + "radiusOfCurve": -1622.5 + } + } + } + ], + "coreData": { + "secMark": 15935, + "transmission": "UNAVAILABLE", + "size": { + "width": 180, + "length": 480 + }, + "heading": 274.8, + "accelSet": { + "accelYaw": -0.65, + "accelLong": -0.05 + }, + "brakes": { + "scs": "unavailable", + "wheelBrakes": { + "leftFront": false, + "rightFront": false, + "unavailable": true, + "leftRear": false, + "rightRear": false + }, + "abs": "off", + "traction": "unavailable", + "brakeBoost": "unavailable", + "auxBrakes": "unavailable" + }, + "accuracy": { + "semiMinor": 2, + "orientation": 0, + "semiMajor": 2 + }, + "id": "11AF9993", + "position": { + "elevation": 1691.9, + "latitude": 39.58795010368396, + "longitude": -105.09088945388118 + }, + "msgCnt": 35, + "speed": 13.84 + } + }, + "dataType": "us.dot.its.jpo.ode.plugin.j2735.J2735Bsm" + }, + "recordGeneratedAt": { + "$date": { + "$numberLong": "1717518195946" + } + } +},{ + "_id": { + "$oid": "665f3fc08c25216ef4f53de8" + }, + "metadata": { + "securityResultCode": "success", + "bsmSource": "RV", + "schemaVersion": 6, + "odePacketID": "", + "sanitized": false, + "recordType": "bsmTx", + "recordGeneratedAt": "", + "maxDurationTime": 0, + "odeTimStartDateTime": "", + "receivedMessageDetails": { + "locationData": { + "elevation": "", + "heading": "", + "latitude": "", + "speed": "", + "longitude": "" + }, + "rxSource": "RV" + }, + "payloadType": "us.dot.its.jpo.ode.model.OdeBsmPayload", + "serialId": { + "recordId": 0, + "serialNumber": 0, + "streamId": "849c3ac6-4747-426f-ae5e-28ac8531b32c", + "bundleSize": 1, + "bundleId": 0 + }, + "logFileName": "", + "odeReceivedAt": "2024-06-04T16:23:16.042Z", + "originIp": "10.11.81.12" + }, + "payload": { + "data": { + "partII": [ + { + "id": "VehicleSafetyExtensions", + "value": { + "pathHistory": { + "crumbData": [ + { + "elevationOffset": 0.6, + "lonOffset": 0.0000387, + "timeOffset": 0.8, + "latOffset": -0.0001604 + }, + { + "elevationOffset": 4.3, + "lonOffset": 0.000231, + "timeOffset": 5.6, + "latOffset": -0.0011603 + }, + { + "elevationOffset": 9.1, + "lonOffset": 0.0003834, + "timeOffset": 11, + "latOffset": -0.0023425 + } + ] + }, + "pathPrediction": { + "confidence": 80, + "radiusOfCurve": -1622.5 + } + } + } + ], + "coreData": { + "secMark": 16042, + "transmission": "UNAVAILABLE", + "size": { + "width": 180, + "length": 480 + }, + "heading": 259.7, + "accelSet": { + "accelYaw": -0.65, + "accelLong": -0.05 + }, + "brakes": { + "scs": "unavailable", + "wheelBrakes": { + "leftFront": false, + "rightFront": false, + "unavailable": true, + "leftRear": false, + "rightRear": false + }, + "abs": "off", + "traction": "unavailable", + "brakeBoost": "unavailable", + "auxBrakes": "unavailable" + }, + "accuracy": { + "semiMinor": 2, + "orientation": 0, + "semiMajor": 2 + }, + "id": "11AF9993", + "position": { + "elevation": 1691.9, + "latitude": 39.58794803664684, + "longitude": -105.09090420844727 + }, + "msgCnt": 36, + "speed": 12.85 + } + }, + "dataType": "us.dot.its.jpo.ode.plugin.j2735.J2735Bsm" + }, + "recordGeneratedAt": { + "$date": { + "$numberLong": "1717518196053" + } + } +},{ + "_id": { + "$oid": "665f3fc08c25216ef4f53de9" + }, + "metadata": { + "securityResultCode": "success", + "bsmSource": "RV", + "schemaVersion": 6, + "odePacketID": "", + "sanitized": false, + "recordType": "bsmTx", + "recordGeneratedAt": "", + "maxDurationTime": 0, + "odeTimStartDateTime": "", + "receivedMessageDetails": { + "locationData": { + "elevation": "", + "heading": "", + "latitude": "", + "speed": "", + "longitude": "" + }, + "rxSource": "RV" + }, + "payloadType": "us.dot.its.jpo.ode.model.OdeBsmPayload", + "serialId": { + "recordId": 0, + "serialNumber": 0, + "streamId": "849c3ac6-4747-426f-ae5e-28ac8531b32c", + "bundleSize": 1, + "bundleId": 0 + }, + "logFileName": "", + "odeReceivedAt": "2024-06-04T16:23:16.149Z", + "originIp": "10.11.81.12" + }, + "payload": { + "data": { + "partII": [ + { + "id": "VehicleSafetyExtensions", + "value": { + "pathHistory": { + "crumbData": [ + { + "elevationOffset": 0.6, + "lonOffset": 0.0000387, + "timeOffset": 0.8, + "latOffset": -0.0001604 + }, + { + "elevationOffset": 4.3, + "lonOffset": 0.000231, + "timeOffset": 5.6, + "latOffset": -0.0011603 + }, + { + "elevationOffset": 9.1, + "lonOffset": 0.0003834, + "timeOffset": 11, + "latOffset": -0.0023425 + } + ] + }, + "pathPrediction": { + "confidence": 80, + "radiusOfCurve": -1622.5 + } + } + } + ], + "coreData": { + "secMark": 16149, + "transmission": "UNAVAILABLE", + "size": { + "width": 180, + "length": 480 + }, + "heading": 270, + "accelSet": { + "accelYaw": -0.65, + "accelLong": -0.05 + }, + "brakes": { + "scs": "unavailable", + "wheelBrakes": { + "leftFront": false, + "rightFront": false, + "unavailable": true, + "leftRear": false, + "rightRear": false + }, + "abs": "off", + "traction": "unavailable", + "brakeBoost": "unavailable", + "auxBrakes": "unavailable" + }, + "accuracy": { + "semiMinor": 2, + "orientation": 0, + "semiMajor": 2 + }, + "id": "11AF9993", + "position": { + "elevation": 1691.9, + "latitude": 39.58794803664684, + "longitude": -105.0909189630134 + }, + "msgCnt": 37, + "speed": 12.64 + } + }, + "dataType": "us.dot.its.jpo.ode.plugin.j2735.J2735Bsm" + }, + "recordGeneratedAt": { + "$date": { + "$numberLong": "1717518196160" + } + } +},{ + "_id": { + "$oid": "665f3fc08c25216ef4f53dea" + }, + "metadata": { + "securityResultCode": "success", + "bsmSource": "RV", + "schemaVersion": 6, + "odePacketID": "", + "sanitized": false, + "recordType": "bsmTx", + "recordGeneratedAt": "", + "maxDurationTime": 0, + "odeTimStartDateTime": "", + "receivedMessageDetails": { + "locationData": { + "elevation": "", + "heading": "", + "latitude": "", + "speed": "", + "longitude": "" + }, + "rxSource": "RV" + }, + "payloadType": "us.dot.its.jpo.ode.model.OdeBsmPayload", + "serialId": { + "recordId": 0, + "serialNumber": 0, + "streamId": "849c3ac6-4747-426f-ae5e-28ac8531b32c", + "bundleSize": 1, + "bundleId": 0 + }, + "logFileName": "", + "odeReceivedAt": "2024-06-04T16:23:16.256Z", + "originIp": "10.11.81.12" + }, + "payload": { + "data": { + "partII": [ + { + "id": "VehicleSafetyExtensions", + "value": { + "pathHistory": { + "crumbData": [ + { + "elevationOffset": 0.6, + "lonOffset": 0.0000387, + "timeOffset": 0.8, + "latOffset": -0.0001604 + }, + { + "elevationOffset": 4.3, + "lonOffset": 0.000231, + "timeOffset": 5.6, + "latOffset": -0.0011603 + }, + { + "elevationOffset": 9.1, + "lonOffset": 0.0003834, + "timeOffset": 11, + "latOffset": -0.0023425 + } + ] + }, + "pathPrediction": { + "confidence": 80, + "radiusOfCurve": -1622.5 + } + } + } + ], + "coreData": { + "secMark": 16256, + "transmission": "UNAVAILABLE", + "size": { + "width": 180, + "length": 480 + }, + "heading": 258.3, + "accelSet": { + "accelYaw": -0.65, + "accelLong": -0.05 + }, + "brakes": { + "scs": "unavailable", + "wheelBrakes": { + "leftFront": false, + "rightFront": false, + "unavailable": true, + "leftRear": false, + "rightRear": false + }, + "abs": "off", + "traction": "unavailable", + "brakeBoost": "unavailable", + "auxBrakes": "unavailable" + }, + "accuracy": { + "semiMinor": 2, + "orientation": 0, + "semiMajor": 2 + }, + "id": "11AF9993", + "position": { + "elevation": 1691.9, + "latitude": 39.58794493609105, + "longitude": -105.09093841221416 + }, + "msgCnt": 38, + "speed": 17.02 + } + }, + "dataType": "us.dot.its.jpo.ode.plugin.j2735.J2735Bsm" + }, + "recordGeneratedAt": { + "$date": { + "$numberLong": "1717518196266" + } + } +},{ + "_id": { + "$oid": "665f3fc08c25216ef4f53deb" + }, + "metadata": { + "securityResultCode": "success", + "bsmSource": "RV", + "schemaVersion": 6, + "odePacketID": "", + "sanitized": false, + "recordType": "bsmTx", + "recordGeneratedAt": "", + "maxDurationTime": 0, + "odeTimStartDateTime": "", + "receivedMessageDetails": { + "locationData": { + "elevation": "", + "heading": "", + "latitude": "", + "speed": "", + "longitude": "" + }, + "rxSource": "RV" + }, + "payloadType": "us.dot.its.jpo.ode.model.OdeBsmPayload", + "serialId": { + "recordId": 0, + "serialNumber": 0, + "streamId": "849c3ac6-4747-426f-ae5e-28ac8531b32c", + "bundleSize": 1, + "bundleId": 0 + }, + "logFileName": "", + "odeReceivedAt": "2024-06-04T16:23:16.364Z", + "originIp": "10.11.81.12" + }, + "payload": { + "data": { + "partII": [ + { + "id": "VehicleSafetyExtensions", + "value": { + "pathHistory": { + "crumbData": [ + { + "elevationOffset": 0.6, + "lonOffset": 0.0000387, + "timeOffset": 0.8, + "latOffset": -0.0001604 + }, + { + "elevationOffset": 4.3, + "lonOffset": 0.000231, + "timeOffset": 5.6, + "latOffset": -0.0011603 + }, + { + "elevationOffset": 9.1, + "lonOffset": 0.0003834, + "timeOffset": 11, + "latOffset": -0.0023425 + } + ] + }, + "pathPrediction": { + "confidence": 80, + "radiusOfCurve": -1622.5 + } + } + } + ], + "coreData": { + "secMark": 16364, + "transmission": "UNAVAILABLE", + "size": { + "width": 180, + "length": 480 + }, + "heading": 225, + "accelSet": { + "accelYaw": -0.65, + "accelLong": -0.05 + }, + "brakes": { + "scs": "unavailable", + "wheelBrakes": { + "leftFront": false, + "rightFront": false, + "unavailable": true, + "leftRear": false, + "rightRear": false + }, + "abs": "off", + "traction": "unavailable", + "brakeBoost": "unavailable", + "auxBrakes": "unavailable" + }, + "accuracy": { + "semiMinor": 2, + "orientation": 0, + "semiMajor": 2 + }, + "id": "11AF9993", + "position": { + "elevation": 1691.9, + "latitude": 39.587933567385285, + "longitude": -105.0909531667803 + }, + "msgCnt": 39, + "speed": 17.88 + } + }, + "dataType": "us.dot.its.jpo.ode.plugin.j2735.J2735Bsm" + }, + "recordGeneratedAt": { + "$date": { + "$numberLong": "1717518196372" + } + } +},{ + "_id": { + "$oid": "665f3fc08c25216ef4f53dec" + }, + "metadata": { + "securityResultCode": "success", + "bsmSource": "RV", + "schemaVersion": 6, + "odePacketID": "", + "sanitized": false, + "recordType": "bsmTx", + "recordGeneratedAt": "", + "maxDurationTime": 0, + "odeTimStartDateTime": "", + "receivedMessageDetails": { + "locationData": { + "elevation": "", + "heading": "", + "latitude": "", + "speed": "", + "longitude": "" + }, + "rxSource": "RV" + }, + "payloadType": "us.dot.its.jpo.ode.model.OdeBsmPayload", + "serialId": { + "recordId": 0, + "serialNumber": 0, + "streamId": "849c3ac6-4747-426f-ae5e-28ac8531b32c", + "bundleSize": 1, + "bundleId": 0 + }, + "logFileName": "", + "odeReceivedAt": "2024-06-04T16:23:16.47Z", + "originIp": "10.11.81.12" + }, + "payload": { + "data": { + "partII": [ + { + "id": "VehicleSafetyExtensions", + "value": { + "pathHistory": { + "crumbData": [ + { + "elevationOffset": 0.6, + "lonOffset": 0.0000387, + "timeOffset": 0.8, + "latOffset": -0.0001604 + }, + { + "elevationOffset": 4.3, + "lonOffset": 0.000231, + "timeOffset": 5.6, + "latOffset": -0.0011603 + }, + { + "elevationOffset": 9.1, + "lonOffset": 0.0003834, + "timeOffset": 11, + "latOffset": -0.0023425 + } + ] + }, + "pathPrediction": { + "confidence": 80, + "radiusOfCurve": -1622.5 + } + } + } + ], + "coreData": { + "secMark": 16470, + "transmission": "UNAVAILABLE", + "size": { + "width": 180, + "length": 480 + }, + "heading": 211.3, + "accelSet": { + "accelYaw": -0.65, + "accelLong": -0.05 + }, + "brakes": { + "scs": "unavailable", + "wheelBrakes": { + "leftFront": false, + "rightFront": false, + "unavailable": true, + "leftRear": false, + "rightRear": false + }, + "abs": "off", + "traction": "unavailable", + "brakeBoost": "unavailable", + "auxBrakes": "unavailable" + }, + "accuracy": { + "semiMinor": 2, + "orientation": 0, + "semiMajor": 2 + }, + "id": "11AF9993", + "position": { + "elevation": 1691.9, + "latitude": 39.587921681918154, + "longitude": -105.09096255604963 + }, + "msgCnt": 40, + "speed": 15.47 + } + }, + "dataType": "us.dot.its.jpo.ode.plugin.j2735.J2735Bsm" + }, + "recordGeneratedAt": { + "$date": { + "$numberLong": "1717518196480" + } + } +},{ + "_id": { + "$oid": "665f3fc08c25216ef4f53ded" + }, + "metadata": { + "securityResultCode": "success", + "bsmSource": "RV", + "schemaVersion": 6, + "odePacketID": "", + "sanitized": false, + "recordType": "bsmTx", + "recordGeneratedAt": "", + "maxDurationTime": 0, + "odeTimStartDateTime": "", + "receivedMessageDetails": { + "locationData": { + "elevation": "", + "heading": "", + "latitude": "", + "speed": "", + "longitude": "" + }, + "rxSource": "RV" + }, + "payloadType": "us.dot.its.jpo.ode.model.OdeBsmPayload", + "serialId": { + "recordId": 0, + "serialNumber": 0, + "streamId": "849c3ac6-4747-426f-ae5e-28ac8531b32c", + "bundleSize": 1, + "bundleId": 0 + }, + "logFileName": "", + "odeReceivedAt": "2024-06-04T16:23:16.578Z", + "originIp": "10.11.81.12" + }, + "payload": { + "data": { + "partII": [ + { + "id": "VehicleSafetyExtensions", + "value": { + "pathHistory": { + "crumbData": [ + { + "elevationOffset": 0.6, + "lonOffset": 0.0000387, + "timeOffset": 0.8, + "latOffset": -0.0001604 + }, + { + "elevationOffset": 4.3, + "lonOffset": 0.000231, + "timeOffset": 5.6, + "latOffset": -0.0011603 + }, + { + "elevationOffset": 9.1, + "lonOffset": 0.0003834, + "timeOffset": 11, + "latOffset": -0.0023425 + } + ] + }, + "pathPrediction": { + "confidence": 80, + "radiusOfCurve": -1622.5 + } + } + } + ], + "coreData": { + "secMark": 16578, + "transmission": "UNAVAILABLE", + "size": { + "width": 180, + "length": 480 + }, + "heading": 185, + "accelSet": { + "accelYaw": -0.65, + "accelLong": -0.05 + }, + "brakes": { + "scs": "unavailable", + "wheelBrakes": { + "leftFront": false, + "rightFront": false, + "unavailable": true, + "leftRear": false, + "rightRear": false + }, + "abs": "off", + "traction": "unavailable", + "brakeBoost": "unavailable", + "auxBrakes": "unavailable" + }, + "accuracy": { + "semiMinor": 2, + "orientation": 0, + "semiMajor": 2 + }, + "id": "11AF9993", + "position": { + "elevation": 1691.9, + "latitude": 39.58790979644899, + "longitude": -105.09096389737384 + }, + "msgCnt": 41, + "speed": 13.27 + } + }, + "dataType": "us.dot.its.jpo.ode.plugin.j2735.J2735Bsm" + }, + "recordGeneratedAt": { + "$date": { + "$numberLong": "1717518196586" + } + } +},{ + "_id": { + "$oid": "665f3fc08c25216ef4f53dee" + }, + "metadata": { + "securityResultCode": "success", + "bsmSource": "RV", + "schemaVersion": 6, + "odePacketID": "", + "sanitized": false, + "recordType": "bsmTx", + "recordGeneratedAt": "", + "maxDurationTime": 0, + "odeTimStartDateTime": "", + "receivedMessageDetails": { + "locationData": { + "elevation": "", + "heading": "", + "latitude": "", + "speed": "", + "longitude": "" + }, + "rxSource": "RV" + }, + "payloadType": "us.dot.its.jpo.ode.model.OdeBsmPayload", + "serialId": { + "recordId": 0, + "serialNumber": 0, + "streamId": "849c3ac6-4747-426f-ae5e-28ac8531b32c", + "bundleSize": 1, + "bundleId": 0 + }, + "logFileName": "", + "odeReceivedAt": "2024-06-04T16:23:16.686Z", + "originIp": "10.11.81.12" + }, + "payload": { + "data": { + "partII": [ + { + "id": "VehicleSafetyExtensions", + "value": { + "pathHistory": { + "crumbData": [ + { + "elevationOffset": 0.6, + "lonOffset": 0.0000387, + "timeOffset": 0.8, + "latOffset": -0.0001604 + }, + { + "elevationOffset": 4.3, + "lonOffset": 0.000231, + "timeOffset": 5.6, + "latOffset": -0.0011603 + }, + { + "elevationOffset": 9.1, + "lonOffset": 0.0003834, + "timeOffset": 11, + "latOffset": -0.0023425 + } + ] + }, + "pathPrediction": { + "confidence": 80, + "radiusOfCurve": -1622.5 + } + } + } + ], + "coreData": { + "secMark": 16686, + "transmission": "UNAVAILABLE", + "size": { + "width": 180, + "length": 480 + }, + "heading": 180, + "accelSet": { + "accelYaw": -0.65, + "accelLong": -0.05 + }, + "brakes": { + "scs": "unavailable", + "wheelBrakes": { + "leftFront": false, + "rightFront": false, + "unavailable": true, + "leftRear": false, + "rightRear": false + }, + "abs": "off", + "traction": "unavailable", + "brakeBoost": "unavailable", + "auxBrakes": "unavailable" + }, + "accuracy": { + "semiMinor": 2, + "orientation": 0, + "semiMajor": 2 + }, + "id": "11AF9993", + "position": { + "elevation": 1691.9, + "latitude": 39.58789946125678, + "longitude": -105.09096389737384 + }, + "msgCnt": 42, + "speed": 11.49 + } + }, + "dataType": "us.dot.its.jpo.ode.plugin.j2735.J2735Bsm" + }, + "recordGeneratedAt": { + "$date": { + "$numberLong": "1717518196693" + } + } +},{ + "_id": { + "$oid": "665f3fc08c25216ef4f53def" + }, + "metadata": { + "securityResultCode": "success", + "bsmSource": "RV", + "schemaVersion": 6, + "odePacketID": "", + "sanitized": false, + "recordType": "bsmTx", + "recordGeneratedAt": "", + "maxDurationTime": 0, + "odeTimStartDateTime": "", + "receivedMessageDetails": { + "locationData": { + "elevation": "", + "heading": "", + "latitude": "", + "speed": "", + "longitude": "" + }, + "rxSource": "RV" + }, + "payloadType": "us.dot.its.jpo.ode.model.OdeBsmPayload", + "serialId": { + "recordId": 0, + "serialNumber": 0, + "streamId": "849c3ac6-4747-426f-ae5e-28ac8531b32c", + "bundleSize": 1, + "bundleId": 0 + }, + "logFileName": "", + "odeReceivedAt": "2024-06-04T16:23:16.793Z", + "originIp": "10.11.81.12" + }, + "payload": { + "data": { + "partII": [ + { + "id": "VehicleSafetyExtensions", + "value": { + "pathHistory": { + "crumbData": [ + { + "elevationOffset": 0.6, + "lonOffset": 0.0000387, + "timeOffset": 0.8, + "latOffset": -0.0001604 + }, + { + "elevationOffset": 4.3, + "lonOffset": 0.000231, + "timeOffset": 5.6, + "latOffset": -0.0011603 + }, + { + "elevationOffset": 9.1, + "lonOffset": 0.0003834, + "timeOffset": 11, + "latOffset": -0.0023425 + } + ] + }, + "pathPrediction": { + "confidence": 80, + "radiusOfCurve": -1622.5 + } + } + } + ], + "coreData": { + "secMark": 16793, + "transmission": "UNAVAILABLE", + "size": { + "width": 180, + "length": 480 + }, + "heading": 178.1, + "accelSet": { + "accelYaw": -0.65, + "accelLong": -0.05 + }, + "brakes": { + "scs": "unavailable", + "wheelBrakes": { + "leftFront": false, + "rightFront": false, + "unavailable": true, + "leftRear": false, + "rightRear": false + }, + "abs": "off", + "traction": "unavailable", + "brakeBoost": "unavailable", + "auxBrakes": "unavailable" + }, + "accuracy": { + "semiMinor": 2, + "orientation": 0, + "semiMajor": 2 + }, + "id": "11AF9993", + "position": { + "elevation": 1691.9, + "latitude": 39.587883958465525, + "longitude": -105.09096322671174 + }, + "msgCnt": 43, + "speed": 17.25 + } + }, + "dataType": "us.dot.its.jpo.ode.plugin.j2735.J2735Bsm" + }, + "recordGeneratedAt": { + "$date": { + "$numberLong": "1717518196800" + } + } +},{ + "_id": { + "$oid": "665f3fc08c25216ef4f53df0" + }, + "metadata": { + "securityResultCode": "success", + "bsmSource": "RV", + "schemaVersion": 6, + "odePacketID": "", + "sanitized": false, + "recordType": "bsmTx", + "recordGeneratedAt": "", + "maxDurationTime": 0, + "odeTimStartDateTime": "", + "receivedMessageDetails": { + "locationData": { + "elevation": "", + "heading": "", + "latitude": "", + "speed": "", + "longitude": "" + }, + "rxSource": "RV" + }, + "payloadType": "us.dot.its.jpo.ode.model.OdeBsmPayload", + "serialId": { + "recordId": 0, + "serialNumber": 0, + "streamId": "849c3ac6-4747-426f-ae5e-28ac8531b32c", + "bundleSize": 1, + "bundleId": 0 + }, + "logFileName": "", + "odeReceivedAt": "2024-06-04T16:23:16.898Z", + "originIp": "10.11.81.12" + }, + "payload": { + "data": { + "partII": [ + { + "id": "VehicleSafetyExtensions", + "value": { + "pathHistory": { + "crumbData": [ + { + "elevationOffset": 0.6, + "lonOffset": 0.0000387, + "timeOffset": 0.8, + "latOffset": -0.0001604 + }, + { + "elevationOffset": 4.3, + "lonOffset": 0.000231, + "timeOffset": 5.6, + "latOffset": -0.0011603 + }, + { + "elevationOffset": 9.1, + "lonOffset": 0.0003834, + "timeOffset": 11, + "latOffset": -0.0023425 + } + ] + }, + "pathPrediction": { + "confidence": 80, + "radiusOfCurve": -1622.5 + } + } + } + ], + "coreData": { + "secMark": 16898, + "transmission": "UNAVAILABLE", + "size": { + "width": 180, + "length": 480 + }, + "heading": 177.5, + "accelSet": { + "accelYaw": -0.65, + "accelLong": -0.05 + }, + "brakes": { + "scs": "unavailable", + "wheelBrakes": { + "leftFront": false, + "rightFront": false, + "unavailable": true, + "leftRear": false, + "rightRear": false + }, + "abs": "off", + "traction": "unavailable", + "brakeBoost": "unavailable", + "auxBrakes": "unavailable" + }, + "accuracy": { + "semiMinor": 2, + "orientation": 0, + "semiMajor": 2 + }, + "id": "11AF9993", + "position": { + "elevation": 1691.9, + "latitude": 39.587872072989924, + "longitude": -105.09096255604963 + }, + "msgCnt": 44, + "speed": 13.23 + } + }, + "dataType": "us.dot.its.jpo.ode.plugin.j2735.J2735Bsm" + }, + "recordGeneratedAt": { + "$date": { + "$numberLong": "1717518196906" + } + } +},{ + "_id": { + "$oid": "665f3fc08c25216ef4f53df1" + }, + "metadata": { + "securityResultCode": "success", + "bsmSource": "RV", + "schemaVersion": 6, + "odePacketID": "", + "sanitized": false, + "recordType": "bsmTx", + "recordGeneratedAt": "", + "maxDurationTime": 0, + "odeTimStartDateTime": "", + "receivedMessageDetails": { + "locationData": { + "elevation": "", + "heading": "", + "latitude": "", + "speed": "", + "longitude": "" + }, + "rxSource": "RV" + }, + "payloadType": "us.dot.its.jpo.ode.model.OdeBsmPayload", + "serialId": { + "recordId": 0, + "serialNumber": 0, + "streamId": "849c3ac6-4747-426f-ae5e-28ac8531b32c", + "bundleSize": 1, + "bundleId": 0 + }, + "logFileName": "", + "odeReceivedAt": "2024-06-04T16:23:17.006Z", + "originIp": "10.11.81.12" + }, + "payload": { + "data": { + "partII": [ + { + "id": "VehicleSafetyExtensions", + "value": { + "pathHistory": { + "crumbData": [ + { + "elevationOffset": 0.6, + "lonOffset": 0.0000387, + "timeOffset": 0.8, + "latOffset": -0.0001604 + }, + { + "elevationOffset": 4.3, + "lonOffset": 0.000231, + "timeOffset": 5.6, + "latOffset": -0.0011603 + }, + { + "elevationOffset": 9.1, + "lonOffset": 0.0003834, + "timeOffset": 11, + "latOffset": -0.0023425 + } + ] + }, + "pathPrediction": { + "confidence": 80, + "radiusOfCurve": -1622.5 + } + } + } + ], + "coreData": { + "secMark": 17006, + "transmission": "UNAVAILABLE", + "size": { + "width": 180, + "length": 480 + }, + "heading": 166, + "accelSet": { + "accelYaw": -0.65, + "accelLong": -0.05 + }, + "brakes": { + "scs": "unavailable", + "wheelBrakes": { + "leftFront": false, + "rightFront": false, + "unavailable": true, + "leftRear": false, + "rightRear": false + }, + "abs": "off", + "traction": "unavailable", + "brakeBoost": "unavailable", + "auxBrakes": "unavailable" + }, + "accuracy": { + "semiMinor": 2, + "orientation": 0, + "semiMajor": 2 + }, + "id": "11AF9993", + "position": { + "elevation": 1691.9, + "latitude": 39.5878596707523, + "longitude": -105.09095853207707 + }, + "msgCnt": 45, + "speed": 14.22 + } + }, + "dataType": "us.dot.its.jpo.ode.plugin.j2735.J2735Bsm" + }, + "recordGeneratedAt": { + "$date": { + "$numberLong": "1717518197012" + } + } +},{ + "_id": { + "$oid": "665f3fc08c25216ef4f53df2" + }, + "metadata": { + "securityResultCode": "success", + "bsmSource": "RV", + "schemaVersion": 6, + "odePacketID": "", + "sanitized": false, + "recordType": "bsmTx", + "recordGeneratedAt": "", + "maxDurationTime": 0, + "odeTimStartDateTime": "", + "receivedMessageDetails": { + "locationData": { + "elevation": "", + "heading": "", + "latitude": "", + "speed": "", + "longitude": "" + }, + "rxSource": "RV" + }, + "payloadType": "us.dot.its.jpo.ode.model.OdeBsmPayload", + "serialId": { + "recordId": 0, + "serialNumber": 0, + "streamId": "849c3ac6-4747-426f-ae5e-28ac8531b32c", + "bundleSize": 1, + "bundleId": 0 + }, + "logFileName": "", + "odeReceivedAt": "2024-06-04T16:23:17.113Z", + "originIp": "10.11.81.12" + }, + "payload": { + "data": { + "partII": [ + { + "id": "VehicleSafetyExtensions", + "value": { + "pathHistory": { + "crumbData": [ + { + "elevationOffset": 0.6, + "lonOffset": 0.0000387, + "timeOffset": 0.8, + "latOffset": -0.0001604 + }, + { + "elevationOffset": 4.3, + "lonOffset": 0.000231, + "timeOffset": 5.6, + "latOffset": -0.0011603 + }, + { + "elevationOffset": 9.1, + "lonOffset": 0.0003834, + "timeOffset": 11, + "latOffset": -0.0023425 + } + ] + }, + "pathPrediction": { + "confidence": 80, + "radiusOfCurve": -1622.5 + } + } + } + ], + "coreData": { + "secMark": 17113, + "transmission": "UNAVAILABLE", + "size": { + "width": 180, + "length": 480 + }, + "heading": 169.7, + "accelSet": { + "accelYaw": -0.65, + "accelLong": -0.05 + }, + "brakes": { + "scs": "unavailable", + "wheelBrakes": { + "leftFront": false, + "rightFront": false, + "unavailable": true, + "leftRear": false, + "rightRear": false + }, + "abs": "off", + "traction": "unavailable", + "brakeBoost": "unavailable", + "auxBrakes": "unavailable" + }, + "accuracy": { + "semiMinor": 2, + "orientation": 0, + "semiMajor": 2 + }, + "id": "11AF9993", + "position": { + "elevation": 1691.9, + "latitude": 39.58784261767199, + "longitude": -105.0909545081045 + }, + "msgCnt": 46, + "speed": 19.27 + } + }, + "dataType": "us.dot.its.jpo.ode.plugin.j2735.J2735Bsm" + }, + "recordGeneratedAt": { + "$date": { + "$numberLong": "1717518197120" + } + } +},{ + "_id": { + "$oid": "665f3fc08c25216ef4f53df3" + }, + "metadata": { + "securityResultCode": "success", + "bsmSource": "RV", + "schemaVersion": 6, + "odePacketID": "", + "sanitized": false, + "recordType": "bsmTx", + "recordGeneratedAt": "", + "maxDurationTime": 0, + "odeTimStartDateTime": "", + "receivedMessageDetails": { + "locationData": { + "elevation": "", + "heading": "", + "latitude": "", + "speed": "", + "longitude": "" + }, + "rxSource": "RV" + }, + "payloadType": "us.dot.its.jpo.ode.model.OdeBsmPayload", + "serialId": { + "recordId": 0, + "serialNumber": 0, + "streamId": "849c3ac6-4747-426f-ae5e-28ac8531b32c", + "bundleSize": 1, + "bundleId": 0 + }, + "logFileName": "", + "odeReceivedAt": "2024-06-04T16:23:17.22Z", + "originIp": "10.11.81.12" + }, + "payload": { + "data": { + "partII": [ + { + "id": "VehicleSafetyExtensions", + "value": { + "pathHistory": { + "crumbData": [ + { + "elevationOffset": 0.6, + "lonOffset": 0.0000387, + "timeOffset": 0.8, + "latOffset": -0.0001604 + }, + { + "elevationOffset": 4.3, + "lonOffset": 0.000231, + "timeOffset": 5.6, + "latOffset": -0.0011603 + }, + { + "elevationOffset": 9.1, + "lonOffset": 0.0003834, + "timeOffset": 11, + "latOffset": -0.0023425 + } + ] + }, + "pathPrediction": { + "confidence": 80, + "radiusOfCurve": -1622.5 + } + } + } + ], + "coreData": { + "secMark": 17220, + "transmission": "UNAVAILABLE", + "size": { + "width": 180, + "length": 480 + }, + "heading": 168.7, + "accelSet": { + "accelYaw": -0.65, + "accelLong": -0.05 + }, + "brakes": { + "scs": "unavailable", + "wheelBrakes": { + "leftFront": false, + "rightFront": false, + "unavailable": true, + "leftRear": false, + "rightRear": false + }, + "abs": "off", + "traction": "unavailable", + "brakeBoost": "unavailable", + "auxBrakes": "unavailable" + }, + "accuracy": { + "semiMinor": 2, + "orientation": 0, + "semiMajor": 2 + }, + "id": "11AF9993", + "position": { + "elevation": 1691.9, + "latitude": 39.587829698668905, + "longitude": -105.090951154794 + }, + "msgCnt": 47, + "speed": 14.65 + } + }, + "dataType": "us.dot.its.jpo.ode.plugin.j2735.J2735Bsm" + }, + "recordGeneratedAt": { + "$date": { + "$numberLong": "1717518197227" + } + } +},{ + "_id": { + "$oid": "665f3fc08c25216ef4f53df4" + }, + "metadata": { + "securityResultCode": "success", + "bsmSource": "RV", + "schemaVersion": 6, + "odePacketID": "", + "sanitized": false, + "recordType": "bsmTx", + "recordGeneratedAt": "", + "maxDurationTime": 0, + "odeTimStartDateTime": "", + "receivedMessageDetails": { + "locationData": { + "elevation": "", + "heading": "", + "latitude": "", + "speed": "", + "longitude": "" + }, + "rxSource": "RV" + }, + "payloadType": "us.dot.its.jpo.ode.model.OdeBsmPayload", + "serialId": { + "recordId": 0, + "serialNumber": 0, + "streamId": "849c3ac6-4747-426f-ae5e-28ac8531b32c", + "bundleSize": 1, + "bundleId": 0 + }, + "logFileName": "", + "odeReceivedAt": "2024-06-04T16:23:17.329Z", + "originIp": "10.11.81.12" + }, + "payload": { + "data": { + "partII": [ + { + "id": "VehicleSafetyExtensions", + "value": { + "pathHistory": { + "crumbData": [ + { + "elevationOffset": 0.6, + "lonOffset": 0.0000387, + "timeOffset": 0.8, + "latOffset": -0.0001604 + }, + { + "elevationOffset": 4.3, + "lonOffset": 0.000231, + "timeOffset": 5.6, + "latOffset": -0.0011603 + }, + { + "elevationOffset": 9.1, + "lonOffset": 0.0003834, + "timeOffset": 11, + "latOffset": -0.0023425 + } + ] + }, + "pathPrediction": { + "confidence": 80, + "radiusOfCurve": -1622.5 + } + } + } + ], + "coreData": { + "secMark": 17329, + "transmission": "UNAVAILABLE", + "size": { + "width": 180, + "length": 480 + }, + "heading": 168, + "accelSet": { + "accelYaw": -0.65, + "accelLong": -0.05 + }, + "brakes": { + "scs": "unavailable", + "wheelBrakes": { + "leftFront": false, + "rightFront": false, + "unavailable": true, + "leftRear": false, + "rightRear": false + }, + "abs": "off", + "traction": "unavailable", + "brakeBoost": "unavailable", + "auxBrakes": "unavailable" + }, + "accuracy": { + "semiMinor": 2, + "orientation": 0, + "semiMajor": 2 + }, + "id": "11AF9993", + "position": { + "elevation": 1691.9, + "latitude": 39.58781264558121, + "longitude": -105.09094646015933 + }, + "msgCnt": 48, + "speed": 19.38 + } + }, + "dataType": "us.dot.its.jpo.ode.plugin.j2735.J2735Bsm" + }, + "recordGeneratedAt": { + "$date": { + "$numberLong": "1717518197334" + } + } +},{ + "_id": { + "$oid": "665f3fc08c25216ef4f53df5" + }, + "metadata": { + "securityResultCode": "success", + "bsmSource": "RV", + "schemaVersion": 6, + "odePacketID": "", + "sanitized": false, + "recordType": "bsmTx", + "recordGeneratedAt": "", + "maxDurationTime": 0, + "odeTimStartDateTime": "", + "receivedMessageDetails": { + "locationData": { + "elevation": "", + "heading": "", + "latitude": "", + "speed": "", + "longitude": "" + }, + "rxSource": "RV" + }, + "payloadType": "us.dot.its.jpo.ode.model.OdeBsmPayload", + "serialId": { + "recordId": 0, + "serialNumber": 0, + "streamId": "849c3ac6-4747-426f-ae5e-28ac8531b32c", + "bundleSize": 1, + "bundleId": 0 + }, + "logFileName": "", + "odeReceivedAt": "2024-06-04T16:23:17.438Z", + "originIp": "10.11.81.12" + }, + "payload": { + "data": { + "partII": [ + { + "id": "VehicleSafetyExtensions", + "value": { + "pathHistory": { + "crumbData": [ + { + "elevationOffset": 0.6, + "lonOffset": 0.0000387, + "timeOffset": 0.8, + "latOffset": -0.0001604 + }, + { + "elevationOffset": 4.3, + "lonOffset": 0.000231, + "timeOffset": 5.6, + "latOffset": -0.0011603 + }, + { + "elevationOffset": 9.1, + "lonOffset": 0.0003834, + "timeOffset": 11, + "latOffset": -0.0023425 + } + ] + }, + "pathPrediction": { + "confidence": 80, + "radiusOfCurve": -1622.5 + } + } + } + ], + "coreData": { + "secMark": 17438, + "transmission": "UNAVAILABLE", + "size": { + "width": 180, + "length": 480 + }, + "heading": 165.3, + "accelSet": { + "accelYaw": -0.65, + "accelLong": -0.05 + }, + "brakes": { + "scs": "unavailable", + "wheelBrakes": { + "leftFront": false, + "rightFront": false, + "unavailable": true, + "leftRear": false, + "rightRear": false + }, + "abs": "off", + "traction": "unavailable", + "brakeBoost": "unavailable", + "auxBrakes": "unavailable" + }, + "accuracy": { + "semiMinor": 2, + "orientation": 0, + "semiMajor": 2 + }, + "id": "11AF9993", + "position": { + "elevation": 1691.9, + "latitude": 39.58779300868712, + "longitude": -105.09093975353838 + }, + "msgCnt": 49, + "speed": 22.58 + } + }, + "dataType": "us.dot.its.jpo.ode.plugin.j2735.J2735Bsm" + }, + "recordGeneratedAt": { + "$date": { + "$numberLong": "1717518197442" + } + } +},{ + "_id": { + "$oid": "665f3fc08c25216ef4f53df6" + }, + "metadata": { + "securityResultCode": "success", + "bsmSource": "RV", + "schemaVersion": 6, + "odePacketID": "", + "sanitized": false, + "recordType": "bsmTx", + "recordGeneratedAt": "", + "maxDurationTime": 0, + "odeTimStartDateTime": "", + "receivedMessageDetails": { + "locationData": { + "elevation": "", + "heading": "", + "latitude": "", + "speed": "", + "longitude": "" + }, + "rxSource": "RV" + }, + "payloadType": "us.dot.its.jpo.ode.model.OdeBsmPayload", + "serialId": { + "recordId": 0, + "serialNumber": 0, + "streamId": "849c3ac6-4747-426f-ae5e-28ac8531b32c", + "bundleSize": 1, + "bundleId": 0 + }, + "logFileName": "", + "odeReceivedAt": "2024-06-04T16:23:17.543Z", + "originIp": "10.11.81.12" + }, + "payload": { + "data": { + "partII": [ + { + "id": "VehicleSafetyExtensions", + "value": { + "pathHistory": { + "crumbData": [ + { + "elevationOffset": 0.6, + "lonOffset": 0.0000387, + "timeOffset": 0.8, + "latOffset": -0.0001604 + }, + { + "elevationOffset": 4.3, + "lonOffset": 0.000231, + "timeOffset": 5.6, + "latOffset": -0.0011603 + }, + { + "elevationOffset": 9.1, + "lonOffset": 0.0003834, + "timeOffset": 11, + "latOffset": -0.0023425 + } + ] + }, + "pathPrediction": { + "confidence": 80, + "radiusOfCurve": -1622.5 + } + } + } + ], + "coreData": { + "secMark": 17543, + "transmission": "UNAVAILABLE", + "size": { + "width": 180, + "length": 480 + }, + "heading": 164.1, + "accelSet": { + "accelYaw": -0.65, + "accelLong": -0.05 + }, + "brakes": { + "scs": "unavailable", + "wheelBrakes": { + "leftFront": false, + "rightFront": false, + "unavailable": true, + "leftRear": false, + "rightRear": false + }, + "abs": "off", + "traction": "unavailable", + "brakeBoost": "unavailable", + "auxBrakes": "unavailable" + }, + "accuracy": { + "semiMinor": 2, + "orientation": 0, + "semiMajor": 2 + }, + "id": "11AF9993", + "position": { + "elevation": 1691.9, + "latitude": 39.58777853939319, + "longitude": -105.0909343882416 + }, + "msgCnt": 50, + "speed": 16.73 + } + }, + "dataType": "us.dot.its.jpo.ode.plugin.j2735.J2735Bsm" + }, + "recordGeneratedAt": { + "$date": { + "$numberLong": "1717518197548" + } + } +},{ + "_id": { + "$oid": "665f3fc08c25216ef4f53df7" + }, + "metadata": { + "securityResultCode": "success", + "bsmSource": "RV", + "schemaVersion": 6, + "odePacketID": "", + "sanitized": false, + "recordType": "bsmTx", + "recordGeneratedAt": "", + "maxDurationTime": 0, + "odeTimStartDateTime": "", + "receivedMessageDetails": { + "locationData": { + "elevation": "", + "heading": "", + "latitude": "", + "speed": "", + "longitude": "" + }, + "rxSource": "RV" + }, + "payloadType": "us.dot.its.jpo.ode.model.OdeBsmPayload", + "serialId": { + "recordId": 0, + "serialNumber": 0, + "streamId": "849c3ac6-4747-426f-ae5e-28ac8531b32c", + "bundleSize": 1, + "bundleId": 0 + }, + "logFileName": "", + "odeReceivedAt": "2024-06-04T16:23:27.659Z", + "originIp": "10.11.81.12" + }, + "payload": { + "data": { + "partII": [ + { + "id": "VehicleSafetyExtensions", + "value": { + "pathHistory": { + "crumbData": [ + { + "elevationOffset": 0.6, + "lonOffset": 0.0000387, + "timeOffset": 0.8, + "latOffset": -0.0001604 + }, + { + "elevationOffset": 4.3, + "lonOffset": 0.000231, + "timeOffset": 5.6, + "latOffset": -0.0011603 + }, + { + "elevationOffset": 9.1, + "lonOffset": 0.0003834, + "timeOffset": 11, + "latOffset": -0.0023425 + } + ] + }, + "pathPrediction": { + "confidence": 80, + "radiusOfCurve": -1622.5 + } + } + } + ], + "coreData": { + "secMark": 27659, + "transmission": "UNAVAILABLE", + "size": { + "width": 180, + "length": 480 + }, + "heading": 169.2, + "accelSet": { + "accelYaw": -0.65, + "accelLong": -0.05 + }, + "brakes": { + "scs": "unavailable", + "wheelBrakes": { + "leftFront": false, + "rightFront": false, + "unavailable": true, + "leftRear": false, + "rightRear": false + }, + "abs": "off", + "traction": "unavailable", + "brakeBoost": "unavailable", + "auxBrakes": "unavailable" + }, + "accuracy": { + "semiMinor": 2, + "orientation": 0, + "semiMajor": 2 + }, + "id": "A0B43C7E", + "position": { + "elevation": 1691.9, + "latitude": 39.587279346902015, + "longitude": -105.09059802750382 + }, + "msgCnt": 1, + "speed": 22.62 + } + }, + "dataType": "us.dot.its.jpo.ode.plugin.j2735.J2735Bsm" + }, + "recordGeneratedAt": { + "$date": { + "$numberLong": "1717518207676" + } + } +},{ + "_id": { + "$oid": "665f3fc08c25216ef4f53df8" + }, + "metadata": { + "securityResultCode": "success", + "bsmSource": "RV", + "schemaVersion": 6, + "odePacketID": "", + "sanitized": false, + "recordType": "bsmTx", + "recordGeneratedAt": "", + "maxDurationTime": 0, + "odeTimStartDateTime": "", + "receivedMessageDetails": { + "locationData": { + "elevation": "", + "heading": "", + "latitude": "", + "speed": "", + "longitude": "" + }, + "rxSource": "RV" + }, + "payloadType": "us.dot.its.jpo.ode.model.OdeBsmPayload", + "serialId": { + "recordId": 0, + "serialNumber": 0, + "streamId": "849c3ac6-4747-426f-ae5e-28ac8531b32c", + "bundleSize": 1, + "bundleId": 0 + }, + "logFileName": "", + "odeReceivedAt": "2024-06-04T16:23:27.771Z", + "originIp": "10.11.81.12" + }, + "payload": { + "data": { + "partII": [ + { + "id": "VehicleSafetyExtensions", + "value": { + "pathHistory": { + "crumbData": [ + { + "elevationOffset": 0.6, + "lonOffset": 0.0000387, + "timeOffset": 0.8, + "latOffset": -0.0001604 + }, + { + "elevationOffset": 4.3, + "lonOffset": 0.000231, + "timeOffset": 5.6, + "latOffset": -0.0011603 + }, + { + "elevationOffset": 9.1, + "lonOffset": 0.0003834, + "timeOffset": 11, + "latOffset": -0.0023425 + } + ] + }, + "pathPrediction": { + "confidence": 80, + "radiusOfCurve": -1622.5 + } + } + } + ], + "coreData": { + "secMark": 27771, + "transmission": "UNAVAILABLE", + "size": { + "width": 180, + "length": 480 + }, + "heading": 342, + "accelSet": { + "accelYaw": -0.65, + "accelLong": -0.05 + }, + "brakes": { + "scs": "unavailable", + "wheelBrakes": { + "leftFront": false, + "rightFront": false, + "unavailable": true, + "leftRear": false, + "rightRear": false + }, + "abs": "off", + "traction": "unavailable", + "brakeBoost": "unavailable", + "auxBrakes": "unavailable" + }, + "accuracy": { + "semiMinor": 2, + "orientation": 0, + "semiMajor": 2 + }, + "id": "A0B43C7E", + "position": { + "elevation": 1691.9, + "latitude": 39.58731758744779, + "longitude": -105.09061412339413 + }, + "msgCnt": 2, + "speed": 44.7 + } + }, + "dataType": "us.dot.its.jpo.ode.plugin.j2735.J2735Bsm" + }, + "recordGeneratedAt": { + "$date": { + "$numberLong": "1717518207779" + } + } +},{ + "_id": { + "$oid": "665f3fc08c25216ef4f53df9" + }, + "metadata": { + "securityResultCode": "success", + "bsmSource": "RV", + "schemaVersion": 6, + "odePacketID": "", + "sanitized": false, + "recordType": "bsmTx", + "recordGeneratedAt": "", + "maxDurationTime": 0, + "odeTimStartDateTime": "", + "receivedMessageDetails": { + "locationData": { + "elevation": "", + "heading": "", + "latitude": "", + "speed": "", + "longitude": "" + }, + "rxSource": "RV" + }, + "payloadType": "us.dot.its.jpo.ode.model.OdeBsmPayload", + "serialId": { + "recordId": 0, + "serialNumber": 0, + "streamId": "849c3ac6-4747-426f-ae5e-28ac8531b32c", + "bundleSize": 1, + "bundleId": 0 + }, + "logFileName": "", + "odeReceivedAt": "2024-06-04T16:23:27.878Z", + "originIp": "10.11.81.12" + }, + "payload": { + "data": { + "partII": [ + { + "id": "VehicleSafetyExtensions", + "value": { + "pathHistory": { + "crumbData": [ + { + "elevationOffset": 0.6, + "lonOffset": 0.0000387, + "timeOffset": 0.8, + "latOffset": -0.0001604 + }, + { + "elevationOffset": 4.3, + "lonOffset": 0.000231, + "timeOffset": 5.6, + "latOffset": -0.0011603 + }, + { + "elevationOffset": 9.1, + "lonOffset": 0.0003834, + "timeOffset": 11, + "latOffset": -0.0023425 + } + ] + }, + "pathPrediction": { + "confidence": 80, + "radiusOfCurve": -1622.5 + } + } + } + ], + "coreData": { + "secMark": 27878, + "transmission": "UNAVAILABLE", + "size": { + "width": 180, + "length": 480 + }, + "heading": 339.9, + "accelSet": { + "accelYaw": -0.65, + "accelLong": -0.05 + }, + "brakes": { + "scs": "unavailable", + "wheelBrakes": { + "leftFront": false, + "rightFront": false, + "unavailable": true, + "leftRear": false, + "rightRear": false + }, + "abs": "off", + "traction": "unavailable", + "brakeBoost": "unavailable", + "auxBrakes": "unavailable" + }, + "accuracy": { + "semiMinor": 2, + "orientation": 0, + "semiMajor": 2 + }, + "id": "A0B43C7E", + "position": { + "elevation": 1691.9, + "latitude": 39.58734859328024, + "longitude": -105.09062887796024 + }, + "msgCnt": 3, + "speed": 36.72 + } + }, + "dataType": "us.dot.its.jpo.ode.plugin.j2735.J2735Bsm" + }, + "recordGeneratedAt": { + "$date": { + "$numberLong": "1717518207886" + } + } +},{ + "_id": { + "$oid": "665f3fc08c25216ef4f53dfa" + }, + "metadata": { + "securityResultCode": "success", + "bsmSource": "RV", + "schemaVersion": 6, + "odePacketID": "", + "sanitized": false, + "recordType": "bsmTx", + "recordGeneratedAt": "", + "maxDurationTime": 0, + "odeTimStartDateTime": "", + "receivedMessageDetails": { + "locationData": { + "elevation": "", + "heading": "", + "latitude": "", + "speed": "", + "longitude": "" + }, + "rxSource": "RV" + }, + "payloadType": "us.dot.its.jpo.ode.model.OdeBsmPayload", + "serialId": { + "recordId": 0, + "serialNumber": 0, + "streamId": "849c3ac6-4747-426f-ae5e-28ac8531b32c", + "bundleSize": 1, + "bundleId": 0 + }, + "logFileName": "", + "odeReceivedAt": "2024-06-04T16:23:27.984Z", + "originIp": "10.11.81.12" + }, + "payload": { + "data": { + "partII": [ + { + "id": "VehicleSafetyExtensions", + "value": { + "pathHistory": { + "crumbData": [ + { + "elevationOffset": 0.6, + "lonOffset": 0.0000387, + "timeOffset": 0.8, + "latOffset": -0.0001604 + }, + { + "elevationOffset": 4.3, + "lonOffset": 0.000231, + "timeOffset": 5.6, + "latOffset": -0.0011603 + }, + { + "elevationOffset": 9.1, + "lonOffset": 0.0003834, + "timeOffset": 11, + "latOffset": -0.0023425 + } + ] + }, + "pathPrediction": { + "confidence": 80, + "radiusOfCurve": -1622.5 + } + } + } + ], + "coreData": { + "secMark": 27984, + "transmission": "UNAVAILABLE", + "size": { + "width": 180, + "length": 480 + }, + "heading": 347.9, + "accelSet": { + "accelYaw": -0.65, + "accelLong": -0.05 + }, + "brakes": { + "scs": "unavailable", + "wheelBrakes": { + "leftFront": false, + "rightFront": false, + "unavailable": true, + "leftRear": false, + "rightRear": false + }, + "abs": "off", + "traction": "unavailable", + "brakeBoost": "unavailable", + "auxBrakes": "unavailable" + }, + "accuracy": { + "semiMinor": 2, + "orientation": 0, + "semiMajor": 2 + }, + "id": "A0B43C7E", + "position": { + "elevation": 1691.9, + "latitude": 39.587377532044655, + "longitude": -105.09063692590537 + }, + "msgCnt": 4, + "speed": 32.91 + } + }, + "dataType": "us.dot.its.jpo.ode.plugin.j2735.J2735Bsm" + }, + "recordGeneratedAt": { + "$date": { + "$numberLong": "1717518207992" + } + } +},{ + "_id": { + "$oid": "665f3fc08c25216ef4f53dfb" + }, + "metadata": { + "securityResultCode": "success", + "bsmSource": "RV", + "schemaVersion": 6, + "odePacketID": "", + "sanitized": false, + "recordType": "bsmTx", + "recordGeneratedAt": "", + "maxDurationTime": 0, + "odeTimStartDateTime": "", + "receivedMessageDetails": { + "locationData": { + "elevation": "", + "heading": "", + "latitude": "", + "speed": "", + "longitude": "" + }, + "rxSource": "RV" + }, + "payloadType": "us.dot.its.jpo.ode.model.OdeBsmPayload", + "serialId": { + "recordId": 0, + "serialNumber": 0, + "streamId": "849c3ac6-4747-426f-ae5e-28ac8531b32c", + "bundleSize": 1, + "bundleId": 0 + }, + "logFileName": "", + "odeReceivedAt": "2024-06-04T16:23:28.092Z", + "originIp": "10.11.81.12" + }, + "payload": { + "data": { + "partII": [ + { + "id": "VehicleSafetyExtensions", + "value": { + "pathHistory": { + "crumbData": [ + { + "elevationOffset": 0.6, + "lonOffset": 0.0000387, + "timeOffset": 0.8, + "latOffset": -0.0001604 + }, + { + "elevationOffset": 4.3, + "lonOffset": 0.000231, + "timeOffset": 5.6, + "latOffset": -0.0011603 + }, + { + "elevationOffset": 9.1, + "lonOffset": 0.0003834, + "timeOffset": 11, + "latOffset": -0.0023425 + } + ] + }, + "pathPrediction": { + "confidence": 80, + "radiusOfCurve": -1622.5 + } + } + } + ], + "coreData": { + "secMark": 28092, + "transmission": "UNAVAILABLE", + "size": { + "width": 180, + "length": 480 + }, + "heading": 347.7, + "accelSet": { + "accelYaw": -0.65, + "accelLong": -0.05 + }, + "brakes": { + "scs": "unavailable", + "wheelBrakes": { + "leftFront": false, + "rightFront": false, + "unavailable": true, + "leftRear": false, + "rightRear": false + }, + "abs": "off", + "traction": "unavailable", + "brakeBoost": "unavailable", + "auxBrakes": "unavailable" + }, + "accuracy": { + "semiMinor": 2, + "orientation": 0, + "semiMajor": 2 + }, + "id": "A0B43C7E", + "position": { + "elevation": 1691.9, + "latitude": 39.58741060490348, + "longitude": -105.09064631517474 + }, + "msgCnt": 5, + "speed": 37.65 + } + }, + "dataType": "us.dot.its.jpo.ode.plugin.j2735.J2735Bsm" + }, + "recordGeneratedAt": { + "$date": { + "$numberLong": "1717518208099" + } + } +},{ + "_id": { + "$oid": "665f3fc08c25216ef4f53dfc" + }, + "metadata": { + "securityResultCode": "success", + "bsmSource": "RV", + "schemaVersion": 6, + "odePacketID": "", + "sanitized": false, + "recordType": "bsmTx", + "recordGeneratedAt": "", + "maxDurationTime": 0, + "odeTimStartDateTime": "", + "receivedMessageDetails": { + "locationData": { + "elevation": "", + "heading": "", + "latitude": "", + "speed": "", + "longitude": "" + }, + "rxSource": "RV" + }, + "payloadType": "us.dot.its.jpo.ode.model.OdeBsmPayload", + "serialId": { + "recordId": 0, + "serialNumber": 0, + "streamId": "849c3ac6-4747-426f-ae5e-28ac8531b32c", + "bundleSize": 1, + "bundleId": 0 + }, + "logFileName": "", + "odeReceivedAt": "2024-06-04T16:23:28.2Z", + "originIp": "10.11.81.12" + }, + "payload": { + "data": { + "partII": [ + { + "id": "VehicleSafetyExtensions", + "value": { + "pathHistory": { + "crumbData": [ + { + "elevationOffset": 0.6, + "lonOffset": 0.0000387, + "timeOffset": 0.8, + "latOffset": -0.0001604 + }, + { + "elevationOffset": 4.3, + "lonOffset": 0.000231, + "timeOffset": 5.6, + "latOffset": -0.0011603 + }, + { + "elevationOffset": 9.1, + "lonOffset": 0.0003834, + "timeOffset": 11, + "latOffset": -0.0023425 + } + ] + }, + "pathPrediction": { + "confidence": 80, + "radiusOfCurve": -1622.5 + } + } + } + ], + "coreData": { + "secMark": 28200, + "transmission": "UNAVAILABLE", + "size": { + "width": 180, + "length": 480 + }, + "heading": 343.3, + "accelSet": { + "accelYaw": -0.65, + "accelLong": -0.05 + }, + "brakes": { + "scs": "unavailable", + "wheelBrakes": { + "leftFront": false, + "rightFront": false, + "unavailable": true, + "leftRear": false, + "rightRear": false + }, + "abs": "off", + "traction": "unavailable", + "brakeBoost": "unavailable", + "auxBrakes": "unavailable" + }, + "accuracy": { + "semiMinor": 2, + "orientation": 0, + "semiMajor": 2 + }, + "id": "A0B43C7E", + "position": { + "elevation": 1691.9, + "latitude": 39.58744161069427, + "longitude": -105.09065838709249 + }, + "msgCnt": 6, + "speed": 36 + } + }, + "dataType": "us.dot.its.jpo.ode.plugin.j2735.J2735Bsm" + }, + "recordGeneratedAt": { + "$date": { + "$numberLong": "1717518208207" + } + } +},{ + "_id": { + "$oid": "665f3fc08c25216ef4f53dfd" + }, + "metadata": { + "securityResultCode": "success", + "bsmSource": "RV", + "schemaVersion": 6, + "odePacketID": "", + "sanitized": false, + "recordType": "bsmTx", + "recordGeneratedAt": "", + "maxDurationTime": 0, + "odeTimStartDateTime": "", + "receivedMessageDetails": { + "locationData": { + "elevation": "", + "heading": "", + "latitude": "", + "speed": "", + "longitude": "" + }, + "rxSource": "RV" + }, + "payloadType": "us.dot.its.jpo.ode.model.OdeBsmPayload", + "serialId": { + "recordId": 0, + "serialNumber": 0, + "streamId": "849c3ac6-4747-426f-ae5e-28ac8531b32c", + "bundleSize": 1, + "bundleId": 0 + }, + "logFileName": "", + "odeReceivedAt": "2024-06-04T16:23:28.308Z", + "originIp": "10.11.81.12" + }, + "payload": { + "data": { + "partII": [ + { + "id": "VehicleSafetyExtensions", + "value": { + "pathHistory": { + "crumbData": [ + { + "elevationOffset": 0.6, + "lonOffset": 0.0000387, + "timeOffset": 0.8, + "latOffset": -0.0001604 + }, + { + "elevationOffset": 4.3, + "lonOffset": 0.000231, + "timeOffset": 5.6, + "latOffset": -0.0011603 + }, + { + "elevationOffset": 9.1, + "lonOffset": 0.0003834, + "timeOffset": 11, + "latOffset": -0.0023425 + } + ] + }, + "pathPrediction": { + "confidence": 80, + "radiusOfCurve": -1622.5 + } + } + } + ], + "coreData": { + "secMark": 28308, + "transmission": "UNAVAILABLE", + "size": { + "width": 180, + "length": 480 + }, + "heading": 348, + "accelSet": { + "accelYaw": -0.65, + "accelLong": -0.05 + }, + "brakes": { + "scs": "unavailable", + "wheelBrakes": { + "leftFront": false, + "rightFront": false, + "unavailable": true, + "leftRear": false, + "rightRear": false + }, + "abs": "off", + "traction": "unavailable", + "brakeBoost": "unavailable", + "auxBrakes": "unavailable" + }, + "accuracy": { + "semiMinor": 2, + "orientation": 0, + "semiMajor": 2 + }, + "id": "A0B43C7E", + "position": { + "elevation": 1691.9, + "latitude": 39.587475717048164, + "longitude": -105.09066777636183 + }, + "msgCnt": 7, + "speed": 38.77 + } + }, + "dataType": "us.dot.its.jpo.ode.plugin.j2735.J2735Bsm" + }, + "recordGeneratedAt": { + "$date": { + "$numberLong": "1717518208314" + } + } +},{ + "_id": { + "$oid": "665f3fc08c25216ef4f53dfe" + }, + "metadata": { + "securityResultCode": "success", + "bsmSource": "RV", + "schemaVersion": 6, + "odePacketID": "", + "sanitized": false, + "recordType": "bsmTx", + "recordGeneratedAt": "", + "maxDurationTime": 0, + "odeTimStartDateTime": "", + "receivedMessageDetails": { + "locationData": { + "elevation": "", + "heading": "", + "latitude": "", + "speed": "", + "longitude": "" + }, + "rxSource": "RV" + }, + "payloadType": "us.dot.its.jpo.ode.model.OdeBsmPayload", + "serialId": { + "recordId": 0, + "serialNumber": 0, + "streamId": "849c3ac6-4747-426f-ae5e-28ac8531b32c", + "bundleSize": 1, + "bundleId": 0 + }, + "logFileName": "", + "odeReceivedAt": "2024-06-04T16:23:28.413Z", + "originIp": "10.11.81.12" + }, + "payload": { + "data": { + "partII": [ + { + "id": "VehicleSafetyExtensions", + "value": { + "pathHistory": { + "crumbData": [ + { + "elevationOffset": 0.6, + "lonOffset": 0.0000387, + "timeOffset": 0.8, + "latOffset": -0.0001604 + }, + { + "elevationOffset": 4.3, + "lonOffset": 0.000231, + "timeOffset": 5.6, + "latOffset": -0.0011603 + }, + { + "elevationOffset": 9.1, + "lonOffset": 0.0003834, + "timeOffset": 11, + "latOffset": -0.0023425 + } + ] + }, + "pathPrediction": { + "confidence": 80, + "radiusOfCurve": -1622.5 + } + } + } + ], + "coreData": { + "secMark": 28413, + "transmission": "UNAVAILABLE", + "size": { + "width": 180, + "length": 480 + }, + "heading": 344.6, + "accelSet": { + "accelYaw": -0.65, + "accelLong": -0.05 + }, + "brakes": { + "scs": "unavailable", + "wheelBrakes": { + "leftFront": false, + "rightFront": false, + "unavailable": true, + "leftRear": false, + "rightRear": false + }, + "abs": "off", + "traction": "unavailable", + "brakeBoost": "unavailable", + "auxBrakes": "unavailable" + }, + "accuracy": { + "semiMinor": 2, + "orientation": 0, + "semiMajor": 2 + }, + "id": "A0B43C7E", + "position": { + "elevation": 1691.9, + "latitude": 39.58750568928466, + "longitude": -105.09067850695536 + }, + "msgCnt": 8, + "speed": 34.57 + } + }, + "dataType": "us.dot.its.jpo.ode.plugin.j2735.J2735Bsm" + }, + "recordGeneratedAt": { + "$date": { + "$numberLong": "1717518208421" + } + } +},{ + "_id": { + "$oid": "665f3fc08c25216ef4f53dff" + }, + "metadata": { + "securityResultCode": "success", + "bsmSource": "RV", + "schemaVersion": 6, + "odePacketID": "", + "sanitized": false, + "recordType": "bsmTx", + "recordGeneratedAt": "", + "maxDurationTime": 0, + "odeTimStartDateTime": "", + "receivedMessageDetails": { + "locationData": { + "elevation": "", + "heading": "", + "latitude": "", + "speed": "", + "longitude": "" + }, + "rxSource": "RV" + }, + "payloadType": "us.dot.its.jpo.ode.model.OdeBsmPayload", + "serialId": { + "recordId": 0, + "serialNumber": 0, + "streamId": "849c3ac6-4747-426f-ae5e-28ac8531b32c", + "bundleSize": 1, + "bundleId": 0 + }, + "logFileName": "", + "odeReceivedAt": "2024-06-04T16:23:28.522Z", + "originIp": "10.11.81.12" + }, + "payload": { + "data": { + "partII": [ + { + "id": "VehicleSafetyExtensions", + "value": { + "pathHistory": { + "crumbData": [ + { + "elevationOffset": 0.6, + "lonOffset": 0.0000387, + "timeOffset": 0.8, + "latOffset": -0.0001604 + }, + { + "elevationOffset": 4.3, + "lonOffset": 0.000231, + "timeOffset": 5.6, + "latOffset": -0.0011603 + }, + { + "elevationOffset": 9.1, + "lonOffset": 0.0003834, + "timeOffset": 11, + "latOffset": -0.0023425 + } + ] + }, + "pathPrediction": { + "confidence": 80, + "radiusOfCurve": -1622.5 + } + } + } + ], + "coreData": { + "secMark": 28522, + "transmission": "UNAVAILABLE", + "size": { + "width": 180, + "length": 480 + }, + "heading": 346, + "accelSet": { + "accelYaw": -0.65, + "accelLong": -0.05 + }, + "brakes": { + "scs": "unavailable", + "wheelBrakes": { + "leftFront": false, + "rightFront": false, + "unavailable": true, + "leftRear": false, + "rightRear": false + }, + "abs": "off", + "traction": "unavailable", + "brakeBoost": "unavailable", + "auxBrakes": "unavailable" + }, + "accuracy": { + "semiMinor": 2, + "orientation": 0, + "semiMajor": 2 + }, + "id": "A0B43C7E", + "position": { + "elevation": 1691.9, + "latitude": 39.58753049388438, + "longitude": -105.09068655490051 + }, + "msgCnt": 9, + "speed": 28.43 + } + }, + "dataType": "us.dot.its.jpo.ode.plugin.j2735.J2735Bsm" + }, + "recordGeneratedAt": { + "$date": { + "$numberLong": "1717518208528" + } + } +},{ + "_id": { + "$oid": "665f3fc08c25216ef4f53e00" + }, + "metadata": { + "securityResultCode": "success", + "bsmSource": "RV", + "schemaVersion": 6, + "odePacketID": "", + "sanitized": false, + "recordType": "bsmTx", + "recordGeneratedAt": "", + "maxDurationTime": 0, + "odeTimStartDateTime": "", + "receivedMessageDetails": { + "locationData": { + "elevation": "", + "heading": "", + "latitude": "", + "speed": "", + "longitude": "" + }, + "rxSource": "RV" + }, + "payloadType": "us.dot.its.jpo.ode.model.OdeBsmPayload", + "serialId": { + "recordId": 0, + "serialNumber": 0, + "streamId": "849c3ac6-4747-426f-ae5e-28ac8531b32c", + "bundleSize": 1, + "bundleId": 0 + }, + "logFileName": "", + "odeReceivedAt": "2024-06-04T16:23:28.629Z", + "originIp": "10.11.81.12" + }, + "payload": { + "data": { + "partII": [ + { + "id": "VehicleSafetyExtensions", + "value": { + "pathHistory": { + "crumbData": [ + { + "elevationOffset": 0.6, + "lonOffset": 0.0000387, + "timeOffset": 0.8, + "latOffset": -0.0001604 + }, + { + "elevationOffset": 4.3, + "lonOffset": 0.000231, + "timeOffset": 5.6, + "latOffset": -0.0011603 + }, + { + "elevationOffset": 9.1, + "lonOffset": 0.0003834, + "timeOffset": 11, + "latOffset": -0.0023425 + } + ] + }, + "pathPrediction": { + "confidence": 80, + "radiusOfCurve": -1622.5 + } + } + } + ], + "coreData": { + "secMark": 28629, + "transmission": "UNAVAILABLE", + "size": { + "width": 180, + "length": 480 + }, + "heading": 347, + "accelSet": { + "accelYaw": -0.65, + "accelLong": -0.05 + }, + "brakes": { + "scs": "unavailable", + "wheelBrakes": { + "leftFront": false, + "rightFront": false, + "unavailable": true, + "leftRear": false, + "rightRear": false + }, + "abs": "off", + "traction": "unavailable", + "brakeBoost": "unavailable", + "auxBrakes": "unavailable" + }, + "accuracy": { + "semiMinor": 2, + "orientation": 0, + "semiMajor": 2 + }, + "id": "A0B43C7E", + "position": { + "elevation": 1691.9, + "latitude": 39.58755633199964, + "longitude": -105.09069429121115 + }, + "msgCnt": 10, + "speed": 29.49 + } + }, + "dataType": "us.dot.its.jpo.ode.plugin.j2735.J2735Bsm" + }, + "recordGeneratedAt": { + "$date": { + "$numberLong": "1717518208636" + } + } +},{ + "_id": { + "$oid": "665f3fc08c25216ef4f53e01" + }, + "metadata": { + "securityResultCode": "success", + "bsmSource": "RV", + "schemaVersion": 6, + "odePacketID": "", + "sanitized": false, + "recordType": "bsmTx", + "recordGeneratedAt": "", + "maxDurationTime": 0, + "odeTimStartDateTime": "", + "receivedMessageDetails": { + "locationData": { + "elevation": "", + "heading": "", + "latitude": "", + "speed": "", + "longitude": "" + }, + "rxSource": "RV" + }, + "payloadType": "us.dot.its.jpo.ode.model.OdeBsmPayload", + "serialId": { + "recordId": 0, + "serialNumber": 0, + "streamId": "849c3ac6-4747-426f-ae5e-28ac8531b32c", + "bundleSize": 1, + "bundleId": 0 + }, + "logFileName": "", + "odeReceivedAt": "2024-06-04T16:23:28.735Z", + "originIp": "10.11.81.12" + }, + "payload": { + "data": { + "partII": [ + { + "id": "VehicleSafetyExtensions", + "value": { + "pathHistory": { + "crumbData": [ + { + "elevationOffset": 0.6, + "lonOffset": 0.0000387, + "timeOffset": 0.8, + "latOffset": -0.0001604 + }, + { + "elevationOffset": 4.3, + "lonOffset": 0.000231, + "timeOffset": 5.6, + "latOffset": -0.0011603 + }, + { + "elevationOffset": 9.1, + "lonOffset": 0.0003834, + "timeOffset": 11, + "latOffset": -0.0023425 + } + ] + }, + "pathPrediction": { + "confidence": 80, + "radiusOfCurve": -1622.5 + } + } + } + ], + "coreData": { + "secMark": 28735, + "transmission": "UNAVAILABLE", + "size": { + "width": 180, + "length": 480 + }, + "heading": 347.2, + "accelSet": { + "accelYaw": -0.65, + "accelLong": -0.05 + }, + "brakes": { + "scs": "unavailable", + "wheelBrakes": { + "leftFront": false, + "rightFront": false, + "unavailable": true, + "leftRear": false, + "rightRear": false + }, + "abs": "off", + "traction": "unavailable", + "brakeBoost": "unavailable", + "auxBrakes": "unavailable" + }, + "accuracy": { + "semiMinor": 2, + "orientation": 0, + "semiMajor": 2 + }, + "id": "A0B43C7E", + "position": { + "elevation": 1691.9, + "latitude": 39.58758578743928, + "longitude": -105.09070300981843 + }, + "msgCnt": 11, + "speed": 33.59 + } + }, + "dataType": "us.dot.its.jpo.ode.plugin.j2735.J2735Bsm" + }, + "recordGeneratedAt": { + "$date": { + "$numberLong": "1717518208743" + } + } +},{ + "_id": { + "$oid": "665f3fc08c25216ef4f53e02" + }, + "metadata": { + "securityResultCode": "success", + "bsmSource": "RV", + "schemaVersion": 6, + "odePacketID": "", + "sanitized": false, + "recordType": "bsmTx", + "recordGeneratedAt": "", + "maxDurationTime": 0, + "odeTimStartDateTime": "", + "receivedMessageDetails": { + "locationData": { + "elevation": "", + "heading": "", + "latitude": "", + "speed": "", + "longitude": "" + }, + "rxSource": "RV" + }, + "payloadType": "us.dot.its.jpo.ode.model.OdeBsmPayload", + "serialId": { + "recordId": 0, + "serialNumber": 0, + "streamId": "849c3ac6-4747-426f-ae5e-28ac8531b32c", + "bundleSize": 1, + "bundleId": 0 + }, + "logFileName": "", + "odeReceivedAt": "2024-06-04T16:23:28.843Z", + "originIp": "10.11.81.12" + }, + "payload": { + "data": { + "partII": [ + { + "id": "VehicleSafetyExtensions", + "value": { + "pathHistory": { + "crumbData": [ + { + "elevationOffset": 0.6, + "lonOffset": 0.0000387, + "timeOffset": 0.8, + "latOffset": -0.0001604 + }, + { + "elevationOffset": 4.3, + "lonOffset": 0.000231, + "timeOffset": 5.6, + "latOffset": -0.0011603 + }, + { + "elevationOffset": 9.1, + "lonOffset": 0.0003834, + "timeOffset": 11, + "latOffset": -0.0023425 + } + ] + }, + "pathPrediction": { + "confidence": 80, + "radiusOfCurve": -1622.5 + } + } + } + ], + "coreData": { + "secMark": 28843, + "transmission": "UNAVAILABLE", + "size": { + "width": 180, + "length": 480 + }, + "heading": 345.2, + "accelSet": { + "accelYaw": -0.65, + "accelLong": -0.05 + }, + "brakes": { + "scs": "unavailable", + "wheelBrakes": { + "leftFront": false, + "rightFront": false, + "unavailable": true, + "leftRear": false, + "rightRear": false + }, + "abs": "off", + "traction": "unavailable", + "brakeBoost": "unavailable", + "auxBrakes": "unavailable" + }, + "accuracy": { + "semiMinor": 2, + "orientation": 0, + "semiMajor": 2 + }, + "id": "A0B43C7E", + "position": { + "elevation": 1691.9, + "latitude": 39.587613175819314, + "longitude": -105.09071239908775 + }, + "msgCnt": 12, + "speed": 31.5 + } + }, + "dataType": "us.dot.its.jpo.ode.plugin.j2735.J2735Bsm" + }, + "recordGeneratedAt": { + "$date": { + "$numberLong": "1717518208850" + } + } +},{ + "_id": { + "$oid": "665f3fc08c25216ef4f53e03" + }, + "metadata": { + "securityResultCode": "success", + "bsmSource": "RV", + "schemaVersion": 6, + "odePacketID": "", + "sanitized": false, + "recordType": "bsmTx", + "recordGeneratedAt": "", + "maxDurationTime": 0, + "odeTimStartDateTime": "", + "receivedMessageDetails": { + "locationData": { + "elevation": "", + "heading": "", + "latitude": "", + "speed": "", + "longitude": "" + }, + "rxSource": "RV" + }, + "payloadType": "us.dot.its.jpo.ode.model.OdeBsmPayload", + "serialId": { + "recordId": 0, + "serialNumber": 0, + "streamId": "849c3ac6-4747-426f-ae5e-28ac8531b32c", + "bundleSize": 1, + "bundleId": 0 + }, + "logFileName": "", + "odeReceivedAt": "2024-06-04T16:23:28.95Z", + "originIp": "10.11.81.12" + }, + "payload": { + "data": { + "partII": [ + { + "id": "VehicleSafetyExtensions", + "value": { + "pathHistory": { + "crumbData": [ + { + "elevationOffset": 0.6, + "lonOffset": 0.0000387, + "timeOffset": 0.8, + "latOffset": -0.0001604 + }, + { + "elevationOffset": 4.3, + "lonOffset": 0.000231, + "timeOffset": 5.6, + "latOffset": -0.0011603 + }, + { + "elevationOffset": 9.1, + "lonOffset": 0.0003834, + "timeOffset": 11, + "latOffset": -0.0023425 + } + ] + }, + "pathPrediction": { + "confidence": 80, + "radiusOfCurve": -1622.5 + } + } + } + ], + "coreData": { + "secMark": 28950, + "transmission": "UNAVAILABLE", + "size": { + "width": 180, + "length": 480 + }, + "heading": 346.2, + "accelSet": { + "accelYaw": -0.65, + "accelLong": -0.05 + }, + "brakes": { + "scs": "unavailable", + "wheelBrakes": { + "leftFront": false, + "rightFront": false, + "unavailable": true, + "leftRear": false, + "rightRear": false + }, + "abs": "off", + "traction": "unavailable", + "brakeBoost": "unavailable", + "auxBrakes": "unavailable" + }, + "accuracy": { + "semiMinor": 2, + "orientation": 0, + "semiMajor": 2 + }, + "id": "A0B43C7E", + "position": { + "elevation": 1691.9, + "latitude": 39.587642631234786, + "longitude": -105.09072178835711 + }, + "msgCnt": 13, + "speed": 33.73 + } + }, + "dataType": "us.dot.its.jpo.ode.plugin.j2735.J2735Bsm" + }, + "recordGeneratedAt": { + "$date": { + "$numberLong": "1717518208957" + } + } +},{ + "_id": { + "$oid": "665f3fc08c25216ef4f53e04" + }, + "metadata": { + "securityResultCode": "success", + "bsmSource": "RV", + "schemaVersion": 6, + "odePacketID": "", + "sanitized": false, + "recordType": "bsmTx", + "recordGeneratedAt": "", + "maxDurationTime": 0, + "odeTimStartDateTime": "", + "receivedMessageDetails": { + "locationData": { + "elevation": "", + "heading": "", + "latitude": "", + "speed": "", + "longitude": "" + }, + "rxSource": "RV" + }, + "payloadType": "us.dot.its.jpo.ode.model.OdeBsmPayload", + "serialId": { + "recordId": 0, + "serialNumber": 0, + "streamId": "849c3ac6-4747-426f-ae5e-28ac8531b32c", + "bundleSize": 1, + "bundleId": 0 + }, + "logFileName": "", + "odeReceivedAt": "2024-06-04T16:23:29.057Z", + "originIp": "10.11.81.12" + }, + "payload": { + "data": { + "partII": [ + { + "id": "VehicleSafetyExtensions", + "value": { + "pathHistory": { + "crumbData": [ + { + "elevationOffset": 0.6, + "lonOffset": 0.0000387, + "timeOffset": 0.8, + "latOffset": -0.0001604 + }, + { + "elevationOffset": 4.3, + "lonOffset": 0.000231, + "timeOffset": 5.6, + "latOffset": -0.0011603 + }, + { + "elevationOffset": 9.1, + "lonOffset": 0.0003834, + "timeOffset": 11, + "latOffset": -0.0023425 + } + ] + }, + "pathPrediction": { + "confidence": 80, + "radiusOfCurve": -1622.5 + } + } + } + ], + "coreData": { + "secMark": 29057, + "transmission": "UNAVAILABLE", + "size": { + "width": 180, + "length": 480 + }, + "heading": 345.6, + "accelSet": { + "accelYaw": -0.65, + "accelLong": -0.05 + }, + "brakes": { + "scs": "unavailable", + "wheelBrakes": { + "leftFront": false, + "rightFront": false, + "unavailable": true, + "leftRear": false, + "rightRear": false + }, + "abs": "off", + "traction": "unavailable", + "brakeBoost": "unavailable", + "auxBrakes": "unavailable" + }, + "accuracy": { + "semiMinor": 2, + "orientation": 0, + "semiMajor": 2 + }, + "id": "A0B43C7E", + "position": { + "elevation": 1691.9, + "latitude": 39.58767673748972, + "longitude": -105.09073318961273 + }, + "msgCnt": 14, + "speed": 39.16 + } + }, + "dataType": "us.dot.its.jpo.ode.plugin.j2735.J2735Bsm" + }, + "recordGeneratedAt": { + "$date": { + "$numberLong": "1717518209065" + } + } +},{ + "_id": { + "$oid": "665f3fc08c25216ef4f53e05" + }, + "metadata": { + "securityResultCode": "success", + "bsmSource": "RV", + "schemaVersion": 6, + "odePacketID": "", + "sanitized": false, + "recordType": "bsmTx", + "recordGeneratedAt": "", + "maxDurationTime": 0, + "odeTimStartDateTime": "", + "receivedMessageDetails": { + "locationData": { + "elevation": "", + "heading": "", + "latitude": "", + "speed": "", + "longitude": "" + }, + "rxSource": "RV" + }, + "payloadType": "us.dot.its.jpo.ode.model.OdeBsmPayload", + "serialId": { + "recordId": 0, + "serialNumber": 0, + "streamId": "849c3ac6-4747-426f-ae5e-28ac8531b32c", + "bundleSize": 1, + "bundleId": 0 + }, + "logFileName": "", + "odeReceivedAt": "2024-06-04T16:23:29.164Z", + "originIp": "10.11.81.12" + }, + "payload": { + "data": { + "partII": [ + { + "id": "VehicleSafetyExtensions", + "value": { + "pathHistory": { + "crumbData": [ + { + "elevationOffset": 0.6, + "lonOffset": 0.0000387, + "timeOffset": 0.8, + "latOffset": -0.0001604 + }, + { + "elevationOffset": 4.3, + "lonOffset": 0.000231, + "timeOffset": 5.6, + "latOffset": -0.0011603 + }, + { + "elevationOffset": 9.1, + "lonOffset": 0.0003834, + "timeOffset": 11, + "latOffset": -0.0023425 + } + ] + }, + "pathPrediction": { + "confidence": 80, + "radiusOfCurve": -1622.5 + } + } + } + ], + "coreData": { + "secMark": 29164, + "transmission": "UNAVAILABLE", + "size": { + "width": 180, + "length": 480 + }, + "heading": 350, + "accelSet": { + "accelYaw": -0.65, + "accelLong": -0.05 + }, + "brakes": { + "scs": "unavailable", + "wheelBrakes": { + "leftFront": false, + "rightFront": false, + "unavailable": true, + "leftRear": false, + "rightRear": false + }, + "abs": "off", + "traction": "unavailable", + "brakeBoost": "unavailable", + "auxBrakes": "unavailable" + }, + "accuracy": { + "semiMinor": 2, + "orientation": 0, + "semiMajor": 2 + }, + "id": "A0B43C7E", + "position": { + "elevation": 1691.9, + "latitude": 39.58769430737206, + "longitude": -105.09073721358531 + }, + "msgCnt": 15, + "speed": 19.84 + } + }, + "dataType": "us.dot.its.jpo.ode.plugin.j2735.J2735Bsm" + }, + "recordGeneratedAt": { + "$date": { + "$numberLong": "1717518209172" + } + } +},{ + "_id": { + "$oid": "665f3fc08c25216ef4f53e06" + }, + "metadata": { + "securityResultCode": "success", + "bsmSource": "RV", + "schemaVersion": 6, + "odePacketID": "", + "sanitized": false, + "recordType": "bsmTx", + "recordGeneratedAt": "", + "maxDurationTime": 0, + "odeTimStartDateTime": "", + "receivedMessageDetails": { + "locationData": { + "elevation": "", + "heading": "", + "latitude": "", + "speed": "", + "longitude": "" + }, + "rxSource": "RV" + }, + "payloadType": "us.dot.its.jpo.ode.model.OdeBsmPayload", + "serialId": { + "recordId": 0, + "serialNumber": 0, + "streamId": "849c3ac6-4747-426f-ae5e-28ac8531b32c", + "bundleSize": 1, + "bundleId": 0 + }, + "logFileName": "", + "odeReceivedAt": "2024-06-04T16:23:29.272Z", + "originIp": "10.11.81.12" + }, + "payload": { + "data": { + "partII": [ + { + "id": "VehicleSafetyExtensions", + "value": { + "pathHistory": { + "crumbData": [ + { + "elevationOffset": 0.6, + "lonOffset": 0.0000387, + "timeOffset": 0.8, + "latOffset": -0.0001604 + }, + { + "elevationOffset": 4.3, + "lonOffset": 0.000231, + "timeOffset": 5.6, + "latOffset": -0.0011603 + }, + { + "elevationOffset": 9.1, + "lonOffset": 0.0003834, + "timeOffset": 11, + "latOffset": -0.0023425 + } + ] + }, + "pathPrediction": { + "confidence": 80, + "radiusOfCurve": -1622.5 + } + } + } + ], + "coreData": { + "secMark": 29272, + "transmission": "UNAVAILABLE", + "size": { + "width": 180, + "length": 480 + }, + "heading": 349, + "accelSet": { + "accelYaw": -0.65, + "accelLong": -0.05 + }, + "brakes": { + "scs": "unavailable", + "wheelBrakes": { + "leftFront": false, + "rightFront": false, + "unavailable": true, + "leftRear": false, + "rightRear": false + }, + "abs": "off", + "traction": "unavailable", + "brakeBoost": "unavailable", + "auxBrakes": "unavailable" + }, + "accuracy": { + "semiMinor": 2, + "orientation": 0, + "semiMajor": 2 + }, + "id": "A0B43C7E", + "position": { + "elevation": 1691.9, + "latitude": 39.58771032696678, + "longitude": -105.09074123755789 + }, + "msgCnt": 16, + "speed": 18.14 + } + }, + "dataType": "us.dot.its.jpo.ode.plugin.j2735.J2735Bsm" + }, + "recordGeneratedAt": { + "$date": { + "$numberLong": "1717518209278" + } + } +},{ + "_id": { + "$oid": "665f3fc08c25216ef4f53e07" + }, + "metadata": { + "securityResultCode": "success", + "bsmSource": "RV", + "schemaVersion": 6, + "odePacketID": "", + "sanitized": false, + "recordType": "bsmTx", + "recordGeneratedAt": "", + "maxDurationTime": 0, + "odeTimStartDateTime": "", + "receivedMessageDetails": { + "locationData": { + "elevation": "", + "heading": "", + "latitude": "", + "speed": "", + "longitude": "" + }, + "rxSource": "RV" + }, + "payloadType": "us.dot.its.jpo.ode.model.OdeBsmPayload", + "serialId": { + "recordId": 0, + "serialNumber": 0, + "streamId": "849c3ac6-4747-426f-ae5e-28ac8531b32c", + "bundleSize": 1, + "bundleId": 0 + }, + "logFileName": "", + "odeReceivedAt": "2024-06-04T16:23:29.379Z", + "originIp": "10.11.81.12" + }, + "payload": { + "data": { + "partII": [ + { + "id": "VehicleSafetyExtensions", + "value": { + "pathHistory": { + "crumbData": [ + { + "elevationOffset": 0.6, + "lonOffset": 0.0000387, + "timeOffset": 0.8, + "latOffset": -0.0001604 + }, + { + "elevationOffset": 4.3, + "lonOffset": 0.000231, + "timeOffset": 5.6, + "latOffset": -0.0011603 + }, + { + "elevationOffset": 9.1, + "lonOffset": 0.0003834, + "timeOffset": 11, + "latOffset": -0.0023425 + } + ] + }, + "pathPrediction": { + "confidence": 80, + "radiusOfCurve": -1622.5 + } + } + } + ], + "coreData": { + "secMark": 29379, + "transmission": "UNAVAILABLE", + "size": { + "width": 180, + "length": 480 + }, + "heading": 345.2, + "accelSet": { + "accelYaw": -0.65, + "accelLong": -0.05 + }, + "brakes": { + "scs": "unavailable", + "wheelBrakes": { + "leftFront": false, + "rightFront": false, + "unavailable": true, + "leftRear": false, + "rightRear": false + }, + "abs": "off", + "traction": "unavailable", + "brakeBoost": "unavailable", + "auxBrakes": "unavailable" + }, + "accuracy": { + "semiMinor": 2, + "orientation": 0, + "semiMajor": 2 + }, + "id": "A0B43C7E", + "position": { + "elevation": 1691.9, + "latitude": 39.58772789684062, + "longitude": -105.09074727351678 + }, + "msgCnt": 17, + "speed": 20.21 + } + }, + "dataType": "us.dot.its.jpo.ode.plugin.j2735.J2735Bsm" + }, + "recordGeneratedAt": { + "$date": { + "$numberLong": "1717518209385" + } + } +},{ + "_id": { + "$oid": "665f3fc08c25216ef4f53e08" + }, + "metadata": { + "securityResultCode": "success", + "bsmSource": "RV", + "schemaVersion": 6, + "odePacketID": "", + "sanitized": false, + "recordType": "bsmTx", + "recordGeneratedAt": "", + "maxDurationTime": 0, + "odeTimStartDateTime": "", + "receivedMessageDetails": { + "locationData": { + "elevation": "", + "heading": "", + "latitude": "", + "speed": "", + "longitude": "" + }, + "rxSource": "RV" + }, + "payloadType": "us.dot.its.jpo.ode.model.OdeBsmPayload", + "serialId": { + "recordId": 0, + "serialNumber": 0, + "streamId": "849c3ac6-4747-426f-ae5e-28ac8531b32c", + "bundleSize": 1, + "bundleId": 0 + }, + "logFileName": "", + "odeReceivedAt": "2024-06-04T16:23:29.486Z", + "originIp": "10.11.81.12" + }, + "payload": { + "data": { + "partII": [ + { + "id": "VehicleSafetyExtensions", + "value": { + "pathHistory": { + "crumbData": [ + { + "elevationOffset": 0.6, + "lonOffset": 0.0000387, + "timeOffset": 0.8, + "latOffset": -0.0001604 + }, + { + "elevationOffset": 4.3, + "lonOffset": 0.000231, + "timeOffset": 5.6, + "latOffset": -0.0011603 + }, + { + "elevationOffset": 9.1, + "lonOffset": 0.0003834, + "timeOffset": 11, + "latOffset": -0.0023425 + } + ] + }, + "pathPrediction": { + "confidence": 80, + "radiusOfCurve": -1622.5 + } + } + } + ], + "coreData": { + "secMark": 29486, + "transmission": "UNAVAILABLE", + "size": { + "width": 180, + "length": 480 + }, + "heading": 346.3, + "accelSet": { + "accelYaw": -0.65, + "accelLong": -0.05 + }, + "brakes": { + "scs": "unavailable", + "wheelBrakes": { + "leftFront": false, + "rightFront": false, + "unavailable": true, + "leftRear": false, + "rightRear": false + }, + "abs": "off", + "traction": "unavailable", + "brakeBoost": "unavailable", + "auxBrakes": "unavailable" + }, + "accuracy": { + "semiMinor": 2, + "orientation": 0, + "semiMajor": 2 + }, + "id": "A0B43C7E", + "position": { + "elevation": 1691.9, + "latitude": 39.58774908403548, + "longitude": -105.09075398013772 + }, + "msgCnt": 18, + "speed": 24.25 + } + }, + "dataType": "us.dot.its.jpo.ode.plugin.j2735.J2735Bsm" + }, + "recordGeneratedAt": { + "$date": { + "$numberLong": "1717518209490" + } + } +},{ + "_id": { + "$oid": "665f3fc08c25216ef4f53e09" + }, + "metadata": { + "securityResultCode": "success", + "bsmSource": "RV", + "schemaVersion": 6, + "odePacketID": "", + "sanitized": false, + "recordType": "bsmTx", + "recordGeneratedAt": "", + "maxDurationTime": 0, + "odeTimStartDateTime": "", + "receivedMessageDetails": { + "locationData": { + "elevation": "", + "heading": "", + "latitude": "", + "speed": "", + "longitude": "" + }, + "rxSource": "RV" + }, + "payloadType": "us.dot.its.jpo.ode.model.OdeBsmPayload", + "serialId": { + "recordId": 0, + "serialNumber": 0, + "streamId": "849c3ac6-4747-426f-ae5e-28ac8531b32c", + "bundleSize": 1, + "bundleId": 0 + }, + "logFileName": "", + "odeReceivedAt": "2024-06-04T16:23:29.594Z", + "originIp": "10.11.81.12" + }, + "payload": { + "data": { + "partII": [ + { + "id": "VehicleSafetyExtensions", + "value": { + "pathHistory": { + "crumbData": [ + { + "elevationOffset": 0.6, + "lonOffset": 0.0000387, + "timeOffset": 0.8, + "latOffset": -0.0001604 + }, + { + "elevationOffset": 4.3, + "lonOffset": 0.000231, + "timeOffset": 5.6, + "latOffset": -0.0011603 + }, + { + "elevationOffset": 9.1, + "lonOffset": 0.0003834, + "timeOffset": 11, + "latOffset": -0.0023425 + } + ] + }, + "pathPrediction": { + "confidence": 80, + "radiusOfCurve": -1622.5 + } + } + } + ], + "coreData": { + "secMark": 29594, + "transmission": "UNAVAILABLE", + "size": { + "width": 180, + "length": 480 + }, + "heading": 351.4, + "accelSet": { + "accelYaw": -0.65, + "accelLong": -0.05 + }, + "brakes": { + "scs": "unavailable", + "wheelBrakes": { + "leftFront": false, + "rightFront": false, + "unavailable": true, + "leftRear": false, + "rightRear": false + }, + "abs": "off", + "traction": "unavailable", + "brakeBoost": "unavailable", + "auxBrakes": "unavailable" + }, + "accuracy": { + "semiMinor": 2, + "orientation": 0, + "semiMajor": 2 + }, + "id": "A0B43C7E", + "position": { + "elevation": 1691.9, + "latitude": 39.58776613713885, + "longitude": -105.0907573334482 + }, + "msgCnt": 19, + "speed": 19.18 + } + }, + "dataType": "us.dot.its.jpo.ode.plugin.j2735.J2735Bsm" + }, + "recordGeneratedAt": { + "$date": { + "$numberLong": "1717518209598" + } + } +},{ + "_id": { + "$oid": "665f3fc08c25216ef4f53e0a" + }, + "metadata": { + "securityResultCode": "success", + "bsmSource": "RV", + "schemaVersion": 6, + "odePacketID": "", + "sanitized": false, + "recordType": "bsmTx", + "recordGeneratedAt": "", + "maxDurationTime": 0, + "odeTimStartDateTime": "", + "receivedMessageDetails": { + "locationData": { + "elevation": "", + "heading": "", + "latitude": "", + "speed": "", + "longitude": "" + }, + "rxSource": "RV" + }, + "payloadType": "us.dot.its.jpo.ode.model.OdeBsmPayload", + "serialId": { + "recordId": 0, + "serialNumber": 0, + "streamId": "849c3ac6-4747-426f-ae5e-28ac8531b32c", + "bundleSize": 1, + "bundleId": 0 + }, + "logFileName": "", + "odeReceivedAt": "2024-06-04T16:23:29.701Z", + "originIp": "10.11.81.12" + }, + "payload": { + "data": { + "partII": [ + { + "id": "VehicleSafetyExtensions", + "value": { + "pathHistory": { + "crumbData": [ + { + "elevationOffset": 0.6, + "lonOffset": 0.0000387, + "timeOffset": 0.8, + "latOffset": -0.0001604 + }, + { + "elevationOffset": 4.3, + "lonOffset": 0.000231, + "timeOffset": 5.6, + "latOffset": -0.0011603 + }, + { + "elevationOffset": 9.1, + "lonOffset": 0.0003834, + "timeOffset": 11, + "latOffset": -0.0023425 + } + ] + }, + "pathPrediction": { + "confidence": 80, + "radiusOfCurve": -1622.5 + } + } + } + ], + "coreData": { + "secMark": 29701, + "transmission": "UNAVAILABLE", + "size": { + "width": 180, + "length": 480 + }, + "heading": 346.7, + "accelSet": { + "accelYaw": -0.65, + "accelLong": -0.05 + }, + "brakes": { + "scs": "unavailable", + "wheelBrakes": { + "leftFront": false, + "rightFront": false, + "unavailable": true, + "leftRear": false, + "rightRear": false + }, + "abs": "off", + "traction": "unavailable", + "brakeBoost": "unavailable", + "auxBrakes": "unavailable" + }, + "accuracy": { + "semiMinor": 2, + "orientation": 0, + "semiMajor": 2 + }, + "id": "A0B43C7E", + "position": { + "elevation": 1691.9, + "latitude": 39.587785774040526, + "longitude": -105.09076336940707 + }, + "msgCnt": 20, + "speed": 22.44 + } + }, + "dataType": "us.dot.its.jpo.ode.plugin.j2735.J2735Bsm" + }, + "recordGeneratedAt": { + "$date": { + "$numberLong": "1717518209706" + } + } +},{ + "_id": { + "$oid": "665f3fc08c25216ef4f53e0b" + }, + "metadata": { + "securityResultCode": "success", + "bsmSource": "RV", + "schemaVersion": 6, + "odePacketID": "", + "sanitized": false, + "recordType": "bsmTx", + "recordGeneratedAt": "", + "maxDurationTime": 0, + "odeTimStartDateTime": "", + "receivedMessageDetails": { + "locationData": { + "elevation": "", + "heading": "", + "latitude": "", + "speed": "", + "longitude": "" + }, + "rxSource": "RV" + }, + "payloadType": "us.dot.its.jpo.ode.model.OdeBsmPayload", + "serialId": { + "recordId": 0, + "serialNumber": 0, + "streamId": "849c3ac6-4747-426f-ae5e-28ac8531b32c", + "bundleSize": 1, + "bundleId": 0 + }, + "logFileName": "", + "odeReceivedAt": "2024-06-04T16:23:29.808Z", + "originIp": "10.11.81.12" + }, + "payload": { + "data": { + "partII": [ + { + "id": "VehicleSafetyExtensions", + "value": { + "pathHistory": { + "crumbData": [ + { + "elevationOffset": 0.6, + "lonOffset": 0.0000387, + "timeOffset": 0.8, + "latOffset": -0.0001604 + }, + { + "elevationOffset": 4.3, + "lonOffset": 0.000231, + "timeOffset": 5.6, + "latOffset": -0.0011603 + }, + { + "elevationOffset": 9.1, + "lonOffset": 0.0003834, + "timeOffset": 11, + "latOffset": -0.0023425 + } + ] + }, + "pathPrediction": { + "confidence": 80, + "radiusOfCurve": -1622.5 + } + } + } + ], + "coreData": { + "secMark": 29808, + "transmission": "UNAVAILABLE", + "size": { + "width": 180, + "length": 480 + }, + "heading": 347.5, + "accelSet": { + "accelYaw": -0.65, + "accelLong": -0.05 + }, + "brakes": { + "scs": "unavailable", + "wheelBrakes": { + "leftFront": false, + "rightFront": false, + "unavailable": true, + "leftRear": false, + "rightRear": false + }, + "abs": "off", + "traction": "unavailable", + "brakeBoost": "unavailable", + "auxBrakes": "unavailable" + }, + "accuracy": { + "semiMinor": 2, + "orientation": 0, + "semiMajor": 2 + }, + "id": "A0B43C7E", + "position": { + "elevation": 1691.9, + "latitude": 39.587804377415964, + "longitude": -105.09076873470384 + }, + "msgCnt": 21, + "speed": 21.19 + } + }, + "dataType": "us.dot.its.jpo.ode.plugin.j2735.J2735Bsm" + }, + "recordGeneratedAt": { + "$date": { + "$numberLong": "1717518209810" + } + } +},{ + "_id": { + "$oid": "665f3fc08c25216ef4f53e0c" + }, + "metadata": { + "securityResultCode": "success", + "bsmSource": "RV", + "schemaVersion": 6, + "odePacketID": "", + "sanitized": false, + "recordType": "bsmTx", + "recordGeneratedAt": "", + "maxDurationTime": 0, + "odeTimStartDateTime": "", + "receivedMessageDetails": { + "locationData": { + "elevation": "", + "heading": "", + "latitude": "", + "speed": "", + "longitude": "" + }, + "rxSource": "RV" + }, + "payloadType": "us.dot.its.jpo.ode.model.OdeBsmPayload", + "serialId": { + "recordId": 0, + "serialNumber": 0, + "streamId": "849c3ac6-4747-426f-ae5e-28ac8531b32c", + "bundleSize": 1, + "bundleId": 0 + }, + "logFileName": "", + "odeReceivedAt": "2024-06-04T16:23:29.916Z", + "originIp": "10.11.81.12" + }, + "payload": { + "data": { + "partII": [ + { + "id": "VehicleSafetyExtensions", + "value": { + "pathHistory": { + "crumbData": [ + { + "elevationOffset": 0.6, + "lonOffset": 0.0000387, + "timeOffset": 0.8, + "latOffset": -0.0001604 + }, + { + "elevationOffset": 4.3, + "lonOffset": 0.000231, + "timeOffset": 5.6, + "latOffset": -0.0011603 + }, + { + "elevationOffset": 9.1, + "lonOffset": 0.0003834, + "timeOffset": 11, + "latOffset": -0.0023425 + } + ] + }, + "pathPrediction": { + "confidence": 80, + "radiusOfCurve": -1622.5 + } + } + } + ], + "coreData": { + "secMark": 29916, + "transmission": "UNAVAILABLE", + "size": { + "width": 180, + "length": 480 + }, + "heading": 349, + "accelSet": { + "accelYaw": -0.65, + "accelLong": -0.05 + }, + "brakes": { + "scs": "unavailable", + "wheelBrakes": { + "leftFront": false, + "rightFront": false, + "unavailable": true, + "leftRear": false, + "rightRear": false + }, + "abs": "off", + "traction": "unavailable", + "brakeBoost": "unavailable", + "auxBrakes": "unavailable" + }, + "accuracy": { + "semiMinor": 2, + "orientation": 0, + "semiMajor": 2 + }, + "id": "A0B43C7E", + "position": { + "elevation": 1691.9, + "latitude": 39.587820396985244, + "longitude": -105.09077275867644 + }, + "msgCnt": 22, + "speed": 18.14 + } + }, + "dataType": "us.dot.its.jpo.ode.plugin.j2735.J2735Bsm" + }, + "recordGeneratedAt": { + "$date": { + "$numberLong": "1717518209917" + } + } +},{ + "_id": { + "$oid": "665f3fc08c25216ef4f53e0d" + }, + "metadata": { + "securityResultCode": "success", + "bsmSource": "RV", + "schemaVersion": 6, + "odePacketID": "", + "sanitized": false, + "recordType": "bsmTx", + "recordGeneratedAt": "", + "maxDurationTime": 0, + "odeTimStartDateTime": "", + "receivedMessageDetails": { + "locationData": { + "elevation": "", + "heading": "", + "latitude": "", + "speed": "", + "longitude": "" + }, + "rxSource": "RV" + }, + "payloadType": "us.dot.its.jpo.ode.model.OdeBsmPayload", + "serialId": { + "recordId": 0, + "serialNumber": 0, + "streamId": "849c3ac6-4747-426f-ae5e-28ac8531b32c", + "bundleSize": 1, + "bundleId": 0 + }, + "logFileName": "", + "odeReceivedAt": "2024-06-04T16:23:30.024Z", + "originIp": "10.11.81.12" + }, + "payload": { + "data": { + "partII": [ + { + "id": "VehicleSafetyExtensions", + "value": { + "pathHistory": { + "crumbData": [ + { + "elevationOffset": 0.6, + "lonOffset": 0.0000387, + "timeOffset": 0.8, + "latOffset": -0.0001604 + }, + { + "elevationOffset": 4.3, + "lonOffset": 0.000231, + "timeOffset": 5.6, + "latOffset": -0.0011603 + }, + { + "elevationOffset": 9.1, + "lonOffset": 0.0003834, + "timeOffset": 11, + "latOffset": -0.0023425 + } + ] + }, + "pathPrediction": { + "confidence": 80, + "radiusOfCurve": -1622.5 + } + } + } + ], + "coreData": { + "secMark": 30024, + "transmission": "UNAVAILABLE", + "size": { + "width": 180, + "length": 480 + }, + "heading": 349.7, + "accelSet": { + "accelYaw": -0.65, + "accelLong": -0.05 + }, + "brakes": { + "scs": "unavailable", + "wheelBrakes": { + "leftFront": false, + "rightFront": false, + "unavailable": true, + "leftRear": false, + "rightRear": false + }, + "abs": "off", + "traction": "unavailable", + "brakeBoost": "unavailable", + "auxBrakes": "unavailable" + }, + "accuracy": { + "semiMinor": 2, + "orientation": 0, + "semiMajor": 2 + }, + "id": "A0B43C7E", + "position": { + "elevation": 1691.9, + "latitude": 39.587837450071035, + "longitude": -105.09077678264897 + }, + "msgCnt": 23, + "speed": 19.27 + } + }, + "dataType": "us.dot.its.jpo.ode.plugin.j2735.J2735Bsm" + }, + "recordGeneratedAt": { + "$date": { + "$numberLong": "1717518210040" + } + } +},{ + "_id": { + "$oid": "665f3fc08c25216ef4f53e0e" + }, + "metadata": { + "securityResultCode": "success", + "bsmSource": "RV", + "schemaVersion": 6, + "odePacketID": "", + "sanitized": false, + "recordType": "bsmTx", + "recordGeneratedAt": "", + "maxDurationTime": 0, + "odeTimStartDateTime": "", + "receivedMessageDetails": { + "locationData": { + "elevation": "", + "heading": "", + "latitude": "", + "speed": "", + "longitude": "" + }, + "rxSource": "RV" + }, + "payloadType": "us.dot.its.jpo.ode.model.OdeBsmPayload", + "serialId": { + "recordId": 0, + "serialNumber": 0, + "streamId": "849c3ac6-4747-426f-ae5e-28ac8531b32c", + "bundleSize": 1, + "bundleId": 0 + }, + "logFileName": "", + "odeReceivedAt": "2024-06-04T16:23:30.129Z", + "originIp": "10.11.81.12" + }, + "payload": { + "data": { + "partII": [ + { + "id": "VehicleSafetyExtensions", + "value": { + "pathHistory": { + "crumbData": [ + { + "elevationOffset": 0.6, + "lonOffset": 0.0000387, + "timeOffset": 0.8, + "latOffset": -0.0001604 + }, + { + "elevationOffset": 4.3, + "lonOffset": 0.000231, + "timeOffset": 5.6, + "latOffset": -0.0011603 + }, + { + "elevationOffset": 9.1, + "lonOffset": 0.0003834, + "timeOffset": 11, + "latOffset": -0.0023425 + } + ] + }, + "pathPrediction": { + "confidence": 80, + "radiusOfCurve": -1622.5 + } + } + } + ], + "coreData": { + "secMark": 30129, + "transmission": "UNAVAILABLE", + "size": { + "width": 180, + "length": 480 + }, + "heading": 349.7, + "accelSet": { + "accelYaw": -0.65, + "accelLong": -0.05 + }, + "brakes": { + "scs": "unavailable", + "wheelBrakes": { + "leftFront": false, + "rightFront": false, + "unavailable": true, + "leftRear": false, + "rightRear": false + }, + "abs": "off", + "traction": "unavailable", + "brakeBoost": "unavailable", + "auxBrakes": "unavailable" + }, + "accuracy": { + "semiMinor": 2, + "orientation": 0, + "semiMajor": 2 + }, + "id": "A0B43C7E", + "position": { + "elevation": 1691.9, + "latitude": 39.58785450315265, + "longitude": -105.0907808066216 + }, + "msgCnt": 24, + "speed": 19.27 + } + }, + "dataType": "us.dot.its.jpo.ode.plugin.j2735.J2735Bsm" + }, + "recordGeneratedAt": { + "$date": { + "$numberLong": "1717518210130" + } + } +},{ + "_id": { + "$oid": "665f3fc08c25216ef4f53e0f" + }, + "metadata": { + "securityResultCode": "success", + "bsmSource": "RV", + "schemaVersion": 6, + "odePacketID": "", + "sanitized": false, + "recordType": "bsmTx", + "recordGeneratedAt": "", + "maxDurationTime": 0, + "odeTimStartDateTime": "", + "receivedMessageDetails": { + "locationData": { + "elevation": "", + "heading": "", + "latitude": "", + "speed": "", + "longitude": "" + }, + "rxSource": "RV" + }, + "payloadType": "us.dot.its.jpo.ode.model.OdeBsmPayload", + "serialId": { + "recordId": 0, + "serialNumber": 0, + "streamId": "849c3ac6-4747-426f-ae5e-28ac8531b32c", + "bundleSize": 1, + "bundleId": 0 + }, + "logFileName": "", + "odeReceivedAt": "2024-06-04T16:23:30.238Z", + "originIp": "10.11.81.12" + }, + "payload": { + "data": { + "partII": [ + { + "id": "VehicleSafetyExtensions", + "value": { + "pathHistory": { + "crumbData": [ + { + "elevationOffset": 0.6, + "lonOffset": 0.0000387, + "timeOffset": 0.8, + "latOffset": -0.0001604 + }, + { + "elevationOffset": 4.3, + "lonOffset": 0.000231, + "timeOffset": 5.6, + "latOffset": -0.0011603 + }, + { + "elevationOffset": 9.1, + "lonOffset": 0.0003834, + "timeOffset": 11, + "latOffset": -0.0023425 + } + ] + }, + "pathPrediction": { + "confidence": 80, + "radiusOfCurve": -1622.5 + } + } + } + ], + "coreData": { + "secMark": 30238, + "transmission": "UNAVAILABLE", + "size": { + "width": 180, + "length": 480 + }, + "heading": 347.3, + "accelSet": { + "accelYaw": -0.65, + "accelLong": -0.05 + }, + "brakes": { + "scs": "unavailable", + "wheelBrakes": { + "leftFront": false, + "rightFront": false, + "unavailable": true, + "leftRear": false, + "rightRear": false + }, + "abs": "off", + "traction": "unavailable", + "brakeBoost": "unavailable", + "auxBrakes": "unavailable" + }, + "accuracy": { + "semiMinor": 2, + "orientation": 0, + "semiMajor": 2 + }, + "id": "A0B43C7E", + "position": { + "elevation": 1691.9, + "latitude": 39.587870522710325, + "longitude": -105.09078550125625 + }, + "msgCnt": 25, + "speed": 18.26 + } + }, + "dataType": "us.dot.its.jpo.ode.plugin.j2735.J2735Bsm" + }, + "recordGeneratedAt": { + "$date": { + "$numberLong": "1717518210252" + } + } +},{ + "_id": { + "$oid": "665f3fc08c25216ef4f53e10" + }, + "metadata": { + "securityResultCode": "success", + "bsmSource": "RV", + "schemaVersion": 6, + "odePacketID": "", + "sanitized": false, + "recordType": "bsmTx", + "recordGeneratedAt": "", + "maxDurationTime": 0, + "odeTimStartDateTime": "", + "receivedMessageDetails": { + "locationData": { + "elevation": "", + "heading": "", + "latitude": "", + "speed": "", + "longitude": "" + }, + "rxSource": "RV" + }, + "payloadType": "us.dot.its.jpo.ode.model.OdeBsmPayload", + "serialId": { + "recordId": 0, + "serialNumber": 0, + "streamId": "849c3ac6-4747-426f-ae5e-28ac8531b32c", + "bundleSize": 1, + "bundleId": 0 + }, + "logFileName": "", + "odeReceivedAt": "2024-06-04T16:23:30.345Z", + "originIp": "10.11.81.12" + }, + "payload": { + "data": { + "partII": [ + { + "id": "VehicleSafetyExtensions", + "value": { + "pathHistory": { + "crumbData": [ + { + "elevationOffset": 0.6, + "lonOffset": 0.0000387, + "timeOffset": 0.8, + "latOffset": -0.0001604 + }, + { + "elevationOffset": 4.3, + "lonOffset": 0.000231, + "timeOffset": 5.6, + "latOffset": -0.0011603 + }, + { + "elevationOffset": 9.1, + "lonOffset": 0.0003834, + "timeOffset": 11, + "latOffset": -0.0023425 + } + ] + }, + "pathPrediction": { + "confidence": 80, + "radiusOfCurve": -1622.5 + } + } + } + ], + "coreData": { + "secMark": 30345, + "transmission": "UNAVAILABLE", + "size": { + "width": 180, + "length": 480 + }, + "heading": 344.9, + "accelSet": { + "accelYaw": -0.65, + "accelLong": -0.05 + }, + "brakes": { + "scs": "unavailable", + "wheelBrakes": { + "leftFront": false, + "rightFront": false, + "unavailable": true, + "leftRear": false, + "rightRear": false + }, + "abs": "off", + "traction": "unavailable", + "brakeBoost": "unavailable", + "auxBrakes": "unavailable" + }, + "accuracy": { + "semiMinor": 2, + "orientation": 0, + "semiMajor": 2 + }, + "id": "A0B43C7E", + "position": { + "elevation": 1691.9, + "latitude": 39.587883958465525, + "longitude": -105.09079019589092 + }, + "msgCnt": 26, + "speed": 15.47 + } + }, + "dataType": "us.dot.its.jpo.ode.plugin.j2735.J2735Bsm" + }, + "recordGeneratedAt": { + "$date": { + "$numberLong": "1717518210358" + } + } +},{ + "_id": { + "$oid": "665f3fc08c25216ef4f53e11" + }, + "metadata": { + "securityResultCode": "success", + "bsmSource": "RV", + "schemaVersion": 6, + "odePacketID": "", + "sanitized": false, + "recordType": "bsmTx", + "recordGeneratedAt": "", + "maxDurationTime": 0, + "odeTimStartDateTime": "", + "receivedMessageDetails": { + "locationData": { + "elevation": "", + "heading": "", + "latitude": "", + "speed": "", + "longitude": "" + }, + "rxSource": "RV" + }, + "payloadType": "us.dot.its.jpo.ode.model.OdeBsmPayload", + "serialId": { + "recordId": 0, + "serialNumber": 0, + "streamId": "849c3ac6-4747-426f-ae5e-28ac8531b32c", + "bundleSize": 1, + "bundleId": 0 + }, + "logFileName": "", + "odeReceivedAt": "2024-06-04T16:23:30.453Z", + "originIp": "10.11.81.12" + }, + "payload": { + "data": { + "partII": [ + { + "id": "VehicleSafetyExtensions", + "value": { + "pathHistory": { + "crumbData": [ + { + "elevationOffset": 0.6, + "lonOffset": 0.0000387, + "timeOffset": 0.8, + "latOffset": -0.0001604 + }, + { + "elevationOffset": 4.3, + "lonOffset": 0.000231, + "timeOffset": 5.6, + "latOffset": -0.0011603 + }, + { + "elevationOffset": 9.1, + "lonOffset": 0.0003834, + "timeOffset": 11, + "latOffset": -0.0023425 + } + ] + }, + "pathPrediction": { + "confidence": 80, + "radiusOfCurve": -1622.5 + } + } + } + ], + "coreData": { + "secMark": 30453, + "transmission": "UNAVAILABLE", + "size": { + "width": 180, + "length": 480 + }, + "heading": 344.5, + "accelSet": { + "accelYaw": -0.65, + "accelLong": -0.05 + }, + "brakes": { + "scs": "unavailable", + "wheelBrakes": { + "leftFront": false, + "rightFront": false, + "unavailable": true, + "leftRear": false, + "rightRear": false + }, + "abs": "off", + "traction": "unavailable", + "brakeBoost": "unavailable", + "auxBrakes": "unavailable" + }, + "accuracy": { + "semiMinor": 2, + "orientation": 0, + "semiMajor": 2 + }, + "id": "A0B43C7E", + "position": { + "elevation": 1691.9, + "latitude": 39.58789326014069, + "longitude": -105.09079354920141 + }, + "msgCnt": 27, + "speed": 10.73 + } + }, + "dataType": "us.dot.its.jpo.ode.plugin.j2735.J2735Bsm" + }, + "recordGeneratedAt": { + "$date": { + "$numberLong": "1717518210465" + } + } +},{ + "_id": { + "$oid": "665f3fc08c25216ef4f53e12" + }, + "metadata": { + "securityResultCode": "success", + "bsmSource": "RV", + "schemaVersion": 6, + "odePacketID": "", + "sanitized": false, + "recordType": "bsmTx", + "recordGeneratedAt": "", + "maxDurationTime": 0, + "odeTimStartDateTime": "", + "receivedMessageDetails": { + "locationData": { + "elevation": "", + "heading": "", + "latitude": "", + "speed": "", + "longitude": "" + }, + "rxSource": "RV" + }, + "payloadType": "us.dot.its.jpo.ode.model.OdeBsmPayload", + "serialId": { + "recordId": 0, + "serialNumber": 0, + "streamId": "849c3ac6-4747-426f-ae5e-28ac8531b32c", + "bundleSize": 1, + "bundleId": 0 + }, + "logFileName": "", + "odeReceivedAt": "2024-06-04T16:23:30.562Z", + "originIp": "10.11.81.12" + }, + "payload": { + "data": { + "partII": [ + { + "id": "VehicleSafetyExtensions", + "value": { + "pathHistory": { + "crumbData": [ + { + "elevationOffset": 0.6, + "lonOffset": 0.0000387, + "timeOffset": 0.8, + "latOffset": -0.0001604 + }, + { + "elevationOffset": 4.3, + "lonOffset": 0.000231, + "timeOffset": 5.6, + "latOffset": -0.0011603 + }, + { + "elevationOffset": 9.1, + "lonOffset": 0.0003834, + "timeOffset": 11, + "latOffset": -0.0023425 + } + ] + }, + "pathPrediction": { + "confidence": 80, + "radiusOfCurve": -1622.5 + } + } + } + ], + "coreData": { + "secMark": 30562, + "transmission": "UNAVAILABLE", + "size": { + "width": 180, + "length": 480 + }, + "heading": 344.5, + "accelSet": { + "accelYaw": -0.65, + "accelLong": -0.05 + }, + "brakes": { + "scs": "unavailable", + "wheelBrakes": { + "leftFront": false, + "rightFront": false, + "unavailable": true, + "leftRear": false, + "rightRear": false + }, + "abs": "off", + "traction": "unavailable", + "brakeBoost": "unavailable", + "auxBrakes": "unavailable" + }, + "accuracy": { + "semiMinor": 2, + "orientation": 0, + "semiMajor": 2 + }, + "id": "A0B43C7E", + "position": { + "elevation": 1691.9, + "latitude": 39.58790256181461, + "longitude": -105.09079690251188 + }, + "msgCnt": 28, + "speed": 10.73 + } + }, + "dataType": "us.dot.its.jpo.ode.plugin.j2735.J2735Bsm" + }, + "recordGeneratedAt": { + "$date": { + "$numberLong": "1717518210572" + } + } +},{ + "_id": { + "$oid": "665f3fc08c25216ef4f53e13" + }, + "metadata": { + "securityResultCode": "success", + "bsmSource": "RV", + "schemaVersion": 6, + "odePacketID": "", + "sanitized": false, + "recordType": "bsmTx", + "recordGeneratedAt": "", + "maxDurationTime": 0, + "odeTimStartDateTime": "", + "receivedMessageDetails": { + "locationData": { + "elevation": "", + "heading": "", + "latitude": "", + "speed": "", + "longitude": "" + }, + "rxSource": "RV" + }, + "payloadType": "us.dot.its.jpo.ode.model.OdeBsmPayload", + "serialId": { + "recordId": 0, + "serialNumber": 0, + "streamId": "849c3ac6-4747-426f-ae5e-28ac8531b32c", + "bundleSize": 1, + "bundleId": 0 + }, + "logFileName": "", + "odeReceivedAt": "2024-06-04T16:23:30.669Z", + "originIp": "10.11.81.12" + }, + "payload": { + "data": { + "partII": [ + { + "id": "VehicleSafetyExtensions", + "value": { + "pathHistory": { + "crumbData": [ + { + "elevationOffset": 0.6, + "lonOffset": 0.0000387, + "timeOffset": 0.8, + "latOffset": -0.0001604 + }, + { + "elevationOffset": 4.3, + "lonOffset": 0.000231, + "timeOffset": 5.6, + "latOffset": -0.0011603 + }, + { + "elevationOffset": 9.1, + "lonOffset": 0.0003834, + "timeOffset": 11, + "latOffset": -0.0023425 + } + ] + }, + "pathPrediction": { + "confidence": 80, + "radiusOfCurve": -1622.5 + } + } + } + ], + "coreData": { + "secMark": 30669, + "transmission": "UNAVAILABLE", + "size": { + "width": 180, + "length": 480 + }, + "heading": 332.4, + "accelSet": { + "accelYaw": -0.65, + "accelLong": -0.05 + }, + "brakes": { + "scs": "unavailable", + "wheelBrakes": { + "leftFront": false, + "rightFront": false, + "unavailable": true, + "leftRear": false, + "rightRear": false + }, + "abs": "off", + "traction": "unavailable", + "brakeBoost": "unavailable", + "auxBrakes": "unavailable" + }, + "accuracy": { + "semiMinor": 2, + "orientation": 0, + "semiMajor": 2 + }, + "id": "A0B43C7E", + "position": { + "elevation": 1691.9, + "latitude": 39.587914447284994, + "longitude": -105.09080495045706 + }, + "msgCnt": 29, + "speed": 14.91 + } + }, + "dataType": "us.dot.its.jpo.ode.plugin.j2735.J2735Bsm" + }, + "recordGeneratedAt": { + "$date": { + "$numberLong": "1717518210679" + } + } +},{ + "_id": { + "$oid": "665f3fc08c25216ef4f53e14" + }, + "metadata": { + "securityResultCode": "success", + "bsmSource": "RV", + "schemaVersion": 6, + "odePacketID": "", + "sanitized": false, + "recordType": "bsmTx", + "recordGeneratedAt": "", + "maxDurationTime": 0, + "odeTimStartDateTime": "", + "receivedMessageDetails": { + "locationData": { + "elevation": "", + "heading": "", + "latitude": "", + "speed": "", + "longitude": "" + }, + "rxSource": "RV" + }, + "payloadType": "us.dot.its.jpo.ode.model.OdeBsmPayload", + "serialId": { + "recordId": 0, + "serialNumber": 0, + "streamId": "849c3ac6-4747-426f-ae5e-28ac8531b32c", + "bundleSize": 1, + "bundleId": 0 + }, + "logFileName": "", + "odeReceivedAt": "2024-06-04T16:23:30.775Z", + "originIp": "10.11.81.12" + }, + "payload": { + "data": { + "partII": [ + { + "id": "VehicleSafetyExtensions", + "value": { + "pathHistory": { + "crumbData": [ + { + "elevationOffset": 0.6, + "lonOffset": 0.0000387, + "timeOffset": 0.8, + "latOffset": -0.0001604 + }, + { + "elevationOffset": 4.3, + "lonOffset": 0.000231, + "timeOffset": 5.6, + "latOffset": -0.0011603 + }, + { + "elevationOffset": 9.1, + "lonOffset": 0.0003834, + "timeOffset": 11, + "latOffset": -0.0023425 + } + ] + }, + "pathPrediction": { + "confidence": 80, + "radiusOfCurve": -1622.5 + } + } + } + ], + "coreData": { + "secMark": 30775, + "transmission": "UNAVAILABLE", + "size": { + "width": 180, + "length": 480 + }, + "heading": 326.3, + "accelSet": { + "accelYaw": -0.65, + "accelLong": -0.05 + }, + "brakes": { + "scs": "unavailable", + "wheelBrakes": { + "leftFront": false, + "rightFront": false, + "unavailable": true, + "leftRear": false, + "rightRear": false + }, + "abs": "off", + "traction": "unavailable", + "brakeBoost": "unavailable", + "auxBrakes": "unavailable" + }, + "accuracy": { + "semiMinor": 2, + "orientation": 0, + "semiMajor": 2 + }, + "id": "A0B43C7E", + "position": { + "elevation": 1691.9, + "latitude": 39.58792529923446, + "longitude": -105.09081433972638 + }, + "msgCnt": 30, + "speed": 14.5 + } + }, + "dataType": "us.dot.its.jpo.ode.plugin.j2735.J2735Bsm" + }, + "recordGeneratedAt": { + "$date": { + "$numberLong": "1717518210786" + } + } +},{ + "_id": { + "$oid": "665f3fc08c25216ef4f53e15" + }, + "metadata": { + "securityResultCode": "success", + "bsmSource": "RV", + "schemaVersion": 6, + "odePacketID": "", + "sanitized": false, + "recordType": "bsmTx", + "recordGeneratedAt": "", + "maxDurationTime": 0, + "odeTimStartDateTime": "", + "receivedMessageDetails": { + "locationData": { + "elevation": "", + "heading": "", + "latitude": "", + "speed": "", + "longitude": "" + }, + "rxSource": "RV" + }, + "payloadType": "us.dot.its.jpo.ode.model.OdeBsmPayload", + "serialId": { + "recordId": 0, + "serialNumber": 0, + "streamId": "849c3ac6-4747-426f-ae5e-28ac8531b32c", + "bundleSize": 1, + "bundleId": 0 + }, + "logFileName": "", + "odeReceivedAt": "2024-06-04T16:23:30.883Z", + "originIp": "10.11.81.12" + }, + "payload": { + "data": { + "partII": [ + { + "id": "VehicleSafetyExtensions", + "value": { + "pathHistory": { + "crumbData": [ + { + "elevationOffset": 0.6, + "lonOffset": 0.0000387, + "timeOffset": 0.8, + "latOffset": -0.0001604 + }, + { + "elevationOffset": 4.3, + "lonOffset": 0.000231, + "timeOffset": 5.6, + "latOffset": -0.0011603 + }, + { + "elevationOffset": 9.1, + "lonOffset": 0.0003834, + "timeOffset": 11, + "latOffset": -0.0023425 + } + ] + }, + "pathPrediction": { + "confidence": 80, + "radiusOfCurve": -1622.5 + } + } + } + ], + "coreData": { + "secMark": 30883, + "transmission": "UNAVAILABLE", + "size": { + "width": 180, + "length": 480 + }, + "heading": 323.1, + "accelSet": { + "accelYaw": -0.65, + "accelLong": -0.05 + }, + "brakes": { + "scs": "unavailable", + "wheelBrakes": { + "leftFront": false, + "rightFront": false, + "unavailable": true, + "leftRear": false, + "rightRear": false + }, + "abs": "off", + "traction": "unavailable", + "brakeBoost": "unavailable", + "auxBrakes": "unavailable" + }, + "accuracy": { + "semiMinor": 2, + "orientation": 0, + "semiMajor": 2 + }, + "id": "A0B43C7E", + "position": { + "elevation": 1691.9, + "latitude": 39.587935634422834, + "longitude": -105.09082439965782 + }, + "msgCnt": 31, + "speed": 14.37 + } + }, + "dataType": "us.dot.its.jpo.ode.plugin.j2735.J2735Bsm" + }, + "recordGeneratedAt": { + "$date": { + "$numberLong": "1717518210893" + } + } +},{ + "_id": { + "$oid": "665f3fc08c25216ef4f53e16" + }, + "metadata": { + "securityResultCode": "success", + "bsmSource": "RV", + "schemaVersion": 6, + "odePacketID": "", + "sanitized": false, + "recordType": "bsmTx", + "recordGeneratedAt": "", + "maxDurationTime": 0, + "odeTimStartDateTime": "", + "receivedMessageDetails": { + "locationData": { + "elevation": "", + "heading": "", + "latitude": "", + "speed": "", + "longitude": "" + }, + "rxSource": "RV" + }, + "payloadType": "us.dot.its.jpo.ode.model.OdeBsmPayload", + "serialId": { + "recordId": 0, + "serialNumber": 0, + "streamId": "849c3ac6-4747-426f-ae5e-28ac8531b32c", + "bundleSize": 1, + "bundleId": 0 + }, + "logFileName": "", + "odeReceivedAt": "2024-06-04T16:23:30.99Z", + "originIp": "10.11.81.12" + }, + "payload": { + "data": { + "partII": [ + { + "id": "VehicleSafetyExtensions", + "value": { + "pathHistory": { + "crumbData": [ + { + "elevationOffset": 0.6, + "lonOffset": 0.0000387, + "timeOffset": 0.8, + "latOffset": -0.0001604 + }, + { + "elevationOffset": 4.3, + "lonOffset": 0.000231, + "timeOffset": 5.6, + "latOffset": -0.0011603 + }, + { + "elevationOffset": 9.1, + "lonOffset": 0.0003834, + "timeOffset": 11, + "latOffset": -0.0023425 + } + ] + }, + "pathPrediction": { + "confidence": 80, + "radiusOfCurve": -1622.5 + } + } + } + ], + "coreData": { + "secMark": 30990, + "transmission": "UNAVAILABLE", + "size": { + "width": 180, + "length": 480 + }, + "heading": 306.5, + "accelSet": { + "accelYaw": -0.65, + "accelLong": -0.05 + }, + "brakes": { + "scs": "unavailable", + "wheelBrakes": { + "leftFront": false, + "rightFront": false, + "unavailable": true, + "leftRear": false, + "rightRear": false + }, + "abs": "off", + "traction": "unavailable", + "brakeBoost": "unavailable", + "auxBrakes": "unavailable" + }, + "accuracy": { + "semiMinor": 2, + "orientation": 0, + "semiMajor": 2 + }, + "id": "A0B43C7E", + "position": { + "elevation": 1691.9, + "latitude": 39.58794441933173, + "longitude": -105.09083982488603 + }, + "msgCnt": 32, + "speed": 16.44 + } + }, + "dataType": "us.dot.its.jpo.ode.plugin.j2735.J2735Bsm" + }, + "recordGeneratedAt": { + "$date": { + "$numberLong": "1717518211000" + } + } +},{ + "_id": { + "$oid": "665f3fc08c25216ef4f53e17" + }, + "metadata": { + "securityResultCode": "success", + "bsmSource": "RV", + "schemaVersion": 6, + "odePacketID": "", + "sanitized": false, + "recordType": "bsmTx", + "recordGeneratedAt": "", + "maxDurationTime": 0, + "odeTimStartDateTime": "", + "receivedMessageDetails": { + "locationData": { + "elevation": "", + "heading": "", + "latitude": "", + "speed": "", + "longitude": "" + }, + "rxSource": "RV" + }, + "payloadType": "us.dot.its.jpo.ode.model.OdeBsmPayload", + "serialId": { + "recordId": 0, + "serialNumber": 0, + "streamId": "849c3ac6-4747-426f-ae5e-28ac8531b32c", + "bundleSize": 1, + "bundleId": 0 + }, + "logFileName": "", + "odeReceivedAt": "2024-06-04T16:23:31.097Z", + "originIp": "10.11.81.12" + }, + "payload": { + "data": { + "partII": [ + { + "id": "VehicleSafetyExtensions", + "value": { + "pathHistory": { + "crumbData": [ + { + "elevationOffset": 0.6, + "lonOffset": 0.0000387, + "timeOffset": 0.8, + "latOffset": -0.0001604 + }, + { + "elevationOffset": 4.3, + "lonOffset": 0.000231, + "timeOffset": 5.6, + "latOffset": -0.0011603 + }, + { + "elevationOffset": 9.1, + "lonOffset": 0.0003834, + "timeOffset": 11, + "latOffset": -0.0023425 + } + ] + }, + "pathPrediction": { + "confidence": 80, + "radiusOfCurve": -1622.5 + } + } + } + ], + "coreData": { + "secMark": 31097, + "transmission": "UNAVAILABLE", + "size": { + "width": 180, + "length": 480 + }, + "heading": 291.2, + "accelSet": { + "accelYaw": -0.65, + "accelLong": -0.05 + }, + "brakes": { + "scs": "unavailable", + "wheelBrakes": { + "leftFront": false, + "rightFront": false, + "unavailable": true, + "leftRear": false, + "rightRear": false + }, + "abs": "off", + "traction": "unavailable", + "brakeBoost": "unavailable", + "auxBrakes": "unavailable" + }, + "accuracy": { + "semiMinor": 2, + "orientation": 0, + "semiMajor": 2 + }, + "id": "A0B43C7E", + "position": { + "elevation": 1691.9, + "latitude": 39.58795062044324, + "longitude": -105.090860615411 + }, + "msgCnt": 33, + "speed": 19.1 + } + }, + "dataType": "us.dot.its.jpo.ode.plugin.j2735.J2735Bsm" + }, + "recordGeneratedAt": { + "$date": { + "$numberLong": "1717518211108" + } + } +},{ + "_id": { + "$oid": "665f3fc08c25216ef4f53e18" + }, + "metadata": { + "securityResultCode": "success", + "bsmSource": "RV", + "schemaVersion": 6, + "odePacketID": "", + "sanitized": false, + "recordType": "bsmTx", + "recordGeneratedAt": "", + "maxDurationTime": 0, + "odeTimStartDateTime": "", + "receivedMessageDetails": { + "locationData": { + "elevation": "", + "heading": "", + "latitude": "", + "speed": "", + "longitude": "" + }, + "rxSource": "RV" + }, + "payloadType": "us.dot.its.jpo.ode.model.OdeBsmPayload", + "serialId": { + "recordId": 0, + "serialNumber": 0, + "streamId": "849c3ac6-4747-426f-ae5e-28ac8531b32c", + "bundleSize": 1, + "bundleId": 0 + }, + "logFileName": "", + "odeReceivedAt": "2024-06-04T16:23:31.203Z", + "originIp": "10.11.81.12" + }, + "payload": { + "data": { + "partII": [ + { + "id": "VehicleSafetyExtensions", + "value": { + "pathHistory": { + "crumbData": [ + { + "elevationOffset": 0.6, + "lonOffset": 0.0000387, + "timeOffset": 0.8, + "latOffset": -0.0001604 + }, + { + "elevationOffset": 4.3, + "lonOffset": 0.000231, + "timeOffset": 5.6, + "latOffset": -0.0011603 + }, + { + "elevationOffset": 9.1, + "lonOffset": 0.0003834, + "timeOffset": 11, + "latOffset": -0.0023425 + } + ] + }, + "pathPrediction": { + "confidence": 80, + "radiusOfCurve": -1622.5 + } + } + } + ], + "coreData": { + "secMark": 31203, + "transmission": "UNAVAILABLE", + "size": { + "width": 180, + "length": 480 + }, + "heading": 261, + "accelSet": { + "accelYaw": -0.65, + "accelLong": -0.05 + }, + "brakes": { + "scs": "unavailable", + "wheelBrakes": { + "leftFront": false, + "rightFront": false, + "unavailable": true, + "leftRear": false, + "rightRear": false + }, + "abs": "off", + "traction": "unavailable", + "brakeBoost": "unavailable", + "auxBrakes": "unavailable" + }, + "accuracy": { + "semiMinor": 2, + "orientation": 0, + "semiMajor": 2 + }, + "id": "A0B43C7E", + "position": { + "elevation": 1691.9, + "latitude": 39.587949070165415, + "longitude": -105.09087335799083 + }, + "msgCnt": 34, + "speed": 11.05 + } + }, + "dataType": "us.dot.its.jpo.ode.plugin.j2735.J2735Bsm" + }, + "recordGeneratedAt": { + "$date": { + "$numberLong": "1717518211214" + } + } +},{ + "_id": { + "$oid": "665f3fc08c25216ef4f53e19" + }, + "metadata": { + "securityResultCode": "success", + "bsmSource": "RV", + "schemaVersion": 6, + "odePacketID": "", + "sanitized": false, + "recordType": "bsmTx", + "recordGeneratedAt": "", + "maxDurationTime": 0, + "odeTimStartDateTime": "", + "receivedMessageDetails": { + "locationData": { + "elevation": "", + "heading": "", + "latitude": "", + "speed": "", + "longitude": "" + }, + "rxSource": "RV" + }, + "payloadType": "us.dot.its.jpo.ode.model.OdeBsmPayload", + "serialId": { + "recordId": 0, + "serialNumber": 0, + "streamId": "849c3ac6-4747-426f-ae5e-28ac8531b32c", + "bundleSize": 1, + "bundleId": 0 + }, + "logFileName": "", + "odeReceivedAt": "2024-06-04T16:23:31.311Z", + "originIp": "10.11.81.12" + }, + "payload": { + "data": { + "partII": [ + { + "id": "VehicleSafetyExtensions", + "value": { + "pathHistory": { + "crumbData": [ + { + "elevationOffset": 0.6, + "lonOffset": 0.0000387, + "timeOffset": 0.8, + "latOffset": -0.0001604 + }, + { + "elevationOffset": 4.3, + "lonOffset": 0.000231, + "timeOffset": 5.6, + "latOffset": -0.0011603 + }, + { + "elevationOffset": 9.1, + "lonOffset": 0.0003834, + "timeOffset": 11, + "latOffset": -0.0023425 + } + ] + }, + "pathPrediction": { + "confidence": 80, + "radiusOfCurve": -1622.5 + } + } + } + ], + "coreData": { + "secMark": 31311, + "transmission": "UNAVAILABLE", + "size": { + "width": 180, + "length": 480 + }, + "heading": 274.8, + "accelSet": { + "accelYaw": -0.65, + "accelLong": -0.05 + }, + "brakes": { + "scs": "unavailable", + "wheelBrakes": { + "leftFront": false, + "rightFront": false, + "unavailable": true, + "leftRear": false, + "rightRear": false + }, + "abs": "off", + "traction": "unavailable", + "brakeBoost": "unavailable", + "auxBrakes": "unavailable" + }, + "accuracy": { + "semiMinor": 2, + "orientation": 0, + "semiMajor": 2 + }, + "id": "A0B43C7E", + "position": { + "elevation": 1691.9, + "latitude": 39.58795010368396, + "longitude": -105.09088945388118 + }, + "msgCnt": 35, + "speed": 13.84 + } + }, + "dataType": "us.dot.its.jpo.ode.plugin.j2735.J2735Bsm" + }, + "recordGeneratedAt": { + "$date": { + "$numberLong": "1717518211321" + } + } +},{ + "_id": { + "$oid": "665f3fc08c25216ef4f53e1a" + }, + "metadata": { + "securityResultCode": "success", + "bsmSource": "RV", + "schemaVersion": 6, + "odePacketID": "", + "sanitized": false, + "recordType": "bsmTx", + "recordGeneratedAt": "", + "maxDurationTime": 0, + "odeTimStartDateTime": "", + "receivedMessageDetails": { + "locationData": { + "elevation": "", + "heading": "", + "latitude": "", + "speed": "", + "longitude": "" + }, + "rxSource": "RV" + }, + "payloadType": "us.dot.its.jpo.ode.model.OdeBsmPayload", + "serialId": { + "recordId": 0, + "serialNumber": 0, + "streamId": "849c3ac6-4747-426f-ae5e-28ac8531b32c", + "bundleSize": 1, + "bundleId": 0 + }, + "logFileName": "", + "odeReceivedAt": "2024-06-04T16:23:31.418Z", + "originIp": "10.11.81.12" + }, + "payload": { + "data": { + "partII": [ + { + "id": "VehicleSafetyExtensions", + "value": { + "pathHistory": { + "crumbData": [ + { + "elevationOffset": 0.6, + "lonOffset": 0.0000387, + "timeOffset": 0.8, + "latOffset": -0.0001604 + }, + { + "elevationOffset": 4.3, + "lonOffset": 0.000231, + "timeOffset": 5.6, + "latOffset": -0.0011603 + }, + { + "elevationOffset": 9.1, + "lonOffset": 0.0003834, + "timeOffset": 11, + "latOffset": -0.0023425 + } + ] + }, + "pathPrediction": { + "confidence": 80, + "radiusOfCurve": -1622.5 + } + } + } + ], + "coreData": { + "secMark": 31418, + "transmission": "UNAVAILABLE", + "size": { + "width": 180, + "length": 480 + }, + "heading": 259.7, + "accelSet": { + "accelYaw": -0.65, + "accelLong": -0.05 + }, + "brakes": { + "scs": "unavailable", + "wheelBrakes": { + "leftFront": false, + "rightFront": false, + "unavailable": true, + "leftRear": false, + "rightRear": false + }, + "abs": "off", + "traction": "unavailable", + "brakeBoost": "unavailable", + "auxBrakes": "unavailable" + }, + "accuracy": { + "semiMinor": 2, + "orientation": 0, + "semiMajor": 2 + }, + "id": "A0B43C7E", + "position": { + "elevation": 1691.9, + "latitude": 39.58794803664684, + "longitude": -105.09090420844727 + }, + "msgCnt": 36, + "speed": 12.85 + } + }, + "dataType": "us.dot.its.jpo.ode.plugin.j2735.J2735Bsm" + }, + "recordGeneratedAt": { + "$date": { + "$numberLong": "1717518211428" + } + } +},{ + "_id": { + "$oid": "665f3fc08c25216ef4f53e1b" + }, + "metadata": { + "securityResultCode": "success", + "bsmSource": "RV", + "schemaVersion": 6, + "odePacketID": "", + "sanitized": false, + "recordType": "bsmTx", + "recordGeneratedAt": "", + "maxDurationTime": 0, + "odeTimStartDateTime": "", + "receivedMessageDetails": { + "locationData": { + "elevation": "", + "heading": "", + "latitude": "", + "speed": "", + "longitude": "" + }, + "rxSource": "RV" + }, + "payloadType": "us.dot.its.jpo.ode.model.OdeBsmPayload", + "serialId": { + "recordId": 0, + "serialNumber": 0, + "streamId": "849c3ac6-4747-426f-ae5e-28ac8531b32c", + "bundleSize": 1, + "bundleId": 0 + }, + "logFileName": "", + "odeReceivedAt": "2024-06-04T16:23:31.525Z", + "originIp": "10.11.81.12" + }, + "payload": { + "data": { + "partII": [ + { + "id": "VehicleSafetyExtensions", + "value": { + "pathHistory": { + "crumbData": [ + { + "elevationOffset": 0.6, + "lonOffset": 0.0000387, + "timeOffset": 0.8, + "latOffset": -0.0001604 + }, + { + "elevationOffset": 4.3, + "lonOffset": 0.000231, + "timeOffset": 5.6, + "latOffset": -0.0011603 + }, + { + "elevationOffset": 9.1, + "lonOffset": 0.0003834, + "timeOffset": 11, + "latOffset": -0.0023425 + } + ] + }, + "pathPrediction": { + "confidence": 80, + "radiusOfCurve": -1622.5 + } + } + } + ], + "coreData": { + "secMark": 31525, + "transmission": "UNAVAILABLE", + "size": { + "width": 180, + "length": 480 + }, + "heading": 270, + "accelSet": { + "accelYaw": -0.65, + "accelLong": -0.05 + }, + "brakes": { + "scs": "unavailable", + "wheelBrakes": { + "leftFront": false, + "rightFront": false, + "unavailable": true, + "leftRear": false, + "rightRear": false + }, + "abs": "off", + "traction": "unavailable", + "brakeBoost": "unavailable", + "auxBrakes": "unavailable" + }, + "accuracy": { + "semiMinor": 2, + "orientation": 0, + "semiMajor": 2 + }, + "id": "A0B43C7E", + "position": { + "elevation": 1691.9, + "latitude": 39.58794803664684, + "longitude": -105.0909189630134 + }, + "msgCnt": 37, + "speed": 12.64 + } + }, + "dataType": "us.dot.its.jpo.ode.plugin.j2735.J2735Bsm" + }, + "recordGeneratedAt": { + "$date": { + "$numberLong": "1717518211536" + } + } +},{ + "_id": { + "$oid": "665f3fc08c25216ef4f53e1c" + }, + "metadata": { + "securityResultCode": "success", + "bsmSource": "RV", + "schemaVersion": 6, + "odePacketID": "", + "sanitized": false, + "recordType": "bsmTx", + "recordGeneratedAt": "", + "maxDurationTime": 0, + "odeTimStartDateTime": "", + "receivedMessageDetails": { + "locationData": { + "elevation": "", + "heading": "", + "latitude": "", + "speed": "", + "longitude": "" + }, + "rxSource": "RV" + }, + "payloadType": "us.dot.its.jpo.ode.model.OdeBsmPayload", + "serialId": { + "recordId": 0, + "serialNumber": 0, + "streamId": "849c3ac6-4747-426f-ae5e-28ac8531b32c", + "bundleSize": 1, + "bundleId": 0 + }, + "logFileName": "", + "odeReceivedAt": "2024-06-04T16:23:31.632Z", + "originIp": "10.11.81.12" + }, + "payload": { + "data": { + "partII": [ + { + "id": "VehicleSafetyExtensions", + "value": { + "pathHistory": { + "crumbData": [ + { + "elevationOffset": 0.6, + "lonOffset": 0.0000387, + "timeOffset": 0.8, + "latOffset": -0.0001604 + }, + { + "elevationOffset": 4.3, + "lonOffset": 0.000231, + "timeOffset": 5.6, + "latOffset": -0.0011603 + }, + { + "elevationOffset": 9.1, + "lonOffset": 0.0003834, + "timeOffset": 11, + "latOffset": -0.0023425 + } + ] + }, + "pathPrediction": { + "confidence": 80, + "radiusOfCurve": -1622.5 + } + } + } + ], + "coreData": { + "secMark": 31632, + "transmission": "UNAVAILABLE", + "size": { + "width": 180, + "length": 480 + }, + "heading": 258.3, + "accelSet": { + "accelYaw": -0.65, + "accelLong": -0.05 + }, + "brakes": { + "scs": "unavailable", + "wheelBrakes": { + "leftFront": false, + "rightFront": false, + "unavailable": true, + "leftRear": false, + "rightRear": false + }, + "abs": "off", + "traction": "unavailable", + "brakeBoost": "unavailable", + "auxBrakes": "unavailable" + }, + "accuracy": { + "semiMinor": 2, + "orientation": 0, + "semiMajor": 2 + }, + "id": "A0B43C7E", + "position": { + "elevation": 1691.9, + "latitude": 39.58794493609105, + "longitude": -105.09093841221416 + }, + "msgCnt": 38, + "speed": 17.02 + } + }, + "dataType": "us.dot.its.jpo.ode.plugin.j2735.J2735Bsm" + }, + "recordGeneratedAt": { + "$date": { + "$numberLong": "1717518211643" + } + } +},{ + "_id": { + "$oid": "665f3fc08c25216ef4f53e1d" + }, + "metadata": { + "securityResultCode": "success", + "bsmSource": "RV", + "schemaVersion": 6, + "odePacketID": "", + "sanitized": false, + "recordType": "bsmTx", + "recordGeneratedAt": "", + "maxDurationTime": 0, + "odeTimStartDateTime": "", + "receivedMessageDetails": { + "locationData": { + "elevation": "", + "heading": "", + "latitude": "", + "speed": "", + "longitude": "" + }, + "rxSource": "RV" + }, + "payloadType": "us.dot.its.jpo.ode.model.OdeBsmPayload", + "serialId": { + "recordId": 0, + "serialNumber": 0, + "streamId": "849c3ac6-4747-426f-ae5e-28ac8531b32c", + "bundleSize": 1, + "bundleId": 0 + }, + "logFileName": "", + "odeReceivedAt": "2024-06-04T16:23:31.74Z", + "originIp": "10.11.81.12" + }, + "payload": { + "data": { + "partII": [ + { + "id": "VehicleSafetyExtensions", + "value": { + "pathHistory": { + "crumbData": [ + { + "elevationOffset": 0.6, + "lonOffset": 0.0000387, + "timeOffset": 0.8, + "latOffset": -0.0001604 + }, + { + "elevationOffset": 4.3, + "lonOffset": 0.000231, + "timeOffset": 5.6, + "latOffset": -0.0011603 + }, + { + "elevationOffset": 9.1, + "lonOffset": 0.0003834, + "timeOffset": 11, + "latOffset": -0.0023425 + } + ] + }, + "pathPrediction": { + "confidence": 80, + "radiusOfCurve": -1622.5 + } + } + } + ], + "coreData": { + "secMark": 31740, + "transmission": "UNAVAILABLE", + "size": { + "width": 180, + "length": 480 + }, + "heading": 225, + "accelSet": { + "accelYaw": -0.65, + "accelLong": -0.05 + }, + "brakes": { + "scs": "unavailable", + "wheelBrakes": { + "leftFront": false, + "rightFront": false, + "unavailable": true, + "leftRear": false, + "rightRear": false + }, + "abs": "off", + "traction": "unavailable", + "brakeBoost": "unavailable", + "auxBrakes": "unavailable" + }, + "accuracy": { + "semiMinor": 2, + "orientation": 0, + "semiMajor": 2 + }, + "id": "A0B43C7E", + "position": { + "elevation": 1691.9, + "latitude": 39.587933567385285, + "longitude": -105.0909531667803 + }, + "msgCnt": 39, + "speed": 17.88 + } + }, + "dataType": "us.dot.its.jpo.ode.plugin.j2735.J2735Bsm" + }, + "recordGeneratedAt": { + "$date": { + "$numberLong": "1717518211750" + } + } +},{ + "_id": { + "$oid": "665f3fc08c25216ef4f53e1e" + }, + "metadata": { + "securityResultCode": "success", + "bsmSource": "RV", + "schemaVersion": 6, + "odePacketID": "", + "sanitized": false, + "recordType": "bsmTx", + "recordGeneratedAt": "", + "maxDurationTime": 0, + "odeTimStartDateTime": "", + "receivedMessageDetails": { + "locationData": { + "elevation": "", + "heading": "", + "latitude": "", + "speed": "", + "longitude": "" + }, + "rxSource": "RV" + }, + "payloadType": "us.dot.its.jpo.ode.model.OdeBsmPayload", + "serialId": { + "recordId": 0, + "serialNumber": 0, + "streamId": "849c3ac6-4747-426f-ae5e-28ac8531b32c", + "bundleSize": 1, + "bundleId": 0 + }, + "logFileName": "", + "odeReceivedAt": "2024-06-04T16:23:31.846Z", + "originIp": "10.11.81.12" + }, + "payload": { + "data": { + "partII": [ + { + "id": "VehicleSafetyExtensions", + "value": { + "pathHistory": { + "crumbData": [ + { + "elevationOffset": 0.6, + "lonOffset": 0.0000387, + "timeOffset": 0.8, + "latOffset": -0.0001604 + }, + { + "elevationOffset": 4.3, + "lonOffset": 0.000231, + "timeOffset": 5.6, + "latOffset": -0.0011603 + }, + { + "elevationOffset": 9.1, + "lonOffset": 0.0003834, + "timeOffset": 11, + "latOffset": -0.0023425 + } + ] + }, + "pathPrediction": { + "confidence": 80, + "radiusOfCurve": -1622.5 + } + } + } + ], + "coreData": { + "secMark": 31846, + "transmission": "UNAVAILABLE", + "size": { + "width": 180, + "length": 480 + }, + "heading": 211.3, + "accelSet": { + "accelYaw": -0.65, + "accelLong": -0.05 + }, + "brakes": { + "scs": "unavailable", + "wheelBrakes": { + "leftFront": false, + "rightFront": false, + "unavailable": true, + "leftRear": false, + "rightRear": false + }, + "abs": "off", + "traction": "unavailable", + "brakeBoost": "unavailable", + "auxBrakes": "unavailable" + }, + "accuracy": { + "semiMinor": 2, + "orientation": 0, + "semiMajor": 2 + }, + "id": "A0B43C7E", + "position": { + "elevation": 1691.9, + "latitude": 39.587921681918154, + "longitude": -105.09096255604963 + }, + "msgCnt": 40, + "speed": 15.47 + } + }, + "dataType": "us.dot.its.jpo.ode.plugin.j2735.J2735Bsm" + }, + "recordGeneratedAt": { + "$date": { + "$numberLong": "1717518211856" + } + } +},{ + "_id": { + "$oid": "665f3fc08c25216ef4f53e1f" + }, + "metadata": { + "securityResultCode": "success", + "bsmSource": "RV", + "schemaVersion": 6, + "odePacketID": "", + "sanitized": false, + "recordType": "bsmTx", + "recordGeneratedAt": "", + "maxDurationTime": 0, + "odeTimStartDateTime": "", + "receivedMessageDetails": { + "locationData": { + "elevation": "", + "heading": "", + "latitude": "", + "speed": "", + "longitude": "" + }, + "rxSource": "RV" + }, + "payloadType": "us.dot.its.jpo.ode.model.OdeBsmPayload", + "serialId": { + "recordId": 0, + "serialNumber": 0, + "streamId": "849c3ac6-4747-426f-ae5e-28ac8531b32c", + "bundleSize": 1, + "bundleId": 0 + }, + "logFileName": "", + "odeReceivedAt": "2024-06-04T16:23:31.954Z", + "originIp": "10.11.81.12" + }, + "payload": { + "data": { + "partII": [ + { + "id": "VehicleSafetyExtensions", + "value": { + "pathHistory": { + "crumbData": [ + { + "elevationOffset": 0.6, + "lonOffset": 0.0000387, + "timeOffset": 0.8, + "latOffset": -0.0001604 + }, + { + "elevationOffset": 4.3, + "lonOffset": 0.000231, + "timeOffset": 5.6, + "latOffset": -0.0011603 + }, + { + "elevationOffset": 9.1, + "lonOffset": 0.0003834, + "timeOffset": 11, + "latOffset": -0.0023425 + } + ] + }, + "pathPrediction": { + "confidence": 80, + "radiusOfCurve": -1622.5 + } + } + } + ], + "coreData": { + "secMark": 31954, + "transmission": "UNAVAILABLE", + "size": { + "width": 180, + "length": 480 + }, + "heading": 185, + "accelSet": { + "accelYaw": -0.65, + "accelLong": -0.05 + }, + "brakes": { + "scs": "unavailable", + "wheelBrakes": { + "leftFront": false, + "rightFront": false, + "unavailable": true, + "leftRear": false, + "rightRear": false + }, + "abs": "off", + "traction": "unavailable", + "brakeBoost": "unavailable", + "auxBrakes": "unavailable" + }, + "accuracy": { + "semiMinor": 2, + "orientation": 0, + "semiMajor": 2 + }, + "id": "A0B43C7E", + "position": { + "elevation": 1691.9, + "latitude": 39.58790979644899, + "longitude": -105.09096389737384 + }, + "msgCnt": 41, + "speed": 13.27 + } + }, + "dataType": "us.dot.its.jpo.ode.plugin.j2735.J2735Bsm" + }, + "recordGeneratedAt": { + "$date": { + "$numberLong": "1717518211964" + } + } +},{ + "_id": { + "$oid": "665f3fc08c25216ef4f53e20" + }, + "metadata": { + "securityResultCode": "success", + "bsmSource": "RV", + "schemaVersion": 6, + "odePacketID": "", + "sanitized": false, + "recordType": "bsmTx", + "recordGeneratedAt": "", + "maxDurationTime": 0, + "odeTimStartDateTime": "", + "receivedMessageDetails": { + "locationData": { + "elevation": "", + "heading": "", + "latitude": "", + "speed": "", + "longitude": "" + }, + "rxSource": "RV" + }, + "payloadType": "us.dot.its.jpo.ode.model.OdeBsmPayload", + "serialId": { + "recordId": 0, + "serialNumber": 0, + "streamId": "849c3ac6-4747-426f-ae5e-28ac8531b32c", + "bundleSize": 1, + "bundleId": 0 + }, + "logFileName": "", + "odeReceivedAt": "2024-06-04T16:23:32.062Z", + "originIp": "10.11.81.12" + }, + "payload": { + "data": { + "partII": [ + { + "id": "VehicleSafetyExtensions", + "value": { + "pathHistory": { + "crumbData": [ + { + "elevationOffset": 0.6, + "lonOffset": 0.0000387, + "timeOffset": 0.8, + "latOffset": -0.0001604 + }, + { + "elevationOffset": 4.3, + "lonOffset": 0.000231, + "timeOffset": 5.6, + "latOffset": -0.0011603 + }, + { + "elevationOffset": 9.1, + "lonOffset": 0.0003834, + "timeOffset": 11, + "latOffset": -0.0023425 + } + ] + }, + "pathPrediction": { + "confidence": 80, + "radiusOfCurve": -1622.5 + } + } + } + ], + "coreData": { + "secMark": 32062, + "transmission": "UNAVAILABLE", + "size": { + "width": 180, + "length": 480 + }, + "heading": 180, + "accelSet": { + "accelYaw": -0.65, + "accelLong": -0.05 + }, + "brakes": { + "scs": "unavailable", + "wheelBrakes": { + "leftFront": false, + "rightFront": false, + "unavailable": true, + "leftRear": false, + "rightRear": false + }, + "abs": "off", + "traction": "unavailable", + "brakeBoost": "unavailable", + "auxBrakes": "unavailable" + }, + "accuracy": { + "semiMinor": 2, + "orientation": 0, + "semiMajor": 2 + }, + "id": "A0B43C7E", + "position": { + "elevation": 1691.9, + "latitude": 39.58789946125678, + "longitude": -105.09096389737384 + }, + "msgCnt": 42, + "speed": 11.49 + } + }, + "dataType": "us.dot.its.jpo.ode.plugin.j2735.J2735Bsm" + }, + "recordGeneratedAt": { + "$date": { + "$numberLong": "1717518212067" + } + } +},{ + "_id": { + "$oid": "665f3fc08c25216ef4f53e21" + }, + "metadata": { + "securityResultCode": "success", + "bsmSource": "RV", + "schemaVersion": 6, + "odePacketID": "", + "sanitized": false, + "recordType": "bsmTx", + "recordGeneratedAt": "", + "maxDurationTime": 0, + "odeTimStartDateTime": "", + "receivedMessageDetails": { + "locationData": { + "elevation": "", + "heading": "", + "latitude": "", + "speed": "", + "longitude": "" + }, + "rxSource": "RV" + }, + "payloadType": "us.dot.its.jpo.ode.model.OdeBsmPayload", + "serialId": { + "recordId": 0, + "serialNumber": 0, + "streamId": "849c3ac6-4747-426f-ae5e-28ac8531b32c", + "bundleSize": 1, + "bundleId": 0 + }, + "logFileName": "", + "odeReceivedAt": "2024-06-04T16:23:32.169Z", + "originIp": "10.11.81.12" + }, + "payload": { + "data": { + "partII": [ + { + "id": "VehicleSafetyExtensions", + "value": { + "pathHistory": { + "crumbData": [ + { + "elevationOffset": 0.6, + "lonOffset": 0.0000387, + "timeOffset": 0.8, + "latOffset": -0.0001604 + }, + { + "elevationOffset": 4.3, + "lonOffset": 0.000231, + "timeOffset": 5.6, + "latOffset": -0.0011603 + }, + { + "elevationOffset": 9.1, + "lonOffset": 0.0003834, + "timeOffset": 11, + "latOffset": -0.0023425 + } + ] + }, + "pathPrediction": { + "confidence": 80, + "radiusOfCurve": -1622.5 + } + } + } + ], + "coreData": { + "secMark": 32169, + "transmission": "UNAVAILABLE", + "size": { + "width": 180, + "length": 480 + }, + "heading": 178.1, + "accelSet": { + "accelYaw": -0.65, + "accelLong": -0.05 + }, + "brakes": { + "scs": "unavailable", + "wheelBrakes": { + "leftFront": false, + "rightFront": false, + "unavailable": true, + "leftRear": false, + "rightRear": false + }, + "abs": "off", + "traction": "unavailable", + "brakeBoost": "unavailable", + "auxBrakes": "unavailable" + }, + "accuracy": { + "semiMinor": 2, + "orientation": 0, + "semiMajor": 2 + }, + "id": "A0B43C7E", + "position": { + "elevation": 1691.9, + "latitude": 39.587883958465525, + "longitude": -105.09096322671174 + }, + "msgCnt": 43, + "speed": 17.25 + } + }, + "dataType": "us.dot.its.jpo.ode.plugin.j2735.J2735Bsm" + }, + "recordGeneratedAt": { + "$date": { + "$numberLong": "1717518212174" + } + } +},{ + "_id": { + "$oid": "665f3fc08c25216ef4f53e22" + }, + "metadata": { + "securityResultCode": "success", + "bsmSource": "RV", + "schemaVersion": 6, + "odePacketID": "", + "sanitized": false, + "recordType": "bsmTx", + "recordGeneratedAt": "", + "maxDurationTime": 0, + "odeTimStartDateTime": "", + "receivedMessageDetails": { + "locationData": { + "elevation": "", + "heading": "", + "latitude": "", + "speed": "", + "longitude": "" + }, + "rxSource": "RV" + }, + "payloadType": "us.dot.its.jpo.ode.model.OdeBsmPayload", + "serialId": { + "recordId": 0, + "serialNumber": 0, + "streamId": "849c3ac6-4747-426f-ae5e-28ac8531b32c", + "bundleSize": 1, + "bundleId": 0 + }, + "logFileName": "", + "odeReceivedAt": "2024-06-04T16:23:32.274Z", + "originIp": "10.11.81.12" + }, + "payload": { + "data": { + "partII": [ + { + "id": "VehicleSafetyExtensions", + "value": { + "pathHistory": { + "crumbData": [ + { + "elevationOffset": 0.6, + "lonOffset": 0.0000387, + "timeOffset": 0.8, + "latOffset": -0.0001604 + }, + { + "elevationOffset": 4.3, + "lonOffset": 0.000231, + "timeOffset": 5.6, + "latOffset": -0.0011603 + }, + { + "elevationOffset": 9.1, + "lonOffset": 0.0003834, + "timeOffset": 11, + "latOffset": -0.0023425 + } + ] + }, + "pathPrediction": { + "confidence": 80, + "radiusOfCurve": -1622.5 + } + } + } + ], + "coreData": { + "secMark": 32274, + "transmission": "UNAVAILABLE", + "size": { + "width": 180, + "length": 480 + }, + "heading": 177.5, + "accelSet": { + "accelYaw": -0.65, + "accelLong": -0.05 + }, + "brakes": { + "scs": "unavailable", + "wheelBrakes": { + "leftFront": false, + "rightFront": false, + "unavailable": true, + "leftRear": false, + "rightRear": false + }, + "abs": "off", + "traction": "unavailable", + "brakeBoost": "unavailable", + "auxBrakes": "unavailable" + }, + "accuracy": { + "semiMinor": 2, + "orientation": 0, + "semiMajor": 2 + }, + "id": "A0B43C7E", + "position": { + "elevation": 1691.9, + "latitude": 39.587872072989924, + "longitude": -105.09096255604963 + }, + "msgCnt": 44, + "speed": 13.23 + } + }, + "dataType": "us.dot.its.jpo.ode.plugin.j2735.J2735Bsm" + }, + "recordGeneratedAt": { + "$date": { + "$numberLong": "1717518212281" + } + } +},{ + "_id": { + "$oid": "665f3fc08c25216ef4f53e23" + }, + "metadata": { + "securityResultCode": "success", + "bsmSource": "RV", + "schemaVersion": 6, + "odePacketID": "", + "sanitized": false, + "recordType": "bsmTx", + "recordGeneratedAt": "", + "maxDurationTime": 0, + "odeTimStartDateTime": "", + "receivedMessageDetails": { + "locationData": { + "elevation": "", + "heading": "", + "latitude": "", + "speed": "", + "longitude": "" + }, + "rxSource": "RV" + }, + "payloadType": "us.dot.its.jpo.ode.model.OdeBsmPayload", + "serialId": { + "recordId": 0, + "serialNumber": 0, + "streamId": "849c3ac6-4747-426f-ae5e-28ac8531b32c", + "bundleSize": 1, + "bundleId": 0 + }, + "logFileName": "", + "odeReceivedAt": "2024-06-04T16:23:32.382Z", + "originIp": "10.11.81.12" + }, + "payload": { + "data": { + "partII": [ + { + "id": "VehicleSafetyExtensions", + "value": { + "pathHistory": { + "crumbData": [ + { + "elevationOffset": 0.6, + "lonOffset": 0.0000387, + "timeOffset": 0.8, + "latOffset": -0.0001604 + }, + { + "elevationOffset": 4.3, + "lonOffset": 0.000231, + "timeOffset": 5.6, + "latOffset": -0.0011603 + }, + { + "elevationOffset": 9.1, + "lonOffset": 0.0003834, + "timeOffset": 11, + "latOffset": -0.0023425 + } + ] + }, + "pathPrediction": { + "confidence": 80, + "radiusOfCurve": -1622.5 + } + } + } + ], + "coreData": { + "secMark": 32382, + "transmission": "UNAVAILABLE", + "size": { + "width": 180, + "length": 480 + }, + "heading": 166, + "accelSet": { + "accelYaw": -0.65, + "accelLong": -0.05 + }, + "brakes": { + "scs": "unavailable", + "wheelBrakes": { + "leftFront": false, + "rightFront": false, + "unavailable": true, + "leftRear": false, + "rightRear": false + }, + "abs": "off", + "traction": "unavailable", + "brakeBoost": "unavailable", + "auxBrakes": "unavailable" + }, + "accuracy": { + "semiMinor": 2, + "orientation": 0, + "semiMajor": 2 + }, + "id": "A0B43C7E", + "position": { + "elevation": 1691.9, + "latitude": 39.5878596707523, + "longitude": -105.09095853207707 + }, + "msgCnt": 45, + "speed": 14.22 + } + }, + "dataType": "us.dot.its.jpo.ode.plugin.j2735.J2735Bsm" + }, + "recordGeneratedAt": { + "$date": { + "$numberLong": "1717518212387" + } + } +},{ + "_id": { + "$oid": "665f3fc08c25216ef4f53e24" + }, + "metadata": { + "securityResultCode": "success", + "bsmSource": "RV", + "schemaVersion": 6, + "odePacketID": "", + "sanitized": false, + "recordType": "bsmTx", + "recordGeneratedAt": "", + "maxDurationTime": 0, + "odeTimStartDateTime": "", + "receivedMessageDetails": { + "locationData": { + "elevation": "", + "heading": "", + "latitude": "", + "speed": "", + "longitude": "" + }, + "rxSource": "RV" + }, + "payloadType": "us.dot.its.jpo.ode.model.OdeBsmPayload", + "serialId": { + "recordId": 0, + "serialNumber": 0, + "streamId": "849c3ac6-4747-426f-ae5e-28ac8531b32c", + "bundleSize": 1, + "bundleId": 0 + }, + "logFileName": "", + "odeReceivedAt": "2024-06-04T16:23:32.489Z", + "originIp": "10.11.81.12" + }, + "payload": { + "data": { + "partII": [ + { + "id": "VehicleSafetyExtensions", + "value": { + "pathHistory": { + "crumbData": [ + { + "elevationOffset": 0.6, + "lonOffset": 0.0000387, + "timeOffset": 0.8, + "latOffset": -0.0001604 + }, + { + "elevationOffset": 4.3, + "lonOffset": 0.000231, + "timeOffset": 5.6, + "latOffset": -0.0011603 + }, + { + "elevationOffset": 9.1, + "lonOffset": 0.0003834, + "timeOffset": 11, + "latOffset": -0.0023425 + } + ] + }, + "pathPrediction": { + "confidence": 80, + "radiusOfCurve": -1622.5 + } + } + } + ], + "coreData": { + "secMark": 32489, + "transmission": "UNAVAILABLE", + "size": { + "width": 180, + "length": 480 + }, + "heading": 169.7, + "accelSet": { + "accelYaw": -0.65, + "accelLong": -0.05 + }, + "brakes": { + "scs": "unavailable", + "wheelBrakes": { + "leftFront": false, + "rightFront": false, + "unavailable": true, + "leftRear": false, + "rightRear": false + }, + "abs": "off", + "traction": "unavailable", + "brakeBoost": "unavailable", + "auxBrakes": "unavailable" + }, + "accuracy": { + "semiMinor": 2, + "orientation": 0, + "semiMajor": 2 + }, + "id": "A0B43C7E", + "position": { + "elevation": 1691.9, + "latitude": 39.58784261767199, + "longitude": -105.0909545081045 + }, + "msgCnt": 46, + "speed": 19.27 + } + }, + "dataType": "us.dot.its.jpo.ode.plugin.j2735.J2735Bsm" + }, + "recordGeneratedAt": { + "$date": { + "$numberLong": "1717518212493" + } + } +},{ + "_id": { + "$oid": "665f3fc08c25216ef4f53e25" + }, + "metadata": { + "securityResultCode": "success", + "bsmSource": "RV", + "schemaVersion": 6, + "odePacketID": "", + "sanitized": false, + "recordType": "bsmTx", + "recordGeneratedAt": "", + "maxDurationTime": 0, + "odeTimStartDateTime": "", + "receivedMessageDetails": { + "locationData": { + "elevation": "", + "heading": "", + "latitude": "", + "speed": "", + "longitude": "" + }, + "rxSource": "RV" + }, + "payloadType": "us.dot.its.jpo.ode.model.OdeBsmPayload", + "serialId": { + "recordId": 0, + "serialNumber": 0, + "streamId": "849c3ac6-4747-426f-ae5e-28ac8531b32c", + "bundleSize": 1, + "bundleId": 0 + }, + "logFileName": "", + "odeReceivedAt": "2024-06-04T16:23:32.596Z", + "originIp": "10.11.81.12" + }, + "payload": { + "data": { + "partII": [ + { + "id": "VehicleSafetyExtensions", + "value": { + "pathHistory": { + "crumbData": [ + { + "elevationOffset": 0.6, + "lonOffset": 0.0000387, + "timeOffset": 0.8, + "latOffset": -0.0001604 + }, + { + "elevationOffset": 4.3, + "lonOffset": 0.000231, + "timeOffset": 5.6, + "latOffset": -0.0011603 + }, + { + "elevationOffset": 9.1, + "lonOffset": 0.0003834, + "timeOffset": 11, + "latOffset": -0.0023425 + } + ] + }, + "pathPrediction": { + "confidence": 80, + "radiusOfCurve": -1622.5 + } + } + } + ], + "coreData": { + "secMark": 32596, + "transmission": "UNAVAILABLE", + "size": { + "width": 180, + "length": 480 + }, + "heading": 168.7, + "accelSet": { + "accelYaw": -0.65, + "accelLong": -0.05 + }, + "brakes": { + "scs": "unavailable", + "wheelBrakes": { + "leftFront": false, + "rightFront": false, + "unavailable": true, + "leftRear": false, + "rightRear": false + }, + "abs": "off", + "traction": "unavailable", + "brakeBoost": "unavailable", + "auxBrakes": "unavailable" + }, + "accuracy": { + "semiMinor": 2, + "orientation": 0, + "semiMajor": 2 + }, + "id": "A0B43C7E", + "position": { + "elevation": 1691.9, + "latitude": 39.587829698668905, + "longitude": -105.090951154794 + }, + "msgCnt": 47, + "speed": 14.65 + } + }, + "dataType": "us.dot.its.jpo.ode.plugin.j2735.J2735Bsm" + }, + "recordGeneratedAt": { + "$date": { + "$numberLong": "1717518212601" + } + } +},{ + "_id": { + "$oid": "665f3fc08c25216ef4f53e26" + }, + "metadata": { + "securityResultCode": "success", + "bsmSource": "RV", + "schemaVersion": 6, + "odePacketID": "", + "sanitized": false, + "recordType": "bsmTx", + "recordGeneratedAt": "", + "maxDurationTime": 0, + "odeTimStartDateTime": "", + "receivedMessageDetails": { + "locationData": { + "elevation": "", + "heading": "", + "latitude": "", + "speed": "", + "longitude": "" + }, + "rxSource": "RV" + }, + "payloadType": "us.dot.its.jpo.ode.model.OdeBsmPayload", + "serialId": { + "recordId": 0, + "serialNumber": 0, + "streamId": "849c3ac6-4747-426f-ae5e-28ac8531b32c", + "bundleSize": 1, + "bundleId": 0 + }, + "logFileName": "", + "odeReceivedAt": "2024-06-04T16:23:32.705Z", + "originIp": "10.11.81.12" + }, + "payload": { + "data": { + "partII": [ + { + "id": "VehicleSafetyExtensions", + "value": { + "pathHistory": { + "crumbData": [ + { + "elevationOffset": 0.6, + "lonOffset": 0.0000387, + "timeOffset": 0.8, + "latOffset": -0.0001604 + }, + { + "elevationOffset": 4.3, + "lonOffset": 0.000231, + "timeOffset": 5.6, + "latOffset": -0.0011603 + }, + { + "elevationOffset": 9.1, + "lonOffset": 0.0003834, + "timeOffset": 11, + "latOffset": -0.0023425 + } + ] + }, + "pathPrediction": { + "confidence": 80, + "radiusOfCurve": -1622.5 + } + } + } + ], + "coreData": { + "secMark": 32705, + "transmission": "UNAVAILABLE", + "size": { + "width": 180, + "length": 480 + }, + "heading": 168, + "accelSet": { + "accelYaw": -0.65, + "accelLong": -0.05 + }, + "brakes": { + "scs": "unavailable", + "wheelBrakes": { + "leftFront": false, + "rightFront": false, + "unavailable": true, + "leftRear": false, + "rightRear": false + }, + "abs": "off", + "traction": "unavailable", + "brakeBoost": "unavailable", + "auxBrakes": "unavailable" + }, + "accuracy": { + "semiMinor": 2, + "orientation": 0, + "semiMajor": 2 + }, + "id": "A0B43C7E", + "position": { + "elevation": 1691.9, + "latitude": 39.58781264558121, + "longitude": -105.09094646015933 + }, + "msgCnt": 48, + "speed": 19.38 + } + }, + "dataType": "us.dot.its.jpo.ode.plugin.j2735.J2735Bsm" + }, + "recordGeneratedAt": { + "$date": { + "$numberLong": "1717518212708" + } + } +},{ + "_id": { + "$oid": "665f3fc08c25216ef4f53e27" + }, + "metadata": { + "securityResultCode": "success", + "bsmSource": "RV", + "schemaVersion": 6, + "odePacketID": "", + "sanitized": false, + "recordType": "bsmTx", + "recordGeneratedAt": "", + "maxDurationTime": 0, + "odeTimStartDateTime": "", + "receivedMessageDetails": { + "locationData": { + "elevation": "", + "heading": "", + "latitude": "", + "speed": "", + "longitude": "" + }, + "rxSource": "RV" + }, + "payloadType": "us.dot.its.jpo.ode.model.OdeBsmPayload", + "serialId": { + "recordId": 0, + "serialNumber": 0, + "streamId": "849c3ac6-4747-426f-ae5e-28ac8531b32c", + "bundleSize": 1, + "bundleId": 0 + }, + "logFileName": "", + "odeReceivedAt": "2024-06-04T16:23:32.814Z", + "originIp": "10.11.81.12" + }, + "payload": { + "data": { + "partII": [ + { + "id": "VehicleSafetyExtensions", + "value": { + "pathHistory": { + "crumbData": [ + { + "elevationOffset": 0.6, + "lonOffset": 0.0000387, + "timeOffset": 0.8, + "latOffset": -0.0001604 + }, + { + "elevationOffset": 4.3, + "lonOffset": 0.000231, + "timeOffset": 5.6, + "latOffset": -0.0011603 + }, + { + "elevationOffset": 9.1, + "lonOffset": 0.0003834, + "timeOffset": 11, + "latOffset": -0.0023425 + } + ] + }, + "pathPrediction": { + "confidence": 80, + "radiusOfCurve": -1622.5 + } + } + } + ], + "coreData": { + "secMark": 32814, + "transmission": "UNAVAILABLE", + "size": { + "width": 180, + "length": 480 + }, + "heading": 165.3, + "accelSet": { + "accelYaw": -0.65, + "accelLong": -0.05 + }, + "brakes": { + "scs": "unavailable", + "wheelBrakes": { + "leftFront": false, + "rightFront": false, + "unavailable": true, + "leftRear": false, + "rightRear": false + }, + "abs": "off", + "traction": "unavailable", + "brakeBoost": "unavailable", + "auxBrakes": "unavailable" + }, + "accuracy": { + "semiMinor": 2, + "orientation": 0, + "semiMajor": 2 + }, + "id": "A0B43C7E", + "position": { + "elevation": 1691.9, + "latitude": 39.58779300868712, + "longitude": -105.09093975353838 + }, + "msgCnt": 49, + "speed": 22.58 + } + }, + "dataType": "us.dot.its.jpo.ode.plugin.j2735.J2735Bsm" + }, + "recordGeneratedAt": { + "$date": { + "$numberLong": "1717518212816" + } + } +},{ + "_id": { + "$oid": "665f3fc08c25216ef4f53e28" + }, + "metadata": { + "securityResultCode": "success", + "bsmSource": "RV", + "schemaVersion": 6, + "odePacketID": "", + "sanitized": false, + "recordType": "bsmTx", + "recordGeneratedAt": "", + "maxDurationTime": 0, + "odeTimStartDateTime": "", + "receivedMessageDetails": { + "locationData": { + "elevation": "", + "heading": "", + "latitude": "", + "speed": "", + "longitude": "" + }, + "rxSource": "RV" + }, + "payloadType": "us.dot.its.jpo.ode.model.OdeBsmPayload", + "serialId": { + "recordId": 0, + "serialNumber": 0, + "streamId": "849c3ac6-4747-426f-ae5e-28ac8531b32c", + "bundleSize": 1, + "bundleId": 0 + }, + "logFileName": "", + "odeReceivedAt": "2024-06-04T16:23:32.919Z", + "originIp": "10.11.81.12" + }, + "payload": { + "data": { + "partII": [ + { + "id": "VehicleSafetyExtensions", + "value": { + "pathHistory": { + "crumbData": [ + { + "elevationOffset": 0.6, + "lonOffset": 0.0000387, + "timeOffset": 0.8, + "latOffset": -0.0001604 + }, + { + "elevationOffset": 4.3, + "lonOffset": 0.000231, + "timeOffset": 5.6, + "latOffset": -0.0011603 + }, + { + "elevationOffset": 9.1, + "lonOffset": 0.0003834, + "timeOffset": 11, + "latOffset": -0.0023425 + } + ] + }, + "pathPrediction": { + "confidence": 80, + "radiusOfCurve": -1622.5 + } + } + } + ], + "coreData": { + "secMark": 32919, + "transmission": "UNAVAILABLE", + "size": { + "width": 180, + "length": 480 + }, + "heading": 164.1, + "accelSet": { + "accelYaw": -0.65, + "accelLong": -0.05 + }, + "brakes": { + "scs": "unavailable", + "wheelBrakes": { + "leftFront": false, + "rightFront": false, + "unavailable": true, + "leftRear": false, + "rightRear": false + }, + "abs": "off", + "traction": "unavailable", + "brakeBoost": "unavailable", + "auxBrakes": "unavailable" + }, + "accuracy": { + "semiMinor": 2, + "orientation": 0, + "semiMajor": 2 + }, + "id": "A0B43C7E", + "position": { + "elevation": 1691.9, + "latitude": 39.58777853939319, + "longitude": -105.0909343882416 + }, + "msgCnt": 50, + "speed": 16.73 + } + }, + "dataType": "us.dot.its.jpo.ode.plugin.j2735.J2735Bsm" + }, + "recordGeneratedAt": { + "$date": { + "$numberLong": "1717518212924" + } + } +}] \ No newline at end of file diff --git a/conflictmonitor/datasets/uturn_export/ProcessedMap.json b/conflictmonitor/datasets/uturn_export/ProcessedMap.json new file mode 100644 index 000000000..78127c4a5 --- /dev/null +++ b/conflictmonitor/datasets/uturn_export/ProcessedMap.json @@ -0,0 +1,2188 @@ +[{ + "_id": { + "$oid": "665f3fbe8c25216ef4f53935" + }, + "connectingLanesFeatureCollection": { + "features": [ + { + "geometry": { + "coordinates": [ + [ + -105.0907089, + 39.587905 + ], + [ + -105.0907875, + 39.58822 + ] + ], + "type": "LineString" + }, + "id": "1-15", + "type": "Feature", + "properties": { + "egressLaneId": 15, + "ingressLaneId": 1, + "signalGroupId": 2 + } + }, + { + "geometry": { + "coordinates": [ + [ + -105.0907462, + 39.5878956 + ], + [ + -105.0908389, + 39.5882151 + ] + ], + "type": "LineString" + }, + "id": "2-14", + "type": "Feature", + "properties": { + "egressLaneId": 14, + "ingressLaneId": 2, + "signalGroupId": 2 + } + }, + { + "geometry": { + "coordinates": [ + [ + -105.0907914, + 39.5878879 + ], + [ + -105.0911626, + 39.5880622 + ] + ], + "type": "LineString" + }, + "id": "3-10", + "type": "Feature", + "properties": { + "egressLaneId": 10, + "ingressLaneId": 3, + "signalGroupId": 2 + } + }, + { + "geometry": { + "coordinates": [ + [ + -105.0911477, + 39.587995 + ], + [ + -105.0907875, + 39.58822 + ] + ], + "type": "LineString" + }, + "id": "8-15", + "type": "Feature", + "properties": { + "egressLaneId": 15, + "ingressLaneId": 8, + "signalGroupId": 4 + } + }, + { + "geometry": { + "coordinates": [ + [ + -105.0911442, + 39.5879589 + ], + [ + -105.0910008, + 39.5878477 + ] + ], + "type": "LineString" + }, + "id": "7-6", + "type": "Feature", + "properties": { + "egressLaneId": 6, + "ingressLaneId": 7 + } + }, + { + "geometry": { + "coordinates": [ + [ + -105.0911534, + 39.5880261 + ], + [ + -105.0908389, + 39.5882151 + ] + ], + "type": "LineString" + }, + "id": "9-14", + "type": "Feature", + "properties": { + "egressLaneId": 14, + "ingressLaneId": 9, + "signalGroupId": 4 + } + }, + { + "geometry": { + "coordinates": [ + [ + -105.0910447, + 39.5881948 + ], + [ + -105.090959, + 39.5878557 + ] + ], + "type": "LineString" + }, + "id": "12-5", + "type": "Feature", + "properties": { + "egressLaneId": 5, + "ingressLaneId": 12, + "signalGroupId": 6 + } + }, + { + "geometry": { + "coordinates": [ + [ + -105.0910013, + 39.5881975 + ], + [ + -105.090914, + 39.5878612 + ] + ], + "type": "LineString" + }, + "id": "13-4", + "type": "Feature", + "properties": { + "egressLaneId": 4, + "ingressLaneId": 13, + "signalGroupId": 6 + } + }, + { + "geometry": { + "coordinates": [ + [ + -105.0910891, + 39.5881859 + ], + [ + -105.0911626, + 39.5880622 + ] + ], + "type": "LineString" + }, + "id": "11-10", + "type": "Feature", + "properties": { + "egressLaneId": 10, + "ingressLaneId": 11 + } + } + ], + "type": "FeatureCollection" + }, + "mapFeatureCollection": { + "features": [ + { + "geometry": { + "coordinates": [ + [ + -105.0907089, + 39.587905 + ], + [ + -105.0906245, + 39.5876246 + ], + [ + -105.0905203, + 39.587281 + ], + [ + -105.0904383, + 39.5870554 + ], + [ + -105.0903588, + 39.5868383 + ], + [ + -105.0902622, + 39.5865865 + ], + [ + -105.0901249, + 39.5862612 + ], + [ + -105.0900451, + 39.5860819 + ], + [ + -105.0899283, + 39.5858283 + ], + [ + -105.0898739, + 39.5857117 + ], + [ + -105.0895814, + 39.5851569 + ], + [ + -105.0888764, + 39.5839527 + ] + ], + "type": "LineString" + }, + "id": 1, + "type": "Feature", + "properties": { + "connectsTo": [ + { + "connectionID": 1, + "signalGroup": 2, + "connectingLane": { + "lane": 15, + "maneuver": { + "maneuverStraightAllowed": true, + "maneuverNoStoppingAllowed": false, + "goWithHalt": false, + "maneuverLeftAllowed": false, + "maneuverUTurnAllowed": false, + "maneuverLeftTurnOnRedAllowed": false, + "reserved1": false, + "maneuverRightAllowed": false, + "maneuverLaneChangeAllowed": false, + "yieldAllwaysRequired": false, + "maneuverRightTurnOnRedAllowed": false, + "caution": false + } + } + } + ], + "laneType": { + "vehicle": { + "isVehicleRevocableLane": false, + "isVehicleFlyOverLane": false, + "permissionOnRequest": false, + "hasIRbeaconCoverage": false, + "restrictedToBusUse": false, + "restrictedToTaxiUse": false, + "restrictedFromPublicUse": false, + "hovLaneUseOnly": false + } + }, + "laneId": 1, + "nodes": [ + { + "delta": [ + 1511, + -1514 + ] + }, + { + "delevation": 10, + "delta": [ + 723, + -3116 + ] + }, + { + "delevation": 20, + "delta": [ + 892, + -3818 + ] + }, + { + "delevation": 20, + "delta": [ + 702, + -2507 + ] + }, + { + "delevation": 10, + "delta": [ + 681, + -2412 + ] + }, + { + "delevation": 10, + "delta": [ + 827, + -2798 + ] + }, + { + "delevation": 20, + "delta": [ + 1176, + -3614 + ] + }, + { + "delta": [ + 683, + -1992 + ] + }, + { + "delevation": 10, + "delta": [ + 1000, + -2818 + ] + }, + { + "delevation": 20, + "delta": [ + 466, + -1295 + ] + }, + { + "delevation": 20, + "delta": [ + 2505, + -6164 + ] + }, + { + "delevation": 70, + "delta": [ + 6037, + -13380 + ] + } + ], + "egressApproach": 0, + "ingressApproach": 1, + "sharedWith": { + "busVehicleTraffic": false, + "trackedVehicleTraffic": false, + "individualMotorizedVehicleTraffic": false, + "taxiVehicleTraffic": false, + "overlappingLaneDescriptionProvided": false, + "cyclistVehicleTraffic": false, + "otherNonMotorizedTrafficTypes": false, + "multipleLanesTreatedAsOneLane": false, + "pedestrianTraffic": false, + "pedestriansTraffic": false + }, + "ingressPath": true, + "maneuvers": { + "maneuverStraightAllowed": true, + "maneuverNoStoppingAllowed": false, + "goWithHalt": false, + "maneuverLeftAllowed": false, + "maneuverUTurnAllowed": false, + "maneuverLeftTurnOnRedAllowed": false, + "reserved1": false, + "maneuverRightAllowed": false, + "maneuverLaneChangeAllowed": false, + "yieldAllwaysRequired": false, + "maneuverRightTurnOnRedAllowed": false, + "caution": false + }, + "egressPath": false + } + }, + { + "geometry": { + "coordinates": [ + [ + -105.0907462, + 39.5878956 + ], + [ + -105.090652, + 39.5875596 + ], + [ + -105.090534, + 39.5871793 + ], + [ + -105.0903457, + 39.5866864 + ], + [ + -105.0902123, + 39.5863581 + ], + [ + -105.0900802, + 39.5860572 + ], + [ + -105.0898164, + 39.5855019 + ], + [ + -105.0895409, + 39.5849856 + ], + [ + -105.088922, + 39.5839259 + ] + ], + "type": "LineString" + }, + "id": 2, + "type": "Feature", + "properties": { + "connectsTo": [ + { + "connectionID": 1, + "signalGroup": 2, + "connectingLane": { + "lane": 14, + "maneuver": { + "maneuverStraightAllowed": true, + "maneuverNoStoppingAllowed": false, + "goWithHalt": false, + "maneuverLeftAllowed": false, + "maneuverUTurnAllowed": false, + "maneuverLeftTurnOnRedAllowed": false, + "reserved1": false, + "maneuverRightAllowed": false, + "maneuverLaneChangeAllowed": false, + "yieldAllwaysRequired": false, + "maneuverRightTurnOnRedAllowed": false, + "caution": false + } + } + } + ], + "laneType": { + "vehicle": { + "isVehicleRevocableLane": false, + "isVehicleFlyOverLane": false, + "permissionOnRequest": false, + "hasIRbeaconCoverage": false, + "restrictedToBusUse": false, + "restrictedToTaxiUse": false, + "restrictedFromPublicUse": false, + "hovLaneUseOnly": false + } + }, + "laneId": 2, + "nodes": [ + { + "delta": [ + 1192, + -1619 + ] + }, + { + "delevation": 30, + "delta": [ + 807, + -3733 + ] + }, + { + "delevation": 10, + "delta": [ + 1010, + -4226 + ] + }, + { + "delevation": 30, + "delta": [ + 1612, + -5477 + ] + }, + { + "delevation": 20, + "delta": [ + 1142, + -3648 + ] + }, + { + "delevation": 10, + "delta": [ + 1131, + -3343 + ] + }, + { + "delevation": 30, + "delta": [ + 2259, + -6170 + ] + }, + { + "delevation": 30, + "delta": [ + 2359, + -5737 + ] + }, + { + "delevation": 50, + "delta": [ + 5300, + -11774 + ] + } + ], + "egressApproach": 0, + "ingressApproach": 1, + "sharedWith": { + "busVehicleTraffic": false, + "trackedVehicleTraffic": false, + "individualMotorizedVehicleTraffic": false, + "taxiVehicleTraffic": false, + "overlappingLaneDescriptionProvided": false, + "cyclistVehicleTraffic": false, + "otherNonMotorizedTrafficTypes": false, + "multipleLanesTreatedAsOneLane": false, + "pedestrianTraffic": false, + "pedestriansTraffic": false + }, + "ingressPath": true, + "maneuvers": { + "maneuverStraightAllowed": true, + "maneuverNoStoppingAllowed": false, + "goWithHalt": false, + "maneuverLeftAllowed": false, + "maneuverUTurnAllowed": false, + "maneuverLeftTurnOnRedAllowed": false, + "reserved1": false, + "maneuverRightAllowed": false, + "maneuverLaneChangeAllowed": false, + "yieldAllwaysRequired": false, + "maneuverRightTurnOnRedAllowed": false, + "caution": false + }, + "egressPath": false + } + }, + { + "geometry": { + "coordinates": [ + [ + -105.0907914, + 39.5878879 + ], + [ + -105.090747, + 39.5877247 + ], + [ + -105.0906498, + 39.5874141 + ], + [ + -105.0906262, + 39.5873356 + ], + [ + -105.0905865, + 39.5872922 + ] + ], + "type": "LineString" + }, + "id": 3, + "type": "Feature", + "properties": { + "connectsTo": [ + { + "connectionID": 1, + "signalGroup": 2, + "connectingLane": { + "lane": 10, + "maneuver": { + "maneuverStraightAllowed": false, + "maneuverNoStoppingAllowed": false, + "goWithHalt": false, + "maneuverLeftAllowed": true, + "maneuverUTurnAllowed": false, + "maneuverLeftTurnOnRedAllowed": false, + "reserved1": false, + "maneuverRightAllowed": false, + "maneuverLaneChangeAllowed": false, + "yieldAllwaysRequired": false, + "maneuverRightTurnOnRedAllowed": false, + "caution": false + } + } + } + ], + "laneType": { + "vehicle": { + "isVehicleRevocableLane": false, + "isVehicleFlyOverLane": false, + "permissionOnRequest": false, + "hasIRbeaconCoverage": false, + "restrictedToBusUse": false, + "restrictedToTaxiUse": false, + "restrictedFromPublicUse": false, + "hovLaneUseOnly": false + } + }, + "laneId": 3, + "nodes": [ + { + "delevation": 10, + "delta": [ + 805, + -1704 + ] + }, + { + "delta": [ + 380, + -1813 + ] + }, + { + "delevation": 30, + "delta": [ + 832, + -3451 + ] + }, + { + "delta": [ + 202, + -872 + ] + }, + { + "delevation": -10, + "delta": [ + 340, + -482 + ] + } + ], + "egressApproach": 0, + "ingressApproach": 1, + "sharedWith": { + "busVehicleTraffic": false, + "trackedVehicleTraffic": false, + "individualMotorizedVehicleTraffic": false, + "taxiVehicleTraffic": false, + "overlappingLaneDescriptionProvided": false, + "cyclistVehicleTraffic": false, + "otherNonMotorizedTrafficTypes": false, + "multipleLanesTreatedAsOneLane": false, + "pedestrianTraffic": false, + "pedestriansTraffic": false + }, + "ingressPath": true, + "maneuvers": { + "maneuverStraightAllowed": false, + "maneuverNoStoppingAllowed": false, + "goWithHalt": false, + "maneuverLeftAllowed": true, + "maneuverUTurnAllowed": false, + "maneuverLeftTurnOnRedAllowed": false, + "reserved1": false, + "maneuverRightAllowed": false, + "maneuverLaneChangeAllowed": false, + "yieldAllwaysRequired": false, + "maneuverRightTurnOnRedAllowed": false, + "caution": false + }, + "egressPath": false + } + }, + { + "geometry": { + "coordinates": [ + [ + -105.0910008, + 39.5878477 + ], + [ + -105.0909927, + 39.5878181 + ] + ], + "type": "LineString" + }, + "id": 6, + "type": "Feature", + "properties": { + "laneType": { + "vehicle": { + "isVehicleRevocableLane": false, + "isVehicleFlyOverLane": false, + "permissionOnRequest": false, + "hasIRbeaconCoverage": false, + "restrictedToBusUse": false, + "restrictedToTaxiUse": false, + "restrictedFromPublicUse": false, + "hovLaneUseOnly": false + } + }, + "laneId": 6, + "nodes": [ + { + "delevation": 20, + "delta": [ + -988, + -2151 + ] + }, + { + "delta": [ + 69, + -329 + ] + } + ], + "egressApproach": 2, + "ingressApproach": 0, + "sharedWith": { + "busVehicleTraffic": false, + "trackedVehicleTraffic": false, + "individualMotorizedVehicleTraffic": false, + "taxiVehicleTraffic": false, + "overlappingLaneDescriptionProvided": false, + "cyclistVehicleTraffic": false, + "otherNonMotorizedTrafficTypes": false, + "multipleLanesTreatedAsOneLane": false, + "pedestrianTraffic": false, + "pedestriansTraffic": false + }, + "ingressPath": false, + "egressPath": true + } + }, + { + "geometry": { + "coordinates": [ + [ + -105.090959, + 39.5878557 + ], + [ + -105.0909501, + 39.5878218 + ] + ], + "type": "LineString" + }, + "id": 5, + "type": "Feature", + "properties": { + "laneType": { + "vehicle": { + "isVehicleRevocableLane": false, + "isVehicleFlyOverLane": false, + "permissionOnRequest": false, + "hasIRbeaconCoverage": false, + "restrictedToBusUse": false, + "restrictedToTaxiUse": false, + "restrictedFromPublicUse": false, + "hovLaneUseOnly": false + } + }, + "laneId": 5, + "nodes": [ + { + "delevation": 10, + "delta": [ + -630, + -2062 + ] + }, + { + "delevation": 10, + "delta": [ + 76, + -377 + ] + } + ], + "egressApproach": 2, + "ingressApproach": 0, + "sharedWith": { + "busVehicleTraffic": false, + "trackedVehicleTraffic": false, + "individualMotorizedVehicleTraffic": false, + "taxiVehicleTraffic": false, + "overlappingLaneDescriptionProvided": false, + "cyclistVehicleTraffic": false, + "otherNonMotorizedTrafficTypes": false, + "multipleLanesTreatedAsOneLane": false, + "pedestrianTraffic": false, + "pedestriansTraffic": false + }, + "ingressPath": false, + "egressPath": true + } + }, + { + "geometry": { + "coordinates": [ + [ + -105.090914, + 39.5878612 + ], + [ + -105.0909051, + 39.5878298 + ] + ], + "type": "LineString" + }, + "id": 4, + "type": "Feature", + "properties": { + "laneType": { + "vehicle": { + "isVehicleRevocableLane": false, + "isVehicleFlyOverLane": false, + "permissionOnRequest": false, + "hasIRbeaconCoverage": false, + "restrictedToBusUse": false, + "restrictedToTaxiUse": false, + "restrictedFromPublicUse": false, + "hovLaneUseOnly": false + } + }, + "laneId": 4, + "nodes": [ + { + "delevation": 10, + "delta": [ + -245, + -2001 + ] + }, + { + "delta": [ + 76, + -349 + ] + } + ], + "egressApproach": 2, + "ingressApproach": 0, + "sharedWith": { + "busVehicleTraffic": false, + "trackedVehicleTraffic": false, + "individualMotorizedVehicleTraffic": false, + "taxiVehicleTraffic": false, + "overlappingLaneDescriptionProvided": false, + "cyclistVehicleTraffic": false, + "otherNonMotorizedTrafficTypes": false, + "multipleLanesTreatedAsOneLane": false, + "pedestrianTraffic": false, + "pedestriansTraffic": false + }, + "ingressPath": false, + "egressPath": true + } + }, + { + "geometry": { + "coordinates": [ + [ + -105.0911626, + 39.5880622 + ], + [ + -105.0912043, + 39.5880536 + ] + ], + "type": "LineString" + }, + "id": 10, + "type": "Feature", + "properties": { + "laneType": { + "vehicle": { + "isVehicleRevocableLane": false, + "isVehicleFlyOverLane": false, + "permissionOnRequest": false, + "hasIRbeaconCoverage": false, + "restrictedToBusUse": false, + "restrictedToTaxiUse": false, + "restrictedFromPublicUse": false, + "hovLaneUseOnly": false + } + }, + "laneId": 10, + "nodes": [ + { + "delevation": 10, + "delta": [ + -2374, + 232 + ] + }, + { + "delta": [ + -357, + -96 + ] + } + ], + "egressApproach": 4, + "ingressApproach": 0, + "sharedWith": { + "busVehicleTraffic": false, + "trackedVehicleTraffic": false, + "individualMotorizedVehicleTraffic": false, + "taxiVehicleTraffic": false, + "overlappingLaneDescriptionProvided": false, + "cyclistVehicleTraffic": false, + "otherNonMotorizedTrafficTypes": false, + "multipleLanesTreatedAsOneLane": false, + "pedestrianTraffic": false, + "pedestriansTraffic": false + }, + "ingressPath": false, + "egressPath": true + } + }, + { + "geometry": { + "coordinates": [ + [ + -105.0911477, + 39.587995 + ], + [ + -105.0914565, + 39.5879427 + ], + [ + -105.0917937, + 39.5879029 + ], + [ + -105.0922121, + 39.5878724 + ], + [ + -105.0926509, + 39.5878748 + ], + [ + -105.0930303, + 39.5879073 + ], + [ + -105.0932697, + 39.5879503 + ], + [ + -105.0937243, + 39.5880569 + ], + [ + -105.0940309, + 39.5881258 + ], + [ + -105.0943257, + 39.5881804 + ], + [ + -105.094592, + 39.5882097 + ] + ], + "type": "LineString" + }, + "id": 8, + "type": "Feature", + "properties": { + "connectsTo": [ + { + "connectionID": 1, + "signalGroup": 4, + "connectingLane": { + "lane": 15, + "maneuver": { + "maneuverStraightAllowed": false, + "maneuverNoStoppingAllowed": false, + "goWithHalt": false, + "maneuverLeftAllowed": true, + "maneuverUTurnAllowed": false, + "maneuverLeftTurnOnRedAllowed": false, + "reserved1": false, + "maneuverRightAllowed": false, + "maneuverLaneChangeAllowed": false, + "yieldAllwaysRequired": false, + "maneuverRightTurnOnRedAllowed": false, + "caution": false + } + } + } + ], + "laneType": { + "vehicle": { + "isVehicleRevocableLane": false, + "isVehicleFlyOverLane": false, + "permissionOnRequest": false, + "hasIRbeaconCoverage": false, + "restrictedToBusUse": false, + "restrictedToTaxiUse": false, + "restrictedFromPublicUse": false, + "hovLaneUseOnly": false + } + }, + "laneId": 8, + "nodes": [ + { + "delevation": 10, + "delta": [ + -2246, + -514 + ] + }, + { + "delta": [ + -2644, + -581 + ] + }, + { + "delevation": 10, + "delta": [ + -2887, + -442 + ] + }, + { + "delevation": 10, + "delta": [ + -3583, + -339 + ] + }, + { + "delta": [ + -3757, + 27 + ] + }, + { + "delevation": -10, + "delta": [ + -3249, + 361 + ] + }, + { + "delta": [ + -2050, + 478 + ] + }, + { + "delta": [ + -3893, + 1184 + ] + }, + { + "delevation": -10, + "delta": [ + -2625, + 766 + ] + }, + { + "delevation": 10, + "delta": [ + -2524, + 607 + ] + }, + { + "delevation": 10, + "delta": [ + -2280, + 325 + ] + } + ], + "egressApproach": 0, + "ingressApproach": 3, + "sharedWith": { + "busVehicleTraffic": false, + "trackedVehicleTraffic": false, + "individualMotorizedVehicleTraffic": false, + "taxiVehicleTraffic": false, + "overlappingLaneDescriptionProvided": false, + "cyclistVehicleTraffic": false, + "otherNonMotorizedTrafficTypes": false, + "multipleLanesTreatedAsOneLane": false, + "pedestrianTraffic": false, + "pedestriansTraffic": false + }, + "ingressPath": true, + "maneuvers": { + "maneuverStraightAllowed": false, + "maneuverNoStoppingAllowed": false, + "goWithHalt": false, + "maneuverLeftAllowed": true, + "maneuverUTurnAllowed": false, + "maneuverLeftTurnOnRedAllowed": false, + "reserved1": false, + "maneuverRightAllowed": false, + "maneuverLaneChangeAllowed": false, + "yieldAllwaysRequired": false, + "maneuverRightTurnOnRedAllowed": false, + "caution": false + }, + "egressPath": false + } + }, + { + "geometry": { + "coordinates": [ + [ + -105.0911442, + 39.5879589 + ], + [ + -105.0914154, + 39.5879165 + ], + [ + -105.0916346, + 39.5878851 + ], + [ + -105.0918433, + 39.5878639 + ], + [ + -105.0921546, + 39.5878547 + ] + ], + "type": "LineString" + }, + "id": 7, + "type": "Feature", + "properties": { + "connectsTo": [ + { + "connectionID": 1, + "connectingLane": { + "lane": 6, + "maneuver": { + "maneuverStraightAllowed": false, + "maneuverNoStoppingAllowed": false, + "goWithHalt": false, + "maneuverLeftAllowed": false, + "maneuverUTurnAllowed": false, + "maneuverLeftTurnOnRedAllowed": false, + "reserved1": false, + "maneuverRightAllowed": true, + "maneuverLaneChangeAllowed": false, + "yieldAllwaysRequired": false, + "maneuverRightTurnOnRedAllowed": false, + "caution": false + } + } + } + ], + "laneType": { + "vehicle": { + "isVehicleRevocableLane": false, + "isVehicleFlyOverLane": false, + "permissionOnRequest": false, + "hasIRbeaconCoverage": false, + "restrictedToBusUse": false, + "restrictedToTaxiUse": false, + "restrictedFromPublicUse": false, + "hovLaneUseOnly": false + } + }, + "laneId": 7, + "nodes": [ + { + "delevation": 10, + "delta": [ + -2216, + -915 + ] + }, + { + "delta": [ + -2322, + -471 + ] + }, + { + "delevation": 10, + "delta": [ + -1877, + -349 + ] + }, + { + "delta": [ + -1787, + -235 + ] + }, + { + "delevation": 10, + "delta": [ + -2666, + -102 + ] + } + ], + "egressApproach": 0, + "ingressApproach": 3, + "sharedWith": { + "busVehicleTraffic": false, + "trackedVehicleTraffic": false, + "individualMotorizedVehicleTraffic": false, + "taxiVehicleTraffic": false, + "overlappingLaneDescriptionProvided": false, + "cyclistVehicleTraffic": false, + "otherNonMotorizedTrafficTypes": false, + "multipleLanesTreatedAsOneLane": false, + "pedestrianTraffic": false, + "pedestriansTraffic": false + }, + "ingressPath": true, + "maneuvers": { + "maneuverStraightAllowed": false, + "maneuverNoStoppingAllowed": false, + "goWithHalt": false, + "maneuverLeftAllowed": false, + "maneuverUTurnAllowed": false, + "maneuverLeftTurnOnRedAllowed": false, + "reserved1": false, + "maneuverRightAllowed": true, + "maneuverLaneChangeAllowed": false, + "yieldAllwaysRequired": false, + "maneuverRightTurnOnRedAllowed": false, + "caution": false + }, + "egressPath": false + } + }, + { + "geometry": { + "coordinates": [ + [ + -105.0911534, + 39.5880261 + ], + [ + -105.091436, + 39.5879812 + ], + [ + -105.0916658, + 39.5879507 + ], + [ + -105.091881, + 39.5879277 + ], + [ + -105.0921287, + 39.5878972 + ] + ], + "type": "LineString" + }, + "id": 9, + "type": "Feature", + "properties": { + "connectsTo": [ + { + "connectionID": 1, + "signalGroup": 4, + "connectingLane": { + "lane": 14, + "maneuver": { + "maneuverStraightAllowed": false, + "maneuverNoStoppingAllowed": false, + "goWithHalt": false, + "maneuverLeftAllowed": true, + "maneuverUTurnAllowed": false, + "maneuverLeftTurnOnRedAllowed": false, + "reserved1": false, + "maneuverRightAllowed": false, + "maneuverLaneChangeAllowed": false, + "yieldAllwaysRequired": false, + "maneuverRightTurnOnRedAllowed": false, + "caution": false + } + } + } + ], + "laneType": { + "vehicle": { + "isVehicleRevocableLane": false, + "isVehicleFlyOverLane": false, + "permissionOnRequest": false, + "hasIRbeaconCoverage": false, + "restrictedToBusUse": false, + "restrictedToTaxiUse": false, + "restrictedFromPublicUse": false, + "hovLaneUseOnly": false + } + }, + "laneId": 9, + "nodes": [ + { + "delevation": 10, + "delta": [ + -2295, + -169 + ] + }, + { + "delta": [ + -2420, + -499 + ] + }, + { + "delevation": 10, + "delta": [ + -1968, + -339 + ] + }, + { + "delta": [ + -1843, + -256 + ] + }, + { + "delta": [ + -2121, + -339 + ] + } + ], + "egressApproach": 0, + "ingressApproach": 3, + "sharedWith": { + "busVehicleTraffic": false, + "trackedVehicleTraffic": false, + "individualMotorizedVehicleTraffic": false, + "taxiVehicleTraffic": false, + "overlappingLaneDescriptionProvided": false, + "cyclistVehicleTraffic": false, + "otherNonMotorizedTrafficTypes": false, + "multipleLanesTreatedAsOneLane": false, + "pedestrianTraffic": false, + "pedestriansTraffic": false + }, + "ingressPath": true, + "maneuvers": { + "maneuverStraightAllowed": false, + "maneuverNoStoppingAllowed": false, + "goWithHalt": false, + "maneuverLeftAllowed": true, + "maneuverUTurnAllowed": false, + "maneuverLeftTurnOnRedAllowed": false, + "reserved1": false, + "maneuverRightAllowed": false, + "maneuverLaneChangeAllowed": false, + "yieldAllwaysRequired": false, + "maneuverRightTurnOnRedAllowed": false, + "caution": false + }, + "egressPath": false + } + }, + { + "geometry": { + "coordinates": [ + [ + -105.0910447, + 39.5881948 + ], + [ + -105.0911481, + 39.5886317 + ], + [ + -105.091196, + 39.588862 + ], + [ + -105.0912349, + 39.5890282 + ], + [ + -105.0912722, + 39.5893202 + ], + [ + -105.0913306, + 39.5897261 + ], + [ + -105.0913695, + 39.5900324 + ], + [ + -105.0914008, + 39.5903008 + ], + [ + -105.0914893, + 39.5913099 + ], + [ + -105.091527, + 39.5923157 + ] + ], + "type": "LineString" + }, + "id": 12, + "type": "Feature", + "properties": { + "connectsTo": [ + { + "connectionID": 1, + "signalGroup": 6, + "connectingLane": { + "lane": 5, + "maneuver": { + "maneuverStraightAllowed": true, + "maneuverNoStoppingAllowed": false, + "goWithHalt": false, + "maneuverLeftAllowed": false, + "maneuverUTurnAllowed": false, + "maneuverLeftTurnOnRedAllowed": false, + "reserved1": false, + "maneuverRightAllowed": false, + "maneuverLaneChangeAllowed": false, + "yieldAllwaysRequired": false, + "maneuverRightTurnOnRedAllowed": false, + "caution": false + } + } + } + ], + "laneType": { + "vehicle": { + "isVehicleRevocableLane": false, + "isVehicleFlyOverLane": false, + "permissionOnRequest": false, + "hasIRbeaconCoverage": false, + "restrictedToBusUse": false, + "restrictedToTaxiUse": false, + "restrictedFromPublicUse": false, + "hovLaneUseOnly": false + } + }, + "laneId": 12, + "nodes": [ + { + "delevation": 10, + "delta": [ + -1364, + 1705 + ] + }, + { + "delevation": -30, + "delta": [ + -885, + 4854 + ] + }, + { + "delevation": 10, + "delta": [ + -410, + 2559 + ] + }, + { + "delevation": -10, + "delta": [ + -333, + 1847 + ] + }, + { + "delevation": -20, + "delta": [ + -319, + 3244 + ] + }, + { + "delta": [ + -500, + 4510 + ] + }, + { + "delevation": -30, + "delta": [ + -333, + 3403 + ] + }, + { + "delta": [ + -268, + 2982 + ] + }, + { + "delevation": -30, + "delta": [ + -758, + 11212 + ] + }, + { + "delevation": -70, + "delta": [ + -323, + 11176 + ] + } + ], + "egressApproach": 0, + "ingressApproach": 5, + "sharedWith": { + "busVehicleTraffic": false, + "trackedVehicleTraffic": false, + "individualMotorizedVehicleTraffic": false, + "taxiVehicleTraffic": false, + "overlappingLaneDescriptionProvided": false, + "cyclistVehicleTraffic": false, + "otherNonMotorizedTrafficTypes": false, + "multipleLanesTreatedAsOneLane": false, + "pedestrianTraffic": false, + "pedestriansTraffic": false + }, + "ingressPath": true, + "maneuvers": { + "maneuverStraightAllowed": true, + "maneuverNoStoppingAllowed": false, + "goWithHalt": false, + "maneuverLeftAllowed": false, + "maneuverUTurnAllowed": false, + "maneuverLeftTurnOnRedAllowed": false, + "reserved1": false, + "maneuverRightAllowed": false, + "maneuverLaneChangeAllowed": false, + "yieldAllwaysRequired": false, + "maneuverRightTurnOnRedAllowed": false, + "caution": false + }, + "egressPath": false + } + }, + { + "geometry": { + "coordinates": [ + [ + -105.0910013, + 39.5881975 + ], + [ + -105.0911059, + 39.5886309 + ], + [ + -105.091144, + 39.5888313 + ], + [ + -105.0911829, + 39.5890442 + ], + [ + -105.0912308, + 39.5893169 + ], + [ + -105.0912689, + 39.5895877 + ], + [ + -105.0913005, + 39.5898143 + ], + [ + -105.0913313, + 39.5900714 + ], + [ + -105.0913597, + 39.5902968 + ], + [ + -105.0914461, + 39.5913017 + ], + [ + -105.0914756, + 39.592324 + ] + ], + "type": "LineString" + }, + "id": 13, + "type": "Feature", + "properties": { + "connectsTo": [ + { + "connectionID": 1, + "signalGroup": 6, + "connectingLane": { + "lane": 4, + "maneuver": { + "maneuverStraightAllowed": true, + "maneuverNoStoppingAllowed": false, + "goWithHalt": false, + "maneuverLeftAllowed": false, + "maneuverUTurnAllowed": false, + "maneuverLeftTurnOnRedAllowed": false, + "reserved1": false, + "maneuverRightAllowed": false, + "maneuverLaneChangeAllowed": false, + "yieldAllwaysRequired": false, + "maneuverRightTurnOnRedAllowed": false, + "caution": false + } + } + } + ], + "laneType": { + "vehicle": { + "isVehicleRevocableLane": false, + "isVehicleFlyOverLane": false, + "permissionOnRequest": false, + "hasIRbeaconCoverage": false, + "restrictedToBusUse": false, + "restrictedToTaxiUse": false, + "restrictedFromPublicUse": false, + "hovLaneUseOnly": false + } + }, + "laneId": 13, + "nodes": [ + { + "delevation": 10, + "delta": [ + -992, + 1735 + ] + }, + { + "delevation": -30, + "delta": [ + -896, + 4816 + ] + }, + { + "delevation": 10, + "delta": [ + -326, + 2227 + ] + }, + { + "delta": [ + -333, + 2366 + ] + }, + { + "delevation": -20, + "delta": [ + -410, + 3030 + ] + }, + { + "delevation": -10, + "delta": [ + -326, + 3009 + ] + }, + { + "delevation": -10, + "delta": [ + -271, + 2518 + ] + }, + { + "delevation": -20, + "delta": [ + -264, + 2857 + ] + }, + { + "delta": [ + -243, + 2504 + ] + }, + { + "delevation": -30, + "delta": [ + -740, + 11165 + ] + }, + { + "delevation": -70, + "delta": [ + -253, + 11359 + ] + } + ], + "egressApproach": 0, + "ingressApproach": 5, + "sharedWith": { + "busVehicleTraffic": false, + "trackedVehicleTraffic": false, + "individualMotorizedVehicleTraffic": false, + "taxiVehicleTraffic": false, + "overlappingLaneDescriptionProvided": false, + "cyclistVehicleTraffic": false, + "otherNonMotorizedTrafficTypes": false, + "multipleLanesTreatedAsOneLane": false, + "pedestrianTraffic": false, + "pedestriansTraffic": false + }, + "ingressPath": true, + "maneuvers": { + "maneuverStraightAllowed": true, + "maneuverNoStoppingAllowed": false, + "goWithHalt": false, + "maneuverLeftAllowed": false, + "maneuverUTurnAllowed": false, + "maneuverLeftTurnOnRedAllowed": false, + "reserved1": false, + "maneuverRightAllowed": false, + "maneuverLaneChangeAllowed": false, + "yieldAllwaysRequired": false, + "maneuverRightTurnOnRedAllowed": false, + "caution": false + }, + "egressPath": false + } + }, + { + "geometry": { + "coordinates": [ + [ + -105.0910891, + 39.5881859 + ], + [ + -105.0911549, + 39.5884681 + ], + [ + -105.091196, + 39.5886783 + ], + [ + -105.091222, + 39.5888049 + ], + [ + -105.0912401, + 39.5889649 + ] + ], + "type": "LineString" + }, + "id": 11, + "type": "Feature", + "properties": { + "connectsTo": [ + { + "connectionID": 1, + "connectingLane": { + "lane": 10, + "maneuver": { + "maneuverStraightAllowed": false, + "maneuverNoStoppingAllowed": false, + "goWithHalt": false, + "maneuverLeftAllowed": false, + "maneuverUTurnAllowed": false, + "maneuverLeftTurnOnRedAllowed": false, + "reserved1": false, + "maneuverRightAllowed": true, + "maneuverLaneChangeAllowed": false, + "yieldAllwaysRequired": false, + "maneuverRightTurnOnRedAllowed": false, + "caution": false + } + } + } + ], + "laneType": { + "vehicle": { + "isVehicleRevocableLane": false, + "isVehicleFlyOverLane": false, + "permissionOnRequest": false, + "hasIRbeaconCoverage": false, + "restrictedToBusUse": false, + "restrictedToTaxiUse": false, + "restrictedFromPublicUse": false, + "hovLaneUseOnly": false + } + }, + "laneId": 11, + "nodes": [ + { + "delevation": 10, + "delta": [ + -1744, + 1607 + ] + }, + { + "delevation": -20, + "delta": [ + -563, + 3136 + ] + }, + { + "delevation": -10, + "delta": [ + -352, + 2336 + ] + }, + { + "delevation": 10, + "delta": [ + -223, + 1407 + ] + }, + { + "delta": [ + -155, + 1778 + ] + } + ], + "egressApproach": 0, + "ingressApproach": 5, + "sharedWith": { + "busVehicleTraffic": false, + "trackedVehicleTraffic": false, + "individualMotorizedVehicleTraffic": false, + "taxiVehicleTraffic": false, + "overlappingLaneDescriptionProvided": false, + "cyclistVehicleTraffic": false, + "otherNonMotorizedTrafficTypes": false, + "multipleLanesTreatedAsOneLane": false, + "pedestrianTraffic": false, + "pedestriansTraffic": false + }, + "ingressPath": true, + "maneuvers": { + "maneuverStraightAllowed": false, + "maneuverNoStoppingAllowed": false, + "goWithHalt": false, + "maneuverLeftAllowed": false, + "maneuverUTurnAllowed": false, + "maneuverLeftTurnOnRedAllowed": false, + "reserved1": false, + "maneuverRightAllowed": true, + "maneuverLaneChangeAllowed": false, + "yieldAllwaysRequired": false, + "maneuverRightTurnOnRedAllowed": false, + "caution": false + }, + "egressPath": false + } + }, + { + "geometry": { + "coordinates": [ + [ + -105.0908389, + 39.5882151 + ], + [ + -105.0908478, + 39.5882471 + ] + ], + "type": "LineString" + }, + "id": 14, + "type": "Feature", + "properties": { + "laneType": { + "vehicle": { + "isVehicleRevocableLane": false, + "isVehicleFlyOverLane": false, + "permissionOnRequest": false, + "hasIRbeaconCoverage": false, + "restrictedToBusUse": false, + "restrictedToTaxiUse": false, + "restrictedFromPublicUse": false, + "hovLaneUseOnly": false + } + }, + "laneId": 14, + "nodes": [ + { + "delevation": -10, + "delta": [ + 398, + 1931 + ] + }, + { + "delta": [ + -76, + 356 + ] + } + ], + "egressApproach": 6, + "ingressApproach": 0, + "sharedWith": { + "busVehicleTraffic": false, + "trackedVehicleTraffic": false, + "individualMotorizedVehicleTraffic": false, + "taxiVehicleTraffic": false, + "overlappingLaneDescriptionProvided": false, + "cyclistVehicleTraffic": false, + "otherNonMotorizedTrafficTypes": false, + "multipleLanesTreatedAsOneLane": false, + "pedestrianTraffic": false, + "pedestriansTraffic": false + }, + "ingressPath": false, + "egressPath": true + } + }, + { + "geometry": { + "coordinates": [ + [ + -105.0907875, + 39.58822 + ], + [ + -105.0907979, + 39.5882514 + ] + ], + "type": "LineString" + }, + "id": 15, + "type": "Feature", + "properties": { + "laneType": { + "vehicle": { + "isVehicleRevocableLane": false, + "isVehicleFlyOverLane": false, + "permissionOnRequest": false, + "hasIRbeaconCoverage": false, + "restrictedToBusUse": false, + "restrictedToTaxiUse": false, + "restrictedFromPublicUse": false, + "hovLaneUseOnly": false + } + }, + "laneId": 15, + "nodes": [ + { + "delevation": -20, + "delta": [ + 838, + 1985 + ] + }, + { + "delta": [ + -89, + 349 + ] + } + ], + "egressApproach": 6, + "ingressApproach": 0, + "sharedWith": { + "busVehicleTraffic": false, + "trackedVehicleTraffic": false, + "individualMotorizedVehicleTraffic": false, + "taxiVehicleTraffic": false, + "overlappingLaneDescriptionProvided": false, + "cyclistVehicleTraffic": false, + "otherNonMotorizedTrafficTypes": false, + "multipleLanesTreatedAsOneLane": false, + "pedestrianTraffic": false, + "pedestriansTraffic": false + }, + "ingressPath": false, + "egressPath": true + } + } + ], + "type": "FeatureCollection" + }, + "properties": { + "validationMessages": [ + { + "schemaPath": "#/$defs/J2735RoadRegulatorID/type", + "jsonPath": "$.payload.data.intersections.intersectionGeometry[0].id.region", + "message": "$.payload.data.intersections.intersectionGeometry[0].id.region: null found, integer expected" + }, + { + "schemaPath": "#/$defs/J2735ConnectsToList_Wrapper/type", + "jsonPath": "$.payload.data.intersections.intersectionGeometry[0].laneSet.GenericLane[3].connectsTo", + "message": "$.payload.data.intersections.intersectionGeometry[0].laneSet.GenericLane[3].connectsTo: null found, object expected" + }, + { + "schemaPath": "#/$defs/J2735AllowedManeuvers/type", + "jsonPath": "$.payload.data.intersections.intersectionGeometry[0].laneSet.GenericLane[3].maneuvers", + "message": "$.payload.data.intersections.intersectionGeometry[0].laneSet.GenericLane[3].maneuvers: null found, object expected" + }, + { + "schemaPath": "#/$defs/J2735ConnectsToList_Wrapper/type", + "jsonPath": "$.payload.data.intersections.intersectionGeometry[0].laneSet.GenericLane[4].connectsTo", + "message": "$.payload.data.intersections.intersectionGeometry[0].laneSet.GenericLane[4].connectsTo: null found, object expected" + }, + { + "schemaPath": "#/$defs/J2735AllowedManeuvers/type", + "jsonPath": "$.payload.data.intersections.intersectionGeometry[0].laneSet.GenericLane[4].maneuvers", + "message": "$.payload.data.intersections.intersectionGeometry[0].laneSet.GenericLane[4].maneuvers: null found, object expected" + }, + { + "schemaPath": "#/$defs/J2735ConnectsToList_Wrapper/type", + "jsonPath": "$.payload.data.intersections.intersectionGeometry[0].laneSet.GenericLane[5].connectsTo", + "message": "$.payload.data.intersections.intersectionGeometry[0].laneSet.GenericLane[5].connectsTo: null found, object expected" + }, + { + "schemaPath": "#/$defs/J2735AllowedManeuvers/type", + "jsonPath": "$.payload.data.intersections.intersectionGeometry[0].laneSet.GenericLane[5].maneuvers", + "message": "$.payload.data.intersections.intersectionGeometry[0].laneSet.GenericLane[5].maneuvers: null found, object expected" + }, + { + "schemaPath": "#/$defs/J2735ConnectsToList_Wrapper/type", + "jsonPath": "$.payload.data.intersections.intersectionGeometry[0].laneSet.GenericLane[6].connectsTo", + "message": "$.payload.data.intersections.intersectionGeometry[0].laneSet.GenericLane[6].connectsTo: null found, object expected" + }, + { + "schemaPath": "#/$defs/J2735AllowedManeuvers/type", + "jsonPath": "$.payload.data.intersections.intersectionGeometry[0].laneSet.GenericLane[6].maneuvers", + "message": "$.payload.data.intersections.intersectionGeometry[0].laneSet.GenericLane[6].maneuvers: null found, object expected" + }, + { + "schemaPath": "#/$defs/J2735SignalGroupID/type", + "jsonPath": "$.payload.data.intersections.intersectionGeometry[0].laneSet.GenericLane[8].connectsTo.connectsTo[0].signalGroup", + "message": "$.payload.data.intersections.intersectionGeometry[0].laneSet.GenericLane[8].connectsTo.connectsTo[0].signalGroup: null found, integer expected" + }, + { + "schemaPath": "#/$defs/J2735SignalGroupID/type", + "jsonPath": "$.payload.data.intersections.intersectionGeometry[0].laneSet.GenericLane[12].connectsTo.connectsTo[0].signalGroup", + "message": "$.payload.data.intersections.intersectionGeometry[0].laneSet.GenericLane[12].connectsTo.connectsTo[0].signalGroup: null found, integer expected" + }, + { + "schemaPath": "#/$defs/J2735ConnectsToList_Wrapper/type", + "jsonPath": "$.payload.data.intersections.intersectionGeometry[0].laneSet.GenericLane[13].connectsTo", + "message": "$.payload.data.intersections.intersectionGeometry[0].laneSet.GenericLane[13].connectsTo: null found, object expected" + }, + { + "schemaPath": "#/$defs/J2735AllowedManeuvers/type", + "jsonPath": "$.payload.data.intersections.intersectionGeometry[0].laneSet.GenericLane[13].maneuvers", + "message": "$.payload.data.intersections.intersectionGeometry[0].laneSet.GenericLane[13].maneuvers: null found, object expected" + }, + { + "schemaPath": "#/$defs/J2735ConnectsToList_Wrapper/type", + "jsonPath": "$.payload.data.intersections.intersectionGeometry[0].laneSet.GenericLane[14].connectsTo", + "message": "$.payload.data.intersections.intersectionGeometry[0].laneSet.GenericLane[14].connectsTo: null found, object expected" + }, + { + "schemaPath": "#/$defs/J2735AllowedManeuvers/type", + "jsonPath": "$.payload.data.intersections.intersectionGeometry[0].laneSet.GenericLane[14].maneuvers", + "message": "$.payload.data.intersections.intersectionGeometry[0].laneSet.GenericLane[14].maneuvers: null found, object expected" + }, + { + "schemaPath": "#/$defs/J2735SpeedLimitList_Wrapper/type", + "jsonPath": "$.payload.data.intersections.intersectionGeometry[0].speedLimits", + "message": "$.payload.data.intersections.intersectionGeometry[0].speedLimits: null found, object expected" + } + ], + "schemaVersion": 1, + "laneWidth": 366, + "revision": 2, + "timeStamp": "2024-06-04T16:22:39.747Z", + "mapSource": "RSU", + "messageType": "MAP", + "cti4501Conformant": false, + "refPoint": { + "elevation": 1691, + "latitude": 39.5880413, + "longitude": -105.0908854 + }, + "intersectionId": 12109, + "odeReceivedAt": "2024-06-04T16:22:39.747Z", + "msgIssueRevision": 2, + "originIp": "10.11.81.12" + }, + "recordGeneratedAt": { + "$date": { + "$numberLong": "1717518159755" + } + } +}] \ No newline at end of file diff --git a/conflictmonitor/datasets/uturn_export/ProcessedSpat.json b/conflictmonitor/datasets/uturn_export/ProcessedSpat.json new file mode 100644 index 000000000..3477f8359 --- /dev/null +++ b/conflictmonitor/datasets/uturn_export/ProcessedSpat.json @@ -0,0 +1,30241 @@ +[{ + "_id": { + "$oid": "665f3fc18c25216ef4f53f6d" + }, + "validationMessages": [ + { + "schemaPath": "#/$defs/J2735RoadRegulatorID/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].id.region", + "message": "$.payload.data.intersectionStateList.intersectionStatelist[0].id.region: null found, integer expected" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[0].state_time_speed.movementEventList[0].timing.startTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[0].state_time_speed.movementEventList[0].timing.nextTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[1].state_time_speed.movementEventList[0].timing.startTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[1].state_time_speed.movementEventList[0].timing.nextTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[2].state_time_speed.movementEventList[0].timing.startTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[2].state_time_speed.movementEventList[0].timing.nextTime", + "message": "DE_TimeMark is missing" + } + ], + "utcTimeStamp": "2024-06-04T16:22:39.393Z", + "schemaVersion": 1, + "recordGeneratedAt": { + "$date": { + "$numberLong": "1717518159567" + } + }, + "revision": 0, + "states": [ + { + "stateTimeSpeed": [ + { + "eventState": "PROTECTED_CLEARANCE", + "timing": { + "minEndTime": "2024-06-04T16:03:17Z", + "maxEndTime": "2024-06-04T16:03:17Z" + } + } + ], + "signalGroup": 2 + }, + { + "stateTimeSpeed": [ + { + "eventState": "STOP_AND_REMAIN", + "timing": { + "minEndTime": "2024-06-04T16:03:20.1Z", + "maxEndTime": "2024-06-04T16:03:20.1Z" + } + } + ], + "signalGroup": 4 + }, + { + "stateTimeSpeed": [ + { + "eventState": "PROTECTED_CLEARANCE", + "timing": { + "minEndTime": "2024-06-04T16:03:17Z", + "maxEndTime": "2024-06-04T16:03:17Z" + } + } + ], + "signalGroup": 6 + } + ], + "messageType": "SPAT", + "cti4501Conformant": false, + "intersectionId": 12109, + "odeReceivedAt": "2024-06-04T16:22:39.393Z", + "originIp": "10.11.81.12", + "status": { + "failureFlash": false, + "noValidSPATisAvailableAtThisTime": false, + "fixedTimeOperation": false, + "standbyOperation": false, + "trafficDependentOperation": false, + "manualControlIsEnabled": false, + "off": false, + "stopTimeIsActivated": false, + "recentChangeInMAPassignedLanesIDsUsed": false, + "recentMAPmessageUpdate": false, + "failureMode": false, + "noValidMAPisAvailableAtThisTime": false, + "signalPriorityIsActive": false, + "preemptIsActive": false + } +},{ + "_id": { + "$oid": "665f3fc18c25216ef4f53f6e" + }, + "validationMessages": [ + { + "schemaPath": "#/$defs/J2735RoadRegulatorID/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].id.region", + "message": "$.payload.data.intersectionStateList.intersectionStatelist[0].id.region: null found, integer expected" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[0].state_time_speed.movementEventList[0].timing.startTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[0].state_time_speed.movementEventList[0].timing.nextTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[1].state_time_speed.movementEventList[0].timing.startTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[1].state_time_speed.movementEventList[0].timing.nextTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[2].state_time_speed.movementEventList[0].timing.startTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[2].state_time_speed.movementEventList[0].timing.nextTime", + "message": "DE_TimeMark is missing" + } + ], + "utcTimeStamp": "2024-06-04T16:22:39.285Z", + "schemaVersion": 1, + "recordGeneratedAt": { + "$date": { + "$numberLong": "1717518159567" + } + }, + "revision": 0, + "states": [ + { + "stateTimeSpeed": [ + { + "eventState": "PROTECTED_CLEARANCE", + "timing": { + "minEndTime": "2024-06-04T16:03:17Z", + "maxEndTime": "2024-06-04T16:03:17Z" + } + } + ], + "signalGroup": 2 + }, + { + "stateTimeSpeed": [ + { + "eventState": "STOP_AND_REMAIN", + "timing": { + "minEndTime": "2024-06-04T16:03:20.1Z", + "maxEndTime": "2024-06-04T16:03:20.1Z" + } + } + ], + "signalGroup": 4 + }, + { + "stateTimeSpeed": [ + { + "eventState": "PROTECTED_CLEARANCE", + "timing": { + "minEndTime": "2024-06-04T16:03:17Z", + "maxEndTime": "2024-06-04T16:03:17Z" + } + } + ], + "signalGroup": 6 + } + ], + "messageType": "SPAT", + "cti4501Conformant": false, + "intersectionId": 12109, + "odeReceivedAt": "2024-06-04T16:22:39.285Z", + "originIp": "10.11.81.12", + "status": { + "failureFlash": false, + "noValidSPATisAvailableAtThisTime": false, + "fixedTimeOperation": false, + "standbyOperation": false, + "trafficDependentOperation": false, + "manualControlIsEnabled": false, + "off": false, + "stopTimeIsActivated": false, + "recentChangeInMAPassignedLanesIDsUsed": false, + "recentMAPmessageUpdate": false, + "failureMode": false, + "noValidMAPisAvailableAtThisTime": false, + "signalPriorityIsActive": false, + "preemptIsActive": false + } +},{ + "_id": { + "$oid": "665f3fc18c25216ef4f53f6f" + }, + "validationMessages": [ + { + "schemaPath": "#/$defs/J2735RoadRegulatorID/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].id.region", + "message": "$.payload.data.intersectionStateList.intersectionStatelist[0].id.region: null found, integer expected" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[0].state_time_speed.movementEventList[0].timing.startTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[0].state_time_speed.movementEventList[0].timing.nextTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[1].state_time_speed.movementEventList[0].timing.startTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[1].state_time_speed.movementEventList[0].timing.nextTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[2].state_time_speed.movementEventList[0].timing.startTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[2].state_time_speed.movementEventList[0].timing.nextTime", + "message": "DE_TimeMark is missing" + } + ], + "utcTimeStamp": "2024-06-04T16:22:39.176Z", + "schemaVersion": 1, + "recordGeneratedAt": { + "$date": { + "$numberLong": "1717518159566" + } + }, + "revision": 0, + "states": [ + { + "stateTimeSpeed": [ + { + "eventState": "PROTECTED_CLEARANCE", + "timing": { + "minEndTime": "2024-06-04T16:03:17Z", + "maxEndTime": "2024-06-04T16:03:17Z" + } + } + ], + "signalGroup": 2 + }, + { + "stateTimeSpeed": [ + { + "eventState": "STOP_AND_REMAIN", + "timing": { + "minEndTime": "2024-06-04T16:03:20.1Z", + "maxEndTime": "2024-06-04T16:03:20.1Z" + } + } + ], + "signalGroup": 4 + }, + { + "stateTimeSpeed": [ + { + "eventState": "PROTECTED_CLEARANCE", + "timing": { + "minEndTime": "2024-06-04T16:03:17Z", + "maxEndTime": "2024-06-04T16:03:17Z" + } + } + ], + "signalGroup": 6 + } + ], + "messageType": "SPAT", + "cti4501Conformant": false, + "intersectionId": 12109, + "odeReceivedAt": "2024-06-04T16:22:39.176Z", + "originIp": "10.11.81.12", + "status": { + "failureFlash": false, + "noValidSPATisAvailableAtThisTime": false, + "fixedTimeOperation": false, + "standbyOperation": false, + "trafficDependentOperation": false, + "manualControlIsEnabled": false, + "off": false, + "stopTimeIsActivated": false, + "recentChangeInMAPassignedLanesIDsUsed": false, + "recentMAPmessageUpdate": false, + "failureMode": false, + "noValidMAPisAvailableAtThisTime": false, + "signalPriorityIsActive": false, + "preemptIsActive": false + } +},{ + "_id": { + "$oid": "665f3fc18c25216ef4f53f70" + }, + "validationMessages": [ + { + "schemaPath": "#/$defs/J2735RoadRegulatorID/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].id.region", + "message": "$.payload.data.intersectionStateList.intersectionStatelist[0].id.region: null found, integer expected" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[0].state_time_speed.movementEventList[0].timing.startTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[0].state_time_speed.movementEventList[0].timing.nextTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[1].state_time_speed.movementEventList[0].timing.startTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[1].state_time_speed.movementEventList[0].timing.nextTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[2].state_time_speed.movementEventList[0].timing.startTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[2].state_time_speed.movementEventList[0].timing.nextTime", + "message": "DE_TimeMark is missing" + } + ], + "utcTimeStamp": "2024-06-04T16:22:39.5Z", + "schemaVersion": 1, + "recordGeneratedAt": { + "$date": { + "$numberLong": "1717518159596" + } + }, + "revision": 0, + "states": [ + { + "stateTimeSpeed": [ + { + "eventState": "PROTECTED_CLEARANCE", + "timing": { + "minEndTime": "2024-06-04T16:03:17Z", + "maxEndTime": "2024-06-04T16:03:17Z" + } + } + ], + "signalGroup": 2 + }, + { + "stateTimeSpeed": [ + { + "eventState": "STOP_AND_REMAIN", + "timing": { + "minEndTime": "2024-06-04T16:03:20.1Z", + "maxEndTime": "2024-06-04T16:03:20.1Z" + } + } + ], + "signalGroup": 4 + }, + { + "stateTimeSpeed": [ + { + "eventState": "PROTECTED_CLEARANCE", + "timing": { + "minEndTime": "2024-06-04T16:03:17Z", + "maxEndTime": "2024-06-04T16:03:17Z" + } + } + ], + "signalGroup": 6 + } + ], + "messageType": "SPAT", + "cti4501Conformant": false, + "intersectionId": 12109, + "odeReceivedAt": "2024-06-04T16:22:39.5Z", + "originIp": "10.11.81.12", + "status": { + "failureFlash": false, + "noValidSPATisAvailableAtThisTime": false, + "fixedTimeOperation": false, + "standbyOperation": false, + "trafficDependentOperation": false, + "manualControlIsEnabled": false, + "off": false, + "stopTimeIsActivated": false, + "recentChangeInMAPassignedLanesIDsUsed": false, + "recentMAPmessageUpdate": false, + "failureMode": false, + "noValidMAPisAvailableAtThisTime": false, + "signalPriorityIsActive": false, + "preemptIsActive": false + } +},{ + "_id": { + "$oid": "665f3fc18c25216ef4f53f71" + }, + "validationMessages": [ + { + "schemaPath": "#/$defs/J2735RoadRegulatorID/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].id.region", + "message": "$.payload.data.intersectionStateList.intersectionStatelist[0].id.region: null found, integer expected" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[0].state_time_speed.movementEventList[0].timing.startTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[0].state_time_speed.movementEventList[0].timing.nextTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[1].state_time_speed.movementEventList[0].timing.startTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[1].state_time_speed.movementEventList[0].timing.nextTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[2].state_time_speed.movementEventList[0].timing.startTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[2].state_time_speed.movementEventList[0].timing.nextTime", + "message": "DE_TimeMark is missing" + } + ], + "utcTimeStamp": "2024-06-04T16:22:39.607Z", + "schemaVersion": 1, + "recordGeneratedAt": { + "$date": { + "$numberLong": "1717518159613" + } + }, + "revision": 0, + "states": [ + { + "stateTimeSpeed": [ + { + "eventState": "PROTECTED_CLEARANCE", + "timing": { + "minEndTime": "2024-06-04T16:03:17Z", + "maxEndTime": "2024-06-04T16:03:17Z" + } + } + ], + "signalGroup": 2 + }, + { + "stateTimeSpeed": [ + { + "eventState": "STOP_AND_REMAIN", + "timing": { + "minEndTime": "2024-06-04T16:03:20.1Z", + "maxEndTime": "2024-06-04T16:03:20.1Z" + } + } + ], + "signalGroup": 4 + }, + { + "stateTimeSpeed": [ + { + "eventState": "PROTECTED_CLEARANCE", + "timing": { + "minEndTime": "2024-06-04T16:03:17Z", + "maxEndTime": "2024-06-04T16:03:17Z" + } + } + ], + "signalGroup": 6 + } + ], + "messageType": "SPAT", + "cti4501Conformant": false, + "intersectionId": 12109, + "odeReceivedAt": "2024-06-04T16:22:39.607Z", + "originIp": "10.11.81.12", + "status": { + "failureFlash": false, + "noValidSPATisAvailableAtThisTime": false, + "fixedTimeOperation": false, + "standbyOperation": false, + "trafficDependentOperation": false, + "manualControlIsEnabled": false, + "off": false, + "stopTimeIsActivated": false, + "recentChangeInMAPassignedLanesIDsUsed": false, + "recentMAPmessageUpdate": false, + "failureMode": false, + "noValidMAPisAvailableAtThisTime": false, + "signalPriorityIsActive": false, + "preemptIsActive": false + } +},{ + "_id": { + "$oid": "665f3fc18c25216ef4f53f72" + }, + "validationMessages": [ + { + "schemaPath": "#/$defs/J2735RoadRegulatorID/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].id.region", + "message": "$.payload.data.intersectionStateList.intersectionStatelist[0].id.region: null found, integer expected" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[0].state_time_speed.movementEventList[0].timing.startTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[0].state_time_speed.movementEventList[0].timing.nextTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[1].state_time_speed.movementEventList[0].timing.startTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[1].state_time_speed.movementEventList[0].timing.nextTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[2].state_time_speed.movementEventList[0].timing.startTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[2].state_time_speed.movementEventList[0].timing.nextTime", + "message": "DE_TimeMark is missing" + } + ], + "utcTimeStamp": "2024-06-04T16:22:39.715Z", + "schemaVersion": 1, + "recordGeneratedAt": { + "$date": { + "$numberLong": "1717518159720" + } + }, + "revision": 0, + "states": [ + { + "stateTimeSpeed": [ + { + "eventState": "PROTECTED_CLEARANCE", + "timing": { + "minEndTime": "2024-06-04T16:03:17Z", + "maxEndTime": "2024-06-04T16:03:17Z" + } + } + ], + "signalGroup": 2 + }, + { + "stateTimeSpeed": [ + { + "eventState": "STOP_AND_REMAIN", + "timing": { + "minEndTime": "2024-06-04T16:03:20.1Z", + "maxEndTime": "2024-06-04T16:03:20.1Z" + } + } + ], + "signalGroup": 4 + }, + { + "stateTimeSpeed": [ + { + "eventState": "PROTECTED_CLEARANCE", + "timing": { + "minEndTime": "2024-06-04T16:03:17Z", + "maxEndTime": "2024-06-04T16:03:17Z" + } + } + ], + "signalGroup": 6 + } + ], + "messageType": "SPAT", + "cti4501Conformant": false, + "intersectionId": 12109, + "odeReceivedAt": "2024-06-04T16:22:39.715Z", + "originIp": "10.11.81.12", + "status": { + "failureFlash": false, + "noValidSPATisAvailableAtThisTime": false, + "fixedTimeOperation": false, + "standbyOperation": false, + "trafficDependentOperation": false, + "manualControlIsEnabled": false, + "off": false, + "stopTimeIsActivated": false, + "recentChangeInMAPassignedLanesIDsUsed": false, + "recentMAPmessageUpdate": false, + "failureMode": false, + "noValidMAPisAvailableAtThisTime": false, + "signalPriorityIsActive": false, + "preemptIsActive": false + } +},{ + "_id": { + "$oid": "665f3fc18c25216ef4f53f73" + }, + "validationMessages": [ + { + "schemaPath": "#/$defs/J2735RoadRegulatorID/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].id.region", + "message": "$.payload.data.intersectionStateList.intersectionStatelist[0].id.region: null found, integer expected" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[0].state_time_speed.movementEventList[0].timing.startTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[0].state_time_speed.movementEventList[0].timing.nextTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[1].state_time_speed.movementEventList[0].timing.startTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[1].state_time_speed.movementEventList[0].timing.nextTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[2].state_time_speed.movementEventList[0].timing.startTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[2].state_time_speed.movementEventList[0].timing.nextTime", + "message": "DE_TimeMark is missing" + } + ], + "utcTimeStamp": "2024-06-04T16:22:39.822Z", + "schemaVersion": 1, + "recordGeneratedAt": { + "$date": { + "$numberLong": "1717518159827" + } + }, + "revision": 0, + "states": [ + { + "stateTimeSpeed": [ + { + "eventState": "PROTECTED_CLEARANCE", + "timing": { + "minEndTime": "2024-06-04T16:03:17Z", + "maxEndTime": "2024-06-04T16:03:17Z" + } + } + ], + "signalGroup": 2 + }, + { + "stateTimeSpeed": [ + { + "eventState": "STOP_AND_REMAIN", + "timing": { + "minEndTime": "2024-06-04T16:03:20.1Z", + "maxEndTime": "2024-06-04T16:03:20.1Z" + } + } + ], + "signalGroup": 4 + }, + { + "stateTimeSpeed": [ + { + "eventState": "PROTECTED_CLEARANCE", + "timing": { + "minEndTime": "2024-06-04T16:03:17Z", + "maxEndTime": "2024-06-04T16:03:17Z" + } + } + ], + "signalGroup": 6 + } + ], + "messageType": "SPAT", + "cti4501Conformant": false, + "intersectionId": 12109, + "odeReceivedAt": "2024-06-04T16:22:39.822Z", + "originIp": "10.11.81.12", + "status": { + "failureFlash": false, + "noValidSPATisAvailableAtThisTime": false, + "fixedTimeOperation": false, + "standbyOperation": false, + "trafficDependentOperation": false, + "manualControlIsEnabled": false, + "off": false, + "stopTimeIsActivated": false, + "recentChangeInMAPassignedLanesIDsUsed": false, + "recentMAPmessageUpdate": false, + "failureMode": false, + "noValidMAPisAvailableAtThisTime": false, + "signalPriorityIsActive": false, + "preemptIsActive": false + } +},{ + "_id": { + "$oid": "665f3fc18c25216ef4f53f74" + }, + "validationMessages": [ + { + "schemaPath": "#/$defs/J2735RoadRegulatorID/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].id.region", + "message": "$.payload.data.intersectionStateList.intersectionStatelist[0].id.region: null found, integer expected" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[0].state_time_speed.movementEventList[0].timing.startTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[0].state_time_speed.movementEventList[0].timing.nextTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[1].state_time_speed.movementEventList[0].timing.startTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[1].state_time_speed.movementEventList[0].timing.nextTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[2].state_time_speed.movementEventList[0].timing.startTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[2].state_time_speed.movementEventList[0].timing.nextTime", + "message": "DE_TimeMark is missing" + } + ], + "utcTimeStamp": "2024-06-04T16:22:39.93Z", + "schemaVersion": 1, + "recordGeneratedAt": { + "$date": { + "$numberLong": "1717518159934" + } + }, + "revision": 0, + "states": [ + { + "stateTimeSpeed": [ + { + "eventState": "PROTECTED_CLEARANCE", + "timing": { + "minEndTime": "2024-06-04T16:03:17Z", + "maxEndTime": "2024-06-04T16:03:17Z" + } + } + ], + "signalGroup": 2 + }, + { + "stateTimeSpeed": [ + { + "eventState": "STOP_AND_REMAIN", + "timing": { + "minEndTime": "2024-06-04T16:03:20.1Z", + "maxEndTime": "2024-06-04T16:03:20.1Z" + } + } + ], + "signalGroup": 4 + }, + { + "stateTimeSpeed": [ + { + "eventState": "PROTECTED_CLEARANCE", + "timing": { + "minEndTime": "2024-06-04T16:03:17Z", + "maxEndTime": "2024-06-04T16:03:17Z" + } + } + ], + "signalGroup": 6 + } + ], + "messageType": "SPAT", + "cti4501Conformant": false, + "intersectionId": 12109, + "odeReceivedAt": "2024-06-04T16:22:39.93Z", + "originIp": "10.11.81.12", + "status": { + "failureFlash": false, + "noValidSPATisAvailableAtThisTime": false, + "fixedTimeOperation": false, + "standbyOperation": false, + "trafficDependentOperation": false, + "manualControlIsEnabled": false, + "off": false, + "stopTimeIsActivated": false, + "recentChangeInMAPassignedLanesIDsUsed": false, + "recentMAPmessageUpdate": false, + "failureMode": false, + "noValidMAPisAvailableAtThisTime": false, + "signalPriorityIsActive": false, + "preemptIsActive": false + } +},{ + "_id": { + "$oid": "665f3fc18c25216ef4f53f75" + }, + "validationMessages": [ + { + "schemaPath": "#/$defs/J2735RoadRegulatorID/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].id.region", + "message": "$.payload.data.intersectionStateList.intersectionStatelist[0].id.region: null found, integer expected" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[0].state_time_speed.movementEventList[0].timing.startTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[0].state_time_speed.movementEventList[0].timing.nextTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[1].state_time_speed.movementEventList[0].timing.startTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[1].state_time_speed.movementEventList[0].timing.nextTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[2].state_time_speed.movementEventList[0].timing.startTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[2].state_time_speed.movementEventList[0].timing.nextTime", + "message": "DE_TimeMark is missing" + } + ], + "utcTimeStamp": "2024-06-04T16:22:40.036Z", + "schemaVersion": 1, + "recordGeneratedAt": { + "$date": { + "$numberLong": "1717518160042" + } + }, + "revision": 0, + "states": [ + { + "stateTimeSpeed": [ + { + "eventState": "PROTECTED_CLEARANCE", + "timing": { + "minEndTime": "2024-06-04T16:03:19Z", + "maxEndTime": "2024-06-04T16:03:19Z" + } + } + ], + "signalGroup": 2 + }, + { + "stateTimeSpeed": [ + { + "eventState": "STOP_AND_REMAIN", + "timing": { + "minEndTime": "2024-06-04T16:03:22.1Z", + "maxEndTime": "2024-06-04T16:03:22.1Z" + } + } + ], + "signalGroup": 4 + }, + { + "stateTimeSpeed": [ + { + "eventState": "PROTECTED_CLEARANCE", + "timing": { + "minEndTime": "2024-06-04T16:03:19Z", + "maxEndTime": "2024-06-04T16:03:19Z" + } + } + ], + "signalGroup": 6 + } + ], + "messageType": "SPAT", + "cti4501Conformant": false, + "intersectionId": 12109, + "odeReceivedAt": "2024-06-04T16:22:40.036Z", + "originIp": "10.11.81.12", + "status": { + "failureFlash": false, + "noValidSPATisAvailableAtThisTime": false, + "fixedTimeOperation": false, + "standbyOperation": false, + "trafficDependentOperation": false, + "manualControlIsEnabled": false, + "off": false, + "stopTimeIsActivated": false, + "recentChangeInMAPassignedLanesIDsUsed": false, + "recentMAPmessageUpdate": false, + "failureMode": false, + "noValidMAPisAvailableAtThisTime": false, + "signalPriorityIsActive": false, + "preemptIsActive": false + } +},{ + "_id": { + "$oid": "665f3fc18c25216ef4f53f76" + }, + "validationMessages": [ + { + "schemaPath": "#/$defs/J2735RoadRegulatorID/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].id.region", + "message": "$.payload.data.intersectionStateList.intersectionStatelist[0].id.region: null found, integer expected" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[0].state_time_speed.movementEventList[0].timing.startTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[0].state_time_speed.movementEventList[0].timing.nextTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[1].state_time_speed.movementEventList[0].timing.startTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[1].state_time_speed.movementEventList[0].timing.nextTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[2].state_time_speed.movementEventList[0].timing.startTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[2].state_time_speed.movementEventList[0].timing.nextTime", + "message": "DE_TimeMark is missing" + } + ], + "utcTimeStamp": "2024-06-04T16:22:40.143Z", + "schemaVersion": 1, + "recordGeneratedAt": { + "$date": { + "$numberLong": "1717518160161" + } + }, + "revision": 0, + "states": [ + { + "stateTimeSpeed": [ + { + "eventState": "PROTECTED_CLEARANCE", + "timing": { + "minEndTime": "2024-06-04T16:03:19Z", + "maxEndTime": "2024-06-04T16:03:19Z" + } + } + ], + "signalGroup": 2 + }, + { + "stateTimeSpeed": [ + { + "eventState": "STOP_AND_REMAIN", + "timing": { + "minEndTime": "2024-06-04T16:03:22.1Z", + "maxEndTime": "2024-06-04T16:03:22.1Z" + } + } + ], + "signalGroup": 4 + }, + { + "stateTimeSpeed": [ + { + "eventState": "PROTECTED_CLEARANCE", + "timing": { + "minEndTime": "2024-06-04T16:03:19Z", + "maxEndTime": "2024-06-04T16:03:19Z" + } + } + ], + "signalGroup": 6 + } + ], + "messageType": "SPAT", + "cti4501Conformant": false, + "intersectionId": 12109, + "odeReceivedAt": "2024-06-04T16:22:40.143Z", + "originIp": "10.11.81.12", + "status": { + "failureFlash": false, + "noValidSPATisAvailableAtThisTime": false, + "fixedTimeOperation": false, + "standbyOperation": false, + "trafficDependentOperation": false, + "manualControlIsEnabled": false, + "off": false, + "stopTimeIsActivated": false, + "recentChangeInMAPassignedLanesIDsUsed": false, + "recentMAPmessageUpdate": false, + "failureMode": false, + "noValidMAPisAvailableAtThisTime": false, + "signalPriorityIsActive": false, + "preemptIsActive": false + } +},{ + "_id": { + "$oid": "665f3fc18c25216ef4f53f77" + }, + "validationMessages": [ + { + "schemaPath": "#/$defs/J2735RoadRegulatorID/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].id.region", + "message": "$.payload.data.intersectionStateList.intersectionStatelist[0].id.region: null found, integer expected" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[0].state_time_speed.movementEventList[0].timing.startTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[0].state_time_speed.movementEventList[0].timing.nextTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[1].state_time_speed.movementEventList[0].timing.startTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[1].state_time_speed.movementEventList[0].timing.nextTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[2].state_time_speed.movementEventList[0].timing.startTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[2].state_time_speed.movementEventList[0].timing.nextTime", + "message": "DE_TimeMark is missing" + } + ], + "utcTimeStamp": "2024-06-04T16:22:40.251Z", + "schemaVersion": 1, + "recordGeneratedAt": { + "$date": { + "$numberLong": "1717518160259" + } + }, + "revision": 0, + "states": [ + { + "stateTimeSpeed": [ + { + "eventState": "PROTECTED_CLEARANCE", + "timing": { + "minEndTime": "2024-06-04T16:03:19Z", + "maxEndTime": "2024-06-04T16:03:19Z" + } + } + ], + "signalGroup": 2 + }, + { + "stateTimeSpeed": [ + { + "eventState": "STOP_AND_REMAIN", + "timing": { + "minEndTime": "2024-06-04T16:03:22.1Z", + "maxEndTime": "2024-06-04T16:03:22.1Z" + } + } + ], + "signalGroup": 4 + }, + { + "stateTimeSpeed": [ + { + "eventState": "PROTECTED_CLEARANCE", + "timing": { + "minEndTime": "2024-06-04T16:03:19Z", + "maxEndTime": "2024-06-04T16:03:19Z" + } + } + ], + "signalGroup": 6 + } + ], + "messageType": "SPAT", + "cti4501Conformant": false, + "intersectionId": 12109, + "odeReceivedAt": "2024-06-04T16:22:40.251Z", + "originIp": "10.11.81.12", + "status": { + "failureFlash": false, + "noValidSPATisAvailableAtThisTime": false, + "fixedTimeOperation": false, + "standbyOperation": false, + "trafficDependentOperation": false, + "manualControlIsEnabled": false, + "off": false, + "stopTimeIsActivated": false, + "recentChangeInMAPassignedLanesIDsUsed": false, + "recentMAPmessageUpdate": false, + "failureMode": false, + "noValidMAPisAvailableAtThisTime": false, + "signalPriorityIsActive": false, + "preemptIsActive": false + } +},{ + "_id": { + "$oid": "665f3fc18c25216ef4f53f78" + }, + "validationMessages": [ + { + "schemaPath": "#/$defs/J2735RoadRegulatorID/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].id.region", + "message": "$.payload.data.intersectionStateList.intersectionStatelist[0].id.region: null found, integer expected" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[0].state_time_speed.movementEventList[0].timing.startTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[0].state_time_speed.movementEventList[0].timing.nextTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[1].state_time_speed.movementEventList[0].timing.startTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[1].state_time_speed.movementEventList[0].timing.nextTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[2].state_time_speed.movementEventList[0].timing.startTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[2].state_time_speed.movementEventList[0].timing.nextTime", + "message": "DE_TimeMark is missing" + } + ], + "utcTimeStamp": "2024-06-04T16:22:40.356Z", + "schemaVersion": 1, + "recordGeneratedAt": { + "$date": { + "$numberLong": "1717518160369" + } + }, + "revision": 0, + "states": [ + { + "stateTimeSpeed": [ + { + "eventState": "PROTECTED_CLEARANCE", + "timing": { + "minEndTime": "2024-06-04T16:03:19Z", + "maxEndTime": "2024-06-04T16:03:19Z" + } + } + ], + "signalGroup": 2 + }, + { + "stateTimeSpeed": [ + { + "eventState": "STOP_AND_REMAIN", + "timing": { + "minEndTime": "2024-06-04T16:03:22.1Z", + "maxEndTime": "2024-06-04T16:03:22.1Z" + } + } + ], + "signalGroup": 4 + }, + { + "stateTimeSpeed": [ + { + "eventState": "PROTECTED_CLEARANCE", + "timing": { + "minEndTime": "2024-06-04T16:03:19Z", + "maxEndTime": "2024-06-04T16:03:19Z" + } + } + ], + "signalGroup": 6 + } + ], + "messageType": "SPAT", + "cti4501Conformant": false, + "intersectionId": 12109, + "odeReceivedAt": "2024-06-04T16:22:40.356Z", + "originIp": "10.11.81.12", + "status": { + "failureFlash": false, + "noValidSPATisAvailableAtThisTime": false, + "fixedTimeOperation": false, + "standbyOperation": false, + "trafficDependentOperation": false, + "manualControlIsEnabled": false, + "off": false, + "stopTimeIsActivated": false, + "recentChangeInMAPassignedLanesIDsUsed": false, + "recentMAPmessageUpdate": false, + "failureMode": false, + "noValidMAPisAvailableAtThisTime": false, + "signalPriorityIsActive": false, + "preemptIsActive": false + } +},{ + "_id": { + "$oid": "665f3fc18c25216ef4f53f79" + }, + "validationMessages": [ + { + "schemaPath": "#/$defs/J2735RoadRegulatorID/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].id.region", + "message": "$.payload.data.intersectionStateList.intersectionStatelist[0].id.region: null found, integer expected" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[0].state_time_speed.movementEventList[0].timing.startTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[0].state_time_speed.movementEventList[0].timing.nextTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[1].state_time_speed.movementEventList[0].timing.startTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[1].state_time_speed.movementEventList[0].timing.nextTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[2].state_time_speed.movementEventList[0].timing.startTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[2].state_time_speed.movementEventList[0].timing.nextTime", + "message": "DE_TimeMark is missing" + } + ], + "utcTimeStamp": "2024-06-04T16:22:40.463Z", + "schemaVersion": 1, + "recordGeneratedAt": { + "$date": { + "$numberLong": "1717518160478" + } + }, + "revision": 0, + "states": [ + { + "stateTimeSpeed": [ + { + "eventState": "PROTECTED_CLEARANCE", + "timing": { + "minEndTime": "2024-06-04T16:03:19Z", + "maxEndTime": "2024-06-04T16:03:19Z" + } + } + ], + "signalGroup": 2 + }, + { + "stateTimeSpeed": [ + { + "eventState": "STOP_AND_REMAIN", + "timing": { + "minEndTime": "2024-06-04T16:03:22.1Z", + "maxEndTime": "2024-06-04T16:03:22.1Z" + } + } + ], + "signalGroup": 4 + }, + { + "stateTimeSpeed": [ + { + "eventState": "PROTECTED_CLEARANCE", + "timing": { + "minEndTime": "2024-06-04T16:03:19Z", + "maxEndTime": "2024-06-04T16:03:19Z" + } + } + ], + "signalGroup": 6 + } + ], + "messageType": "SPAT", + "cti4501Conformant": false, + "intersectionId": 12109, + "odeReceivedAt": "2024-06-04T16:22:40.463Z", + "originIp": "10.11.81.12", + "status": { + "failureFlash": false, + "noValidSPATisAvailableAtThisTime": false, + "fixedTimeOperation": false, + "standbyOperation": false, + "trafficDependentOperation": false, + "manualControlIsEnabled": false, + "off": false, + "stopTimeIsActivated": false, + "recentChangeInMAPassignedLanesIDsUsed": false, + "recentMAPmessageUpdate": false, + "failureMode": false, + "noValidMAPisAvailableAtThisTime": false, + "signalPriorityIsActive": false, + "preemptIsActive": false + } +},{ + "_id": { + "$oid": "665f3fc18c25216ef4f53f7a" + }, + "validationMessages": [ + { + "schemaPath": "#/$defs/J2735RoadRegulatorID/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].id.region", + "message": "$.payload.data.intersectionStateList.intersectionStatelist[0].id.region: null found, integer expected" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[0].state_time_speed.movementEventList[0].timing.startTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[0].state_time_speed.movementEventList[0].timing.nextTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[1].state_time_speed.movementEventList[0].timing.startTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[1].state_time_speed.movementEventList[0].timing.nextTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[2].state_time_speed.movementEventList[0].timing.startTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[2].state_time_speed.movementEventList[0].timing.nextTime", + "message": "DE_TimeMark is missing" + } + ], + "utcTimeStamp": "2024-06-04T16:22:40.571Z", + "schemaVersion": 1, + "recordGeneratedAt": { + "$date": { + "$numberLong": "1717518160582" + } + }, + "revision": 0, + "states": [ + { + "stateTimeSpeed": [ + { + "eventState": "PROTECTED_CLEARANCE", + "timing": { + "minEndTime": "2024-06-04T16:03:19Z", + "maxEndTime": "2024-06-04T16:03:19Z" + } + } + ], + "signalGroup": 2 + }, + { + "stateTimeSpeed": [ + { + "eventState": "STOP_AND_REMAIN", + "timing": { + "minEndTime": "2024-06-04T16:03:22.1Z", + "maxEndTime": "2024-06-04T16:03:22.1Z" + } + } + ], + "signalGroup": 4 + }, + { + "stateTimeSpeed": [ + { + "eventState": "PROTECTED_CLEARANCE", + "timing": { + "minEndTime": "2024-06-04T16:03:19Z", + "maxEndTime": "2024-06-04T16:03:19Z" + } + } + ], + "signalGroup": 6 + } + ], + "messageType": "SPAT", + "cti4501Conformant": false, + "intersectionId": 12109, + "odeReceivedAt": "2024-06-04T16:22:40.571Z", + "originIp": "10.11.81.12", + "status": { + "failureFlash": false, + "noValidSPATisAvailableAtThisTime": false, + "fixedTimeOperation": false, + "standbyOperation": false, + "trafficDependentOperation": false, + "manualControlIsEnabled": false, + "off": false, + "stopTimeIsActivated": false, + "recentChangeInMAPassignedLanesIDsUsed": false, + "recentMAPmessageUpdate": false, + "failureMode": false, + "noValidMAPisAvailableAtThisTime": false, + "signalPriorityIsActive": false, + "preemptIsActive": false + } +},{ + "_id": { + "$oid": "665f3fc18c25216ef4f53f7b" + }, + "validationMessages": [ + { + "schemaPath": "#/$defs/J2735RoadRegulatorID/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].id.region", + "message": "$.payload.data.intersectionStateList.intersectionStatelist[0].id.region: null found, integer expected" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[0].state_time_speed.movementEventList[0].timing.startTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[0].state_time_speed.movementEventList[0].timing.nextTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[1].state_time_speed.movementEventList[0].timing.startTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[1].state_time_speed.movementEventList[0].timing.nextTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[2].state_time_speed.movementEventList[0].timing.startTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[2].state_time_speed.movementEventList[0].timing.nextTime", + "message": "DE_TimeMark is missing" + } + ], + "utcTimeStamp": "2024-06-04T16:22:40.677Z", + "schemaVersion": 1, + "recordGeneratedAt": { + "$date": { + "$numberLong": "1717518160691" + } + }, + "revision": 0, + "states": [ + { + "stateTimeSpeed": [ + { + "eventState": "PROTECTED_CLEARANCE", + "timing": { + "minEndTime": "2024-06-04T16:03:19Z", + "maxEndTime": "2024-06-04T16:03:19Z" + } + } + ], + "signalGroup": 2 + }, + { + "stateTimeSpeed": [ + { + "eventState": "STOP_AND_REMAIN", + "timing": { + "minEndTime": "2024-06-04T16:03:22.1Z", + "maxEndTime": "2024-06-04T16:03:22.1Z" + } + } + ], + "signalGroup": 4 + }, + { + "stateTimeSpeed": [ + { + "eventState": "PROTECTED_CLEARANCE", + "timing": { + "minEndTime": "2024-06-04T16:03:19Z", + "maxEndTime": "2024-06-04T16:03:19Z" + } + } + ], + "signalGroup": 6 + } + ], + "messageType": "SPAT", + "cti4501Conformant": false, + "intersectionId": 12109, + "odeReceivedAt": "2024-06-04T16:22:40.677Z", + "originIp": "10.11.81.12", + "status": { + "failureFlash": false, + "noValidSPATisAvailableAtThisTime": false, + "fixedTimeOperation": false, + "standbyOperation": false, + "trafficDependentOperation": false, + "manualControlIsEnabled": false, + "off": false, + "stopTimeIsActivated": false, + "recentChangeInMAPassignedLanesIDsUsed": false, + "recentMAPmessageUpdate": false, + "failureMode": false, + "noValidMAPisAvailableAtThisTime": false, + "signalPriorityIsActive": false, + "preemptIsActive": false + } +},{ + "_id": { + "$oid": "665f3fc18c25216ef4f53f7c" + }, + "validationMessages": [ + { + "schemaPath": "#/$defs/J2735RoadRegulatorID/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].id.region", + "message": "$.payload.data.intersectionStateList.intersectionStatelist[0].id.region: null found, integer expected" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[0].state_time_speed.movementEventList[0].timing.startTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[0].state_time_speed.movementEventList[0].timing.nextTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[1].state_time_speed.movementEventList[0].timing.startTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[1].state_time_speed.movementEventList[0].timing.nextTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[2].state_time_speed.movementEventList[0].timing.startTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[2].state_time_speed.movementEventList[0].timing.nextTime", + "message": "DE_TimeMark is missing" + } + ], + "utcTimeStamp": "2024-06-04T16:22:40.785Z", + "schemaVersion": 1, + "recordGeneratedAt": { + "$date": { + "$numberLong": "1717518160794" + } + }, + "revision": 0, + "states": [ + { + "stateTimeSpeed": [ + { + "eventState": "PROTECTED_CLEARANCE", + "timing": { + "minEndTime": "2024-06-04T16:03:19Z", + "maxEndTime": "2024-06-04T16:03:19Z" + } + } + ], + "signalGroup": 2 + }, + { + "stateTimeSpeed": [ + { + "eventState": "STOP_AND_REMAIN", + "timing": { + "minEndTime": "2024-06-04T16:03:22.1Z", + "maxEndTime": "2024-06-04T16:03:22.1Z" + } + } + ], + "signalGroup": 4 + }, + { + "stateTimeSpeed": [ + { + "eventState": "PROTECTED_CLEARANCE", + "timing": { + "minEndTime": "2024-06-04T16:03:19Z", + "maxEndTime": "2024-06-04T16:03:19Z" + } + } + ], + "signalGroup": 6 + } + ], + "messageType": "SPAT", + "cti4501Conformant": false, + "intersectionId": 12109, + "odeReceivedAt": "2024-06-04T16:22:40.785Z", + "originIp": "10.11.81.12", + "status": { + "failureFlash": false, + "noValidSPATisAvailableAtThisTime": false, + "fixedTimeOperation": false, + "standbyOperation": false, + "trafficDependentOperation": false, + "manualControlIsEnabled": false, + "off": false, + "stopTimeIsActivated": false, + "recentChangeInMAPassignedLanesIDsUsed": false, + "recentMAPmessageUpdate": false, + "failureMode": false, + "noValidMAPisAvailableAtThisTime": false, + "signalPriorityIsActive": false, + "preemptIsActive": false + } +},{ + "_id": { + "$oid": "665f3fc18c25216ef4f53f7d" + }, + "validationMessages": [ + { + "schemaPath": "#/$defs/J2735RoadRegulatorID/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].id.region", + "message": "$.payload.data.intersectionStateList.intersectionStatelist[0].id.region: null found, integer expected" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[0].state_time_speed.movementEventList[0].timing.startTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[0].state_time_speed.movementEventList[0].timing.nextTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[1].state_time_speed.movementEventList[0].timing.startTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[1].state_time_speed.movementEventList[0].timing.nextTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[2].state_time_speed.movementEventList[0].timing.startTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[2].state_time_speed.movementEventList[0].timing.nextTime", + "message": "DE_TimeMark is missing" + } + ], + "utcTimeStamp": "2024-06-04T16:22:40.892Z", + "schemaVersion": 1, + "recordGeneratedAt": { + "$date": { + "$numberLong": "1717518160904" + } + }, + "revision": 0, + "states": [ + { + "stateTimeSpeed": [ + { + "eventState": "PROTECTED_CLEARANCE", + "timing": { + "minEndTime": "2024-06-04T16:03:19Z", + "maxEndTime": "2024-06-04T16:03:19Z" + } + } + ], + "signalGroup": 2 + }, + { + "stateTimeSpeed": [ + { + "eventState": "STOP_AND_REMAIN", + "timing": { + "minEndTime": "2024-06-04T16:03:22.1Z", + "maxEndTime": "2024-06-04T16:03:22.1Z" + } + } + ], + "signalGroup": 4 + }, + { + "stateTimeSpeed": [ + { + "eventState": "PROTECTED_CLEARANCE", + "timing": { + "minEndTime": "2024-06-04T16:03:19Z", + "maxEndTime": "2024-06-04T16:03:19Z" + } + } + ], + "signalGroup": 6 + } + ], + "messageType": "SPAT", + "cti4501Conformant": false, + "intersectionId": 12109, + "odeReceivedAt": "2024-06-04T16:22:40.892Z", + "originIp": "10.11.81.12", + "status": { + "failureFlash": false, + "noValidSPATisAvailableAtThisTime": false, + "fixedTimeOperation": false, + "standbyOperation": false, + "trafficDependentOperation": false, + "manualControlIsEnabled": false, + "off": false, + "stopTimeIsActivated": false, + "recentChangeInMAPassignedLanesIDsUsed": false, + "recentMAPmessageUpdate": false, + "failureMode": false, + "noValidMAPisAvailableAtThisTime": false, + "signalPriorityIsActive": false, + "preemptIsActive": false + } +},{ + "_id": { + "$oid": "665f3fc18c25216ef4f53f7e" + }, + "validationMessages": [ + { + "schemaPath": "#/$defs/J2735RoadRegulatorID/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].id.region", + "message": "$.payload.data.intersectionStateList.intersectionStatelist[0].id.region: null found, integer expected" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[0].state_time_speed.movementEventList[0].timing.startTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[0].state_time_speed.movementEventList[0].timing.nextTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[1].state_time_speed.movementEventList[0].timing.startTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[1].state_time_speed.movementEventList[0].timing.nextTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[2].state_time_speed.movementEventList[0].timing.startTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[2].state_time_speed.movementEventList[0].timing.nextTime", + "message": "DE_TimeMark is missing" + } + ], + "utcTimeStamp": "2024-06-04T16:22:40.999Z", + "schemaVersion": 1, + "recordGeneratedAt": { + "$date": { + "$numberLong": "1717518161009" + } + }, + "revision": 0, + "states": [ + { + "stateTimeSpeed": [ + { + "eventState": "PROTECTED_CLEARANCE", + "timing": { + "minEndTime": "2024-06-04T16:03:19Z", + "maxEndTime": "2024-06-04T16:03:19Z" + } + } + ], + "signalGroup": 2 + }, + { + "stateTimeSpeed": [ + { + "eventState": "STOP_AND_REMAIN", + "timing": { + "minEndTime": "2024-06-04T16:03:22.1Z", + "maxEndTime": "2024-06-04T16:03:22.1Z" + } + } + ], + "signalGroup": 4 + }, + { + "stateTimeSpeed": [ + { + "eventState": "PROTECTED_CLEARANCE", + "timing": { + "minEndTime": "2024-06-04T16:03:19Z", + "maxEndTime": "2024-06-04T16:03:19Z" + } + } + ], + "signalGroup": 6 + } + ], + "messageType": "SPAT", + "cti4501Conformant": false, + "intersectionId": 12109, + "odeReceivedAt": "2024-06-04T16:22:40.999Z", + "originIp": "10.11.81.12", + "status": { + "failureFlash": false, + "noValidSPATisAvailableAtThisTime": false, + "fixedTimeOperation": false, + "standbyOperation": false, + "trafficDependentOperation": false, + "manualControlIsEnabled": false, + "off": false, + "stopTimeIsActivated": false, + "recentChangeInMAPassignedLanesIDsUsed": false, + "recentMAPmessageUpdate": false, + "failureMode": false, + "noValidMAPisAvailableAtThisTime": false, + "signalPriorityIsActive": false, + "preemptIsActive": false + } +},{ + "_id": { + "$oid": "665f3fc18c25216ef4f53f7f" + }, + "validationMessages": [ + { + "schemaPath": "#/$defs/J2735RoadRegulatorID/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].id.region", + "message": "$.payload.data.intersectionStateList.intersectionStatelist[0].id.region: null found, integer expected" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[0].state_time_speed.movementEventList[0].timing.startTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[0].state_time_speed.movementEventList[0].timing.nextTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[1].state_time_speed.movementEventList[0].timing.startTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[1].state_time_speed.movementEventList[0].timing.nextTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[2].state_time_speed.movementEventList[0].timing.startTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[2].state_time_speed.movementEventList[0].timing.nextTime", + "message": "DE_TimeMark is missing" + } + ], + "utcTimeStamp": "2024-06-04T16:22:41.106Z", + "schemaVersion": 1, + "recordGeneratedAt": { + "$date": { + "$numberLong": "1717518161114" + } + }, + "revision": 0, + "states": [ + { + "stateTimeSpeed": [ + { + "eventState": "PROTECTED_CLEARANCE", + "timing": { + "minEndTime": "2024-06-04T16:03:19Z", + "maxEndTime": "2024-06-04T16:03:19Z" + } + } + ], + "signalGroup": 2 + }, + { + "stateTimeSpeed": [ + { + "eventState": "STOP_AND_REMAIN", + "timing": { + "minEndTime": "2024-06-04T16:03:22.1Z", + "maxEndTime": "2024-06-04T16:03:22.1Z" + } + } + ], + "signalGroup": 4 + }, + { + "stateTimeSpeed": [ + { + "eventState": "PROTECTED_CLEARANCE", + "timing": { + "minEndTime": "2024-06-04T16:03:19Z", + "maxEndTime": "2024-06-04T16:03:19Z" + } + } + ], + "signalGroup": 6 + } + ], + "messageType": "SPAT", + "cti4501Conformant": false, + "intersectionId": 12109, + "odeReceivedAt": "2024-06-04T16:22:41.106Z", + "originIp": "10.11.81.12", + "status": { + "failureFlash": false, + "noValidSPATisAvailableAtThisTime": false, + "fixedTimeOperation": false, + "standbyOperation": false, + "trafficDependentOperation": false, + "manualControlIsEnabled": false, + "off": false, + "stopTimeIsActivated": false, + "recentChangeInMAPassignedLanesIDsUsed": false, + "recentMAPmessageUpdate": false, + "failureMode": false, + "noValidMAPisAvailableAtThisTime": false, + "signalPriorityIsActive": false, + "preemptIsActive": false + } +},{ + "_id": { + "$oid": "665f3fc18c25216ef4f53f80" + }, + "validationMessages": [ + { + "schemaPath": "#/$defs/J2735RoadRegulatorID/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].id.region", + "message": "$.payload.data.intersectionStateList.intersectionStatelist[0].id.region: null found, integer expected" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[0].state_time_speed.movementEventList[0].timing.startTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[0].state_time_speed.movementEventList[0].timing.nextTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[1].state_time_speed.movementEventList[0].timing.startTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[1].state_time_speed.movementEventList[0].timing.nextTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[2].state_time_speed.movementEventList[0].timing.startTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[2].state_time_speed.movementEventList[0].timing.nextTime", + "message": "DE_TimeMark is missing" + } + ], + "utcTimeStamp": "2024-06-04T16:22:41.2Z", + "schemaVersion": 1, + "recordGeneratedAt": { + "$date": { + "$numberLong": "1717518161206" + } + }, + "revision": 0, + "states": [ + { + "stateTimeSpeed": [ + { + "eventState": "PROTECTED_CLEARANCE", + "timing": { + "minEndTime": "2024-06-04T16:03:19Z", + "maxEndTime": "2024-06-04T16:03:19Z" + } + } + ], + "signalGroup": 2 + }, + { + "stateTimeSpeed": [ + { + "eventState": "STOP_AND_REMAIN", + "timing": { + "minEndTime": "2024-06-04T16:03:22.1Z", + "maxEndTime": "2024-06-04T16:03:22.1Z" + } + } + ], + "signalGroup": 4 + }, + { + "stateTimeSpeed": [ + { + "eventState": "PROTECTED_CLEARANCE", + "timing": { + "minEndTime": "2024-06-04T16:03:19Z", + "maxEndTime": "2024-06-04T16:03:19Z" + } + } + ], + "signalGroup": 6 + } + ], + "messageType": "SPAT", + "cti4501Conformant": false, + "intersectionId": 12109, + "odeReceivedAt": "2024-06-04T16:22:41.2Z", + "originIp": "10.11.81.12", + "status": { + "failureFlash": false, + "noValidSPATisAvailableAtThisTime": false, + "fixedTimeOperation": false, + "standbyOperation": false, + "trafficDependentOperation": false, + "manualControlIsEnabled": false, + "off": false, + "stopTimeIsActivated": false, + "recentChangeInMAPassignedLanesIDsUsed": false, + "recentMAPmessageUpdate": false, + "failureMode": false, + "noValidMAPisAvailableAtThisTime": false, + "signalPriorityIsActive": false, + "preemptIsActive": false + } +},{ + "_id": { + "$oid": "665f3fc18c25216ef4f53f81" + }, + "validationMessages": [ + { + "schemaPath": "#/$defs/J2735RoadRegulatorID/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].id.region", + "message": "$.payload.data.intersectionStateList.intersectionStatelist[0].id.region: null found, integer expected" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[0].state_time_speed.movementEventList[0].timing.startTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[0].state_time_speed.movementEventList[0].timing.nextTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[1].state_time_speed.movementEventList[0].timing.startTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[1].state_time_speed.movementEventList[0].timing.nextTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[2].state_time_speed.movementEventList[0].timing.startTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[2].state_time_speed.movementEventList[0].timing.nextTime", + "message": "DE_TimeMark is missing" + } + ], + "utcTimeStamp": "2024-06-04T16:22:41.307Z", + "schemaVersion": 1, + "recordGeneratedAt": { + "$date": { + "$numberLong": "1717518161312" + } + }, + "revision": 0, + "states": [ + { + "stateTimeSpeed": [ + { + "eventState": "PROTECTED_CLEARANCE", + "timing": { + "minEndTime": "2024-06-04T16:03:19Z", + "maxEndTime": "2024-06-04T16:03:19Z" + } + } + ], + "signalGroup": 2 + }, + { + "stateTimeSpeed": [ + { + "eventState": "STOP_AND_REMAIN", + "timing": { + "minEndTime": "2024-06-04T16:03:22.1Z", + "maxEndTime": "2024-06-04T16:03:22.1Z" + } + } + ], + "signalGroup": 4 + }, + { + "stateTimeSpeed": [ + { + "eventState": "PROTECTED_CLEARANCE", + "timing": { + "minEndTime": "2024-06-04T16:03:19Z", + "maxEndTime": "2024-06-04T16:03:19Z" + } + } + ], + "signalGroup": 6 + } + ], + "messageType": "SPAT", + "cti4501Conformant": false, + "intersectionId": 12109, + "odeReceivedAt": "2024-06-04T16:22:41.307Z", + "originIp": "10.11.81.12", + "status": { + "failureFlash": false, + "noValidSPATisAvailableAtThisTime": false, + "fixedTimeOperation": false, + "standbyOperation": false, + "trafficDependentOperation": false, + "manualControlIsEnabled": false, + "off": false, + "stopTimeIsActivated": false, + "recentChangeInMAPassignedLanesIDsUsed": false, + "recentMAPmessageUpdate": false, + "failureMode": false, + "noValidMAPisAvailableAtThisTime": false, + "signalPriorityIsActive": false, + "preemptIsActive": false + } +},{ + "_id": { + "$oid": "665f3fc18c25216ef4f53f82" + }, + "validationMessages": [ + { + "schemaPath": "#/$defs/J2735RoadRegulatorID/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].id.region", + "message": "$.payload.data.intersectionStateList.intersectionStatelist[0].id.region: null found, integer expected" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[0].state_time_speed.movementEventList[0].timing.startTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[0].state_time_speed.movementEventList[0].timing.nextTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[1].state_time_speed.movementEventList[0].timing.startTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[1].state_time_speed.movementEventList[0].timing.nextTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[2].state_time_speed.movementEventList[0].timing.startTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[2].state_time_speed.movementEventList[0].timing.nextTime", + "message": "DE_TimeMark is missing" + } + ], + "utcTimeStamp": "2024-06-04T16:22:41.412Z", + "schemaVersion": 1, + "recordGeneratedAt": { + "$date": { + "$numberLong": "1717518161419" + } + }, + "revision": 0, + "states": [ + { + "stateTimeSpeed": [ + { + "eventState": "PROTECTED_CLEARANCE", + "timing": { + "minEndTime": "2024-06-04T16:03:19Z", + "maxEndTime": "2024-06-04T16:03:19Z" + } + } + ], + "signalGroup": 2 + }, + { + "stateTimeSpeed": [ + { + "eventState": "STOP_AND_REMAIN", + "timing": { + "minEndTime": "2024-06-04T16:03:22.1Z", + "maxEndTime": "2024-06-04T16:03:22.1Z" + } + } + ], + "signalGroup": 4 + }, + { + "stateTimeSpeed": [ + { + "eventState": "PROTECTED_CLEARANCE", + "timing": { + "minEndTime": "2024-06-04T16:03:19Z", + "maxEndTime": "2024-06-04T16:03:19Z" + } + } + ], + "signalGroup": 6 + } + ], + "messageType": "SPAT", + "cti4501Conformant": false, + "intersectionId": 12109, + "odeReceivedAt": "2024-06-04T16:22:41.412Z", + "originIp": "10.11.81.12", + "status": { + "failureFlash": false, + "noValidSPATisAvailableAtThisTime": false, + "fixedTimeOperation": false, + "standbyOperation": false, + "trafficDependentOperation": false, + "manualControlIsEnabled": false, + "off": false, + "stopTimeIsActivated": false, + "recentChangeInMAPassignedLanesIDsUsed": false, + "recentMAPmessageUpdate": false, + "failureMode": false, + "noValidMAPisAvailableAtThisTime": false, + "signalPriorityIsActive": false, + "preemptIsActive": false + } +},{ + "_id": { + "$oid": "665f3fc18c25216ef4f53f83" + }, + "validationMessages": [ + { + "schemaPath": "#/$defs/J2735RoadRegulatorID/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].id.region", + "message": "$.payload.data.intersectionStateList.intersectionStatelist[0].id.region: null found, integer expected" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[0].state_time_speed.movementEventList[0].timing.startTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[0].state_time_speed.movementEventList[0].timing.nextTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[1].state_time_speed.movementEventList[0].timing.startTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[1].state_time_speed.movementEventList[0].timing.nextTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[2].state_time_speed.movementEventList[0].timing.startTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[2].state_time_speed.movementEventList[0].timing.nextTime", + "message": "DE_TimeMark is missing" + } + ], + "utcTimeStamp": "2024-06-04T16:22:41.521Z", + "schemaVersion": 1, + "recordGeneratedAt": { + "$date": { + "$numberLong": "1717518161527" + } + }, + "revision": 0, + "states": [ + { + "stateTimeSpeed": [ + { + "eventState": "PROTECTED_CLEARANCE", + "timing": { + "minEndTime": "2024-06-04T16:03:19Z", + "maxEndTime": "2024-06-04T16:03:19Z" + } + } + ], + "signalGroup": 2 + }, + { + "stateTimeSpeed": [ + { + "eventState": "STOP_AND_REMAIN", + "timing": { + "minEndTime": "2024-06-04T16:03:22.1Z", + "maxEndTime": "2024-06-04T16:03:22.1Z" + } + } + ], + "signalGroup": 4 + }, + { + "stateTimeSpeed": [ + { + "eventState": "PROTECTED_CLEARANCE", + "timing": { + "minEndTime": "2024-06-04T16:03:19Z", + "maxEndTime": "2024-06-04T16:03:19Z" + } + } + ], + "signalGroup": 6 + } + ], + "messageType": "SPAT", + "cti4501Conformant": false, + "intersectionId": 12109, + "odeReceivedAt": "2024-06-04T16:22:41.521Z", + "originIp": "10.11.81.12", + "status": { + "failureFlash": false, + "noValidSPATisAvailableAtThisTime": false, + "fixedTimeOperation": false, + "standbyOperation": false, + "trafficDependentOperation": false, + "manualControlIsEnabled": false, + "off": false, + "stopTimeIsActivated": false, + "recentChangeInMAPassignedLanesIDsUsed": false, + "recentMAPmessageUpdate": false, + "failureMode": false, + "noValidMAPisAvailableAtThisTime": false, + "signalPriorityIsActive": false, + "preemptIsActive": false + } +},{ + "_id": { + "$oid": "665f3fc18c25216ef4f53f84" + }, + "validationMessages": [ + { + "schemaPath": "#/$defs/J2735RoadRegulatorID/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].id.region", + "message": "$.payload.data.intersectionStateList.intersectionStatelist[0].id.region: null found, integer expected" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[0].state_time_speed.movementEventList[0].timing.startTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[0].state_time_speed.movementEventList[0].timing.nextTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[1].state_time_speed.movementEventList[0].timing.startTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[1].state_time_speed.movementEventList[0].timing.nextTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[2].state_time_speed.movementEventList[0].timing.startTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[2].state_time_speed.movementEventList[0].timing.nextTime", + "message": "DE_TimeMark is missing" + } + ], + "utcTimeStamp": "2024-06-04T16:22:41.628Z", + "schemaVersion": 1, + "recordGeneratedAt": { + "$date": { + "$numberLong": "1717518161636" + } + }, + "revision": 0, + "states": [ + { + "stateTimeSpeed": [ + { + "eventState": "PROTECTED_CLEARANCE", + "timing": { + "minEndTime": "2024-06-04T16:03:19Z", + "maxEndTime": "2024-06-04T16:03:19Z" + } + } + ], + "signalGroup": 2 + }, + { + "stateTimeSpeed": [ + { + "eventState": "STOP_AND_REMAIN", + "timing": { + "minEndTime": "2024-06-04T16:03:22.1Z", + "maxEndTime": "2024-06-04T16:03:22.1Z" + } + } + ], + "signalGroup": 4 + }, + { + "stateTimeSpeed": [ + { + "eventState": "PROTECTED_CLEARANCE", + "timing": { + "minEndTime": "2024-06-04T16:03:19Z", + "maxEndTime": "2024-06-04T16:03:19Z" + } + } + ], + "signalGroup": 6 + } + ], + "messageType": "SPAT", + "cti4501Conformant": false, + "intersectionId": 12109, + "odeReceivedAt": "2024-06-04T16:22:41.628Z", + "originIp": "10.11.81.12", + "status": { + "failureFlash": false, + "noValidSPATisAvailableAtThisTime": false, + "fixedTimeOperation": false, + "standbyOperation": false, + "trafficDependentOperation": false, + "manualControlIsEnabled": false, + "off": false, + "stopTimeIsActivated": false, + "recentChangeInMAPassignedLanesIDsUsed": false, + "recentMAPmessageUpdate": false, + "failureMode": false, + "noValidMAPisAvailableAtThisTime": false, + "signalPriorityIsActive": false, + "preemptIsActive": false + } +},{ + "_id": { + "$oid": "665f3fc18c25216ef4f53f85" + }, + "validationMessages": [ + { + "schemaPath": "#/$defs/J2735RoadRegulatorID/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].id.region", + "message": "$.payload.data.intersectionStateList.intersectionStatelist[0].id.region: null found, integer expected" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[0].state_time_speed.movementEventList[0].timing.startTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[0].state_time_speed.movementEventList[0].timing.nextTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[1].state_time_speed.movementEventList[0].timing.startTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[1].state_time_speed.movementEventList[0].timing.nextTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[2].state_time_speed.movementEventList[0].timing.startTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[2].state_time_speed.movementEventList[0].timing.nextTime", + "message": "DE_TimeMark is missing" + } + ], + "utcTimeStamp": "2024-06-04T16:22:41.734Z", + "schemaVersion": 1, + "recordGeneratedAt": { + "$date": { + "$numberLong": "1717518161740" + } + }, + "revision": 0, + "states": [ + { + "stateTimeSpeed": [ + { + "eventState": "PROTECTED_CLEARANCE", + "timing": { + "minEndTime": "2024-06-04T16:03:19Z", + "maxEndTime": "2024-06-04T16:03:19Z" + } + } + ], + "signalGroup": 2 + }, + { + "stateTimeSpeed": [ + { + "eventState": "STOP_AND_REMAIN", + "timing": { + "minEndTime": "2024-06-04T16:03:22.1Z", + "maxEndTime": "2024-06-04T16:03:22.1Z" + } + } + ], + "signalGroup": 4 + }, + { + "stateTimeSpeed": [ + { + "eventState": "PROTECTED_CLEARANCE", + "timing": { + "minEndTime": "2024-06-04T16:03:19Z", + "maxEndTime": "2024-06-04T16:03:19Z" + } + } + ], + "signalGroup": 6 + } + ], + "messageType": "SPAT", + "cti4501Conformant": false, + "intersectionId": 12109, + "odeReceivedAt": "2024-06-04T16:22:41.734Z", + "originIp": "10.11.81.12", + "status": { + "failureFlash": false, + "noValidSPATisAvailableAtThisTime": false, + "fixedTimeOperation": false, + "standbyOperation": false, + "trafficDependentOperation": false, + "manualControlIsEnabled": false, + "off": false, + "stopTimeIsActivated": false, + "recentChangeInMAPassignedLanesIDsUsed": false, + "recentMAPmessageUpdate": false, + "failureMode": false, + "noValidMAPisAvailableAtThisTime": false, + "signalPriorityIsActive": false, + "preemptIsActive": false + } +},{ + "_id": { + "$oid": "665f3fc18c25216ef4f53f86" + }, + "validationMessages": [ + { + "schemaPath": "#/$defs/J2735RoadRegulatorID/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].id.region", + "message": "$.payload.data.intersectionStateList.intersectionStatelist[0].id.region: null found, integer expected" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[0].state_time_speed.movementEventList[0].timing.startTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[0].state_time_speed.movementEventList[0].timing.nextTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[1].state_time_speed.movementEventList[0].timing.startTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[1].state_time_speed.movementEventList[0].timing.nextTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[2].state_time_speed.movementEventList[0].timing.startTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[2].state_time_speed.movementEventList[0].timing.nextTime", + "message": "DE_TimeMark is missing" + } + ], + "utcTimeStamp": "2024-06-04T16:22:41.842Z", + "schemaVersion": 1, + "recordGeneratedAt": { + "$date": { + "$numberLong": "1717518161849" + } + }, + "revision": 0, + "states": [ + { + "stateTimeSpeed": [ + { + "eventState": "PROTECTED_CLEARANCE", + "timing": { + "minEndTime": "2024-06-04T16:03:19Z", + "maxEndTime": "2024-06-04T16:03:19Z" + } + } + ], + "signalGroup": 2 + }, + { + "stateTimeSpeed": [ + { + "eventState": "STOP_AND_REMAIN", + "timing": { + "minEndTime": "2024-06-04T16:03:22.1Z", + "maxEndTime": "2024-06-04T16:03:22.1Z" + } + } + ], + "signalGroup": 4 + }, + { + "stateTimeSpeed": [ + { + "eventState": "PROTECTED_CLEARANCE", + "timing": { + "minEndTime": "2024-06-04T16:03:19Z", + "maxEndTime": "2024-06-04T16:03:19Z" + } + } + ], + "signalGroup": 6 + } + ], + "messageType": "SPAT", + "cti4501Conformant": false, + "intersectionId": 12109, + "odeReceivedAt": "2024-06-04T16:22:41.842Z", + "originIp": "10.11.81.12", + "status": { + "failureFlash": false, + "noValidSPATisAvailableAtThisTime": false, + "fixedTimeOperation": false, + "standbyOperation": false, + "trafficDependentOperation": false, + "manualControlIsEnabled": false, + "off": false, + "stopTimeIsActivated": false, + "recentChangeInMAPassignedLanesIDsUsed": false, + "recentMAPmessageUpdate": false, + "failureMode": false, + "noValidMAPisAvailableAtThisTime": false, + "signalPriorityIsActive": false, + "preemptIsActive": false + } +},{ + "_id": { + "$oid": "665f3fc18c25216ef4f53f87" + }, + "validationMessages": [ + { + "schemaPath": "#/$defs/J2735RoadRegulatorID/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].id.region", + "message": "$.payload.data.intersectionStateList.intersectionStatelist[0].id.region: null found, integer expected" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[0].state_time_speed.movementEventList[0].timing.startTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[0].state_time_speed.movementEventList[0].timing.nextTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[1].state_time_speed.movementEventList[0].timing.startTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[1].state_time_speed.movementEventList[0].timing.nextTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[2].state_time_speed.movementEventList[0].timing.startTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[2].state_time_speed.movementEventList[0].timing.nextTime", + "message": "DE_TimeMark is missing" + } + ], + "utcTimeStamp": "2024-06-04T16:22:41.949Z", + "schemaVersion": 1, + "recordGeneratedAt": { + "$date": { + "$numberLong": "1717518161954" + } + }, + "revision": 0, + "states": [ + { + "stateTimeSpeed": [ + { + "eventState": "PROTECTED_CLEARANCE", + "timing": { + "minEndTime": "2024-06-04T16:03:19Z", + "maxEndTime": "2024-06-04T16:03:19Z" + } + } + ], + "signalGroup": 2 + }, + { + "stateTimeSpeed": [ + { + "eventState": "STOP_AND_REMAIN", + "timing": { + "minEndTime": "2024-06-04T16:03:22.1Z", + "maxEndTime": "2024-06-04T16:03:22.1Z" + } + } + ], + "signalGroup": 4 + }, + { + "stateTimeSpeed": [ + { + "eventState": "PROTECTED_CLEARANCE", + "timing": { + "minEndTime": "2024-06-04T16:03:19Z", + "maxEndTime": "2024-06-04T16:03:19Z" + } + } + ], + "signalGroup": 6 + } + ], + "messageType": "SPAT", + "cti4501Conformant": false, + "intersectionId": 12109, + "odeReceivedAt": "2024-06-04T16:22:41.949Z", + "originIp": "10.11.81.12", + "status": { + "failureFlash": false, + "noValidSPATisAvailableAtThisTime": false, + "fixedTimeOperation": false, + "standbyOperation": false, + "trafficDependentOperation": false, + "manualControlIsEnabled": false, + "off": false, + "stopTimeIsActivated": false, + "recentChangeInMAPassignedLanesIDsUsed": false, + "recentMAPmessageUpdate": false, + "failureMode": false, + "noValidMAPisAvailableAtThisTime": false, + "signalPriorityIsActive": false, + "preemptIsActive": false + } +},{ + "_id": { + "$oid": "665f3fc18c25216ef4f53f88" + }, + "validationMessages": [ + { + "schemaPath": "#/$defs/J2735RoadRegulatorID/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].id.region", + "message": "$.payload.data.intersectionStateList.intersectionStatelist[0].id.region: null found, integer expected" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[0].state_time_speed.movementEventList[0].timing.startTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[0].state_time_speed.movementEventList[0].timing.nextTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[1].state_time_speed.movementEventList[0].timing.startTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[1].state_time_speed.movementEventList[0].timing.nextTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[2].state_time_speed.movementEventList[0].timing.startTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[2].state_time_speed.movementEventList[0].timing.nextTime", + "message": "DE_TimeMark is missing" + } + ], + "utcTimeStamp": "2024-06-04T16:22:42.056Z", + "schemaVersion": 1, + "recordGeneratedAt": { + "$date": { + "$numberLong": "1717518162061" + } + }, + "revision": 0, + "states": [ + { + "stateTimeSpeed": [ + { + "eventState": "PROTECTED_CLEARANCE", + "timing": { + "minEndTime": "2024-06-04T16:03:19Z", + "maxEndTime": "2024-06-04T16:03:19Z" + } + } + ], + "signalGroup": 2 + }, + { + "stateTimeSpeed": [ + { + "eventState": "STOP_AND_REMAIN", + "timing": { + "minEndTime": "2024-06-04T16:03:22.1Z", + "maxEndTime": "2024-06-04T16:03:22.1Z" + } + } + ], + "signalGroup": 4 + }, + { + "stateTimeSpeed": [ + { + "eventState": "PROTECTED_CLEARANCE", + "timing": { + "minEndTime": "2024-06-04T16:03:19Z", + "maxEndTime": "2024-06-04T16:03:19Z" + } + } + ], + "signalGroup": 6 + } + ], + "messageType": "SPAT", + "cti4501Conformant": false, + "intersectionId": 12109, + "odeReceivedAt": "2024-06-04T16:22:42.056Z", + "originIp": "10.11.81.12", + "status": { + "failureFlash": false, + "noValidSPATisAvailableAtThisTime": false, + "fixedTimeOperation": false, + "standbyOperation": false, + "trafficDependentOperation": false, + "manualControlIsEnabled": false, + "off": false, + "stopTimeIsActivated": false, + "recentChangeInMAPassignedLanesIDsUsed": false, + "recentMAPmessageUpdate": false, + "failureMode": false, + "noValidMAPisAvailableAtThisTime": false, + "signalPriorityIsActive": false, + "preemptIsActive": false + } +},{ + "_id": { + "$oid": "665f3fc18c25216ef4f53f89" + }, + "validationMessages": [ + { + "schemaPath": "#/$defs/J2735RoadRegulatorID/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].id.region", + "message": "$.payload.data.intersectionStateList.intersectionStatelist[0].id.region: null found, integer expected" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[0].state_time_speed.movementEventList[0].timing.startTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[0].state_time_speed.movementEventList[0].timing.nextTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[1].state_time_speed.movementEventList[0].timing.startTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[1].state_time_speed.movementEventList[0].timing.nextTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[2].state_time_speed.movementEventList[0].timing.startTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[2].state_time_speed.movementEventList[0].timing.nextTime", + "message": "DE_TimeMark is missing" + } + ], + "utcTimeStamp": "2024-06-04T16:22:42.163Z", + "schemaVersion": 1, + "recordGeneratedAt": { + "$date": { + "$numberLong": "1717518162167" + } + }, + "revision": 0, + "states": [ + { + "stateTimeSpeed": [ + { + "eventState": "PROTECTED_CLEARANCE", + "timing": { + "minEndTime": "2024-06-04T16:03:18Z", + "maxEndTime": "2024-06-04T16:03:18Z" + } + } + ], + "signalGroup": 2 + }, + { + "stateTimeSpeed": [ + { + "eventState": "STOP_AND_REMAIN", + "timing": { + "minEndTime": "2024-06-04T16:03:21.1Z", + "maxEndTime": "2024-06-04T16:03:21.1Z" + } + } + ], + "signalGroup": 4 + }, + { + "stateTimeSpeed": [ + { + "eventState": "PROTECTED_CLEARANCE", + "timing": { + "minEndTime": "2024-06-04T16:03:18Z", + "maxEndTime": "2024-06-04T16:03:18Z" + } + } + ], + "signalGroup": 6 + } + ], + "messageType": "SPAT", + "cti4501Conformant": false, + "intersectionId": 12109, + "odeReceivedAt": "2024-06-04T16:22:42.163Z", + "originIp": "10.11.81.12", + "status": { + "failureFlash": false, + "noValidSPATisAvailableAtThisTime": false, + "fixedTimeOperation": false, + "standbyOperation": false, + "trafficDependentOperation": false, + "manualControlIsEnabled": false, + "off": false, + "stopTimeIsActivated": false, + "recentChangeInMAPassignedLanesIDsUsed": false, + "recentMAPmessageUpdate": false, + "failureMode": false, + "noValidMAPisAvailableAtThisTime": false, + "signalPriorityIsActive": false, + "preemptIsActive": false + } +},{ + "_id": { + "$oid": "665f3fc18c25216ef4f53f8a" + }, + "validationMessages": [ + { + "schemaPath": "#/$defs/J2735RoadRegulatorID/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].id.region", + "message": "$.payload.data.intersectionStateList.intersectionStatelist[0].id.region: null found, integer expected" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[0].state_time_speed.movementEventList[0].timing.startTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[0].state_time_speed.movementEventList[0].timing.nextTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[1].state_time_speed.movementEventList[0].timing.startTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[1].state_time_speed.movementEventList[0].timing.nextTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[2].state_time_speed.movementEventList[0].timing.startTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[2].state_time_speed.movementEventList[0].timing.nextTime", + "message": "DE_TimeMark is missing" + } + ], + "utcTimeStamp": "2024-06-04T16:22:42.269Z", + "schemaVersion": 1, + "recordGeneratedAt": { + "$date": { + "$numberLong": "1717518162273" + } + }, + "revision": 0, + "states": [ + { + "stateTimeSpeed": [ + { + "eventState": "PROTECTED_CLEARANCE", + "timing": { + "minEndTime": "2024-06-04T16:03:18Z", + "maxEndTime": "2024-06-04T16:03:18Z" + } + } + ], + "signalGroup": 2 + }, + { + "stateTimeSpeed": [ + { + "eventState": "STOP_AND_REMAIN", + "timing": { + "minEndTime": "2024-06-04T16:03:21.1Z", + "maxEndTime": "2024-06-04T16:03:21.1Z" + } + } + ], + "signalGroup": 4 + }, + { + "stateTimeSpeed": [ + { + "eventState": "PROTECTED_CLEARANCE", + "timing": { + "minEndTime": "2024-06-04T16:03:18Z", + "maxEndTime": "2024-06-04T16:03:18Z" + } + } + ], + "signalGroup": 6 + } + ], + "messageType": "SPAT", + "cti4501Conformant": false, + "intersectionId": 12109, + "odeReceivedAt": "2024-06-04T16:22:42.269Z", + "originIp": "10.11.81.12", + "status": { + "failureFlash": false, + "noValidSPATisAvailableAtThisTime": false, + "fixedTimeOperation": false, + "standbyOperation": false, + "trafficDependentOperation": false, + "manualControlIsEnabled": false, + "off": false, + "stopTimeIsActivated": false, + "recentChangeInMAPassignedLanesIDsUsed": false, + "recentMAPmessageUpdate": false, + "failureMode": false, + "noValidMAPisAvailableAtThisTime": false, + "signalPriorityIsActive": false, + "preemptIsActive": false + } +},{ + "_id": { + "$oid": "665f3fc18c25216ef4f53f8b" + }, + "validationMessages": [ + { + "schemaPath": "#/$defs/J2735RoadRegulatorID/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].id.region", + "message": "$.payload.data.intersectionStateList.intersectionStatelist[0].id.region: null found, integer expected" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[0].state_time_speed.movementEventList[0].timing.startTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[0].state_time_speed.movementEventList[0].timing.nextTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[1].state_time_speed.movementEventList[0].timing.startTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[1].state_time_speed.movementEventList[0].timing.nextTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[2].state_time_speed.movementEventList[0].timing.startTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[2].state_time_speed.movementEventList[0].timing.nextTime", + "message": "DE_TimeMark is missing" + } + ], + "utcTimeStamp": "2024-06-04T16:22:42.378Z", + "schemaVersion": 1, + "recordGeneratedAt": { + "$date": { + "$numberLong": "1717518162382" + } + }, + "revision": 0, + "states": [ + { + "stateTimeSpeed": [ + { + "eventState": "PROTECTED_CLEARANCE", + "timing": { + "minEndTime": "2024-06-04T16:03:18Z", + "maxEndTime": "2024-06-04T16:03:18Z" + } + } + ], + "signalGroup": 2 + }, + { + "stateTimeSpeed": [ + { + "eventState": "STOP_AND_REMAIN", + "timing": { + "minEndTime": "2024-06-04T16:03:21.1Z", + "maxEndTime": "2024-06-04T16:03:21.1Z" + } + } + ], + "signalGroup": 4 + }, + { + "stateTimeSpeed": [ + { + "eventState": "PROTECTED_CLEARANCE", + "timing": { + "minEndTime": "2024-06-04T16:03:18Z", + "maxEndTime": "2024-06-04T16:03:18Z" + } + } + ], + "signalGroup": 6 + } + ], + "messageType": "SPAT", + "cti4501Conformant": false, + "intersectionId": 12109, + "odeReceivedAt": "2024-06-04T16:22:42.378Z", + "originIp": "10.11.81.12", + "status": { + "failureFlash": false, + "noValidSPATisAvailableAtThisTime": false, + "fixedTimeOperation": false, + "standbyOperation": false, + "trafficDependentOperation": false, + "manualControlIsEnabled": false, + "off": false, + "stopTimeIsActivated": false, + "recentChangeInMAPassignedLanesIDsUsed": false, + "recentMAPmessageUpdate": false, + "failureMode": false, + "noValidMAPisAvailableAtThisTime": false, + "signalPriorityIsActive": false, + "preemptIsActive": false + } +},{ + "_id": { + "$oid": "665f3fc18c25216ef4f53f8c" + }, + "validationMessages": [ + { + "schemaPath": "#/$defs/J2735RoadRegulatorID/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].id.region", + "message": "$.payload.data.intersectionStateList.intersectionStatelist[0].id.region: null found, integer expected" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[0].state_time_speed.movementEventList[0].timing.startTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[0].state_time_speed.movementEventList[0].timing.nextTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[1].state_time_speed.movementEventList[0].timing.startTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[1].state_time_speed.movementEventList[0].timing.nextTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[2].state_time_speed.movementEventList[0].timing.startTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[2].state_time_speed.movementEventList[0].timing.nextTime", + "message": "DE_TimeMark is missing" + } + ], + "utcTimeStamp": "2024-06-04T16:22:42.485Z", + "schemaVersion": 1, + "recordGeneratedAt": { + "$date": { + "$numberLong": "1717518162486" + } + }, + "revision": 0, + "states": [ + { + "stateTimeSpeed": [ + { + "eventState": "PROTECTED_CLEARANCE", + "timing": { + "minEndTime": "2024-06-04T16:03:18Z", + "maxEndTime": "2024-06-04T16:03:18Z" + } + } + ], + "signalGroup": 2 + }, + { + "stateTimeSpeed": [ + { + "eventState": "STOP_AND_REMAIN", + "timing": { + "minEndTime": "2024-06-04T16:03:21.1Z", + "maxEndTime": "2024-06-04T16:03:21.1Z" + } + } + ], + "signalGroup": 4 + }, + { + "stateTimeSpeed": [ + { + "eventState": "PROTECTED_CLEARANCE", + "timing": { + "minEndTime": "2024-06-04T16:03:18Z", + "maxEndTime": "2024-06-04T16:03:18Z" + } + } + ], + "signalGroup": 6 + } + ], + "messageType": "SPAT", + "cti4501Conformant": false, + "intersectionId": 12109, + "odeReceivedAt": "2024-06-04T16:22:42.485Z", + "originIp": "10.11.81.12", + "status": { + "failureFlash": false, + "noValidSPATisAvailableAtThisTime": false, + "fixedTimeOperation": false, + "standbyOperation": false, + "trafficDependentOperation": false, + "manualControlIsEnabled": false, + "off": false, + "stopTimeIsActivated": false, + "recentChangeInMAPassignedLanesIDsUsed": false, + "recentMAPmessageUpdate": false, + "failureMode": false, + "noValidMAPisAvailableAtThisTime": false, + "signalPriorityIsActive": false, + "preemptIsActive": false + } +},{ + "_id": { + "$oid": "665f3fc18c25216ef4f53f8d" + }, + "validationMessages": [ + { + "schemaPath": "#/$defs/J2735RoadRegulatorID/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].id.region", + "message": "$.payload.data.intersectionStateList.intersectionStatelist[0].id.region: null found, integer expected" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[0].state_time_speed.movementEventList[0].timing.startTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[0].state_time_speed.movementEventList[0].timing.nextTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[1].state_time_speed.movementEventList[0].timing.startTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[1].state_time_speed.movementEventList[0].timing.nextTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[2].state_time_speed.movementEventList[0].timing.startTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[2].state_time_speed.movementEventList[0].timing.nextTime", + "message": "DE_TimeMark is missing" + } + ], + "utcTimeStamp": "2024-06-04T16:22:42.593Z", + "schemaVersion": 1, + "recordGeneratedAt": { + "$date": { + "$numberLong": "1717518162593" + } + }, + "revision": 0, + "states": [ + { + "stateTimeSpeed": [ + { + "eventState": "PROTECTED_CLEARANCE", + "timing": { + "minEndTime": "2024-06-04T16:03:18Z", + "maxEndTime": "2024-06-04T16:03:18Z" + } + } + ], + "signalGroup": 2 + }, + { + "stateTimeSpeed": [ + { + "eventState": "STOP_AND_REMAIN", + "timing": { + "minEndTime": "2024-06-04T16:03:21.1Z", + "maxEndTime": "2024-06-04T16:03:21.1Z" + } + } + ], + "signalGroup": 4 + }, + { + "stateTimeSpeed": [ + { + "eventState": "PROTECTED_CLEARANCE", + "timing": { + "minEndTime": "2024-06-04T16:03:18Z", + "maxEndTime": "2024-06-04T16:03:18Z" + } + } + ], + "signalGroup": 6 + } + ], + "messageType": "SPAT", + "cti4501Conformant": false, + "intersectionId": 12109, + "odeReceivedAt": "2024-06-04T16:22:42.593Z", + "originIp": "10.11.81.12", + "status": { + "failureFlash": false, + "noValidSPATisAvailableAtThisTime": false, + "fixedTimeOperation": false, + "standbyOperation": false, + "trafficDependentOperation": false, + "manualControlIsEnabled": false, + "off": false, + "stopTimeIsActivated": false, + "recentChangeInMAPassignedLanesIDsUsed": false, + "recentMAPmessageUpdate": false, + "failureMode": false, + "noValidMAPisAvailableAtThisTime": false, + "signalPriorityIsActive": false, + "preemptIsActive": false + } +},{ + "_id": { + "$oid": "665f3fc18c25216ef4f53f8e" + }, + "validationMessages": [ + { + "schemaPath": "#/$defs/J2735RoadRegulatorID/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].id.region", + "message": "$.payload.data.intersectionStateList.intersectionStatelist[0].id.region: null found, integer expected" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[0].state_time_speed.movementEventList[0].timing.startTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[0].state_time_speed.movementEventList[0].timing.nextTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[1].state_time_speed.movementEventList[0].timing.startTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[1].state_time_speed.movementEventList[0].timing.nextTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[2].state_time_speed.movementEventList[0].timing.startTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[2].state_time_speed.movementEventList[0].timing.nextTime", + "message": "DE_TimeMark is missing" + } + ], + "utcTimeStamp": "2024-06-04T16:22:42.7Z", + "schemaVersion": 1, + "recordGeneratedAt": { + "$date": { + "$numberLong": "1717518162715" + } + }, + "revision": 0, + "states": [ + { + "stateTimeSpeed": [ + { + "eventState": "PROTECTED_CLEARANCE", + "timing": { + "minEndTime": "2024-06-04T16:03:18Z", + "maxEndTime": "2024-06-04T16:03:18Z" + } + } + ], + "signalGroup": 2 + }, + { + "stateTimeSpeed": [ + { + "eventState": "STOP_AND_REMAIN", + "timing": { + "minEndTime": "2024-06-04T16:03:21.1Z", + "maxEndTime": "2024-06-04T16:03:21.1Z" + } + } + ], + "signalGroup": 4 + }, + { + "stateTimeSpeed": [ + { + "eventState": "PROTECTED_CLEARANCE", + "timing": { + "minEndTime": "2024-06-04T16:03:18Z", + "maxEndTime": "2024-06-04T16:03:18Z" + } + } + ], + "signalGroup": 6 + } + ], + "messageType": "SPAT", + "cti4501Conformant": false, + "intersectionId": 12109, + "odeReceivedAt": "2024-06-04T16:22:42.7Z", + "originIp": "10.11.81.12", + "status": { + "failureFlash": false, + "noValidSPATisAvailableAtThisTime": false, + "fixedTimeOperation": false, + "standbyOperation": false, + "trafficDependentOperation": false, + "manualControlIsEnabled": false, + "off": false, + "stopTimeIsActivated": false, + "recentChangeInMAPassignedLanesIDsUsed": false, + "recentMAPmessageUpdate": false, + "failureMode": false, + "noValidMAPisAvailableAtThisTime": false, + "signalPriorityIsActive": false, + "preemptIsActive": false + } +},{ + "_id": { + "$oid": "665f3fc18c25216ef4f53f8f" + }, + "validationMessages": [ + { + "schemaPath": "#/$defs/J2735RoadRegulatorID/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].id.region", + "message": "$.payload.data.intersectionStateList.intersectionStatelist[0].id.region: null found, integer expected" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[0].state_time_speed.movementEventList[0].timing.startTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[0].state_time_speed.movementEventList[0].timing.nextTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[1].state_time_speed.movementEventList[0].timing.startTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[1].state_time_speed.movementEventList[0].timing.nextTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[2].state_time_speed.movementEventList[0].timing.startTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[2].state_time_speed.movementEventList[0].timing.nextTime", + "message": "DE_TimeMark is missing" + } + ], + "utcTimeStamp": "2024-06-04T16:22:42.808Z", + "schemaVersion": 1, + "recordGeneratedAt": { + "$date": { + "$numberLong": "1717518162823" + } + }, + "revision": 0, + "states": [ + { + "stateTimeSpeed": [ + { + "eventState": "PROTECTED_CLEARANCE", + "timing": { + "minEndTime": "2024-06-04T16:03:18Z", + "maxEndTime": "2024-06-04T16:03:18Z" + } + } + ], + "signalGroup": 2 + }, + { + "stateTimeSpeed": [ + { + "eventState": "STOP_AND_REMAIN", + "timing": { + "minEndTime": "2024-06-04T16:03:21.1Z", + "maxEndTime": "2024-06-04T16:03:21.1Z" + } + } + ], + "signalGroup": 4 + }, + { + "stateTimeSpeed": [ + { + "eventState": "PROTECTED_CLEARANCE", + "timing": { + "minEndTime": "2024-06-04T16:03:18Z", + "maxEndTime": "2024-06-04T16:03:18Z" + } + } + ], + "signalGroup": 6 + } + ], + "messageType": "SPAT", + "cti4501Conformant": false, + "intersectionId": 12109, + "odeReceivedAt": "2024-06-04T16:22:42.808Z", + "originIp": "10.11.81.12", + "status": { + "failureFlash": false, + "noValidSPATisAvailableAtThisTime": false, + "fixedTimeOperation": false, + "standbyOperation": false, + "trafficDependentOperation": false, + "manualControlIsEnabled": false, + "off": false, + "stopTimeIsActivated": false, + "recentChangeInMAPassignedLanesIDsUsed": false, + "recentMAPmessageUpdate": false, + "failureMode": false, + "noValidMAPisAvailableAtThisTime": false, + "signalPriorityIsActive": false, + "preemptIsActive": false + } +},{ + "_id": { + "$oid": "665f3fc18c25216ef4f53f90" + }, + "validationMessages": [ + { + "schemaPath": "#/$defs/J2735RoadRegulatorID/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].id.region", + "message": "$.payload.data.intersectionStateList.intersectionStatelist[0].id.region: null found, integer expected" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[0].state_time_speed.movementEventList[0].timing.startTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[0].state_time_speed.movementEventList[0].timing.nextTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[1].state_time_speed.movementEventList[0].timing.startTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[1].state_time_speed.movementEventList[0].timing.nextTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[2].state_time_speed.movementEventList[0].timing.startTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[2].state_time_speed.movementEventList[0].timing.nextTime", + "message": "DE_TimeMark is missing" + } + ], + "utcTimeStamp": "2024-06-04T16:22:42.915Z", + "schemaVersion": 1, + "recordGeneratedAt": { + "$date": { + "$numberLong": "1717518162929" + } + }, + "revision": 0, + "states": [ + { + "stateTimeSpeed": [ + { + "eventState": "PROTECTED_CLEARANCE", + "timing": { + "minEndTime": "2024-06-04T16:03:18Z", + "maxEndTime": "2024-06-04T16:03:18Z" + } + } + ], + "signalGroup": 2 + }, + { + "stateTimeSpeed": [ + { + "eventState": "STOP_AND_REMAIN", + "timing": { + "minEndTime": "2024-06-04T16:03:21.1Z", + "maxEndTime": "2024-06-04T16:03:21.1Z" + } + } + ], + "signalGroup": 4 + }, + { + "stateTimeSpeed": [ + { + "eventState": "PROTECTED_CLEARANCE", + "timing": { + "minEndTime": "2024-06-04T16:03:18Z", + "maxEndTime": "2024-06-04T16:03:18Z" + } + } + ], + "signalGroup": 6 + } + ], + "messageType": "SPAT", + "cti4501Conformant": false, + "intersectionId": 12109, + "odeReceivedAt": "2024-06-04T16:22:42.915Z", + "originIp": "10.11.81.12", + "status": { + "failureFlash": false, + "noValidSPATisAvailableAtThisTime": false, + "fixedTimeOperation": false, + "standbyOperation": false, + "trafficDependentOperation": false, + "manualControlIsEnabled": false, + "off": false, + "stopTimeIsActivated": false, + "recentChangeInMAPassignedLanesIDsUsed": false, + "recentMAPmessageUpdate": false, + "failureMode": false, + "noValidMAPisAvailableAtThisTime": false, + "signalPriorityIsActive": false, + "preemptIsActive": false + } +},{ + "_id": { + "$oid": "665f3fc18c25216ef4f53f91" + }, + "validationMessages": [ + { + "schemaPath": "#/$defs/J2735RoadRegulatorID/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].id.region", + "message": "$.payload.data.intersectionStateList.intersectionStatelist[0].id.region: null found, integer expected" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[0].state_time_speed.movementEventList[0].timing.startTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[0].state_time_speed.movementEventList[0].timing.nextTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[1].state_time_speed.movementEventList[0].timing.startTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[1].state_time_speed.movementEventList[0].timing.nextTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[2].state_time_speed.movementEventList[0].timing.startTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[2].state_time_speed.movementEventList[0].timing.nextTime", + "message": "DE_TimeMark is missing" + } + ], + "utcTimeStamp": "2024-06-04T16:22:43.023Z", + "schemaVersion": 1, + "recordGeneratedAt": { + "$date": { + "$numberLong": "1717518163035" + } + }, + "revision": 0, + "states": [ + { + "stateTimeSpeed": [ + { + "eventState": "PROTECTED_CLEARANCE", + "timing": { + "minEndTime": "2024-06-04T16:03:18Z", + "maxEndTime": "2024-06-04T16:03:18Z" + } + } + ], + "signalGroup": 2 + }, + { + "stateTimeSpeed": [ + { + "eventState": "STOP_AND_REMAIN", + "timing": { + "minEndTime": "2024-06-04T16:03:21.1Z", + "maxEndTime": "2024-06-04T16:03:21.1Z" + } + } + ], + "signalGroup": 4 + }, + { + "stateTimeSpeed": [ + { + "eventState": "PROTECTED_CLEARANCE", + "timing": { + "minEndTime": "2024-06-04T16:03:18Z", + "maxEndTime": "2024-06-04T16:03:18Z" + } + } + ], + "signalGroup": 6 + } + ], + "messageType": "SPAT", + "cti4501Conformant": false, + "intersectionId": 12109, + "odeReceivedAt": "2024-06-04T16:22:43.023Z", + "originIp": "10.11.81.12", + "status": { + "failureFlash": false, + "noValidSPATisAvailableAtThisTime": false, + "fixedTimeOperation": false, + "standbyOperation": false, + "trafficDependentOperation": false, + "manualControlIsEnabled": false, + "off": false, + "stopTimeIsActivated": false, + "recentChangeInMAPassignedLanesIDsUsed": false, + "recentMAPmessageUpdate": false, + "failureMode": false, + "noValidMAPisAvailableAtThisTime": false, + "signalPriorityIsActive": false, + "preemptIsActive": false + } +},{ + "_id": { + "$oid": "665f3fc18c25216ef4f53f92" + }, + "validationMessages": [ + { + "schemaPath": "#/$defs/J2735RoadRegulatorID/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].id.region", + "message": "$.payload.data.intersectionStateList.intersectionStatelist[0].id.region: null found, integer expected" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[0].state_time_speed.movementEventList[0].timing.startTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[0].state_time_speed.movementEventList[0].timing.nextTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[1].state_time_speed.movementEventList[0].timing.startTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[1].state_time_speed.movementEventList[0].timing.nextTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[2].state_time_speed.movementEventList[0].timing.startTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[2].state_time_speed.movementEventList[0].timing.nextTime", + "message": "DE_TimeMark is missing" + } + ], + "utcTimeStamp": "2024-06-04T16:22:43.129Z", + "schemaVersion": 1, + "recordGeneratedAt": { + "$date": { + "$numberLong": "1717518163142" + } + }, + "revision": 0, + "states": [ + { + "stateTimeSpeed": [ + { + "eventState": "PROTECTED_CLEARANCE", + "timing": { + "minEndTime": "2024-06-04T16:03:18Z", + "maxEndTime": "2024-06-04T16:03:18Z" + } + } + ], + "signalGroup": 2 + }, + { + "stateTimeSpeed": [ + { + "eventState": "STOP_AND_REMAIN", + "timing": { + "minEndTime": "2024-06-04T16:03:21.1Z", + "maxEndTime": "2024-06-04T16:03:21.1Z" + } + } + ], + "signalGroup": 4 + }, + { + "stateTimeSpeed": [ + { + "eventState": "PROTECTED_CLEARANCE", + "timing": { + "minEndTime": "2024-06-04T16:03:18Z", + "maxEndTime": "2024-06-04T16:03:18Z" + } + } + ], + "signalGroup": 6 + } + ], + "messageType": "SPAT", + "cti4501Conformant": false, + "intersectionId": 12109, + "odeReceivedAt": "2024-06-04T16:22:43.129Z", + "originIp": "10.11.81.12", + "status": { + "failureFlash": false, + "noValidSPATisAvailableAtThisTime": false, + "fixedTimeOperation": false, + "standbyOperation": false, + "trafficDependentOperation": false, + "manualControlIsEnabled": false, + "off": false, + "stopTimeIsActivated": false, + "recentChangeInMAPassignedLanesIDsUsed": false, + "recentMAPmessageUpdate": false, + "failureMode": false, + "noValidMAPisAvailableAtThisTime": false, + "signalPriorityIsActive": false, + "preemptIsActive": false + } +},{ + "_id": { + "$oid": "665f3fc18c25216ef4f53f93" + }, + "validationMessages": [ + { + "schemaPath": "#/$defs/J2735RoadRegulatorID/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].id.region", + "message": "$.payload.data.intersectionStateList.intersectionStatelist[0].id.region: null found, integer expected" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[0].state_time_speed.movementEventList[0].timing.startTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[0].state_time_speed.movementEventList[0].timing.nextTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[1].state_time_speed.movementEventList[0].timing.startTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[1].state_time_speed.movementEventList[0].timing.nextTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[2].state_time_speed.movementEventList[0].timing.startTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[2].state_time_speed.movementEventList[0].timing.nextTime", + "message": "DE_TimeMark is missing" + } + ], + "utcTimeStamp": "2024-06-04T16:22:43.237Z", + "schemaVersion": 1, + "recordGeneratedAt": { + "$date": { + "$numberLong": "1717518163250" + } + }, + "revision": 0, + "states": [ + { + "stateTimeSpeed": [ + { + "eventState": "PROTECTED_CLEARANCE", + "timing": { + "minEndTime": "2024-06-04T16:03:17Z", + "maxEndTime": "2024-06-04T16:03:17Z" + } + } + ], + "signalGroup": 2 + }, + { + "stateTimeSpeed": [ + { + "eventState": "STOP_AND_REMAIN", + "timing": { + "minEndTime": "2024-06-04T16:03:20.1Z", + "maxEndTime": "2024-06-04T16:03:20.1Z" + } + } + ], + "signalGroup": 4 + }, + { + "stateTimeSpeed": [ + { + "eventState": "PROTECTED_CLEARANCE", + "timing": { + "minEndTime": "2024-06-04T16:03:17Z", + "maxEndTime": "2024-06-04T16:03:17Z" + } + } + ], + "signalGroup": 6 + } + ], + "messageType": "SPAT", + "cti4501Conformant": false, + "intersectionId": 12109, + "odeReceivedAt": "2024-06-04T16:22:43.237Z", + "originIp": "10.11.81.12", + "status": { + "failureFlash": false, + "noValidSPATisAvailableAtThisTime": false, + "fixedTimeOperation": false, + "standbyOperation": false, + "trafficDependentOperation": false, + "manualControlIsEnabled": false, + "off": false, + "stopTimeIsActivated": false, + "recentChangeInMAPassignedLanesIDsUsed": false, + "recentMAPmessageUpdate": false, + "failureMode": false, + "noValidMAPisAvailableAtThisTime": false, + "signalPriorityIsActive": false, + "preemptIsActive": false + } +},{ + "_id": { + "$oid": "665f3fc18c25216ef4f53f94" + }, + "validationMessages": [ + { + "schemaPath": "#/$defs/J2735RoadRegulatorID/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].id.region", + "message": "$.payload.data.intersectionStateList.intersectionStatelist[0].id.region: null found, integer expected" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[0].state_time_speed.movementEventList[0].timing.startTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[0].state_time_speed.movementEventList[0].timing.nextTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[1].state_time_speed.movementEventList[0].timing.startTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[1].state_time_speed.movementEventList[0].timing.nextTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[2].state_time_speed.movementEventList[0].timing.startTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[2].state_time_speed.movementEventList[0].timing.nextTime", + "message": "DE_TimeMark is missing" + } + ], + "utcTimeStamp": "2024-06-04T16:22:43.329Z", + "schemaVersion": 1, + "recordGeneratedAt": { + "$date": { + "$numberLong": "1717518163342" + } + }, + "revision": 0, + "states": [ + { + "stateTimeSpeed": [ + { + "eventState": "PROTECTED_CLEARANCE", + "timing": { + "minEndTime": "2024-06-04T16:03:17Z", + "maxEndTime": "2024-06-04T16:03:17Z" + } + } + ], + "signalGroup": 2 + }, + { + "stateTimeSpeed": [ + { + "eventState": "STOP_AND_REMAIN", + "timing": { + "minEndTime": "2024-06-04T16:03:20.1Z", + "maxEndTime": "2024-06-04T16:03:20.1Z" + } + } + ], + "signalGroup": 4 + }, + { + "stateTimeSpeed": [ + { + "eventState": "PROTECTED_CLEARANCE", + "timing": { + "minEndTime": "2024-06-04T16:03:17Z", + "maxEndTime": "2024-06-04T16:03:17Z" + } + } + ], + "signalGroup": 6 + } + ], + "messageType": "SPAT", + "cti4501Conformant": false, + "intersectionId": 12109, + "odeReceivedAt": "2024-06-04T16:22:43.329Z", + "originIp": "10.11.81.12", + "status": { + "failureFlash": false, + "noValidSPATisAvailableAtThisTime": false, + "fixedTimeOperation": false, + "standbyOperation": false, + "trafficDependentOperation": false, + "manualControlIsEnabled": false, + "off": false, + "stopTimeIsActivated": false, + "recentChangeInMAPassignedLanesIDsUsed": false, + "recentMAPmessageUpdate": false, + "failureMode": false, + "noValidMAPisAvailableAtThisTime": false, + "signalPriorityIsActive": false, + "preemptIsActive": false + } +},{ + "_id": { + "$oid": "665f3fc18c25216ef4f53f95" + }, + "validationMessages": [ + { + "schemaPath": "#/$defs/J2735RoadRegulatorID/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].id.region", + "message": "$.payload.data.intersectionStateList.intersectionStatelist[0].id.region: null found, integer expected" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[0].state_time_speed.movementEventList[0].timing.startTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[0].state_time_speed.movementEventList[0].timing.nextTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[1].state_time_speed.movementEventList[0].timing.startTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[1].state_time_speed.movementEventList[0].timing.nextTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[2].state_time_speed.movementEventList[0].timing.startTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[2].state_time_speed.movementEventList[0].timing.nextTime", + "message": "DE_TimeMark is missing" + } + ], + "utcTimeStamp": "2024-06-04T16:22:43.437Z", + "schemaVersion": 1, + "recordGeneratedAt": { + "$date": { + "$numberLong": "1717518163449" + } + }, + "revision": 0, + "states": [ + { + "stateTimeSpeed": [ + { + "eventState": "PROTECTED_CLEARANCE", + "timing": { + "minEndTime": "2024-06-04T16:03:17Z", + "maxEndTime": "2024-06-04T16:03:17Z" + } + } + ], + "signalGroup": 2 + }, + { + "stateTimeSpeed": [ + { + "eventState": "STOP_AND_REMAIN", + "timing": { + "minEndTime": "2024-06-04T16:03:20.1Z", + "maxEndTime": "2024-06-04T16:03:20.1Z" + } + } + ], + "signalGroup": 4 + }, + { + "stateTimeSpeed": [ + { + "eventState": "PROTECTED_CLEARANCE", + "timing": { + "minEndTime": "2024-06-04T16:03:17Z", + "maxEndTime": "2024-06-04T16:03:17Z" + } + } + ], + "signalGroup": 6 + } + ], + "messageType": "SPAT", + "cti4501Conformant": false, + "intersectionId": 12109, + "odeReceivedAt": "2024-06-04T16:22:43.437Z", + "originIp": "10.11.81.12", + "status": { + "failureFlash": false, + "noValidSPATisAvailableAtThisTime": false, + "fixedTimeOperation": false, + "standbyOperation": false, + "trafficDependentOperation": false, + "manualControlIsEnabled": false, + "off": false, + "stopTimeIsActivated": false, + "recentChangeInMAPassignedLanesIDsUsed": false, + "recentMAPmessageUpdate": false, + "failureMode": false, + "noValidMAPisAvailableAtThisTime": false, + "signalPriorityIsActive": false, + "preemptIsActive": false + } +},{ + "_id": { + "$oid": "665f3fc18c25216ef4f53f96" + }, + "validationMessages": [ + { + "schemaPath": "#/$defs/J2735RoadRegulatorID/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].id.region", + "message": "$.payload.data.intersectionStateList.intersectionStatelist[0].id.region: null found, integer expected" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[0].state_time_speed.movementEventList[0].timing.startTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[0].state_time_speed.movementEventList[0].timing.nextTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[1].state_time_speed.movementEventList[0].timing.startTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[1].state_time_speed.movementEventList[0].timing.nextTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[2].state_time_speed.movementEventList[0].timing.startTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[2].state_time_speed.movementEventList[0].timing.nextTime", + "message": "DE_TimeMark is missing" + } + ], + "utcTimeStamp": "2024-06-04T16:22:43.545Z", + "schemaVersion": 1, + "recordGeneratedAt": { + "$date": { + "$numberLong": "1717518163555" + } + }, + "revision": 0, + "states": [ + { + "stateTimeSpeed": [ + { + "eventState": "PROTECTED_CLEARANCE", + "timing": { + "minEndTime": "2024-06-04T16:03:17Z", + "maxEndTime": "2024-06-04T16:03:17Z" + } + } + ], + "signalGroup": 2 + }, + { + "stateTimeSpeed": [ + { + "eventState": "STOP_AND_REMAIN", + "timing": { + "minEndTime": "2024-06-04T16:03:20.1Z", + "maxEndTime": "2024-06-04T16:03:20.1Z" + } + } + ], + "signalGroup": 4 + }, + { + "stateTimeSpeed": [ + { + "eventState": "PROTECTED_CLEARANCE", + "timing": { + "minEndTime": "2024-06-04T16:03:17Z", + "maxEndTime": "2024-06-04T16:03:17Z" + } + } + ], + "signalGroup": 6 + } + ], + "messageType": "SPAT", + "cti4501Conformant": false, + "intersectionId": 12109, + "odeReceivedAt": "2024-06-04T16:22:43.545Z", + "originIp": "10.11.81.12", + "status": { + "failureFlash": false, + "noValidSPATisAvailableAtThisTime": false, + "fixedTimeOperation": false, + "standbyOperation": false, + "trafficDependentOperation": false, + "manualControlIsEnabled": false, + "off": false, + "stopTimeIsActivated": false, + "recentChangeInMAPassignedLanesIDsUsed": false, + "recentMAPmessageUpdate": false, + "failureMode": false, + "noValidMAPisAvailableAtThisTime": false, + "signalPriorityIsActive": false, + "preemptIsActive": false + } +},{ + "_id": { + "$oid": "665f3fc18c25216ef4f53f97" + }, + "validationMessages": [ + { + "schemaPath": "#/$defs/J2735RoadRegulatorID/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].id.region", + "message": "$.payload.data.intersectionStateList.intersectionStatelist[0].id.region: null found, integer expected" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[0].state_time_speed.movementEventList[0].timing.startTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[0].state_time_speed.movementEventList[0].timing.nextTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[1].state_time_speed.movementEventList[0].timing.startTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[1].state_time_speed.movementEventList[0].timing.nextTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[2].state_time_speed.movementEventList[0].timing.startTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[2].state_time_speed.movementEventList[0].timing.nextTime", + "message": "DE_TimeMark is missing" + } + ], + "utcTimeStamp": "2024-06-04T16:22:43.653Z", + "schemaVersion": 1, + "recordGeneratedAt": { + "$date": { + "$numberLong": "1717518163660" + } + }, + "revision": 0, + "states": [ + { + "stateTimeSpeed": [ + { + "eventState": "PROTECTED_CLEARANCE", + "timing": { + "minEndTime": "2024-06-04T16:03:17Z", + "maxEndTime": "2024-06-04T16:03:17Z" + } + } + ], + "signalGroup": 2 + }, + { + "stateTimeSpeed": [ + { + "eventState": "STOP_AND_REMAIN", + "timing": { + "minEndTime": "2024-06-04T16:03:20.1Z", + "maxEndTime": "2024-06-04T16:03:20.1Z" + } + } + ], + "signalGroup": 4 + }, + { + "stateTimeSpeed": [ + { + "eventState": "PROTECTED_CLEARANCE", + "timing": { + "minEndTime": "2024-06-04T16:03:17Z", + "maxEndTime": "2024-06-04T16:03:17Z" + } + } + ], + "signalGroup": 6 + } + ], + "messageType": "SPAT", + "cti4501Conformant": false, + "intersectionId": 12109, + "odeReceivedAt": "2024-06-04T16:22:43.653Z", + "originIp": "10.11.81.12", + "status": { + "failureFlash": false, + "noValidSPATisAvailableAtThisTime": false, + "fixedTimeOperation": false, + "standbyOperation": false, + "trafficDependentOperation": false, + "manualControlIsEnabled": false, + "off": false, + "stopTimeIsActivated": false, + "recentChangeInMAPassignedLanesIDsUsed": false, + "recentMAPmessageUpdate": false, + "failureMode": false, + "noValidMAPisAvailableAtThisTime": false, + "signalPriorityIsActive": false, + "preemptIsActive": false + } +},{ + "_id": { + "$oid": "665f3fc18c25216ef4f53f98" + }, + "validationMessages": [ + { + "schemaPath": "#/$defs/J2735RoadRegulatorID/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].id.region", + "message": "$.payload.data.intersectionStateList.intersectionStatelist[0].id.region: null found, integer expected" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[0].state_time_speed.movementEventList[0].timing.startTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[0].state_time_speed.movementEventList[0].timing.nextTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[1].state_time_speed.movementEventList[0].timing.startTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[1].state_time_speed.movementEventList[0].timing.nextTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[2].state_time_speed.movementEventList[0].timing.startTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[2].state_time_speed.movementEventList[0].timing.nextTime", + "message": "DE_TimeMark is missing" + } + ], + "utcTimeStamp": "2024-06-04T16:22:43.759Z", + "schemaVersion": 1, + "recordGeneratedAt": { + "$date": { + "$numberLong": "1717518163767" + } + }, + "revision": 0, + "states": [ + { + "stateTimeSpeed": [ + { + "eventState": "PROTECTED_CLEARANCE", + "timing": { + "minEndTime": "2024-06-04T16:03:17Z", + "maxEndTime": "2024-06-04T16:03:17Z" + } + } + ], + "signalGroup": 2 + }, + { + "stateTimeSpeed": [ + { + "eventState": "STOP_AND_REMAIN", + "timing": { + "minEndTime": "2024-06-04T16:03:20.1Z", + "maxEndTime": "2024-06-04T16:03:20.1Z" + } + } + ], + "signalGroup": 4 + }, + { + "stateTimeSpeed": [ + { + "eventState": "PROTECTED_CLEARANCE", + "timing": { + "minEndTime": "2024-06-04T16:03:17Z", + "maxEndTime": "2024-06-04T16:03:17Z" + } + } + ], + "signalGroup": 6 + } + ], + "messageType": "SPAT", + "cti4501Conformant": false, + "intersectionId": 12109, + "odeReceivedAt": "2024-06-04T16:22:43.759Z", + "originIp": "10.11.81.12", + "status": { + "failureFlash": false, + "noValidSPATisAvailableAtThisTime": false, + "fixedTimeOperation": false, + "standbyOperation": false, + "trafficDependentOperation": false, + "manualControlIsEnabled": false, + "off": false, + "stopTimeIsActivated": false, + "recentChangeInMAPassignedLanesIDsUsed": false, + "recentMAPmessageUpdate": false, + "failureMode": false, + "noValidMAPisAvailableAtThisTime": false, + "signalPriorityIsActive": false, + "preemptIsActive": false + } +},{ + "_id": { + "$oid": "665f3fc18c25216ef4f53f99" + }, + "validationMessages": [ + { + "schemaPath": "#/$defs/J2735RoadRegulatorID/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].id.region", + "message": "$.payload.data.intersectionStateList.intersectionStatelist[0].id.region: null found, integer expected" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[0].state_time_speed.movementEventList[0].timing.startTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[0].state_time_speed.movementEventList[0].timing.nextTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[1].state_time_speed.movementEventList[0].timing.startTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[1].state_time_speed.movementEventList[0].timing.nextTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[2].state_time_speed.movementEventList[0].timing.startTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[2].state_time_speed.movementEventList[0].timing.nextTime", + "message": "DE_TimeMark is missing" + } + ], + "utcTimeStamp": "2024-06-04T16:22:43.866Z", + "schemaVersion": 1, + "recordGeneratedAt": { + "$date": { + "$numberLong": "1717518163873" + } + }, + "revision": 0, + "states": [ + { + "stateTimeSpeed": [ + { + "eventState": "PROTECTED_CLEARANCE", + "timing": { + "minEndTime": "2024-06-04T16:03:17Z", + "maxEndTime": "2024-06-04T16:03:17Z" + } + } + ], + "signalGroup": 2 + }, + { + "stateTimeSpeed": [ + { + "eventState": "STOP_AND_REMAIN", + "timing": { + "minEndTime": "2024-06-04T16:03:20.1Z", + "maxEndTime": "2024-06-04T16:03:20.1Z" + } + } + ], + "signalGroup": 4 + }, + { + "stateTimeSpeed": [ + { + "eventState": "PROTECTED_CLEARANCE", + "timing": { + "minEndTime": "2024-06-04T16:03:17Z", + "maxEndTime": "2024-06-04T16:03:17Z" + } + } + ], + "signalGroup": 6 + } + ], + "messageType": "SPAT", + "cti4501Conformant": false, + "intersectionId": 12109, + "odeReceivedAt": "2024-06-04T16:22:43.866Z", + "originIp": "10.11.81.12", + "status": { + "failureFlash": false, + "noValidSPATisAvailableAtThisTime": false, + "fixedTimeOperation": false, + "standbyOperation": false, + "trafficDependentOperation": false, + "manualControlIsEnabled": false, + "off": false, + "stopTimeIsActivated": false, + "recentChangeInMAPassignedLanesIDsUsed": false, + "recentMAPmessageUpdate": false, + "failureMode": false, + "noValidMAPisAvailableAtThisTime": false, + "signalPriorityIsActive": false, + "preemptIsActive": false + } +},{ + "_id": { + "$oid": "665f3fc18c25216ef4f53f9a" + }, + "validationMessages": [ + { + "schemaPath": "#/$defs/J2735RoadRegulatorID/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].id.region", + "message": "$.payload.data.intersectionStateList.intersectionStatelist[0].id.region: null found, integer expected" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[0].state_time_speed.movementEventList[0].timing.startTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[0].state_time_speed.movementEventList[0].timing.nextTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[1].state_time_speed.movementEventList[0].timing.startTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[1].state_time_speed.movementEventList[0].timing.nextTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[2].state_time_speed.movementEventList[0].timing.startTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[2].state_time_speed.movementEventList[0].timing.nextTime", + "message": "DE_TimeMark is missing" + } + ], + "utcTimeStamp": "2024-06-04T16:22:43.974Z", + "schemaVersion": 1, + "recordGeneratedAt": { + "$date": { + "$numberLong": "1717518163980" + } + }, + "revision": 0, + "states": [ + { + "stateTimeSpeed": [ + { + "eventState": "PROTECTED_CLEARANCE", + "timing": { + "minEndTime": "2024-06-04T16:03:17Z", + "maxEndTime": "2024-06-04T16:03:17Z" + } + } + ], + "signalGroup": 2 + }, + { + "stateTimeSpeed": [ + { + "eventState": "STOP_AND_REMAIN", + "timing": { + "minEndTime": "2024-06-04T16:03:20.1Z", + "maxEndTime": "2024-06-04T16:03:20.1Z" + } + } + ], + "signalGroup": 4 + }, + { + "stateTimeSpeed": [ + { + "eventState": "PROTECTED_CLEARANCE", + "timing": { + "minEndTime": "2024-06-04T16:03:17Z", + "maxEndTime": "2024-06-04T16:03:17Z" + } + } + ], + "signalGroup": 6 + } + ], + "messageType": "SPAT", + "cti4501Conformant": false, + "intersectionId": 12109, + "odeReceivedAt": "2024-06-04T16:22:43.974Z", + "originIp": "10.11.81.12", + "status": { + "failureFlash": false, + "noValidSPATisAvailableAtThisTime": false, + "fixedTimeOperation": false, + "standbyOperation": false, + "trafficDependentOperation": false, + "manualControlIsEnabled": false, + "off": false, + "stopTimeIsActivated": false, + "recentChangeInMAPassignedLanesIDsUsed": false, + "recentMAPmessageUpdate": false, + "failureMode": false, + "noValidMAPisAvailableAtThisTime": false, + "signalPriorityIsActive": false, + "preemptIsActive": false + } +},{ + "_id": { + "$oid": "665f3fc18c25216ef4f53f9b" + }, + "validationMessages": [ + { + "schemaPath": "#/$defs/J2735RoadRegulatorID/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].id.region", + "message": "$.payload.data.intersectionStateList.intersectionStatelist[0].id.region: null found, integer expected" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[0].state_time_speed.movementEventList[0].timing.startTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[0].state_time_speed.movementEventList[0].timing.nextTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[1].state_time_speed.movementEventList[0].timing.startTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[1].state_time_speed.movementEventList[0].timing.nextTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[2].state_time_speed.movementEventList[0].timing.startTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[2].state_time_speed.movementEventList[0].timing.nextTime", + "message": "DE_TimeMark is missing" + } + ], + "utcTimeStamp": "2024-06-04T16:22:44.081Z", + "schemaVersion": 1, + "recordGeneratedAt": { + "$date": { + "$numberLong": "1717518164086" + } + }, + "revision": 0, + "states": [ + { + "stateTimeSpeed": [ + { + "eventState": "PROTECTED_CLEARANCE", + "timing": { + "minEndTime": "2024-06-04T16:03:17Z", + "maxEndTime": "2024-06-04T16:03:17Z" + } + } + ], + "signalGroup": 2 + }, + { + "stateTimeSpeed": [ + { + "eventState": "STOP_AND_REMAIN", + "timing": { + "minEndTime": "2024-06-04T16:03:20.1Z", + "maxEndTime": "2024-06-04T16:03:20.1Z" + } + } + ], + "signalGroup": 4 + }, + { + "stateTimeSpeed": [ + { + "eventState": "PROTECTED_CLEARANCE", + "timing": { + "minEndTime": "2024-06-04T16:03:17Z", + "maxEndTime": "2024-06-04T16:03:17Z" + } + } + ], + "signalGroup": 6 + } + ], + "messageType": "SPAT", + "cti4501Conformant": false, + "intersectionId": 12109, + "odeReceivedAt": "2024-06-04T16:22:44.081Z", + "originIp": "10.11.81.12", + "status": { + "failureFlash": false, + "noValidSPATisAvailableAtThisTime": false, + "fixedTimeOperation": false, + "standbyOperation": false, + "trafficDependentOperation": false, + "manualControlIsEnabled": false, + "off": false, + "stopTimeIsActivated": false, + "recentChangeInMAPassignedLanesIDsUsed": false, + "recentMAPmessageUpdate": false, + "failureMode": false, + "noValidMAPisAvailableAtThisTime": false, + "signalPriorityIsActive": false, + "preemptIsActive": false + } +},{ + "_id": { + "$oid": "665f3fc18c25216ef4f53f9c" + }, + "validationMessages": [ + { + "schemaPath": "#/$defs/J2735RoadRegulatorID/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].id.region", + "message": "$.payload.data.intersectionStateList.intersectionStatelist[0].id.region: null found, integer expected" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[0].state_time_speed.movementEventList[0].timing.startTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[0].state_time_speed.movementEventList[0].timing.nextTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[1].state_time_speed.movementEventList[0].timing.startTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[1].state_time_speed.movementEventList[0].timing.nextTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[2].state_time_speed.movementEventList[0].timing.startTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[2].state_time_speed.movementEventList[0].timing.nextTime", + "message": "DE_TimeMark is missing" + } + ], + "utcTimeStamp": "2024-06-04T16:22:44.188Z", + "schemaVersion": 1, + "recordGeneratedAt": { + "$date": { + "$numberLong": "1717518164192" + } + }, + "revision": 0, + "states": [ + { + "stateTimeSpeed": [ + { + "eventState": "PROTECTED_CLEARANCE", + "timing": { + "minEndTime": "2024-06-04T16:03:17Z", + "maxEndTime": "2024-06-04T16:03:17Z" + } + } + ], + "signalGroup": 2 + }, + { + "stateTimeSpeed": [ + { + "eventState": "STOP_AND_REMAIN", + "timing": { + "minEndTime": "2024-06-04T16:03:20.1Z", + "maxEndTime": "2024-06-04T16:03:20.1Z" + } + } + ], + "signalGroup": 4 + }, + { + "stateTimeSpeed": [ + { + "eventState": "PROTECTED_CLEARANCE", + "timing": { + "minEndTime": "2024-06-04T16:03:17Z", + "maxEndTime": "2024-06-04T16:03:17Z" + } + } + ], + "signalGroup": 6 + } + ], + "messageType": "SPAT", + "cti4501Conformant": false, + "intersectionId": 12109, + "odeReceivedAt": "2024-06-04T16:22:44.188Z", + "originIp": "10.11.81.12", + "status": { + "failureFlash": false, + "noValidSPATisAvailableAtThisTime": false, + "fixedTimeOperation": false, + "standbyOperation": false, + "trafficDependentOperation": false, + "manualControlIsEnabled": false, + "off": false, + "stopTimeIsActivated": false, + "recentChangeInMAPassignedLanesIDsUsed": false, + "recentMAPmessageUpdate": false, + "failureMode": false, + "noValidMAPisAvailableAtThisTime": false, + "signalPriorityIsActive": false, + "preemptIsActive": false + } +},{ + "_id": { + "$oid": "665f3fc18c25216ef4f53f9d" + }, + "validationMessages": [ + { + "schemaPath": "#/$defs/J2735RoadRegulatorID/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].id.region", + "message": "$.payload.data.intersectionStateList.intersectionStatelist[0].id.region: null found, integer expected" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[0].state_time_speed.movementEventList[0].timing.startTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[0].state_time_speed.movementEventList[0].timing.nextTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[1].state_time_speed.movementEventList[0].timing.startTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[1].state_time_speed.movementEventList[0].timing.nextTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[2].state_time_speed.movementEventList[0].timing.startTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[2].state_time_speed.movementEventList[0].timing.nextTime", + "message": "DE_TimeMark is missing" + } + ], + "utcTimeStamp": "2024-06-04T16:22:44.295Z", + "schemaVersion": 1, + "recordGeneratedAt": { + "$date": { + "$numberLong": "1717518164299" + } + }, + "revision": 0, + "states": [ + { + "stateTimeSpeed": [ + { + "eventState": "PROTECTED_CLEARANCE", + "timing": { + "minEndTime": "2024-06-04T16:03:19Z", + "maxEndTime": "2024-06-04T16:03:19Z" + } + } + ], + "signalGroup": 2 + }, + { + "stateTimeSpeed": [ + { + "eventState": "STOP_AND_REMAIN", + "timing": { + "minEndTime": "2024-06-04T16:03:22.1Z", + "maxEndTime": "2024-06-04T16:03:22.1Z" + } + } + ], + "signalGroup": 4 + }, + { + "stateTimeSpeed": [ + { + "eventState": "PROTECTED_CLEARANCE", + "timing": { + "minEndTime": "2024-06-04T16:03:19Z", + "maxEndTime": "2024-06-04T16:03:19Z" + } + } + ], + "signalGroup": 6 + } + ], + "messageType": "SPAT", + "cti4501Conformant": false, + "intersectionId": 12109, + "odeReceivedAt": "2024-06-04T16:22:44.295Z", + "originIp": "10.11.81.12", + "status": { + "failureFlash": false, + "noValidSPATisAvailableAtThisTime": false, + "fixedTimeOperation": false, + "standbyOperation": false, + "trafficDependentOperation": false, + "manualControlIsEnabled": false, + "off": false, + "stopTimeIsActivated": false, + "recentChangeInMAPassignedLanesIDsUsed": false, + "recentMAPmessageUpdate": false, + "failureMode": false, + "noValidMAPisAvailableAtThisTime": false, + "signalPriorityIsActive": false, + "preemptIsActive": false + } +},{ + "_id": { + "$oid": "665f3fc18c25216ef4f53f9e" + }, + "validationMessages": [ + { + "schemaPath": "#/$defs/J2735RoadRegulatorID/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].id.region", + "message": "$.payload.data.intersectionStateList.intersectionStatelist[0].id.region: null found, integer expected" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[0].state_time_speed.movementEventList[0].timing.startTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[0].state_time_speed.movementEventList[0].timing.nextTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[1].state_time_speed.movementEventList[0].timing.startTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[1].state_time_speed.movementEventList[0].timing.nextTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[2].state_time_speed.movementEventList[0].timing.startTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[2].state_time_speed.movementEventList[0].timing.nextTime", + "message": "DE_TimeMark is missing" + } + ], + "utcTimeStamp": "2024-06-04T16:22:44.401Z", + "schemaVersion": 1, + "recordGeneratedAt": { + "$date": { + "$numberLong": "1717518164405" + } + }, + "revision": 0, + "states": [ + { + "stateTimeSpeed": [ + { + "eventState": "PROTECTED_CLEARANCE", + "timing": { + "minEndTime": "2024-06-04T16:03:19Z", + "maxEndTime": "2024-06-04T16:03:19Z" + } + } + ], + "signalGroup": 2 + }, + { + "stateTimeSpeed": [ + { + "eventState": "STOP_AND_REMAIN", + "timing": { + "minEndTime": "2024-06-04T16:03:22.1Z", + "maxEndTime": "2024-06-04T16:03:22.1Z" + } + } + ], + "signalGroup": 4 + }, + { + "stateTimeSpeed": [ + { + "eventState": "PROTECTED_CLEARANCE", + "timing": { + "minEndTime": "2024-06-04T16:03:19Z", + "maxEndTime": "2024-06-04T16:03:19Z" + } + } + ], + "signalGroup": 6 + } + ], + "messageType": "SPAT", + "cti4501Conformant": false, + "intersectionId": 12109, + "odeReceivedAt": "2024-06-04T16:22:44.401Z", + "originIp": "10.11.81.12", + "status": { + "failureFlash": false, + "noValidSPATisAvailableAtThisTime": false, + "fixedTimeOperation": false, + "standbyOperation": false, + "trafficDependentOperation": false, + "manualControlIsEnabled": false, + "off": false, + "stopTimeIsActivated": false, + "recentChangeInMAPassignedLanesIDsUsed": false, + "recentMAPmessageUpdate": false, + "failureMode": false, + "noValidMAPisAvailableAtThisTime": false, + "signalPriorityIsActive": false, + "preemptIsActive": false + } +},{ + "_id": { + "$oid": "665f3fc18c25216ef4f53f9f" + }, + "validationMessages": [ + { + "schemaPath": "#/$defs/J2735RoadRegulatorID/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].id.region", + "message": "$.payload.data.intersectionStateList.intersectionStatelist[0].id.region: null found, integer expected" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[0].state_time_speed.movementEventList[0].timing.startTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[0].state_time_speed.movementEventList[0].timing.nextTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[1].state_time_speed.movementEventList[0].timing.startTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[1].state_time_speed.movementEventList[0].timing.nextTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[2].state_time_speed.movementEventList[0].timing.startTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[2].state_time_speed.movementEventList[0].timing.nextTime", + "message": "DE_TimeMark is missing" + } + ], + "utcTimeStamp": "2024-06-04T16:22:44.509Z", + "schemaVersion": 1, + "recordGeneratedAt": { + "$date": { + "$numberLong": "1717518164512" + } + }, + "revision": 0, + "states": [ + { + "stateTimeSpeed": [ + { + "eventState": "PROTECTED_CLEARANCE", + "timing": { + "minEndTime": "2024-06-04T16:03:19Z", + "maxEndTime": "2024-06-04T16:03:19Z" + } + } + ], + "signalGroup": 2 + }, + { + "stateTimeSpeed": [ + { + "eventState": "STOP_AND_REMAIN", + "timing": { + "minEndTime": "2024-06-04T16:03:22.1Z", + "maxEndTime": "2024-06-04T16:03:22.1Z" + } + } + ], + "signalGroup": 4 + }, + { + "stateTimeSpeed": [ + { + "eventState": "PROTECTED_CLEARANCE", + "timing": { + "minEndTime": "2024-06-04T16:03:19Z", + "maxEndTime": "2024-06-04T16:03:19Z" + } + } + ], + "signalGroup": 6 + } + ], + "messageType": "SPAT", + "cti4501Conformant": false, + "intersectionId": 12109, + "odeReceivedAt": "2024-06-04T16:22:44.509Z", + "originIp": "10.11.81.12", + "status": { + "failureFlash": false, + "noValidSPATisAvailableAtThisTime": false, + "fixedTimeOperation": false, + "standbyOperation": false, + "trafficDependentOperation": false, + "manualControlIsEnabled": false, + "off": false, + "stopTimeIsActivated": false, + "recentChangeInMAPassignedLanesIDsUsed": false, + "recentMAPmessageUpdate": false, + "failureMode": false, + "noValidMAPisAvailableAtThisTime": false, + "signalPriorityIsActive": false, + "preemptIsActive": false + } +},{ + "_id": { + "$oid": "665f3fc18c25216ef4f53fa0" + }, + "validationMessages": [ + { + "schemaPath": "#/$defs/J2735RoadRegulatorID/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].id.region", + "message": "$.payload.data.intersectionStateList.intersectionStatelist[0].id.region: null found, integer expected" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[0].state_time_speed.movementEventList[0].timing.startTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[0].state_time_speed.movementEventList[0].timing.nextTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[1].state_time_speed.movementEventList[0].timing.startTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[1].state_time_speed.movementEventList[0].timing.nextTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[2].state_time_speed.movementEventList[0].timing.startTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[2].state_time_speed.movementEventList[0].timing.nextTime", + "message": "DE_TimeMark is missing" + } + ], + "utcTimeStamp": "2024-06-04T16:22:44.615Z", + "schemaVersion": 1, + "recordGeneratedAt": { + "$date": { + "$numberLong": "1717518164619" + } + }, + "revision": 0, + "states": [ + { + "stateTimeSpeed": [ + { + "eventState": "PROTECTED_CLEARANCE", + "timing": { + "minEndTime": "2024-06-04T16:03:19Z", + "maxEndTime": "2024-06-04T16:03:19Z" + } + } + ], + "signalGroup": 2 + }, + { + "stateTimeSpeed": [ + { + "eventState": "STOP_AND_REMAIN", + "timing": { + "minEndTime": "2024-06-04T16:03:22.1Z", + "maxEndTime": "2024-06-04T16:03:22.1Z" + } + } + ], + "signalGroup": 4 + }, + { + "stateTimeSpeed": [ + { + "eventState": "PROTECTED_CLEARANCE", + "timing": { + "minEndTime": "2024-06-04T16:03:19Z", + "maxEndTime": "2024-06-04T16:03:19Z" + } + } + ], + "signalGroup": 6 + } + ], + "messageType": "SPAT", + "cti4501Conformant": false, + "intersectionId": 12109, + "odeReceivedAt": "2024-06-04T16:22:44.615Z", + "originIp": "10.11.81.12", + "status": { + "failureFlash": false, + "noValidSPATisAvailableAtThisTime": false, + "fixedTimeOperation": false, + "standbyOperation": false, + "trafficDependentOperation": false, + "manualControlIsEnabled": false, + "off": false, + "stopTimeIsActivated": false, + "recentChangeInMAPassignedLanesIDsUsed": false, + "recentMAPmessageUpdate": false, + "failureMode": false, + "noValidMAPisAvailableAtThisTime": false, + "signalPriorityIsActive": false, + "preemptIsActive": false + } +},{ + "_id": { + "$oid": "665f3fc18c25216ef4f53fa1" + }, + "validationMessages": [ + { + "schemaPath": "#/$defs/J2735RoadRegulatorID/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].id.region", + "message": "$.payload.data.intersectionStateList.intersectionStatelist[0].id.region: null found, integer expected" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[0].state_time_speed.movementEventList[0].timing.startTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[0].state_time_speed.movementEventList[0].timing.nextTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[1].state_time_speed.movementEventList[0].timing.startTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[1].state_time_speed.movementEventList[0].timing.nextTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[2].state_time_speed.movementEventList[0].timing.startTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[2].state_time_speed.movementEventList[0].timing.nextTime", + "message": "DE_TimeMark is missing" + } + ], + "utcTimeStamp": "2024-06-04T16:22:44.723Z", + "schemaVersion": 1, + "recordGeneratedAt": { + "$date": { + "$numberLong": "1717518164727" + } + }, + "revision": 0, + "states": [ + { + "stateTimeSpeed": [ + { + "eventState": "PROTECTED_CLEARANCE", + "timing": { + "minEndTime": "2024-06-04T16:03:19Z", + "maxEndTime": "2024-06-04T16:03:19Z" + } + } + ], + "signalGroup": 2 + }, + { + "stateTimeSpeed": [ + { + "eventState": "STOP_AND_REMAIN", + "timing": { + "minEndTime": "2024-06-04T16:03:22.1Z", + "maxEndTime": "2024-06-04T16:03:22.1Z" + } + } + ], + "signalGroup": 4 + }, + { + "stateTimeSpeed": [ + { + "eventState": "PROTECTED_CLEARANCE", + "timing": { + "minEndTime": "2024-06-04T16:03:19Z", + "maxEndTime": "2024-06-04T16:03:19Z" + } + } + ], + "signalGroup": 6 + } + ], + "messageType": "SPAT", + "cti4501Conformant": false, + "intersectionId": 12109, + "odeReceivedAt": "2024-06-04T16:22:44.723Z", + "originIp": "10.11.81.12", + "status": { + "failureFlash": false, + "noValidSPATisAvailableAtThisTime": false, + "fixedTimeOperation": false, + "standbyOperation": false, + "trafficDependentOperation": false, + "manualControlIsEnabled": false, + "off": false, + "stopTimeIsActivated": false, + "recentChangeInMAPassignedLanesIDsUsed": false, + "recentMAPmessageUpdate": false, + "failureMode": false, + "noValidMAPisAvailableAtThisTime": false, + "signalPriorityIsActive": false, + "preemptIsActive": false + } +},{ + "_id": { + "$oid": "665f3fc18c25216ef4f53fa2" + }, + "validationMessages": [ + { + "schemaPath": "#/$defs/J2735RoadRegulatorID/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].id.region", + "message": "$.payload.data.intersectionStateList.intersectionStatelist[0].id.region: null found, integer expected" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[0].state_time_speed.movementEventList[0].timing.startTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[0].state_time_speed.movementEventList[0].timing.nextTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[1].state_time_speed.movementEventList[0].timing.startTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[1].state_time_speed.movementEventList[0].timing.nextTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[2].state_time_speed.movementEventList[0].timing.startTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[2].state_time_speed.movementEventList[0].timing.nextTime", + "message": "DE_TimeMark is missing" + } + ], + "utcTimeStamp": "2024-06-04T16:22:44.83Z", + "schemaVersion": 1, + "recordGeneratedAt": { + "$date": { + "$numberLong": "1717518164833" + } + }, + "revision": 0, + "states": [ + { + "stateTimeSpeed": [ + { + "eventState": "PROTECTED_CLEARANCE", + "timing": { + "minEndTime": "2024-06-04T16:03:19Z", + "maxEndTime": "2024-06-04T16:03:19Z" + } + } + ], + "signalGroup": 2 + }, + { + "stateTimeSpeed": [ + { + "eventState": "STOP_AND_REMAIN", + "timing": { + "minEndTime": "2024-06-04T16:03:22.1Z", + "maxEndTime": "2024-06-04T16:03:22.1Z" + } + } + ], + "signalGroup": 4 + }, + { + "stateTimeSpeed": [ + { + "eventState": "PROTECTED_CLEARANCE", + "timing": { + "minEndTime": "2024-06-04T16:03:19Z", + "maxEndTime": "2024-06-04T16:03:19Z" + } + } + ], + "signalGroup": 6 + } + ], + "messageType": "SPAT", + "cti4501Conformant": false, + "intersectionId": 12109, + "odeReceivedAt": "2024-06-04T16:22:44.83Z", + "originIp": "10.11.81.12", + "status": { + "failureFlash": false, + "noValidSPATisAvailableAtThisTime": false, + "fixedTimeOperation": false, + "standbyOperation": false, + "trafficDependentOperation": false, + "manualControlIsEnabled": false, + "off": false, + "stopTimeIsActivated": false, + "recentChangeInMAPassignedLanesIDsUsed": false, + "recentMAPmessageUpdate": false, + "failureMode": false, + "noValidMAPisAvailableAtThisTime": false, + "signalPriorityIsActive": false, + "preemptIsActive": false + } +},{ + "_id": { + "$oid": "665f3fc18c25216ef4f53fa3" + }, + "validationMessages": [ + { + "schemaPath": "#/$defs/J2735RoadRegulatorID/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].id.region", + "message": "$.payload.data.intersectionStateList.intersectionStatelist[0].id.region: null found, integer expected" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[0].state_time_speed.movementEventList[0].timing.startTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[0].state_time_speed.movementEventList[0].timing.nextTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[1].state_time_speed.movementEventList[0].timing.startTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[1].state_time_speed.movementEventList[0].timing.nextTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[2].state_time_speed.movementEventList[0].timing.startTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[2].state_time_speed.movementEventList[0].timing.nextTime", + "message": "DE_TimeMark is missing" + } + ], + "utcTimeStamp": "2024-06-04T16:22:44.936Z", + "schemaVersion": 1, + "recordGeneratedAt": { + "$date": { + "$numberLong": "1717518164938" + } + }, + "revision": 0, + "states": [ + { + "stateTimeSpeed": [ + { + "eventState": "PROTECTED_CLEARANCE", + "timing": { + "minEndTime": "2024-06-04T16:03:19Z", + "maxEndTime": "2024-06-04T16:03:19Z" + } + } + ], + "signalGroup": 2 + }, + { + "stateTimeSpeed": [ + { + "eventState": "STOP_AND_REMAIN", + "timing": { + "minEndTime": "2024-06-04T16:03:22.1Z", + "maxEndTime": "2024-06-04T16:03:22.1Z" + } + } + ], + "signalGroup": 4 + }, + { + "stateTimeSpeed": [ + { + "eventState": "PROTECTED_CLEARANCE", + "timing": { + "minEndTime": "2024-06-04T16:03:19Z", + "maxEndTime": "2024-06-04T16:03:19Z" + } + } + ], + "signalGroup": 6 + } + ], + "messageType": "SPAT", + "cti4501Conformant": false, + "intersectionId": 12109, + "odeReceivedAt": "2024-06-04T16:22:44.936Z", + "originIp": "10.11.81.12", + "status": { + "failureFlash": false, + "noValidSPATisAvailableAtThisTime": false, + "fixedTimeOperation": false, + "standbyOperation": false, + "trafficDependentOperation": false, + "manualControlIsEnabled": false, + "off": false, + "stopTimeIsActivated": false, + "recentChangeInMAPassignedLanesIDsUsed": false, + "recentMAPmessageUpdate": false, + "failureMode": false, + "noValidMAPisAvailableAtThisTime": false, + "signalPriorityIsActive": false, + "preemptIsActive": false + } +},{ + "_id": { + "$oid": "665f3fc18c25216ef4f53fa4" + }, + "validationMessages": [ + { + "schemaPath": "#/$defs/J2735RoadRegulatorID/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].id.region", + "message": "$.payload.data.intersectionStateList.intersectionStatelist[0].id.region: null found, integer expected" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[0].state_time_speed.movementEventList[0].timing.startTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[0].state_time_speed.movementEventList[0].timing.nextTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[1].state_time_speed.movementEventList[0].timing.startTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[1].state_time_speed.movementEventList[0].timing.nextTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[2].state_time_speed.movementEventList[0].timing.startTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[2].state_time_speed.movementEventList[0].timing.nextTime", + "message": "DE_TimeMark is missing" + } + ], + "utcTimeStamp": "2024-06-04T16:22:45.045Z", + "schemaVersion": 1, + "recordGeneratedAt": { + "$date": { + "$numberLong": "1717518165061" + } + }, + "revision": 0, + "states": [ + { + "stateTimeSpeed": [ + { + "eventState": "PROTECTED_CLEARANCE", + "timing": { + "minEndTime": "2024-06-04T16:03:19Z", + "maxEndTime": "2024-06-04T16:03:19Z" + } + } + ], + "signalGroup": 2 + }, + { + "stateTimeSpeed": [ + { + "eventState": "STOP_AND_REMAIN", + "timing": { + "minEndTime": "2024-06-04T16:03:22.1Z", + "maxEndTime": "2024-06-04T16:03:22.1Z" + } + } + ], + "signalGroup": 4 + }, + { + "stateTimeSpeed": [ + { + "eventState": "PROTECTED_CLEARANCE", + "timing": { + "minEndTime": "2024-06-04T16:03:19Z", + "maxEndTime": "2024-06-04T16:03:19Z" + } + } + ], + "signalGroup": 6 + } + ], + "messageType": "SPAT", + "cti4501Conformant": false, + "intersectionId": 12109, + "odeReceivedAt": "2024-06-04T16:22:45.045Z", + "originIp": "10.11.81.12", + "status": { + "failureFlash": false, + "noValidSPATisAvailableAtThisTime": false, + "fixedTimeOperation": false, + "standbyOperation": false, + "trafficDependentOperation": false, + "manualControlIsEnabled": false, + "off": false, + "stopTimeIsActivated": false, + "recentChangeInMAPassignedLanesIDsUsed": false, + "recentMAPmessageUpdate": false, + "failureMode": false, + "noValidMAPisAvailableAtThisTime": false, + "signalPriorityIsActive": false, + "preemptIsActive": false + } +},{ + "_id": { + "$oid": "665f3fc18c25216ef4f53fa5" + }, + "validationMessages": [ + { + "schemaPath": "#/$defs/J2735RoadRegulatorID/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].id.region", + "message": "$.payload.data.intersectionStateList.intersectionStatelist[0].id.region: null found, integer expected" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[0].state_time_speed.movementEventList[0].timing.startTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[0].state_time_speed.movementEventList[0].timing.nextTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[1].state_time_speed.movementEventList[0].timing.startTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[1].state_time_speed.movementEventList[0].timing.nextTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[2].state_time_speed.movementEventList[0].timing.startTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[2].state_time_speed.movementEventList[0].timing.nextTime", + "message": "DE_TimeMark is missing" + } + ], + "utcTimeStamp": "2024-06-04T16:22:45.152Z", + "schemaVersion": 1, + "recordGeneratedAt": { + "$date": { + "$numberLong": "1717518165167" + } + }, + "revision": 0, + "states": [ + { + "stateTimeSpeed": [ + { + "eventState": "PROTECTED_CLEARANCE", + "timing": { + "minEndTime": "2024-06-04T16:03:19Z", + "maxEndTime": "2024-06-04T16:03:19Z" + } + } + ], + "signalGroup": 2 + }, + { + "stateTimeSpeed": [ + { + "eventState": "STOP_AND_REMAIN", + "timing": { + "minEndTime": "2024-06-04T16:03:22.1Z", + "maxEndTime": "2024-06-04T16:03:22.1Z" + } + } + ], + "signalGroup": 4 + }, + { + "stateTimeSpeed": [ + { + "eventState": "PROTECTED_CLEARANCE", + "timing": { + "minEndTime": "2024-06-04T16:03:19Z", + "maxEndTime": "2024-06-04T16:03:19Z" + } + } + ], + "signalGroup": 6 + } + ], + "messageType": "SPAT", + "cti4501Conformant": false, + "intersectionId": 12109, + "odeReceivedAt": "2024-06-04T16:22:45.152Z", + "originIp": "10.11.81.12", + "status": { + "failureFlash": false, + "noValidSPATisAvailableAtThisTime": false, + "fixedTimeOperation": false, + "standbyOperation": false, + "trafficDependentOperation": false, + "manualControlIsEnabled": false, + "off": false, + "stopTimeIsActivated": false, + "recentChangeInMAPassignedLanesIDsUsed": false, + "recentMAPmessageUpdate": false, + "failureMode": false, + "noValidMAPisAvailableAtThisTime": false, + "signalPriorityIsActive": false, + "preemptIsActive": false + } +},{ + "_id": { + "$oid": "665f3fc18c25216ef4f53fa6" + }, + "validationMessages": [ + { + "schemaPath": "#/$defs/J2735RoadRegulatorID/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].id.region", + "message": "$.payload.data.intersectionStateList.intersectionStatelist[0].id.region: null found, integer expected" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[0].state_time_speed.movementEventList[0].timing.startTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[0].state_time_speed.movementEventList[0].timing.nextTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[1].state_time_speed.movementEventList[0].timing.startTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[1].state_time_speed.movementEventList[0].timing.nextTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[2].state_time_speed.movementEventList[0].timing.startTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[2].state_time_speed.movementEventList[0].timing.nextTime", + "message": "DE_TimeMark is missing" + } + ], + "utcTimeStamp": "2024-06-04T16:22:45.258Z", + "schemaVersion": 1, + "recordGeneratedAt": { + "$date": { + "$numberLong": "1717518165260" + } + }, + "revision": 0, + "states": [ + { + "stateTimeSpeed": [ + { + "eventState": "PROTECTED_CLEARANCE", + "timing": { + "minEndTime": "2024-06-04T16:03:19Z", + "maxEndTime": "2024-06-04T16:03:19Z" + } + } + ], + "signalGroup": 2 + }, + { + "stateTimeSpeed": [ + { + "eventState": "STOP_AND_REMAIN", + "timing": { + "minEndTime": "2024-06-04T16:03:22.1Z", + "maxEndTime": "2024-06-04T16:03:22.1Z" + } + } + ], + "signalGroup": 4 + }, + { + "stateTimeSpeed": [ + { + "eventState": "PROTECTED_CLEARANCE", + "timing": { + "minEndTime": "2024-06-04T16:03:19Z", + "maxEndTime": "2024-06-04T16:03:19Z" + } + } + ], + "signalGroup": 6 + } + ], + "messageType": "SPAT", + "cti4501Conformant": false, + "intersectionId": 12109, + "odeReceivedAt": "2024-06-04T16:22:45.258Z", + "originIp": "10.11.81.12", + "status": { + "failureFlash": false, + "noValidSPATisAvailableAtThisTime": false, + "fixedTimeOperation": false, + "standbyOperation": false, + "trafficDependentOperation": false, + "manualControlIsEnabled": false, + "off": false, + "stopTimeIsActivated": false, + "recentChangeInMAPassignedLanesIDsUsed": false, + "recentMAPmessageUpdate": false, + "failureMode": false, + "noValidMAPisAvailableAtThisTime": false, + "signalPriorityIsActive": false, + "preemptIsActive": false + } +},{ + "_id": { + "$oid": "665f3fc18c25216ef4f53fa7" + }, + "validationMessages": [ + { + "schemaPath": "#/$defs/J2735RoadRegulatorID/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].id.region", + "message": "$.payload.data.intersectionStateList.intersectionStatelist[0].id.region: null found, integer expected" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[0].state_time_speed.movementEventList[0].timing.startTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[0].state_time_speed.movementEventList[0].timing.nextTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[1].state_time_speed.movementEventList[0].timing.startTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[1].state_time_speed.movementEventList[0].timing.nextTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[2].state_time_speed.movementEventList[0].timing.startTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[2].state_time_speed.movementEventList[0].timing.nextTime", + "message": "DE_TimeMark is missing" + } + ], + "utcTimeStamp": "2024-06-04T16:22:45.365Z", + "schemaVersion": 1, + "recordGeneratedAt": { + "$date": { + "$numberLong": "1717518165366" + } + }, + "revision": 0, + "states": [ + { + "stateTimeSpeed": [ + { + "eventState": "PROTECTED_CLEARANCE", + "timing": { + "minEndTime": "2024-06-04T16:03:19Z", + "maxEndTime": "2024-06-04T16:03:19Z" + } + } + ], + "signalGroup": 2 + }, + { + "stateTimeSpeed": [ + { + "eventState": "STOP_AND_REMAIN", + "timing": { + "minEndTime": "2024-06-04T16:03:22.1Z", + "maxEndTime": "2024-06-04T16:03:22.1Z" + } + } + ], + "signalGroup": 4 + }, + { + "stateTimeSpeed": [ + { + "eventState": "PROTECTED_CLEARANCE", + "timing": { + "minEndTime": "2024-06-04T16:03:19Z", + "maxEndTime": "2024-06-04T16:03:19Z" + } + } + ], + "signalGroup": 6 + } + ], + "messageType": "SPAT", + "cti4501Conformant": false, + "intersectionId": 12109, + "odeReceivedAt": "2024-06-04T16:22:45.365Z", + "originIp": "10.11.81.12", + "status": { + "failureFlash": false, + "noValidSPATisAvailableAtThisTime": false, + "fixedTimeOperation": false, + "standbyOperation": false, + "trafficDependentOperation": false, + "manualControlIsEnabled": false, + "off": false, + "stopTimeIsActivated": false, + "recentChangeInMAPassignedLanesIDsUsed": false, + "recentMAPmessageUpdate": false, + "failureMode": false, + "noValidMAPisAvailableAtThisTime": false, + "signalPriorityIsActive": false, + "preemptIsActive": false + } +},{ + "_id": { + "$oid": "665f3fc18c25216ef4f53fa8" + }, + "validationMessages": [ + { + "schemaPath": "#/$defs/J2735RoadRegulatorID/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].id.region", + "message": "$.payload.data.intersectionStateList.intersectionStatelist[0].id.region: null found, integer expected" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[0].state_time_speed.movementEventList[0].timing.startTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[0].state_time_speed.movementEventList[0].timing.nextTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[1].state_time_speed.movementEventList[0].timing.startTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[1].state_time_speed.movementEventList[0].timing.nextTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[2].state_time_speed.movementEventList[0].timing.startTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[2].state_time_speed.movementEventList[0].timing.nextTime", + "message": "DE_TimeMark is missing" + } + ], + "utcTimeStamp": "2024-06-04T16:22:45.457Z", + "schemaVersion": 1, + "recordGeneratedAt": { + "$date": { + "$numberLong": "1717518165458" + } + }, + "revision": 0, + "states": [ + { + "stateTimeSpeed": [ + { + "eventState": "PROTECTED_CLEARANCE", + "timing": { + "minEndTime": "2024-06-04T16:03:19Z", + "maxEndTime": "2024-06-04T16:03:19Z" + } + } + ], + "signalGroup": 2 + }, + { + "stateTimeSpeed": [ + { + "eventState": "STOP_AND_REMAIN", + "timing": { + "minEndTime": "2024-06-04T16:03:22.1Z", + "maxEndTime": "2024-06-04T16:03:22.1Z" + } + } + ], + "signalGroup": 4 + }, + { + "stateTimeSpeed": [ + { + "eventState": "PROTECTED_CLEARANCE", + "timing": { + "minEndTime": "2024-06-04T16:03:19Z", + "maxEndTime": "2024-06-04T16:03:19Z" + } + } + ], + "signalGroup": 6 + } + ], + "messageType": "SPAT", + "cti4501Conformant": false, + "intersectionId": 12109, + "odeReceivedAt": "2024-06-04T16:22:45.457Z", + "originIp": "10.11.81.12", + "status": { + "failureFlash": false, + "noValidSPATisAvailableAtThisTime": false, + "fixedTimeOperation": false, + "standbyOperation": false, + "trafficDependentOperation": false, + "manualControlIsEnabled": false, + "off": false, + "stopTimeIsActivated": false, + "recentChangeInMAPassignedLanesIDsUsed": false, + "recentMAPmessageUpdate": false, + "failureMode": false, + "noValidMAPisAvailableAtThisTime": false, + "signalPriorityIsActive": false, + "preemptIsActive": false + } +},{ + "_id": { + "$oid": "665f3fc18c25216ef4f53fa9" + }, + "validationMessages": [ + { + "schemaPath": "#/$defs/J2735RoadRegulatorID/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].id.region", + "message": "$.payload.data.intersectionStateList.intersectionStatelist[0].id.region: null found, integer expected" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[0].state_time_speed.movementEventList[0].timing.startTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[0].state_time_speed.movementEventList[0].timing.nextTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[1].state_time_speed.movementEventList[0].timing.startTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[1].state_time_speed.movementEventList[0].timing.nextTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[2].state_time_speed.movementEventList[0].timing.startTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[2].state_time_speed.movementEventList[0].timing.nextTime", + "message": "DE_TimeMark is missing" + } + ], + "utcTimeStamp": "2024-06-04T16:22:45.565Z", + "schemaVersion": 1, + "recordGeneratedAt": { + "$date": { + "$numberLong": "1717518165585" + } + }, + "revision": 0, + "states": [ + { + "stateTimeSpeed": [ + { + "eventState": "PROTECTED_CLEARANCE", + "timing": { + "minEndTime": "2024-06-04T16:03:19Z", + "maxEndTime": "2024-06-04T16:03:19Z" + } + } + ], + "signalGroup": 2 + }, + { + "stateTimeSpeed": [ + { + "eventState": "STOP_AND_REMAIN", + "timing": { + "minEndTime": "2024-06-04T16:03:22.1Z", + "maxEndTime": "2024-06-04T16:03:22.1Z" + } + } + ], + "signalGroup": 4 + }, + { + "stateTimeSpeed": [ + { + "eventState": "PROTECTED_CLEARANCE", + "timing": { + "minEndTime": "2024-06-04T16:03:19Z", + "maxEndTime": "2024-06-04T16:03:19Z" + } + } + ], + "signalGroup": 6 + } + ], + "messageType": "SPAT", + "cti4501Conformant": false, + "intersectionId": 12109, + "odeReceivedAt": "2024-06-04T16:22:45.565Z", + "originIp": "10.11.81.12", + "status": { + "failureFlash": false, + "noValidSPATisAvailableAtThisTime": false, + "fixedTimeOperation": false, + "standbyOperation": false, + "trafficDependentOperation": false, + "manualControlIsEnabled": false, + "off": false, + "stopTimeIsActivated": false, + "recentChangeInMAPassignedLanesIDsUsed": false, + "recentMAPmessageUpdate": false, + "failureMode": false, + "noValidMAPisAvailableAtThisTime": false, + "signalPriorityIsActive": false, + "preemptIsActive": false + } +},{ + "_id": { + "$oid": "665f3fc18c25216ef4f53faa" + }, + "validationMessages": [ + { + "schemaPath": "#/$defs/J2735RoadRegulatorID/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].id.region", + "message": "$.payload.data.intersectionStateList.intersectionStatelist[0].id.region: null found, integer expected" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[0].state_time_speed.movementEventList[0].timing.startTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[0].state_time_speed.movementEventList[0].timing.nextTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[1].state_time_speed.movementEventList[0].timing.startTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[1].state_time_speed.movementEventList[0].timing.nextTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[2].state_time_speed.movementEventList[0].timing.startTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[2].state_time_speed.movementEventList[0].timing.nextTime", + "message": "DE_TimeMark is missing" + } + ], + "utcTimeStamp": "2024-06-04T16:22:45.673Z", + "schemaVersion": 1, + "recordGeneratedAt": { + "$date": { + "$numberLong": "1717518165686" + } + }, + "revision": 0, + "states": [ + { + "stateTimeSpeed": [ + { + "eventState": "PROTECTED_CLEARANCE", + "timing": { + "minEndTime": "2024-06-04T16:03:19Z", + "maxEndTime": "2024-06-04T16:03:19Z" + } + } + ], + "signalGroup": 2 + }, + { + "stateTimeSpeed": [ + { + "eventState": "STOP_AND_REMAIN", + "timing": { + "minEndTime": "2024-06-04T16:03:22.1Z", + "maxEndTime": "2024-06-04T16:03:22.1Z" + } + } + ], + "signalGroup": 4 + }, + { + "stateTimeSpeed": [ + { + "eventState": "PROTECTED_CLEARANCE", + "timing": { + "minEndTime": "2024-06-04T16:03:19Z", + "maxEndTime": "2024-06-04T16:03:19Z" + } + } + ], + "signalGroup": 6 + } + ], + "messageType": "SPAT", + "cti4501Conformant": false, + "intersectionId": 12109, + "odeReceivedAt": "2024-06-04T16:22:45.673Z", + "originIp": "10.11.81.12", + "status": { + "failureFlash": false, + "noValidSPATisAvailableAtThisTime": false, + "fixedTimeOperation": false, + "standbyOperation": false, + "trafficDependentOperation": false, + "manualControlIsEnabled": false, + "off": false, + "stopTimeIsActivated": false, + "recentChangeInMAPassignedLanesIDsUsed": false, + "recentMAPmessageUpdate": false, + "failureMode": false, + "noValidMAPisAvailableAtThisTime": false, + "signalPriorityIsActive": false, + "preemptIsActive": false + } +},{ + "_id": { + "$oid": "665f3fc18c25216ef4f53fab" + }, + "validationMessages": [ + { + "schemaPath": "#/$defs/J2735RoadRegulatorID/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].id.region", + "message": "$.payload.data.intersectionStateList.intersectionStatelist[0].id.region: null found, integer expected" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[0].state_time_speed.movementEventList[0].timing.startTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[0].state_time_speed.movementEventList[0].timing.nextTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[1].state_time_speed.movementEventList[0].timing.startTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[1].state_time_speed.movementEventList[0].timing.nextTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[2].state_time_speed.movementEventList[0].timing.startTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[2].state_time_speed.movementEventList[0].timing.nextTime", + "message": "DE_TimeMark is missing" + } + ], + "utcTimeStamp": "2024-06-04T16:22:45.78Z", + "schemaVersion": 1, + "recordGeneratedAt": { + "$date": { + "$numberLong": "1717518165792" + } + }, + "revision": 0, + "states": [ + { + "stateTimeSpeed": [ + { + "eventState": "PROTECTED_CLEARANCE", + "timing": { + "minEndTime": "2024-06-04T16:03:19Z", + "maxEndTime": "2024-06-04T16:03:19Z" + } + } + ], + "signalGroup": 2 + }, + { + "stateTimeSpeed": [ + { + "eventState": "STOP_AND_REMAIN", + "timing": { + "minEndTime": "2024-06-04T16:03:22.1Z", + "maxEndTime": "2024-06-04T16:03:22.1Z" + } + } + ], + "signalGroup": 4 + }, + { + "stateTimeSpeed": [ + { + "eventState": "PROTECTED_CLEARANCE", + "timing": { + "minEndTime": "2024-06-04T16:03:19Z", + "maxEndTime": "2024-06-04T16:03:19Z" + } + } + ], + "signalGroup": 6 + } + ], + "messageType": "SPAT", + "cti4501Conformant": false, + "intersectionId": 12109, + "odeReceivedAt": "2024-06-04T16:22:45.78Z", + "originIp": "10.11.81.12", + "status": { + "failureFlash": false, + "noValidSPATisAvailableAtThisTime": false, + "fixedTimeOperation": false, + "standbyOperation": false, + "trafficDependentOperation": false, + "manualControlIsEnabled": false, + "off": false, + "stopTimeIsActivated": false, + "recentChangeInMAPassignedLanesIDsUsed": false, + "recentMAPmessageUpdate": false, + "failureMode": false, + "noValidMAPisAvailableAtThisTime": false, + "signalPriorityIsActive": false, + "preemptIsActive": false + } +},{ + "_id": { + "$oid": "665f3fc18c25216ef4f53fac" + }, + "validationMessages": [ + { + "schemaPath": "#/$defs/J2735RoadRegulatorID/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].id.region", + "message": "$.payload.data.intersectionStateList.intersectionStatelist[0].id.region: null found, integer expected" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[0].state_time_speed.movementEventList[0].timing.startTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[0].state_time_speed.movementEventList[0].timing.nextTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[1].state_time_speed.movementEventList[0].timing.startTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[1].state_time_speed.movementEventList[0].timing.nextTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[2].state_time_speed.movementEventList[0].timing.startTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[2].state_time_speed.movementEventList[0].timing.nextTime", + "message": "DE_TimeMark is missing" + } + ], + "utcTimeStamp": "2024-06-04T16:22:45.886Z", + "schemaVersion": 1, + "recordGeneratedAt": { + "$date": { + "$numberLong": "1717518165900" + } + }, + "revision": 0, + "states": [ + { + "stateTimeSpeed": [ + { + "eventState": "PROTECTED_CLEARANCE", + "timing": { + "minEndTime": "2024-06-04T16:03:19Z", + "maxEndTime": "2024-06-04T16:03:19Z" + } + } + ], + "signalGroup": 2 + }, + { + "stateTimeSpeed": [ + { + "eventState": "STOP_AND_REMAIN", + "timing": { + "minEndTime": "2024-06-04T16:03:22.1Z", + "maxEndTime": "2024-06-04T16:03:22.1Z" + } + } + ], + "signalGroup": 4 + }, + { + "stateTimeSpeed": [ + { + "eventState": "PROTECTED_CLEARANCE", + "timing": { + "minEndTime": "2024-06-04T16:03:19Z", + "maxEndTime": "2024-06-04T16:03:19Z" + } + } + ], + "signalGroup": 6 + } + ], + "messageType": "SPAT", + "cti4501Conformant": false, + "intersectionId": 12109, + "odeReceivedAt": "2024-06-04T16:22:45.886Z", + "originIp": "10.11.81.12", + "status": { + "failureFlash": false, + "noValidSPATisAvailableAtThisTime": false, + "fixedTimeOperation": false, + "standbyOperation": false, + "trafficDependentOperation": false, + "manualControlIsEnabled": false, + "off": false, + "stopTimeIsActivated": false, + "recentChangeInMAPassignedLanesIDsUsed": false, + "recentMAPmessageUpdate": false, + "failureMode": false, + "noValidMAPisAvailableAtThisTime": false, + "signalPriorityIsActive": false, + "preemptIsActive": false + } +},{ + "_id": { + "$oid": "665f3fc18c25216ef4f53fad" + }, + "validationMessages": [ + { + "schemaPath": "#/$defs/J2735RoadRegulatorID/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].id.region", + "message": "$.payload.data.intersectionStateList.intersectionStatelist[0].id.region: null found, integer expected" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[0].state_time_speed.movementEventList[0].timing.startTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[0].state_time_speed.movementEventList[0].timing.nextTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[1].state_time_speed.movementEventList[0].timing.startTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[1].state_time_speed.movementEventList[0].timing.nextTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[2].state_time_speed.movementEventList[0].timing.startTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[2].state_time_speed.movementEventList[0].timing.nextTime", + "message": "DE_TimeMark is missing" + } + ], + "utcTimeStamp": "2024-06-04T16:22:45.994Z", + "schemaVersion": 1, + "recordGeneratedAt": { + "$date": { + "$numberLong": "1717518166005" + } + }, + "revision": 0, + "states": [ + { + "stateTimeSpeed": [ + { + "eventState": "PROTECTED_CLEARANCE", + "timing": { + "minEndTime": "2024-06-04T16:03:19Z", + "maxEndTime": "2024-06-04T16:03:19Z" + } + } + ], + "signalGroup": 2 + }, + { + "stateTimeSpeed": [ + { + "eventState": "STOP_AND_REMAIN", + "timing": { + "minEndTime": "2024-06-04T16:03:22.1Z", + "maxEndTime": "2024-06-04T16:03:22.1Z" + } + } + ], + "signalGroup": 4 + }, + { + "stateTimeSpeed": [ + { + "eventState": "PROTECTED_CLEARANCE", + "timing": { + "minEndTime": "2024-06-04T16:03:19Z", + "maxEndTime": "2024-06-04T16:03:19Z" + } + } + ], + "signalGroup": 6 + } + ], + "messageType": "SPAT", + "cti4501Conformant": false, + "intersectionId": 12109, + "odeReceivedAt": "2024-06-04T16:22:45.994Z", + "originIp": "10.11.81.12", + "status": { + "failureFlash": false, + "noValidSPATisAvailableAtThisTime": false, + "fixedTimeOperation": false, + "standbyOperation": false, + "trafficDependentOperation": false, + "manualControlIsEnabled": false, + "off": false, + "stopTimeIsActivated": false, + "recentChangeInMAPassignedLanesIDsUsed": false, + "recentMAPmessageUpdate": false, + "failureMode": false, + "noValidMAPisAvailableAtThisTime": false, + "signalPriorityIsActive": false, + "preemptIsActive": false + } +},{ + "_id": { + "$oid": "665f3fc18c25216ef4f53fae" + }, + "validationMessages": [ + { + "schemaPath": "#/$defs/J2735RoadRegulatorID/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].id.region", + "message": "$.payload.data.intersectionStateList.intersectionStatelist[0].id.region: null found, integer expected" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[0].state_time_speed.movementEventList[0].timing.startTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[0].state_time_speed.movementEventList[0].timing.nextTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[1].state_time_speed.movementEventList[0].timing.startTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[1].state_time_speed.movementEventList[0].timing.nextTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[2].state_time_speed.movementEventList[0].timing.startTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[2].state_time_speed.movementEventList[0].timing.nextTime", + "message": "DE_TimeMark is missing" + } + ], + "utcTimeStamp": "2024-06-04T16:22:46.101Z", + "schemaVersion": 1, + "recordGeneratedAt": { + "$date": { + "$numberLong": "1717518166111" + } + }, + "revision": 0, + "states": [ + { + "stateTimeSpeed": [ + { + "eventState": "PROTECTED_CLEARANCE", + "timing": { + "minEndTime": "2024-06-04T16:03:19Z", + "maxEndTime": "2024-06-04T16:03:19Z" + } + } + ], + "signalGroup": 2 + }, + { + "stateTimeSpeed": [ + { + "eventState": "STOP_AND_REMAIN", + "timing": { + "minEndTime": "2024-06-04T16:03:22.1Z", + "maxEndTime": "2024-06-04T16:03:22.1Z" + } + } + ], + "signalGroup": 4 + }, + { + "stateTimeSpeed": [ + { + "eventState": "PROTECTED_CLEARANCE", + "timing": { + "minEndTime": "2024-06-04T16:03:19Z", + "maxEndTime": "2024-06-04T16:03:19Z" + } + } + ], + "signalGroup": 6 + } + ], + "messageType": "SPAT", + "cti4501Conformant": false, + "intersectionId": 12109, + "odeReceivedAt": "2024-06-04T16:22:46.101Z", + "originIp": "10.11.81.12", + "status": { + "failureFlash": false, + "noValidSPATisAvailableAtThisTime": false, + "fixedTimeOperation": false, + "standbyOperation": false, + "trafficDependentOperation": false, + "manualControlIsEnabled": false, + "off": false, + "stopTimeIsActivated": false, + "recentChangeInMAPassignedLanesIDsUsed": false, + "recentMAPmessageUpdate": false, + "failureMode": false, + "noValidMAPisAvailableAtThisTime": false, + "signalPriorityIsActive": false, + "preemptIsActive": false + } +},{ + "_id": { + "$oid": "665f3fc18c25216ef4f53faf" + }, + "validationMessages": [ + { + "schemaPath": "#/$defs/J2735RoadRegulatorID/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].id.region", + "message": "$.payload.data.intersectionStateList.intersectionStatelist[0].id.region: null found, integer expected" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[0].state_time_speed.movementEventList[0].timing.startTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[0].state_time_speed.movementEventList[0].timing.nextTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[1].state_time_speed.movementEventList[0].timing.startTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[1].state_time_speed.movementEventList[0].timing.nextTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[2].state_time_speed.movementEventList[0].timing.startTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[2].state_time_speed.movementEventList[0].timing.nextTime", + "message": "DE_TimeMark is missing" + } + ], + "utcTimeStamp": "2024-06-04T16:22:46.209Z", + "schemaVersion": 1, + "recordGeneratedAt": { + "$date": { + "$numberLong": "1717518166219" + } + }, + "revision": 0, + "states": [ + { + "stateTimeSpeed": [ + { + "eventState": "PROTECTED_CLEARANCE", + "timing": { + "minEndTime": "2024-06-04T16:03:19Z", + "maxEndTime": "2024-06-04T16:03:19Z" + } + } + ], + "signalGroup": 2 + }, + { + "stateTimeSpeed": [ + { + "eventState": "STOP_AND_REMAIN", + "timing": { + "minEndTime": "2024-06-04T16:03:22.1Z", + "maxEndTime": "2024-06-04T16:03:22.1Z" + } + } + ], + "signalGroup": 4 + }, + { + "stateTimeSpeed": [ + { + "eventState": "PROTECTED_CLEARANCE", + "timing": { + "minEndTime": "2024-06-04T16:03:19Z", + "maxEndTime": "2024-06-04T16:03:19Z" + } + } + ], + "signalGroup": 6 + } + ], + "messageType": "SPAT", + "cti4501Conformant": false, + "intersectionId": 12109, + "odeReceivedAt": "2024-06-04T16:22:46.209Z", + "originIp": "10.11.81.12", + "status": { + "failureFlash": false, + "noValidSPATisAvailableAtThisTime": false, + "fixedTimeOperation": false, + "standbyOperation": false, + "trafficDependentOperation": false, + "manualControlIsEnabled": false, + "off": false, + "stopTimeIsActivated": false, + "recentChangeInMAPassignedLanesIDsUsed": false, + "recentMAPmessageUpdate": false, + "failureMode": false, + "noValidMAPisAvailableAtThisTime": false, + "signalPriorityIsActive": false, + "preemptIsActive": false + } +},{ + "_id": { + "$oid": "665f3fc18c25216ef4f53fb0" + }, + "validationMessages": [ + { + "schemaPath": "#/$defs/J2735RoadRegulatorID/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].id.region", + "message": "$.payload.data.intersectionStateList.intersectionStatelist[0].id.region: null found, integer expected" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[0].state_time_speed.movementEventList[0].timing.startTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[0].state_time_speed.movementEventList[0].timing.nextTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[1].state_time_speed.movementEventList[0].timing.startTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[1].state_time_speed.movementEventList[0].timing.nextTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[2].state_time_speed.movementEventList[0].timing.startTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[2].state_time_speed.movementEventList[0].timing.nextTime", + "message": "DE_TimeMark is missing" + } + ], + "utcTimeStamp": "2024-06-04T16:22:46.316Z", + "schemaVersion": 1, + "recordGeneratedAt": { + "$date": { + "$numberLong": "1717518166325" + } + }, + "revision": 0, + "states": [ + { + "stateTimeSpeed": [ + { + "eventState": "PROTECTED_CLEARANCE", + "timing": { + "minEndTime": "2024-06-04T16:03:19Z", + "maxEndTime": "2024-06-04T16:03:19Z" + } + } + ], + "signalGroup": 2 + }, + { + "stateTimeSpeed": [ + { + "eventState": "STOP_AND_REMAIN", + "timing": { + "minEndTime": "2024-06-04T16:03:22.1Z", + "maxEndTime": "2024-06-04T16:03:22.1Z" + } + } + ], + "signalGroup": 4 + }, + { + "stateTimeSpeed": [ + { + "eventState": "PROTECTED_CLEARANCE", + "timing": { + "minEndTime": "2024-06-04T16:03:19Z", + "maxEndTime": "2024-06-04T16:03:19Z" + } + } + ], + "signalGroup": 6 + } + ], + "messageType": "SPAT", + "cti4501Conformant": false, + "intersectionId": 12109, + "odeReceivedAt": "2024-06-04T16:22:46.316Z", + "originIp": "10.11.81.12", + "status": { + "failureFlash": false, + "noValidSPATisAvailableAtThisTime": false, + "fixedTimeOperation": false, + "standbyOperation": false, + "trafficDependentOperation": false, + "manualControlIsEnabled": false, + "off": false, + "stopTimeIsActivated": false, + "recentChangeInMAPassignedLanesIDsUsed": false, + "recentMAPmessageUpdate": false, + "failureMode": false, + "noValidMAPisAvailableAtThisTime": false, + "signalPriorityIsActive": false, + "preemptIsActive": false + } +},{ + "_id": { + "$oid": "665f3fc18c25216ef4f53fb1" + }, + "validationMessages": [ + { + "schemaPath": "#/$defs/J2735RoadRegulatorID/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].id.region", + "message": "$.payload.data.intersectionStateList.intersectionStatelist[0].id.region: null found, integer expected" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[0].state_time_speed.movementEventList[0].timing.startTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[0].state_time_speed.movementEventList[0].timing.nextTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[1].state_time_speed.movementEventList[0].timing.startTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[1].state_time_speed.movementEventList[0].timing.nextTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[2].state_time_speed.movementEventList[0].timing.startTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[2].state_time_speed.movementEventList[0].timing.nextTime", + "message": "DE_TimeMark is missing" + } + ], + "utcTimeStamp": "2024-06-04T16:22:46.409Z", + "schemaVersion": 1, + "recordGeneratedAt": { + "$date": { + "$numberLong": "1717518166417" + } + }, + "revision": 0, + "states": [ + { + "stateTimeSpeed": [ + { + "eventState": "PROTECTED_CLEARANCE", + "timing": { + "minEndTime": "2024-06-04T16:03:18Z", + "maxEndTime": "2024-06-04T16:03:18Z" + } + } + ], + "signalGroup": 2 + }, + { + "stateTimeSpeed": [ + { + "eventState": "STOP_AND_REMAIN", + "timing": { + "minEndTime": "2024-06-04T16:03:21.1Z", + "maxEndTime": "2024-06-04T16:03:21.1Z" + } + } + ], + "signalGroup": 4 + }, + { + "stateTimeSpeed": [ + { + "eventState": "PROTECTED_CLEARANCE", + "timing": { + "minEndTime": "2024-06-04T16:03:18Z", + "maxEndTime": "2024-06-04T16:03:18Z" + } + } + ], + "signalGroup": 6 + } + ], + "messageType": "SPAT", + "cti4501Conformant": false, + "intersectionId": 12109, + "odeReceivedAt": "2024-06-04T16:22:46.409Z", + "originIp": "10.11.81.12", + "status": { + "failureFlash": false, + "noValidSPATisAvailableAtThisTime": false, + "fixedTimeOperation": false, + "standbyOperation": false, + "trafficDependentOperation": false, + "manualControlIsEnabled": false, + "off": false, + "stopTimeIsActivated": false, + "recentChangeInMAPassignedLanesIDsUsed": false, + "recentMAPmessageUpdate": false, + "failureMode": false, + "noValidMAPisAvailableAtThisTime": false, + "signalPriorityIsActive": false, + "preemptIsActive": false + } +},{ + "_id": { + "$oid": "665f3fc18c25216ef4f53fb2" + }, + "validationMessages": [ + { + "schemaPath": "#/$defs/J2735RoadRegulatorID/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].id.region", + "message": "$.payload.data.intersectionStateList.intersectionStatelist[0].id.region: null found, integer expected" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[0].state_time_speed.movementEventList[0].timing.startTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[0].state_time_speed.movementEventList[0].timing.nextTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[1].state_time_speed.movementEventList[0].timing.startTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[1].state_time_speed.movementEventList[0].timing.nextTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[2].state_time_speed.movementEventList[0].timing.startTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[2].state_time_speed.movementEventList[0].timing.nextTime", + "message": "DE_TimeMark is missing" + } + ], + "utcTimeStamp": "2024-06-04T16:22:46.518Z", + "schemaVersion": 1, + "recordGeneratedAt": { + "$date": { + "$numberLong": "1717518166524" + } + }, + "revision": 0, + "states": [ + { + "stateTimeSpeed": [ + { + "eventState": "PROTECTED_CLEARANCE", + "timing": { + "minEndTime": "2024-06-04T16:03:18Z", + "maxEndTime": "2024-06-04T16:03:18Z" + } + } + ], + "signalGroup": 2 + }, + { + "stateTimeSpeed": [ + { + "eventState": "STOP_AND_REMAIN", + "timing": { + "minEndTime": "2024-06-04T16:03:21.1Z", + "maxEndTime": "2024-06-04T16:03:21.1Z" + } + } + ], + "signalGroup": 4 + }, + { + "stateTimeSpeed": [ + { + "eventState": "PROTECTED_CLEARANCE", + "timing": { + "minEndTime": "2024-06-04T16:03:18Z", + "maxEndTime": "2024-06-04T16:03:18Z" + } + } + ], + "signalGroup": 6 + } + ], + "messageType": "SPAT", + "cti4501Conformant": false, + "intersectionId": 12109, + "odeReceivedAt": "2024-06-04T16:22:46.518Z", + "originIp": "10.11.81.12", + "status": { + "failureFlash": false, + "noValidSPATisAvailableAtThisTime": false, + "fixedTimeOperation": false, + "standbyOperation": false, + "trafficDependentOperation": false, + "manualControlIsEnabled": false, + "off": false, + "stopTimeIsActivated": false, + "recentChangeInMAPassignedLanesIDsUsed": false, + "recentMAPmessageUpdate": false, + "failureMode": false, + "noValidMAPisAvailableAtThisTime": false, + "signalPriorityIsActive": false, + "preemptIsActive": false + } +},{ + "_id": { + "$oid": "665f3fc18c25216ef4f53fb3" + }, + "validationMessages": [ + { + "schemaPath": "#/$defs/J2735RoadRegulatorID/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].id.region", + "message": "$.payload.data.intersectionStateList.intersectionStatelist[0].id.region: null found, integer expected" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[0].state_time_speed.movementEventList[0].timing.startTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[0].state_time_speed.movementEventList[0].timing.nextTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[1].state_time_speed.movementEventList[0].timing.startTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[1].state_time_speed.movementEventList[0].timing.nextTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[2].state_time_speed.movementEventList[0].timing.startTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[2].state_time_speed.movementEventList[0].timing.nextTime", + "message": "DE_TimeMark is missing" + } + ], + "utcTimeStamp": "2024-06-04T16:22:53.88Z", + "schemaVersion": 1, + "recordGeneratedAt": { + "$date": { + "$numberLong": "1717518174143" + } + }, + "revision": 0, + "states": [ + { + "stateTimeSpeed": [ + { + "eventState": "PROTECTED_CLEARANCE", + "timing": { + "minEndTime": "2024-06-04T16:03:17Z", + "maxEndTime": "2024-06-04T16:03:17Z" + } + } + ], + "signalGroup": 2 + }, + { + "stateTimeSpeed": [ + { + "eventState": "STOP_AND_REMAIN", + "timing": { + "minEndTime": "2024-06-04T16:03:20.1Z", + "maxEndTime": "2024-06-04T16:03:20.1Z" + } + } + ], + "signalGroup": 4 + }, + { + "stateTimeSpeed": [ + { + "eventState": "PROTECTED_CLEARANCE", + "timing": { + "minEndTime": "2024-06-04T16:03:17Z", + "maxEndTime": "2024-06-04T16:03:17Z" + } + } + ], + "signalGroup": 6 + } + ], + "messageType": "SPAT", + "cti4501Conformant": false, + "intersectionId": 12109, + "odeReceivedAt": "2024-06-04T16:22:53.88Z", + "originIp": "10.11.81.12", + "status": { + "failureFlash": false, + "noValidSPATisAvailableAtThisTime": false, + "fixedTimeOperation": false, + "standbyOperation": false, + "trafficDependentOperation": false, + "manualControlIsEnabled": false, + "off": false, + "stopTimeIsActivated": false, + "recentChangeInMAPassignedLanesIDsUsed": false, + "recentMAPmessageUpdate": false, + "failureMode": false, + "noValidMAPisAvailableAtThisTime": false, + "signalPriorityIsActive": false, + "preemptIsActive": false + } +},{ + "_id": { + "$oid": "665f3fc18c25216ef4f53fb4" + }, + "validationMessages": [ + { + "schemaPath": "#/$defs/J2735RoadRegulatorID/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].id.region", + "message": "$.payload.data.intersectionStateList.intersectionStatelist[0].id.region: null found, integer expected" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[0].state_time_speed.movementEventList[0].timing.startTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[0].state_time_speed.movementEventList[0].timing.nextTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[1].state_time_speed.movementEventList[0].timing.startTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[1].state_time_speed.movementEventList[0].timing.nextTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[2].state_time_speed.movementEventList[0].timing.startTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[2].state_time_speed.movementEventList[0].timing.nextTime", + "message": "DE_TimeMark is missing" + } + ], + "utcTimeStamp": "2024-06-04T16:22:53.988Z", + "schemaVersion": 1, + "recordGeneratedAt": { + "$date": { + "$numberLong": "1717518174143" + } + }, + "revision": 0, + "states": [ + { + "stateTimeSpeed": [ + { + "eventState": "PROTECTED_CLEARANCE", + "timing": { + "minEndTime": "2024-06-04T16:03:17Z", + "maxEndTime": "2024-06-04T16:03:17Z" + } + } + ], + "signalGroup": 2 + }, + { + "stateTimeSpeed": [ + { + "eventState": "STOP_AND_REMAIN", + "timing": { + "minEndTime": "2024-06-04T16:03:20.1Z", + "maxEndTime": "2024-06-04T16:03:20.1Z" + } + } + ], + "signalGroup": 4 + }, + { + "stateTimeSpeed": [ + { + "eventState": "PROTECTED_CLEARANCE", + "timing": { + "minEndTime": "2024-06-04T16:03:17Z", + "maxEndTime": "2024-06-04T16:03:17Z" + } + } + ], + "signalGroup": 6 + } + ], + "messageType": "SPAT", + "cti4501Conformant": false, + "intersectionId": 12109, + "odeReceivedAt": "2024-06-04T16:22:53.988Z", + "originIp": "10.11.81.12", + "status": { + "failureFlash": false, + "noValidSPATisAvailableAtThisTime": false, + "fixedTimeOperation": false, + "standbyOperation": false, + "trafficDependentOperation": false, + "manualControlIsEnabled": false, + "off": false, + "stopTimeIsActivated": false, + "recentChangeInMAPassignedLanesIDsUsed": false, + "recentMAPmessageUpdate": false, + "failureMode": false, + "noValidMAPisAvailableAtThisTime": false, + "signalPriorityIsActive": false, + "preemptIsActive": false + } +},{ + "_id": { + "$oid": "665f3fc18c25216ef4f53fb5" + }, + "validationMessages": [ + { + "schemaPath": "#/$defs/J2735RoadRegulatorID/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].id.region", + "message": "$.payload.data.intersectionStateList.intersectionStatelist[0].id.region: null found, integer expected" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[0].state_time_speed.movementEventList[0].timing.startTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[0].state_time_speed.movementEventList[0].timing.nextTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[1].state_time_speed.movementEventList[0].timing.startTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[1].state_time_speed.movementEventList[0].timing.nextTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[2].state_time_speed.movementEventList[0].timing.startTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[2].state_time_speed.movementEventList[0].timing.nextTime", + "message": "DE_TimeMark is missing" + } + ], + "utcTimeStamp": "2024-06-04T16:22:53.771Z", + "schemaVersion": 1, + "recordGeneratedAt": { + "$date": { + "$numberLong": "1717518174143" + } + }, + "revision": 0, + "states": [ + { + "stateTimeSpeed": [ + { + "eventState": "PROTECTED_CLEARANCE", + "timing": { + "minEndTime": "2024-06-04T16:03:17Z", + "maxEndTime": "2024-06-04T16:03:17Z" + } + } + ], + "signalGroup": 2 + }, + { + "stateTimeSpeed": [ + { + "eventState": "STOP_AND_REMAIN", + "timing": { + "minEndTime": "2024-06-04T16:03:20.1Z", + "maxEndTime": "2024-06-04T16:03:20.1Z" + } + } + ], + "signalGroup": 4 + }, + { + "stateTimeSpeed": [ + { + "eventState": "PROTECTED_CLEARANCE", + "timing": { + "minEndTime": "2024-06-04T16:03:17Z", + "maxEndTime": "2024-06-04T16:03:17Z" + } + } + ], + "signalGroup": 6 + } + ], + "messageType": "SPAT", + "cti4501Conformant": false, + "intersectionId": 12109, + "odeReceivedAt": "2024-06-04T16:22:53.771Z", + "originIp": "10.11.81.12", + "status": { + "failureFlash": false, + "noValidSPATisAvailableAtThisTime": false, + "fixedTimeOperation": false, + "standbyOperation": false, + "trafficDependentOperation": false, + "manualControlIsEnabled": false, + "off": false, + "stopTimeIsActivated": false, + "recentChangeInMAPassignedLanesIDsUsed": false, + "recentMAPmessageUpdate": false, + "failureMode": false, + "noValidMAPisAvailableAtThisTime": false, + "signalPriorityIsActive": false, + "preemptIsActive": false + } +},{ + "_id": { + "$oid": "665f3fc18c25216ef4f53fb6" + }, + "validationMessages": [ + { + "schemaPath": "#/$defs/J2735RoadRegulatorID/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].id.region", + "message": "$.payload.data.intersectionStateList.intersectionStatelist[0].id.region: null found, integer expected" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[0].state_time_speed.movementEventList[0].timing.startTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[0].state_time_speed.movementEventList[0].timing.nextTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[1].state_time_speed.movementEventList[0].timing.startTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[1].state_time_speed.movementEventList[0].timing.nextTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[2].state_time_speed.movementEventList[0].timing.startTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[2].state_time_speed.movementEventList[0].timing.nextTime", + "message": "DE_TimeMark is missing" + } + ], + "utcTimeStamp": "2024-06-04T16:22:54.095Z", + "schemaVersion": 1, + "recordGeneratedAt": { + "$date": { + "$numberLong": "1717518174165" + } + }, + "revision": 0, + "states": [ + { + "stateTimeSpeed": [ + { + "eventState": "PROTECTED_CLEARANCE", + "timing": { + "minEndTime": "2024-06-04T16:03:17Z", + "maxEndTime": "2024-06-04T16:03:17Z" + } + } + ], + "signalGroup": 2 + }, + { + "stateTimeSpeed": [ + { + "eventState": "STOP_AND_REMAIN", + "timing": { + "minEndTime": "2024-06-04T16:03:20.1Z", + "maxEndTime": "2024-06-04T16:03:20.1Z" + } + } + ], + "signalGroup": 4 + }, + { + "stateTimeSpeed": [ + { + "eventState": "PROTECTED_CLEARANCE", + "timing": { + "minEndTime": "2024-06-04T16:03:17Z", + "maxEndTime": "2024-06-04T16:03:17Z" + } + } + ], + "signalGroup": 6 + } + ], + "messageType": "SPAT", + "cti4501Conformant": false, + "intersectionId": 12109, + "odeReceivedAt": "2024-06-04T16:22:54.095Z", + "originIp": "10.11.81.12", + "status": { + "failureFlash": false, + "noValidSPATisAvailableAtThisTime": false, + "fixedTimeOperation": false, + "standbyOperation": false, + "trafficDependentOperation": false, + "manualControlIsEnabled": false, + "off": false, + "stopTimeIsActivated": false, + "recentChangeInMAPassignedLanesIDsUsed": false, + "recentMAPmessageUpdate": false, + "failureMode": false, + "noValidMAPisAvailableAtThisTime": false, + "signalPriorityIsActive": false, + "preemptIsActive": false + } +},{ + "_id": { + "$oid": "665f3fc18c25216ef4f53fb7" + }, + "validationMessages": [ + { + "schemaPath": "#/$defs/J2735RoadRegulatorID/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].id.region", + "message": "$.payload.data.intersectionStateList.intersectionStatelist[0].id.region: null found, integer expected" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[0].state_time_speed.movementEventList[0].timing.startTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[0].state_time_speed.movementEventList[0].timing.nextTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[1].state_time_speed.movementEventList[0].timing.startTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[1].state_time_speed.movementEventList[0].timing.nextTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[2].state_time_speed.movementEventList[0].timing.startTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[2].state_time_speed.movementEventList[0].timing.nextTime", + "message": "DE_TimeMark is missing" + } + ], + "utcTimeStamp": "2024-06-04T16:22:54.202Z", + "schemaVersion": 1, + "recordGeneratedAt": { + "$date": { + "$numberLong": "1717518174213" + } + }, + "revision": 0, + "states": [ + { + "stateTimeSpeed": [ + { + "eventState": "PROTECTED_CLEARANCE", + "timing": { + "minEndTime": "2024-06-04T16:03:17Z", + "maxEndTime": "2024-06-04T16:03:17Z" + } + } + ], + "signalGroup": 2 + }, + { + "stateTimeSpeed": [ + { + "eventState": "STOP_AND_REMAIN", + "timing": { + "minEndTime": "2024-06-04T16:03:20.1Z", + "maxEndTime": "2024-06-04T16:03:20.1Z" + } + } + ], + "signalGroup": 4 + }, + { + "stateTimeSpeed": [ + { + "eventState": "PROTECTED_CLEARANCE", + "timing": { + "minEndTime": "2024-06-04T16:03:17Z", + "maxEndTime": "2024-06-04T16:03:17Z" + } + } + ], + "signalGroup": 6 + } + ], + "messageType": "SPAT", + "cti4501Conformant": false, + "intersectionId": 12109, + "odeReceivedAt": "2024-06-04T16:22:54.202Z", + "originIp": "10.11.81.12", + "status": { + "failureFlash": false, + "noValidSPATisAvailableAtThisTime": false, + "fixedTimeOperation": false, + "standbyOperation": false, + "trafficDependentOperation": false, + "manualControlIsEnabled": false, + "off": false, + "stopTimeIsActivated": false, + "recentChangeInMAPassignedLanesIDsUsed": false, + "recentMAPmessageUpdate": false, + "failureMode": false, + "noValidMAPisAvailableAtThisTime": false, + "signalPriorityIsActive": false, + "preemptIsActive": false + } +},{ + "_id": { + "$oid": "665f3fc18c25216ef4f53fb8" + }, + "validationMessages": [ + { + "schemaPath": "#/$defs/J2735RoadRegulatorID/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].id.region", + "message": "$.payload.data.intersectionStateList.intersectionStatelist[0].id.region: null found, integer expected" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[0].state_time_speed.movementEventList[0].timing.startTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[0].state_time_speed.movementEventList[0].timing.nextTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[1].state_time_speed.movementEventList[0].timing.startTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[1].state_time_speed.movementEventList[0].timing.nextTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[2].state_time_speed.movementEventList[0].timing.startTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[2].state_time_speed.movementEventList[0].timing.nextTime", + "message": "DE_TimeMark is missing" + } + ], + "utcTimeStamp": "2024-06-04T16:22:54.31Z", + "schemaVersion": 1, + "recordGeneratedAt": { + "$date": { + "$numberLong": "1717518174321" + } + }, + "revision": 0, + "states": [ + { + "stateTimeSpeed": [ + { + "eventState": "PROTECTED_CLEARANCE", + "timing": { + "minEndTime": "2024-06-04T16:03:17Z", + "maxEndTime": "2024-06-04T16:03:17Z" + } + } + ], + "signalGroup": 2 + }, + { + "stateTimeSpeed": [ + { + "eventState": "STOP_AND_REMAIN", + "timing": { + "minEndTime": "2024-06-04T16:03:20.1Z", + "maxEndTime": "2024-06-04T16:03:20.1Z" + } + } + ], + "signalGroup": 4 + }, + { + "stateTimeSpeed": [ + { + "eventState": "PROTECTED_CLEARANCE", + "timing": { + "minEndTime": "2024-06-04T16:03:17Z", + "maxEndTime": "2024-06-04T16:03:17Z" + } + } + ], + "signalGroup": 6 + } + ], + "messageType": "SPAT", + "cti4501Conformant": false, + "intersectionId": 12109, + "odeReceivedAt": "2024-06-04T16:22:54.31Z", + "originIp": "10.11.81.12", + "status": { + "failureFlash": false, + "noValidSPATisAvailableAtThisTime": false, + "fixedTimeOperation": false, + "standbyOperation": false, + "trafficDependentOperation": false, + "manualControlIsEnabled": false, + "off": false, + "stopTimeIsActivated": false, + "recentChangeInMAPassignedLanesIDsUsed": false, + "recentMAPmessageUpdate": false, + "failureMode": false, + "noValidMAPisAvailableAtThisTime": false, + "signalPriorityIsActive": false, + "preemptIsActive": false + } +},{ + "_id": { + "$oid": "665f3fc18c25216ef4f53fb9" + }, + "validationMessages": [ + { + "schemaPath": "#/$defs/J2735RoadRegulatorID/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].id.region", + "message": "$.payload.data.intersectionStateList.intersectionStatelist[0].id.region: null found, integer expected" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[0].state_time_speed.movementEventList[0].timing.startTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[0].state_time_speed.movementEventList[0].timing.nextTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[1].state_time_speed.movementEventList[0].timing.startTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[1].state_time_speed.movementEventList[0].timing.nextTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[2].state_time_speed.movementEventList[0].timing.startTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[2].state_time_speed.movementEventList[0].timing.nextTime", + "message": "DE_TimeMark is missing" + } + ], + "utcTimeStamp": "2024-06-04T16:22:54.417Z", + "schemaVersion": 1, + "recordGeneratedAt": { + "$date": { + "$numberLong": "1717518174427" + } + }, + "revision": 0, + "states": [ + { + "stateTimeSpeed": [ + { + "eventState": "PROTECTED_CLEARANCE", + "timing": { + "minEndTime": "2024-06-04T16:03:17Z", + "maxEndTime": "2024-06-04T16:03:17Z" + } + } + ], + "signalGroup": 2 + }, + { + "stateTimeSpeed": [ + { + "eventState": "STOP_AND_REMAIN", + "timing": { + "minEndTime": "2024-06-04T16:03:20.1Z", + "maxEndTime": "2024-06-04T16:03:20.1Z" + } + } + ], + "signalGroup": 4 + }, + { + "stateTimeSpeed": [ + { + "eventState": "PROTECTED_CLEARANCE", + "timing": { + "minEndTime": "2024-06-04T16:03:17Z", + "maxEndTime": "2024-06-04T16:03:17Z" + } + } + ], + "signalGroup": 6 + } + ], + "messageType": "SPAT", + "cti4501Conformant": false, + "intersectionId": 12109, + "odeReceivedAt": "2024-06-04T16:22:54.417Z", + "originIp": "10.11.81.12", + "status": { + "failureFlash": false, + "noValidSPATisAvailableAtThisTime": false, + "fixedTimeOperation": false, + "standbyOperation": false, + "trafficDependentOperation": false, + "manualControlIsEnabled": false, + "off": false, + "stopTimeIsActivated": false, + "recentChangeInMAPassignedLanesIDsUsed": false, + "recentMAPmessageUpdate": false, + "failureMode": false, + "noValidMAPisAvailableAtThisTime": false, + "signalPriorityIsActive": false, + "preemptIsActive": false + } +},{ + "_id": { + "$oid": "665f3fc18c25216ef4f53fba" + }, + "validationMessages": [ + { + "schemaPath": "#/$defs/J2735RoadRegulatorID/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].id.region", + "message": "$.payload.data.intersectionStateList.intersectionStatelist[0].id.region: null found, integer expected" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[0].state_time_speed.movementEventList[0].timing.startTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[0].state_time_speed.movementEventList[0].timing.nextTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[1].state_time_speed.movementEventList[0].timing.startTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[1].state_time_speed.movementEventList[0].timing.nextTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[2].state_time_speed.movementEventList[0].timing.startTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[2].state_time_speed.movementEventList[0].timing.nextTime", + "message": "DE_TimeMark is missing" + } + ], + "utcTimeStamp": "2024-06-04T16:22:54.525Z", + "schemaVersion": 1, + "recordGeneratedAt": { + "$date": { + "$numberLong": "1717518174532" + } + }, + "revision": 0, + "states": [ + { + "stateTimeSpeed": [ + { + "eventState": "PROTECTED_CLEARANCE", + "timing": { + "minEndTime": "2024-06-04T16:03:17Z", + "maxEndTime": "2024-06-04T16:03:17Z" + } + } + ], + "signalGroup": 2 + }, + { + "stateTimeSpeed": [ + { + "eventState": "STOP_AND_REMAIN", + "timing": { + "minEndTime": "2024-06-04T16:03:20.1Z", + "maxEndTime": "2024-06-04T16:03:20.1Z" + } + } + ], + "signalGroup": 4 + }, + { + "stateTimeSpeed": [ + { + "eventState": "PROTECTED_CLEARANCE", + "timing": { + "minEndTime": "2024-06-04T16:03:17Z", + "maxEndTime": "2024-06-04T16:03:17Z" + } + } + ], + "signalGroup": 6 + } + ], + "messageType": "SPAT", + "cti4501Conformant": false, + "intersectionId": 12109, + "odeReceivedAt": "2024-06-04T16:22:54.525Z", + "originIp": "10.11.81.12", + "status": { + "failureFlash": false, + "noValidSPATisAvailableAtThisTime": false, + "fixedTimeOperation": false, + "standbyOperation": false, + "trafficDependentOperation": false, + "manualControlIsEnabled": false, + "off": false, + "stopTimeIsActivated": false, + "recentChangeInMAPassignedLanesIDsUsed": false, + "recentMAPmessageUpdate": false, + "failureMode": false, + "noValidMAPisAvailableAtThisTime": false, + "signalPriorityIsActive": false, + "preemptIsActive": false + } +},{ + "_id": { + "$oid": "665f3fc18c25216ef4f53fbb" + }, + "validationMessages": [ + { + "schemaPath": "#/$defs/J2735RoadRegulatorID/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].id.region", + "message": "$.payload.data.intersectionStateList.intersectionStatelist[0].id.region: null found, integer expected" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[0].state_time_speed.movementEventList[0].timing.startTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[0].state_time_speed.movementEventList[0].timing.nextTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[1].state_time_speed.movementEventList[0].timing.startTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[1].state_time_speed.movementEventList[0].timing.nextTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[2].state_time_speed.movementEventList[0].timing.startTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[2].state_time_speed.movementEventList[0].timing.nextTime", + "message": "DE_TimeMark is missing" + } + ], + "utcTimeStamp": "2024-06-04T16:22:54.631Z", + "schemaVersion": 1, + "recordGeneratedAt": { + "$date": { + "$numberLong": "1717518174641" + } + }, + "revision": 0, + "states": [ + { + "stateTimeSpeed": [ + { + "eventState": "PROTECTED_CLEARANCE", + "timing": { + "minEndTime": "2024-06-04T16:03:19Z", + "maxEndTime": "2024-06-04T16:03:19Z" + } + } + ], + "signalGroup": 2 + }, + { + "stateTimeSpeed": [ + { + "eventState": "STOP_AND_REMAIN", + "timing": { + "minEndTime": "2024-06-04T16:03:22.1Z", + "maxEndTime": "2024-06-04T16:03:22.1Z" + } + } + ], + "signalGroup": 4 + }, + { + "stateTimeSpeed": [ + { + "eventState": "PROTECTED_CLEARANCE", + "timing": { + "minEndTime": "2024-06-04T16:03:19Z", + "maxEndTime": "2024-06-04T16:03:19Z" + } + } + ], + "signalGroup": 6 + } + ], + "messageType": "SPAT", + "cti4501Conformant": false, + "intersectionId": 12109, + "odeReceivedAt": "2024-06-04T16:22:54.631Z", + "originIp": "10.11.81.12", + "status": { + "failureFlash": false, + "noValidSPATisAvailableAtThisTime": false, + "fixedTimeOperation": false, + "standbyOperation": false, + "trafficDependentOperation": false, + "manualControlIsEnabled": false, + "off": false, + "stopTimeIsActivated": false, + "recentChangeInMAPassignedLanesIDsUsed": false, + "recentMAPmessageUpdate": false, + "failureMode": false, + "noValidMAPisAvailableAtThisTime": false, + "signalPriorityIsActive": false, + "preemptIsActive": false + } +},{ + "_id": { + "$oid": "665f3fc18c25216ef4f53fbc" + }, + "validationMessages": [ + { + "schemaPath": "#/$defs/J2735RoadRegulatorID/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].id.region", + "message": "$.payload.data.intersectionStateList.intersectionStatelist[0].id.region: null found, integer expected" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[0].state_time_speed.movementEventList[0].timing.startTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[0].state_time_speed.movementEventList[0].timing.nextTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[1].state_time_speed.movementEventList[0].timing.startTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[1].state_time_speed.movementEventList[0].timing.nextTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[2].state_time_speed.movementEventList[0].timing.startTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[2].state_time_speed.movementEventList[0].timing.nextTime", + "message": "DE_TimeMark is missing" + } + ], + "utcTimeStamp": "2024-06-04T16:22:54.738Z", + "schemaVersion": 1, + "recordGeneratedAt": { + "$date": { + "$numberLong": "1717518174746" + } + }, + "revision": 0, + "states": [ + { + "stateTimeSpeed": [ + { + "eventState": "PROTECTED_CLEARANCE", + "timing": { + "minEndTime": "2024-06-04T16:03:19Z", + "maxEndTime": "2024-06-04T16:03:19Z" + } + } + ], + "signalGroup": 2 + }, + { + "stateTimeSpeed": [ + { + "eventState": "STOP_AND_REMAIN", + "timing": { + "minEndTime": "2024-06-04T16:03:22.1Z", + "maxEndTime": "2024-06-04T16:03:22.1Z" + } + } + ], + "signalGroup": 4 + }, + { + "stateTimeSpeed": [ + { + "eventState": "PROTECTED_CLEARANCE", + "timing": { + "minEndTime": "2024-06-04T16:03:19Z", + "maxEndTime": "2024-06-04T16:03:19Z" + } + } + ], + "signalGroup": 6 + } + ], + "messageType": "SPAT", + "cti4501Conformant": false, + "intersectionId": 12109, + "odeReceivedAt": "2024-06-04T16:22:54.738Z", + "originIp": "10.11.81.12", + "status": { + "failureFlash": false, + "noValidSPATisAvailableAtThisTime": false, + "fixedTimeOperation": false, + "standbyOperation": false, + "trafficDependentOperation": false, + "manualControlIsEnabled": false, + "off": false, + "stopTimeIsActivated": false, + "recentChangeInMAPassignedLanesIDsUsed": false, + "recentMAPmessageUpdate": false, + "failureMode": false, + "noValidMAPisAvailableAtThisTime": false, + "signalPriorityIsActive": false, + "preemptIsActive": false + } +},{ + "_id": { + "$oid": "665f3fc18c25216ef4f53fbd" + }, + "validationMessages": [ + { + "schemaPath": "#/$defs/J2735RoadRegulatorID/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].id.region", + "message": "$.payload.data.intersectionStateList.intersectionStatelist[0].id.region: null found, integer expected" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[0].state_time_speed.movementEventList[0].timing.startTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[0].state_time_speed.movementEventList[0].timing.nextTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[1].state_time_speed.movementEventList[0].timing.startTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[1].state_time_speed.movementEventList[0].timing.nextTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[2].state_time_speed.movementEventList[0].timing.startTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[2].state_time_speed.movementEventList[0].timing.nextTime", + "message": "DE_TimeMark is missing" + } + ], + "utcTimeStamp": "2024-06-04T16:22:54.846Z", + "schemaVersion": 1, + "recordGeneratedAt": { + "$date": { + "$numberLong": "1717518174854" + } + }, + "revision": 0, + "states": [ + { + "stateTimeSpeed": [ + { + "eventState": "PROTECTED_CLEARANCE", + "timing": { + "minEndTime": "2024-06-04T16:03:19Z", + "maxEndTime": "2024-06-04T16:03:19Z" + } + } + ], + "signalGroup": 2 + }, + { + "stateTimeSpeed": [ + { + "eventState": "STOP_AND_REMAIN", + "timing": { + "minEndTime": "2024-06-04T16:03:22.1Z", + "maxEndTime": "2024-06-04T16:03:22.1Z" + } + } + ], + "signalGroup": 4 + }, + { + "stateTimeSpeed": [ + { + "eventState": "PROTECTED_CLEARANCE", + "timing": { + "minEndTime": "2024-06-04T16:03:19Z", + "maxEndTime": "2024-06-04T16:03:19Z" + } + } + ], + "signalGroup": 6 + } + ], + "messageType": "SPAT", + "cti4501Conformant": false, + "intersectionId": 12109, + "odeReceivedAt": "2024-06-04T16:22:54.846Z", + "originIp": "10.11.81.12", + "status": { + "failureFlash": false, + "noValidSPATisAvailableAtThisTime": false, + "fixedTimeOperation": false, + "standbyOperation": false, + "trafficDependentOperation": false, + "manualControlIsEnabled": false, + "off": false, + "stopTimeIsActivated": false, + "recentChangeInMAPassignedLanesIDsUsed": false, + "recentMAPmessageUpdate": false, + "failureMode": false, + "noValidMAPisAvailableAtThisTime": false, + "signalPriorityIsActive": false, + "preemptIsActive": false + } +},{ + "_id": { + "$oid": "665f3fc18c25216ef4f53fbe" + }, + "validationMessages": [ + { + "schemaPath": "#/$defs/J2735RoadRegulatorID/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].id.region", + "message": "$.payload.data.intersectionStateList.intersectionStatelist[0].id.region: null found, integer expected" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[0].state_time_speed.movementEventList[0].timing.startTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[0].state_time_speed.movementEventList[0].timing.nextTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[1].state_time_speed.movementEventList[0].timing.startTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[1].state_time_speed.movementEventList[0].timing.nextTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[2].state_time_speed.movementEventList[0].timing.startTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[2].state_time_speed.movementEventList[0].timing.nextTime", + "message": "DE_TimeMark is missing" + } + ], + "utcTimeStamp": "2024-06-04T16:22:54.951Z", + "schemaVersion": 1, + "recordGeneratedAt": { + "$date": { + "$numberLong": "1717518174961" + } + }, + "revision": 0, + "states": [ + { + "stateTimeSpeed": [ + { + "eventState": "PROTECTED_CLEARANCE", + "timing": { + "minEndTime": "2024-06-04T16:03:19Z", + "maxEndTime": "2024-06-04T16:03:19Z" + } + } + ], + "signalGroup": 2 + }, + { + "stateTimeSpeed": [ + { + "eventState": "STOP_AND_REMAIN", + "timing": { + "minEndTime": "2024-06-04T16:03:22.1Z", + "maxEndTime": "2024-06-04T16:03:22.1Z" + } + } + ], + "signalGroup": 4 + }, + { + "stateTimeSpeed": [ + { + "eventState": "PROTECTED_CLEARANCE", + "timing": { + "minEndTime": "2024-06-04T16:03:19Z", + "maxEndTime": "2024-06-04T16:03:19Z" + } + } + ], + "signalGroup": 6 + } + ], + "messageType": "SPAT", + "cti4501Conformant": false, + "intersectionId": 12109, + "odeReceivedAt": "2024-06-04T16:22:54.951Z", + "originIp": "10.11.81.12", + "status": { + "failureFlash": false, + "noValidSPATisAvailableAtThisTime": false, + "fixedTimeOperation": false, + "standbyOperation": false, + "trafficDependentOperation": false, + "manualControlIsEnabled": false, + "off": false, + "stopTimeIsActivated": false, + "recentChangeInMAPassignedLanesIDsUsed": false, + "recentMAPmessageUpdate": false, + "failureMode": false, + "noValidMAPisAvailableAtThisTime": false, + "signalPriorityIsActive": false, + "preemptIsActive": false + } +},{ + "_id": { + "$oid": "665f3fc18c25216ef4f53fbf" + }, + "validationMessages": [ + { + "schemaPath": "#/$defs/J2735RoadRegulatorID/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].id.region", + "message": "$.payload.data.intersectionStateList.intersectionStatelist[0].id.region: null found, integer expected" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[0].state_time_speed.movementEventList[0].timing.startTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[0].state_time_speed.movementEventList[0].timing.nextTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[1].state_time_speed.movementEventList[0].timing.startTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[1].state_time_speed.movementEventList[0].timing.nextTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[2].state_time_speed.movementEventList[0].timing.startTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[2].state_time_speed.movementEventList[0].timing.nextTime", + "message": "DE_TimeMark is missing" + } + ], + "utcTimeStamp": "2024-06-04T16:22:55.058Z", + "schemaVersion": 1, + "recordGeneratedAt": { + "$date": { + "$numberLong": "1717518175066" + } + }, + "revision": 0, + "states": [ + { + "stateTimeSpeed": [ + { + "eventState": "PROTECTED_CLEARANCE", + "timing": { + "minEndTime": "2024-06-04T16:03:19Z", + "maxEndTime": "2024-06-04T16:03:19Z" + } + } + ], + "signalGroup": 2 + }, + { + "stateTimeSpeed": [ + { + "eventState": "STOP_AND_REMAIN", + "timing": { + "minEndTime": "2024-06-04T16:03:22.1Z", + "maxEndTime": "2024-06-04T16:03:22.1Z" + } + } + ], + "signalGroup": 4 + }, + { + "stateTimeSpeed": [ + { + "eventState": "PROTECTED_CLEARANCE", + "timing": { + "minEndTime": "2024-06-04T16:03:19Z", + "maxEndTime": "2024-06-04T16:03:19Z" + } + } + ], + "signalGroup": 6 + } + ], + "messageType": "SPAT", + "cti4501Conformant": false, + "intersectionId": 12109, + "odeReceivedAt": "2024-06-04T16:22:55.058Z", + "originIp": "10.11.81.12", + "status": { + "failureFlash": false, + "noValidSPATisAvailableAtThisTime": false, + "fixedTimeOperation": false, + "standbyOperation": false, + "trafficDependentOperation": false, + "manualControlIsEnabled": false, + "off": false, + "stopTimeIsActivated": false, + "recentChangeInMAPassignedLanesIDsUsed": false, + "recentMAPmessageUpdate": false, + "failureMode": false, + "noValidMAPisAvailableAtThisTime": false, + "signalPriorityIsActive": false, + "preemptIsActive": false + } +},{ + "_id": { + "$oid": "665f3fc18c25216ef4f53fc0" + }, + "validationMessages": [ + { + "schemaPath": "#/$defs/J2735RoadRegulatorID/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].id.region", + "message": "$.payload.data.intersectionStateList.intersectionStatelist[0].id.region: null found, integer expected" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[0].state_time_speed.movementEventList[0].timing.startTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[0].state_time_speed.movementEventList[0].timing.nextTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[1].state_time_speed.movementEventList[0].timing.startTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[1].state_time_speed.movementEventList[0].timing.nextTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[2].state_time_speed.movementEventList[0].timing.startTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[2].state_time_speed.movementEventList[0].timing.nextTime", + "message": "DE_TimeMark is missing" + } + ], + "utcTimeStamp": "2024-06-04T16:22:55.166Z", + "schemaVersion": 1, + "recordGeneratedAt": { + "$date": { + "$numberLong": "1717518175173" + } + }, + "revision": 0, + "states": [ + { + "stateTimeSpeed": [ + { + "eventState": "PROTECTED_CLEARANCE", + "timing": { + "minEndTime": "2024-06-04T16:03:19Z", + "maxEndTime": "2024-06-04T16:03:19Z" + } + } + ], + "signalGroup": 2 + }, + { + "stateTimeSpeed": [ + { + "eventState": "STOP_AND_REMAIN", + "timing": { + "minEndTime": "2024-06-04T16:03:22.1Z", + "maxEndTime": "2024-06-04T16:03:22.1Z" + } + } + ], + "signalGroup": 4 + }, + { + "stateTimeSpeed": [ + { + "eventState": "PROTECTED_CLEARANCE", + "timing": { + "minEndTime": "2024-06-04T16:03:19Z", + "maxEndTime": "2024-06-04T16:03:19Z" + } + } + ], + "signalGroup": 6 + } + ], + "messageType": "SPAT", + "cti4501Conformant": false, + "intersectionId": 12109, + "odeReceivedAt": "2024-06-04T16:22:55.166Z", + "originIp": "10.11.81.12", + "status": { + "failureFlash": false, + "noValidSPATisAvailableAtThisTime": false, + "fixedTimeOperation": false, + "standbyOperation": false, + "trafficDependentOperation": false, + "manualControlIsEnabled": false, + "off": false, + "stopTimeIsActivated": false, + "recentChangeInMAPassignedLanesIDsUsed": false, + "recentMAPmessageUpdate": false, + "failureMode": false, + "noValidMAPisAvailableAtThisTime": false, + "signalPriorityIsActive": false, + "preemptIsActive": false + } +},{ + "_id": { + "$oid": "665f3fc18c25216ef4f53fc1" + }, + "validationMessages": [ + { + "schemaPath": "#/$defs/J2735RoadRegulatorID/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].id.region", + "message": "$.payload.data.intersectionStateList.intersectionStatelist[0].id.region: null found, integer expected" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[0].state_time_speed.movementEventList[0].timing.startTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[0].state_time_speed.movementEventList[0].timing.nextTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[1].state_time_speed.movementEventList[0].timing.startTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[1].state_time_speed.movementEventList[0].timing.nextTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[2].state_time_speed.movementEventList[0].timing.startTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[2].state_time_speed.movementEventList[0].timing.nextTime", + "message": "DE_TimeMark is missing" + } + ], + "utcTimeStamp": "2024-06-04T16:22:55.272Z", + "schemaVersion": 1, + "recordGeneratedAt": { + "$date": { + "$numberLong": "1717518175281" + } + }, + "revision": 0, + "states": [ + { + "stateTimeSpeed": [ + { + "eventState": "PROTECTED_CLEARANCE", + "timing": { + "minEndTime": "2024-06-04T16:03:19Z", + "maxEndTime": "2024-06-04T16:03:19Z" + } + } + ], + "signalGroup": 2 + }, + { + "stateTimeSpeed": [ + { + "eventState": "STOP_AND_REMAIN", + "timing": { + "minEndTime": "2024-06-04T16:03:22.1Z", + "maxEndTime": "2024-06-04T16:03:22.1Z" + } + } + ], + "signalGroup": 4 + }, + { + "stateTimeSpeed": [ + { + "eventState": "PROTECTED_CLEARANCE", + "timing": { + "minEndTime": "2024-06-04T16:03:19Z", + "maxEndTime": "2024-06-04T16:03:19Z" + } + } + ], + "signalGroup": 6 + } + ], + "messageType": "SPAT", + "cti4501Conformant": false, + "intersectionId": 12109, + "odeReceivedAt": "2024-06-04T16:22:55.272Z", + "originIp": "10.11.81.12", + "status": { + "failureFlash": false, + "noValidSPATisAvailableAtThisTime": false, + "fixedTimeOperation": false, + "standbyOperation": false, + "trafficDependentOperation": false, + "manualControlIsEnabled": false, + "off": false, + "stopTimeIsActivated": false, + "recentChangeInMAPassignedLanesIDsUsed": false, + "recentMAPmessageUpdate": false, + "failureMode": false, + "noValidMAPisAvailableAtThisTime": false, + "signalPriorityIsActive": false, + "preemptIsActive": false + } +},{ + "_id": { + "$oid": "665f3fc18c25216ef4f53fc2" + }, + "validationMessages": [ + { + "schemaPath": "#/$defs/J2735RoadRegulatorID/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].id.region", + "message": "$.payload.data.intersectionStateList.intersectionStatelist[0].id.region: null found, integer expected" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[0].state_time_speed.movementEventList[0].timing.startTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[0].state_time_speed.movementEventList[0].timing.nextTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[1].state_time_speed.movementEventList[0].timing.startTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[1].state_time_speed.movementEventList[0].timing.nextTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[2].state_time_speed.movementEventList[0].timing.startTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[2].state_time_speed.movementEventList[0].timing.nextTime", + "message": "DE_TimeMark is missing" + } + ], + "utcTimeStamp": "2024-06-04T16:22:55.38Z", + "schemaVersion": 1, + "recordGeneratedAt": { + "$date": { + "$numberLong": "1717518175388" + } + }, + "revision": 0, + "states": [ + { + "stateTimeSpeed": [ + { + "eventState": "PROTECTED_CLEARANCE", + "timing": { + "minEndTime": "2024-06-04T16:03:19Z", + "maxEndTime": "2024-06-04T16:03:19Z" + } + } + ], + "signalGroup": 2 + }, + { + "stateTimeSpeed": [ + { + "eventState": "STOP_AND_REMAIN", + "timing": { + "minEndTime": "2024-06-04T16:03:22.1Z", + "maxEndTime": "2024-06-04T16:03:22.1Z" + } + } + ], + "signalGroup": 4 + }, + { + "stateTimeSpeed": [ + { + "eventState": "PROTECTED_CLEARANCE", + "timing": { + "minEndTime": "2024-06-04T16:03:19Z", + "maxEndTime": "2024-06-04T16:03:19Z" + } + } + ], + "signalGroup": 6 + } + ], + "messageType": "SPAT", + "cti4501Conformant": false, + "intersectionId": 12109, + "odeReceivedAt": "2024-06-04T16:22:55.38Z", + "originIp": "10.11.81.12", + "status": { + "failureFlash": false, + "noValidSPATisAvailableAtThisTime": false, + "fixedTimeOperation": false, + "standbyOperation": false, + "trafficDependentOperation": false, + "manualControlIsEnabled": false, + "off": false, + "stopTimeIsActivated": false, + "recentChangeInMAPassignedLanesIDsUsed": false, + "recentMAPmessageUpdate": false, + "failureMode": false, + "noValidMAPisAvailableAtThisTime": false, + "signalPriorityIsActive": false, + "preemptIsActive": false + } +},{ + "_id": { + "$oid": "665f3fc18c25216ef4f53fc3" + }, + "validationMessages": [ + { + "schemaPath": "#/$defs/J2735RoadRegulatorID/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].id.region", + "message": "$.payload.data.intersectionStateList.intersectionStatelist[0].id.region: null found, integer expected" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[0].state_time_speed.movementEventList[0].timing.startTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[0].state_time_speed.movementEventList[0].timing.nextTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[1].state_time_speed.movementEventList[0].timing.startTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[1].state_time_speed.movementEventList[0].timing.nextTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[2].state_time_speed.movementEventList[0].timing.startTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[2].state_time_speed.movementEventList[0].timing.nextTime", + "message": "DE_TimeMark is missing" + } + ], + "utcTimeStamp": "2024-06-04T16:22:55.487Z", + "schemaVersion": 1, + "recordGeneratedAt": { + "$date": { + "$numberLong": "1717518175494" + } + }, + "revision": 0, + "states": [ + { + "stateTimeSpeed": [ + { + "eventState": "PROTECTED_CLEARANCE", + "timing": { + "minEndTime": "2024-06-04T16:03:19Z", + "maxEndTime": "2024-06-04T16:03:19Z" + } + } + ], + "signalGroup": 2 + }, + { + "stateTimeSpeed": [ + { + "eventState": "STOP_AND_REMAIN", + "timing": { + "minEndTime": "2024-06-04T16:03:22.1Z", + "maxEndTime": "2024-06-04T16:03:22.1Z" + } + } + ], + "signalGroup": 4 + }, + { + "stateTimeSpeed": [ + { + "eventState": "PROTECTED_CLEARANCE", + "timing": { + "minEndTime": "2024-06-04T16:03:19Z", + "maxEndTime": "2024-06-04T16:03:19Z" + } + } + ], + "signalGroup": 6 + } + ], + "messageType": "SPAT", + "cti4501Conformant": false, + "intersectionId": 12109, + "odeReceivedAt": "2024-06-04T16:22:55.487Z", + "originIp": "10.11.81.12", + "status": { + "failureFlash": false, + "noValidSPATisAvailableAtThisTime": false, + "fixedTimeOperation": false, + "standbyOperation": false, + "trafficDependentOperation": false, + "manualControlIsEnabled": false, + "off": false, + "stopTimeIsActivated": false, + "recentChangeInMAPassignedLanesIDsUsed": false, + "recentMAPmessageUpdate": false, + "failureMode": false, + "noValidMAPisAvailableAtThisTime": false, + "signalPriorityIsActive": false, + "preemptIsActive": false + } +},{ + "_id": { + "$oid": "665f3fc18c25216ef4f53fc4" + }, + "validationMessages": [ + { + "schemaPath": "#/$defs/J2735RoadRegulatorID/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].id.region", + "message": "$.payload.data.intersectionStateList.intersectionStatelist[0].id.region: null found, integer expected" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[0].state_time_speed.movementEventList[0].timing.startTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[0].state_time_speed.movementEventList[0].timing.nextTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[1].state_time_speed.movementEventList[0].timing.startTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[1].state_time_speed.movementEventList[0].timing.nextTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[2].state_time_speed.movementEventList[0].timing.startTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[2].state_time_speed.movementEventList[0].timing.nextTime", + "message": "DE_TimeMark is missing" + } + ], + "utcTimeStamp": "2024-06-04T16:22:55.594Z", + "schemaVersion": 1, + "recordGeneratedAt": { + "$date": { + "$numberLong": "1717518175602" + } + }, + "revision": 0, + "states": [ + { + "stateTimeSpeed": [ + { + "eventState": "PROTECTED_CLEARANCE", + "timing": { + "minEndTime": "2024-06-04T16:03:19Z", + "maxEndTime": "2024-06-04T16:03:19Z" + } + } + ], + "signalGroup": 2 + }, + { + "stateTimeSpeed": [ + { + "eventState": "STOP_AND_REMAIN", + "timing": { + "minEndTime": "2024-06-04T16:03:22.1Z", + "maxEndTime": "2024-06-04T16:03:22.1Z" + } + } + ], + "signalGroup": 4 + }, + { + "stateTimeSpeed": [ + { + "eventState": "PROTECTED_CLEARANCE", + "timing": { + "minEndTime": "2024-06-04T16:03:19Z", + "maxEndTime": "2024-06-04T16:03:19Z" + } + } + ], + "signalGroup": 6 + } + ], + "messageType": "SPAT", + "cti4501Conformant": false, + "intersectionId": 12109, + "odeReceivedAt": "2024-06-04T16:22:55.594Z", + "originIp": "10.11.81.12", + "status": { + "failureFlash": false, + "noValidSPATisAvailableAtThisTime": false, + "fixedTimeOperation": false, + "standbyOperation": false, + "trafficDependentOperation": false, + "manualControlIsEnabled": false, + "off": false, + "stopTimeIsActivated": false, + "recentChangeInMAPassignedLanesIDsUsed": false, + "recentMAPmessageUpdate": false, + "failureMode": false, + "noValidMAPisAvailableAtThisTime": false, + "signalPriorityIsActive": false, + "preemptIsActive": false + } +},{ + "_id": { + "$oid": "665f3fc18c25216ef4f53fc5" + }, + "validationMessages": [ + { + "schemaPath": "#/$defs/J2735RoadRegulatorID/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].id.region", + "message": "$.payload.data.intersectionStateList.intersectionStatelist[0].id.region: null found, integer expected" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[0].state_time_speed.movementEventList[0].timing.startTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[0].state_time_speed.movementEventList[0].timing.nextTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[1].state_time_speed.movementEventList[0].timing.startTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[1].state_time_speed.movementEventList[0].timing.nextTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[2].state_time_speed.movementEventList[0].timing.startTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[2].state_time_speed.movementEventList[0].timing.nextTime", + "message": "DE_TimeMark is missing" + } + ], + "utcTimeStamp": "2024-06-04T16:22:55.701Z", + "schemaVersion": 1, + "recordGeneratedAt": { + "$date": { + "$numberLong": "1717518175708" + } + }, + "revision": 0, + "states": [ + { + "stateTimeSpeed": [ + { + "eventState": "PROTECTED_CLEARANCE", + "timing": { + "minEndTime": "2024-06-04T16:03:19Z", + "maxEndTime": "2024-06-04T16:03:19Z" + } + } + ], + "signalGroup": 2 + }, + { + "stateTimeSpeed": [ + { + "eventState": "STOP_AND_REMAIN", + "timing": { + "minEndTime": "2024-06-04T16:03:22.1Z", + "maxEndTime": "2024-06-04T16:03:22.1Z" + } + } + ], + "signalGroup": 4 + }, + { + "stateTimeSpeed": [ + { + "eventState": "PROTECTED_CLEARANCE", + "timing": { + "minEndTime": "2024-06-04T16:03:19Z", + "maxEndTime": "2024-06-04T16:03:19Z" + } + } + ], + "signalGroup": 6 + } + ], + "messageType": "SPAT", + "cti4501Conformant": false, + "intersectionId": 12109, + "odeReceivedAt": "2024-06-04T16:22:55.701Z", + "originIp": "10.11.81.12", + "status": { + "failureFlash": false, + "noValidSPATisAvailableAtThisTime": false, + "fixedTimeOperation": false, + "standbyOperation": false, + "trafficDependentOperation": false, + "manualControlIsEnabled": false, + "off": false, + "stopTimeIsActivated": false, + "recentChangeInMAPassignedLanesIDsUsed": false, + "recentMAPmessageUpdate": false, + "failureMode": false, + "noValidMAPisAvailableAtThisTime": false, + "signalPriorityIsActive": false, + "preemptIsActive": false + } +},{ + "_id": { + "$oid": "665f3fc18c25216ef4f53fc6" + }, + "validationMessages": [ + { + "schemaPath": "#/$defs/J2735RoadRegulatorID/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].id.region", + "message": "$.payload.data.intersectionStateList.intersectionStatelist[0].id.region: null found, integer expected" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[0].state_time_speed.movementEventList[0].timing.startTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[0].state_time_speed.movementEventList[0].timing.nextTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[1].state_time_speed.movementEventList[0].timing.startTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[1].state_time_speed.movementEventList[0].timing.nextTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[2].state_time_speed.movementEventList[0].timing.startTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[2].state_time_speed.movementEventList[0].timing.nextTime", + "message": "DE_TimeMark is missing" + } + ], + "utcTimeStamp": "2024-06-04T16:22:55.795Z", + "schemaVersion": 1, + "recordGeneratedAt": { + "$date": { + "$numberLong": "1717518175800" + } + }, + "revision": 0, + "states": [ + { + "stateTimeSpeed": [ + { + "eventState": "PROTECTED_CLEARANCE", + "timing": { + "minEndTime": "2024-06-04T16:03:19Z", + "maxEndTime": "2024-06-04T16:03:19Z" + } + } + ], + "signalGroup": 2 + }, + { + "stateTimeSpeed": [ + { + "eventState": "STOP_AND_REMAIN", + "timing": { + "minEndTime": "2024-06-04T16:03:22.1Z", + "maxEndTime": "2024-06-04T16:03:22.1Z" + } + } + ], + "signalGroup": 4 + }, + { + "stateTimeSpeed": [ + { + "eventState": "PROTECTED_CLEARANCE", + "timing": { + "minEndTime": "2024-06-04T16:03:19Z", + "maxEndTime": "2024-06-04T16:03:19Z" + } + } + ], + "signalGroup": 6 + } + ], + "messageType": "SPAT", + "cti4501Conformant": false, + "intersectionId": 12109, + "odeReceivedAt": "2024-06-04T16:22:55.795Z", + "originIp": "10.11.81.12", + "status": { + "failureFlash": false, + "noValidSPATisAvailableAtThisTime": false, + "fixedTimeOperation": false, + "standbyOperation": false, + "trafficDependentOperation": false, + "manualControlIsEnabled": false, + "off": false, + "stopTimeIsActivated": false, + "recentChangeInMAPassignedLanesIDsUsed": false, + "recentMAPmessageUpdate": false, + "failureMode": false, + "noValidMAPisAvailableAtThisTime": false, + "signalPriorityIsActive": false, + "preemptIsActive": false + } +},{ + "_id": { + "$oid": "665f3fc18c25216ef4f53fc7" + }, + "validationMessages": [ + { + "schemaPath": "#/$defs/J2735RoadRegulatorID/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].id.region", + "message": "$.payload.data.intersectionStateList.intersectionStatelist[0].id.region: null found, integer expected" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[0].state_time_speed.movementEventList[0].timing.startTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[0].state_time_speed.movementEventList[0].timing.nextTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[1].state_time_speed.movementEventList[0].timing.startTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[1].state_time_speed.movementEventList[0].timing.nextTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[2].state_time_speed.movementEventList[0].timing.startTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[2].state_time_speed.movementEventList[0].timing.nextTime", + "message": "DE_TimeMark is missing" + } + ], + "utcTimeStamp": "2024-06-04T16:22:55.902Z", + "schemaVersion": 1, + "recordGeneratedAt": { + "$date": { + "$numberLong": "1717518175906" + } + }, + "revision": 0, + "states": [ + { + "stateTimeSpeed": [ + { + "eventState": "PROTECTED_CLEARANCE", + "timing": { + "minEndTime": "2024-06-04T16:03:19Z", + "maxEndTime": "2024-06-04T16:03:19Z" + } + } + ], + "signalGroup": 2 + }, + { + "stateTimeSpeed": [ + { + "eventState": "STOP_AND_REMAIN", + "timing": { + "minEndTime": "2024-06-04T16:03:22.1Z", + "maxEndTime": "2024-06-04T16:03:22.1Z" + } + } + ], + "signalGroup": 4 + }, + { + "stateTimeSpeed": [ + { + "eventState": "PROTECTED_CLEARANCE", + "timing": { + "minEndTime": "2024-06-04T16:03:19Z", + "maxEndTime": "2024-06-04T16:03:19Z" + } + } + ], + "signalGroup": 6 + } + ], + "messageType": "SPAT", + "cti4501Conformant": false, + "intersectionId": 12109, + "odeReceivedAt": "2024-06-04T16:22:55.902Z", + "originIp": "10.11.81.12", + "status": { + "failureFlash": false, + "noValidSPATisAvailableAtThisTime": false, + "fixedTimeOperation": false, + "standbyOperation": false, + "trafficDependentOperation": false, + "manualControlIsEnabled": false, + "off": false, + "stopTimeIsActivated": false, + "recentChangeInMAPassignedLanesIDsUsed": false, + "recentMAPmessageUpdate": false, + "failureMode": false, + "noValidMAPisAvailableAtThisTime": false, + "signalPriorityIsActive": false, + "preemptIsActive": false + } +},{ + "_id": { + "$oid": "665f3fc18c25216ef4f53fc8" + }, + "validationMessages": [ + { + "schemaPath": "#/$defs/J2735RoadRegulatorID/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].id.region", + "message": "$.payload.data.intersectionStateList.intersectionStatelist[0].id.region: null found, integer expected" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[0].state_time_speed.movementEventList[0].timing.startTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[0].state_time_speed.movementEventList[0].timing.nextTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[1].state_time_speed.movementEventList[0].timing.startTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[1].state_time_speed.movementEventList[0].timing.nextTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[2].state_time_speed.movementEventList[0].timing.startTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[2].state_time_speed.movementEventList[0].timing.nextTime", + "message": "DE_TimeMark is missing" + } + ], + "utcTimeStamp": "2024-06-04T16:22:56.007Z", + "schemaVersion": 1, + "recordGeneratedAt": { + "$date": { + "$numberLong": "1717518176013" + } + }, + "revision": 0, + "states": [ + { + "stateTimeSpeed": [ + { + "eventState": "PROTECTED_CLEARANCE", + "timing": { + "minEndTime": "2024-06-04T16:03:19Z", + "maxEndTime": "2024-06-04T16:03:19Z" + } + } + ], + "signalGroup": 2 + }, + { + "stateTimeSpeed": [ + { + "eventState": "STOP_AND_REMAIN", + "timing": { + "minEndTime": "2024-06-04T16:03:22.1Z", + "maxEndTime": "2024-06-04T16:03:22.1Z" + } + } + ], + "signalGroup": 4 + }, + { + "stateTimeSpeed": [ + { + "eventState": "PROTECTED_CLEARANCE", + "timing": { + "minEndTime": "2024-06-04T16:03:19Z", + "maxEndTime": "2024-06-04T16:03:19Z" + } + } + ], + "signalGroup": 6 + } + ], + "messageType": "SPAT", + "cti4501Conformant": false, + "intersectionId": 12109, + "odeReceivedAt": "2024-06-04T16:22:56.007Z", + "originIp": "10.11.81.12", + "status": { + "failureFlash": false, + "noValidSPATisAvailableAtThisTime": false, + "fixedTimeOperation": false, + "standbyOperation": false, + "trafficDependentOperation": false, + "manualControlIsEnabled": false, + "off": false, + "stopTimeIsActivated": false, + "recentChangeInMAPassignedLanesIDsUsed": false, + "recentMAPmessageUpdate": false, + "failureMode": false, + "noValidMAPisAvailableAtThisTime": false, + "signalPriorityIsActive": false, + "preemptIsActive": false + } +},{ + "_id": { + "$oid": "665f3fc18c25216ef4f53fc9" + }, + "validationMessages": [ + { + "schemaPath": "#/$defs/J2735RoadRegulatorID/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].id.region", + "message": "$.payload.data.intersectionStateList.intersectionStatelist[0].id.region: null found, integer expected" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[0].state_time_speed.movementEventList[0].timing.startTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[0].state_time_speed.movementEventList[0].timing.nextTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[1].state_time_speed.movementEventList[0].timing.startTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[1].state_time_speed.movementEventList[0].timing.nextTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[2].state_time_speed.movementEventList[0].timing.startTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[2].state_time_speed.movementEventList[0].timing.nextTime", + "message": "DE_TimeMark is missing" + } + ], + "utcTimeStamp": "2024-06-04T16:22:56.116Z", + "schemaVersion": 1, + "recordGeneratedAt": { + "$date": { + "$numberLong": "1717518176119" + } + }, + "revision": 0, + "states": [ + { + "stateTimeSpeed": [ + { + "eventState": "PROTECTED_CLEARANCE", + "timing": { + "minEndTime": "2024-06-04T16:03:19Z", + "maxEndTime": "2024-06-04T16:03:19Z" + } + } + ], + "signalGroup": 2 + }, + { + "stateTimeSpeed": [ + { + "eventState": "STOP_AND_REMAIN", + "timing": { + "minEndTime": "2024-06-04T16:03:22.1Z", + "maxEndTime": "2024-06-04T16:03:22.1Z" + } + } + ], + "signalGroup": 4 + }, + { + "stateTimeSpeed": [ + { + "eventState": "PROTECTED_CLEARANCE", + "timing": { + "minEndTime": "2024-06-04T16:03:19Z", + "maxEndTime": "2024-06-04T16:03:19Z" + } + } + ], + "signalGroup": 6 + } + ], + "messageType": "SPAT", + "cti4501Conformant": false, + "intersectionId": 12109, + "odeReceivedAt": "2024-06-04T16:22:56.116Z", + "originIp": "10.11.81.12", + "status": { + "failureFlash": false, + "noValidSPATisAvailableAtThisTime": false, + "fixedTimeOperation": false, + "standbyOperation": false, + "trafficDependentOperation": false, + "manualControlIsEnabled": false, + "off": false, + "stopTimeIsActivated": false, + "recentChangeInMAPassignedLanesIDsUsed": false, + "recentMAPmessageUpdate": false, + "failureMode": false, + "noValidMAPisAvailableAtThisTime": false, + "signalPriorityIsActive": false, + "preemptIsActive": false + } +},{ + "_id": { + "$oid": "665f3fc18c25216ef4f53fca" + }, + "validationMessages": [ + { + "schemaPath": "#/$defs/J2735RoadRegulatorID/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].id.region", + "message": "$.payload.data.intersectionStateList.intersectionStatelist[0].id.region: null found, integer expected" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[0].state_time_speed.movementEventList[0].timing.startTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[0].state_time_speed.movementEventList[0].timing.nextTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[1].state_time_speed.movementEventList[0].timing.startTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[1].state_time_speed.movementEventList[0].timing.nextTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[2].state_time_speed.movementEventList[0].timing.startTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[2].state_time_speed.movementEventList[0].timing.nextTime", + "message": "DE_TimeMark is missing" + } + ], + "utcTimeStamp": "2024-06-04T16:22:56.223Z", + "schemaVersion": 1, + "recordGeneratedAt": { + "$date": { + "$numberLong": "1717518176226" + } + }, + "revision": 0, + "states": [ + { + "stateTimeSpeed": [ + { + "eventState": "PROTECTED_CLEARANCE", + "timing": { + "minEndTime": "2024-06-04T16:03:19Z", + "maxEndTime": "2024-06-04T16:03:19Z" + } + } + ], + "signalGroup": 2 + }, + { + "stateTimeSpeed": [ + { + "eventState": "STOP_AND_REMAIN", + "timing": { + "minEndTime": "2024-06-04T16:03:22.1Z", + "maxEndTime": "2024-06-04T16:03:22.1Z" + } + } + ], + "signalGroup": 4 + }, + { + "stateTimeSpeed": [ + { + "eventState": "PROTECTED_CLEARANCE", + "timing": { + "minEndTime": "2024-06-04T16:03:19Z", + "maxEndTime": "2024-06-04T16:03:19Z" + } + } + ], + "signalGroup": 6 + } + ], + "messageType": "SPAT", + "cti4501Conformant": false, + "intersectionId": 12109, + "odeReceivedAt": "2024-06-04T16:22:56.223Z", + "originIp": "10.11.81.12", + "status": { + "failureFlash": false, + "noValidSPATisAvailableAtThisTime": false, + "fixedTimeOperation": false, + "standbyOperation": false, + "trafficDependentOperation": false, + "manualControlIsEnabled": false, + "off": false, + "stopTimeIsActivated": false, + "recentChangeInMAPassignedLanesIDsUsed": false, + "recentMAPmessageUpdate": false, + "failureMode": false, + "noValidMAPisAvailableAtThisTime": false, + "signalPriorityIsActive": false, + "preemptIsActive": false + } +},{ + "_id": { + "$oid": "665f3fc18c25216ef4f53fcb" + }, + "validationMessages": [ + { + "schemaPath": "#/$defs/J2735RoadRegulatorID/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].id.region", + "message": "$.payload.data.intersectionStateList.intersectionStatelist[0].id.region: null found, integer expected" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[0].state_time_speed.movementEventList[0].timing.startTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[0].state_time_speed.movementEventList[0].timing.nextTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[1].state_time_speed.movementEventList[0].timing.startTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[1].state_time_speed.movementEventList[0].timing.nextTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[2].state_time_speed.movementEventList[0].timing.startTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[2].state_time_speed.movementEventList[0].timing.nextTime", + "message": "DE_TimeMark is missing" + } + ], + "utcTimeStamp": "2024-06-04T16:22:56.329Z", + "schemaVersion": 1, + "recordGeneratedAt": { + "$date": { + "$numberLong": "1717518176334" + } + }, + "revision": 0, + "states": [ + { + "stateTimeSpeed": [ + { + "eventState": "PROTECTED_CLEARANCE", + "timing": { + "minEndTime": "2024-06-04T16:03:19Z", + "maxEndTime": "2024-06-04T16:03:19Z" + } + } + ], + "signalGroup": 2 + }, + { + "stateTimeSpeed": [ + { + "eventState": "STOP_AND_REMAIN", + "timing": { + "minEndTime": "2024-06-04T16:03:22.1Z", + "maxEndTime": "2024-06-04T16:03:22.1Z" + } + } + ], + "signalGroup": 4 + }, + { + "stateTimeSpeed": [ + { + "eventState": "PROTECTED_CLEARANCE", + "timing": { + "minEndTime": "2024-06-04T16:03:19Z", + "maxEndTime": "2024-06-04T16:03:19Z" + } + } + ], + "signalGroup": 6 + } + ], + "messageType": "SPAT", + "cti4501Conformant": false, + "intersectionId": 12109, + "odeReceivedAt": "2024-06-04T16:22:56.329Z", + "originIp": "10.11.81.12", + "status": { + "failureFlash": false, + "noValidSPATisAvailableAtThisTime": false, + "fixedTimeOperation": false, + "standbyOperation": false, + "trafficDependentOperation": false, + "manualControlIsEnabled": false, + "off": false, + "stopTimeIsActivated": false, + "recentChangeInMAPassignedLanesIDsUsed": false, + "recentMAPmessageUpdate": false, + "failureMode": false, + "noValidMAPisAvailableAtThisTime": false, + "signalPriorityIsActive": false, + "preemptIsActive": false + } +},{ + "_id": { + "$oid": "665f3fc18c25216ef4f53fcc" + }, + "validationMessages": [ + { + "schemaPath": "#/$defs/J2735RoadRegulatorID/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].id.region", + "message": "$.payload.data.intersectionStateList.intersectionStatelist[0].id.region: null found, integer expected" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[0].state_time_speed.movementEventList[0].timing.startTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[0].state_time_speed.movementEventList[0].timing.nextTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[1].state_time_speed.movementEventList[0].timing.startTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[1].state_time_speed.movementEventList[0].timing.nextTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[2].state_time_speed.movementEventList[0].timing.startTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[2].state_time_speed.movementEventList[0].timing.nextTime", + "message": "DE_TimeMark is missing" + } + ], + "utcTimeStamp": "2024-06-04T16:22:56.437Z", + "schemaVersion": 1, + "recordGeneratedAt": { + "$date": { + "$numberLong": "1717518176441" + } + }, + "revision": 0, + "states": [ + { + "stateTimeSpeed": [ + { + "eventState": "PROTECTED_CLEARANCE", + "timing": { + "minEndTime": "2024-06-04T16:03:19Z", + "maxEndTime": "2024-06-04T16:03:19Z" + } + } + ], + "signalGroup": 2 + }, + { + "stateTimeSpeed": [ + { + "eventState": "STOP_AND_REMAIN", + "timing": { + "minEndTime": "2024-06-04T16:03:22.1Z", + "maxEndTime": "2024-06-04T16:03:22.1Z" + } + } + ], + "signalGroup": 4 + }, + { + "stateTimeSpeed": [ + { + "eventState": "PROTECTED_CLEARANCE", + "timing": { + "minEndTime": "2024-06-04T16:03:19Z", + "maxEndTime": "2024-06-04T16:03:19Z" + } + } + ], + "signalGroup": 6 + } + ], + "messageType": "SPAT", + "cti4501Conformant": false, + "intersectionId": 12109, + "odeReceivedAt": "2024-06-04T16:22:56.437Z", + "originIp": "10.11.81.12", + "status": { + "failureFlash": false, + "noValidSPATisAvailableAtThisTime": false, + "fixedTimeOperation": false, + "standbyOperation": false, + "trafficDependentOperation": false, + "manualControlIsEnabled": false, + "off": false, + "stopTimeIsActivated": false, + "recentChangeInMAPassignedLanesIDsUsed": false, + "recentMAPmessageUpdate": false, + "failureMode": false, + "noValidMAPisAvailableAtThisTime": false, + "signalPriorityIsActive": false, + "preemptIsActive": false + } +},{ + "_id": { + "$oid": "665f3fc18c25216ef4f53fcd" + }, + "validationMessages": [ + { + "schemaPath": "#/$defs/J2735RoadRegulatorID/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].id.region", + "message": "$.payload.data.intersectionStateList.intersectionStatelist[0].id.region: null found, integer expected" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[0].state_time_speed.movementEventList[0].timing.startTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[0].state_time_speed.movementEventList[0].timing.nextTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[1].state_time_speed.movementEventList[0].timing.startTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[1].state_time_speed.movementEventList[0].timing.nextTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[2].state_time_speed.movementEventList[0].timing.startTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[2].state_time_speed.movementEventList[0].timing.nextTime", + "message": "DE_TimeMark is missing" + } + ], + "utcTimeStamp": "2024-06-04T16:22:56.544Z", + "schemaVersion": 1, + "recordGeneratedAt": { + "$date": { + "$numberLong": "1717518176548" + } + }, + "revision": 0, + "states": [ + { + "stateTimeSpeed": [ + { + "eventState": "PROTECTED_CLEARANCE", + "timing": { + "minEndTime": "2024-06-04T16:03:19Z", + "maxEndTime": "2024-06-04T16:03:19Z" + } + } + ], + "signalGroup": 2 + }, + { + "stateTimeSpeed": [ + { + "eventState": "STOP_AND_REMAIN", + "timing": { + "minEndTime": "2024-06-04T16:03:22.1Z", + "maxEndTime": "2024-06-04T16:03:22.1Z" + } + } + ], + "signalGroup": 4 + }, + { + "stateTimeSpeed": [ + { + "eventState": "PROTECTED_CLEARANCE", + "timing": { + "minEndTime": "2024-06-04T16:03:19Z", + "maxEndTime": "2024-06-04T16:03:19Z" + } + } + ], + "signalGroup": 6 + } + ], + "messageType": "SPAT", + "cti4501Conformant": false, + "intersectionId": 12109, + "odeReceivedAt": "2024-06-04T16:22:56.544Z", + "originIp": "10.11.81.12", + "status": { + "failureFlash": false, + "noValidSPATisAvailableAtThisTime": false, + "fixedTimeOperation": false, + "standbyOperation": false, + "trafficDependentOperation": false, + "manualControlIsEnabled": false, + "off": false, + "stopTimeIsActivated": false, + "recentChangeInMAPassignedLanesIDsUsed": false, + "recentMAPmessageUpdate": false, + "failureMode": false, + "noValidMAPisAvailableAtThisTime": false, + "signalPriorityIsActive": false, + "preemptIsActive": false + } +},{ + "_id": { + "$oid": "665f3fc18c25216ef4f53fce" + }, + "validationMessages": [ + { + "schemaPath": "#/$defs/J2735RoadRegulatorID/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].id.region", + "message": "$.payload.data.intersectionStateList.intersectionStatelist[0].id.region: null found, integer expected" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[0].state_time_speed.movementEventList[0].timing.startTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[0].state_time_speed.movementEventList[0].timing.nextTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[1].state_time_speed.movementEventList[0].timing.startTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[1].state_time_speed.movementEventList[0].timing.nextTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[2].state_time_speed.movementEventList[0].timing.startTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[2].state_time_speed.movementEventList[0].timing.nextTime", + "message": "DE_TimeMark is missing" + } + ], + "utcTimeStamp": "2024-06-04T16:22:56.651Z", + "schemaVersion": 1, + "recordGeneratedAt": { + "$date": { + "$numberLong": "1717518176656" + } + }, + "revision": 0, + "states": [ + { + "stateTimeSpeed": [ + { + "eventState": "PROTECTED_CLEARANCE", + "timing": { + "minEndTime": "2024-06-04T16:03:19Z", + "maxEndTime": "2024-06-04T16:03:19Z" + } + } + ], + "signalGroup": 2 + }, + { + "stateTimeSpeed": [ + { + "eventState": "STOP_AND_REMAIN", + "timing": { + "minEndTime": "2024-06-04T16:03:22.1Z", + "maxEndTime": "2024-06-04T16:03:22.1Z" + } + } + ], + "signalGroup": 4 + }, + { + "stateTimeSpeed": [ + { + "eventState": "PROTECTED_CLEARANCE", + "timing": { + "minEndTime": "2024-06-04T16:03:19Z", + "maxEndTime": "2024-06-04T16:03:19Z" + } + } + ], + "signalGroup": 6 + } + ], + "messageType": "SPAT", + "cti4501Conformant": false, + "intersectionId": 12109, + "odeReceivedAt": "2024-06-04T16:22:56.651Z", + "originIp": "10.11.81.12", + "status": { + "failureFlash": false, + "noValidSPATisAvailableAtThisTime": false, + "fixedTimeOperation": false, + "standbyOperation": false, + "trafficDependentOperation": false, + "manualControlIsEnabled": false, + "off": false, + "stopTimeIsActivated": false, + "recentChangeInMAPassignedLanesIDsUsed": false, + "recentMAPmessageUpdate": false, + "failureMode": false, + "noValidMAPisAvailableAtThisTime": false, + "signalPriorityIsActive": false, + "preemptIsActive": false + } +},{ + "_id": { + "$oid": "665f3fc18c25216ef4f53fcf" + }, + "validationMessages": [ + { + "schemaPath": "#/$defs/J2735RoadRegulatorID/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].id.region", + "message": "$.payload.data.intersectionStateList.intersectionStatelist[0].id.region: null found, integer expected" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[0].state_time_speed.movementEventList[0].timing.startTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[0].state_time_speed.movementEventList[0].timing.nextTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[1].state_time_speed.movementEventList[0].timing.startTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[1].state_time_speed.movementEventList[0].timing.nextTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[2].state_time_speed.movementEventList[0].timing.startTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[2].state_time_speed.movementEventList[0].timing.nextTime", + "message": "DE_TimeMark is missing" + } + ], + "utcTimeStamp": "2024-06-04T16:22:56.758Z", + "schemaVersion": 1, + "recordGeneratedAt": { + "$date": { + "$numberLong": "1717518176763" + } + }, + "revision": 0, + "states": [ + { + "stateTimeSpeed": [ + { + "eventState": "PROTECTED_CLEARANCE", + "timing": { + "minEndTime": "2024-06-04T16:03:18Z", + "maxEndTime": "2024-06-04T16:03:18Z" + } + } + ], + "signalGroup": 2 + }, + { + "stateTimeSpeed": [ + { + "eventState": "STOP_AND_REMAIN", + "timing": { + "minEndTime": "2024-06-04T16:03:21.1Z", + "maxEndTime": "2024-06-04T16:03:21.1Z" + } + } + ], + "signalGroup": 4 + }, + { + "stateTimeSpeed": [ + { + "eventState": "PROTECTED_CLEARANCE", + "timing": { + "minEndTime": "2024-06-04T16:03:18Z", + "maxEndTime": "2024-06-04T16:03:18Z" + } + } + ], + "signalGroup": 6 + } + ], + "messageType": "SPAT", + "cti4501Conformant": false, + "intersectionId": 12109, + "odeReceivedAt": "2024-06-04T16:22:56.758Z", + "originIp": "10.11.81.12", + "status": { + "failureFlash": false, + "noValidSPATisAvailableAtThisTime": false, + "fixedTimeOperation": false, + "standbyOperation": false, + "trafficDependentOperation": false, + "manualControlIsEnabled": false, + "off": false, + "stopTimeIsActivated": false, + "recentChangeInMAPassignedLanesIDsUsed": false, + "recentMAPmessageUpdate": false, + "failureMode": false, + "noValidMAPisAvailableAtThisTime": false, + "signalPriorityIsActive": false, + "preemptIsActive": false + } +},{ + "_id": { + "$oid": "665f3fc18c25216ef4f53fd0" + }, + "validationMessages": [ + { + "schemaPath": "#/$defs/J2735RoadRegulatorID/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].id.region", + "message": "$.payload.data.intersectionStateList.intersectionStatelist[0].id.region: null found, integer expected" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[0].state_time_speed.movementEventList[0].timing.startTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[0].state_time_speed.movementEventList[0].timing.nextTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[1].state_time_speed.movementEventList[0].timing.startTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[1].state_time_speed.movementEventList[0].timing.nextTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[2].state_time_speed.movementEventList[0].timing.startTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[2].state_time_speed.movementEventList[0].timing.nextTime", + "message": "DE_TimeMark is missing" + } + ], + "utcTimeStamp": "2024-06-04T16:22:56.864Z", + "schemaVersion": 1, + "recordGeneratedAt": { + "$date": { + "$numberLong": "1717518176870" + } + }, + "revision": 0, + "states": [ + { + "stateTimeSpeed": [ + { + "eventState": "PROTECTED_CLEARANCE", + "timing": { + "minEndTime": "2024-06-04T16:03:18Z", + "maxEndTime": "2024-06-04T16:03:18Z" + } + } + ], + "signalGroup": 2 + }, + { + "stateTimeSpeed": [ + { + "eventState": "STOP_AND_REMAIN", + "timing": { + "minEndTime": "2024-06-04T16:03:21.1Z", + "maxEndTime": "2024-06-04T16:03:21.1Z" + } + } + ], + "signalGroup": 4 + }, + { + "stateTimeSpeed": [ + { + "eventState": "PROTECTED_CLEARANCE", + "timing": { + "minEndTime": "2024-06-04T16:03:18Z", + "maxEndTime": "2024-06-04T16:03:18Z" + } + } + ], + "signalGroup": 6 + } + ], + "messageType": "SPAT", + "cti4501Conformant": false, + "intersectionId": 12109, + "odeReceivedAt": "2024-06-04T16:22:56.864Z", + "originIp": "10.11.81.12", + "status": { + "failureFlash": false, + "noValidSPATisAvailableAtThisTime": false, + "fixedTimeOperation": false, + "standbyOperation": false, + "trafficDependentOperation": false, + "manualControlIsEnabled": false, + "off": false, + "stopTimeIsActivated": false, + "recentChangeInMAPassignedLanesIDsUsed": false, + "recentMAPmessageUpdate": false, + "failureMode": false, + "noValidMAPisAvailableAtThisTime": false, + "signalPriorityIsActive": false, + "preemptIsActive": false + } +},{ + "_id": { + "$oid": "665f3fc18c25216ef4f53fd1" + }, + "validationMessages": [ + { + "schemaPath": "#/$defs/J2735RoadRegulatorID/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].id.region", + "message": "$.payload.data.intersectionStateList.intersectionStatelist[0].id.region: null found, integer expected" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[0].state_time_speed.movementEventList[0].timing.startTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[0].state_time_speed.movementEventList[0].timing.nextTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[1].state_time_speed.movementEventList[0].timing.startTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[1].state_time_speed.movementEventList[0].timing.nextTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[2].state_time_speed.movementEventList[0].timing.startTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[2].state_time_speed.movementEventList[0].timing.nextTime", + "message": "DE_TimeMark is missing" + } + ], + "utcTimeStamp": "2024-06-04T16:22:56.973Z", + "schemaVersion": 1, + "recordGeneratedAt": { + "$date": { + "$numberLong": "1717518176977" + } + }, + "revision": 0, + "states": [ + { + "stateTimeSpeed": [ + { + "eventState": "PROTECTED_CLEARANCE", + "timing": { + "minEndTime": "2024-06-04T16:03:18Z", + "maxEndTime": "2024-06-04T16:03:18Z" + } + } + ], + "signalGroup": 2 + }, + { + "stateTimeSpeed": [ + { + "eventState": "STOP_AND_REMAIN", + "timing": { + "minEndTime": "2024-06-04T16:03:21.1Z", + "maxEndTime": "2024-06-04T16:03:21.1Z" + } + } + ], + "signalGroup": 4 + }, + { + "stateTimeSpeed": [ + { + "eventState": "PROTECTED_CLEARANCE", + "timing": { + "minEndTime": "2024-06-04T16:03:18Z", + "maxEndTime": "2024-06-04T16:03:18Z" + } + } + ], + "signalGroup": 6 + } + ], + "messageType": "SPAT", + "cti4501Conformant": false, + "intersectionId": 12109, + "odeReceivedAt": "2024-06-04T16:22:56.973Z", + "originIp": "10.11.81.12", + "status": { + "failureFlash": false, + "noValidSPATisAvailableAtThisTime": false, + "fixedTimeOperation": false, + "standbyOperation": false, + "trafficDependentOperation": false, + "manualControlIsEnabled": false, + "off": false, + "stopTimeIsActivated": false, + "recentChangeInMAPassignedLanesIDsUsed": false, + "recentMAPmessageUpdate": false, + "failureMode": false, + "noValidMAPisAvailableAtThisTime": false, + "signalPriorityIsActive": false, + "preemptIsActive": false + } +},{ + "_id": { + "$oid": "665f3fc18c25216ef4f53fd2" + }, + "validationMessages": [ + { + "schemaPath": "#/$defs/J2735RoadRegulatorID/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].id.region", + "message": "$.payload.data.intersectionStateList.intersectionStatelist[0].id.region: null found, integer expected" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[0].state_time_speed.movementEventList[0].timing.startTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[0].state_time_speed.movementEventList[0].timing.nextTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[1].state_time_speed.movementEventList[0].timing.startTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[1].state_time_speed.movementEventList[0].timing.nextTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[2].state_time_speed.movementEventList[0].timing.startTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[2].state_time_speed.movementEventList[0].timing.nextTime", + "message": "DE_TimeMark is missing" + } + ], + "utcTimeStamp": "2024-06-04T16:22:57.08Z", + "schemaVersion": 1, + "recordGeneratedAt": { + "$date": { + "$numberLong": "1717518177083" + } + }, + "revision": 0, + "states": [ + { + "stateTimeSpeed": [ + { + "eventState": "PROTECTED_CLEARANCE", + "timing": { + "minEndTime": "2024-06-04T16:03:18Z", + "maxEndTime": "2024-06-04T16:03:18Z" + } + } + ], + "signalGroup": 2 + }, + { + "stateTimeSpeed": [ + { + "eventState": "STOP_AND_REMAIN", + "timing": { + "minEndTime": "2024-06-04T16:03:21.1Z", + "maxEndTime": "2024-06-04T16:03:21.1Z" + } + } + ], + "signalGroup": 4 + }, + { + "stateTimeSpeed": [ + { + "eventState": "PROTECTED_CLEARANCE", + "timing": { + "minEndTime": "2024-06-04T16:03:18Z", + "maxEndTime": "2024-06-04T16:03:18Z" + } + } + ], + "signalGroup": 6 + } + ], + "messageType": "SPAT", + "cti4501Conformant": false, + "intersectionId": 12109, + "odeReceivedAt": "2024-06-04T16:22:57.08Z", + "originIp": "10.11.81.12", + "status": { + "failureFlash": false, + "noValidSPATisAvailableAtThisTime": false, + "fixedTimeOperation": false, + "standbyOperation": false, + "trafficDependentOperation": false, + "manualControlIsEnabled": false, + "off": false, + "stopTimeIsActivated": false, + "recentChangeInMAPassignedLanesIDsUsed": false, + "recentMAPmessageUpdate": false, + "failureMode": false, + "noValidMAPisAvailableAtThisTime": false, + "signalPriorityIsActive": false, + "preemptIsActive": false + } +},{ + "_id": { + "$oid": "665f3fc18c25216ef4f53fd3" + }, + "validationMessages": [ + { + "schemaPath": "#/$defs/J2735RoadRegulatorID/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].id.region", + "message": "$.payload.data.intersectionStateList.intersectionStatelist[0].id.region: null found, integer expected" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[0].state_time_speed.movementEventList[0].timing.startTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[0].state_time_speed.movementEventList[0].timing.nextTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[1].state_time_speed.movementEventList[0].timing.startTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[1].state_time_speed.movementEventList[0].timing.nextTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[2].state_time_speed.movementEventList[0].timing.startTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[2].state_time_speed.movementEventList[0].timing.nextTime", + "message": "DE_TimeMark is missing" + } + ], + "utcTimeStamp": "2024-06-04T16:22:57.188Z", + "schemaVersion": 1, + "recordGeneratedAt": { + "$date": { + "$numberLong": "1717518177188" + } + }, + "revision": 0, + "states": [ + { + "stateTimeSpeed": [ + { + "eventState": "PROTECTED_CLEARANCE", + "timing": { + "minEndTime": "2024-06-04T16:03:18Z", + "maxEndTime": "2024-06-04T16:03:18Z" + } + } + ], + "signalGroup": 2 + }, + { + "stateTimeSpeed": [ + { + "eventState": "STOP_AND_REMAIN", + "timing": { + "minEndTime": "2024-06-04T16:03:21.1Z", + "maxEndTime": "2024-06-04T16:03:21.1Z" + } + } + ], + "signalGroup": 4 + }, + { + "stateTimeSpeed": [ + { + "eventState": "PROTECTED_CLEARANCE", + "timing": { + "minEndTime": "2024-06-04T16:03:18Z", + "maxEndTime": "2024-06-04T16:03:18Z" + } + } + ], + "signalGroup": 6 + } + ], + "messageType": "SPAT", + "cti4501Conformant": false, + "intersectionId": 12109, + "odeReceivedAt": "2024-06-04T16:22:57.188Z", + "originIp": "10.11.81.12", + "status": { + "failureFlash": false, + "noValidSPATisAvailableAtThisTime": false, + "fixedTimeOperation": false, + "standbyOperation": false, + "trafficDependentOperation": false, + "manualControlIsEnabled": false, + "off": false, + "stopTimeIsActivated": false, + "recentChangeInMAPassignedLanesIDsUsed": false, + "recentMAPmessageUpdate": false, + "failureMode": false, + "noValidMAPisAvailableAtThisTime": false, + "signalPriorityIsActive": false, + "preemptIsActive": false + } +},{ + "_id": { + "$oid": "665f3fc18c25216ef4f53fd4" + }, + "validationMessages": [ + { + "schemaPath": "#/$defs/J2735RoadRegulatorID/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].id.region", + "message": "$.payload.data.intersectionStateList.intersectionStatelist[0].id.region: null found, integer expected" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[0].state_time_speed.movementEventList[0].timing.startTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[0].state_time_speed.movementEventList[0].timing.nextTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[1].state_time_speed.movementEventList[0].timing.startTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[1].state_time_speed.movementEventList[0].timing.nextTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[2].state_time_speed.movementEventList[0].timing.startTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[2].state_time_speed.movementEventList[0].timing.nextTime", + "message": "DE_TimeMark is missing" + } + ], + "utcTimeStamp": "2024-06-04T16:22:57.295Z", + "schemaVersion": 1, + "recordGeneratedAt": { + "$date": { + "$numberLong": "1717518177296" + } + }, + "revision": 0, + "states": [ + { + "stateTimeSpeed": [ + { + "eventState": "PROTECTED_CLEARANCE", + "timing": { + "minEndTime": "2024-06-04T16:03:18Z", + "maxEndTime": "2024-06-04T16:03:18Z" + } + } + ], + "signalGroup": 2 + }, + { + "stateTimeSpeed": [ + { + "eventState": "STOP_AND_REMAIN", + "timing": { + "minEndTime": "2024-06-04T16:03:21.1Z", + "maxEndTime": "2024-06-04T16:03:21.1Z" + } + } + ], + "signalGroup": 4 + }, + { + "stateTimeSpeed": [ + { + "eventState": "PROTECTED_CLEARANCE", + "timing": { + "minEndTime": "2024-06-04T16:03:18Z", + "maxEndTime": "2024-06-04T16:03:18Z" + } + } + ], + "signalGroup": 6 + } + ], + "messageType": "SPAT", + "cti4501Conformant": false, + "intersectionId": 12109, + "odeReceivedAt": "2024-06-04T16:22:57.295Z", + "originIp": "10.11.81.12", + "status": { + "failureFlash": false, + "noValidSPATisAvailableAtThisTime": false, + "fixedTimeOperation": false, + "standbyOperation": false, + "trafficDependentOperation": false, + "manualControlIsEnabled": false, + "off": false, + "stopTimeIsActivated": false, + "recentChangeInMAPassignedLanesIDsUsed": false, + "recentMAPmessageUpdate": false, + "failureMode": false, + "noValidMAPisAvailableAtThisTime": false, + "signalPriorityIsActive": false, + "preemptIsActive": false + } +},{ + "_id": { + "$oid": "665f3fc18c25216ef4f53fd5" + }, + "validationMessages": [ + { + "schemaPath": "#/$defs/J2735RoadRegulatorID/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].id.region", + "message": "$.payload.data.intersectionStateList.intersectionStatelist[0].id.region: null found, integer expected" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[0].state_time_speed.movementEventList[0].timing.startTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[0].state_time_speed.movementEventList[0].timing.nextTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[1].state_time_speed.movementEventList[0].timing.startTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[1].state_time_speed.movementEventList[0].timing.nextTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[2].state_time_speed.movementEventList[0].timing.startTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[2].state_time_speed.movementEventList[0].timing.nextTime", + "message": "DE_TimeMark is missing" + } + ], + "utcTimeStamp": "2024-06-04T16:22:57.403Z", + "schemaVersion": 1, + "recordGeneratedAt": { + "$date": { + "$numberLong": "1717518177416" + } + }, + "revision": 0, + "states": [ + { + "stateTimeSpeed": [ + { + "eventState": "PROTECTED_CLEARANCE", + "timing": { + "minEndTime": "2024-06-04T16:03:18Z", + "maxEndTime": "2024-06-04T16:03:18Z" + } + } + ], + "signalGroup": 2 + }, + { + "stateTimeSpeed": [ + { + "eventState": "STOP_AND_REMAIN", + "timing": { + "minEndTime": "2024-06-04T16:03:21.1Z", + "maxEndTime": "2024-06-04T16:03:21.1Z" + } + } + ], + "signalGroup": 4 + }, + { + "stateTimeSpeed": [ + { + "eventState": "PROTECTED_CLEARANCE", + "timing": { + "minEndTime": "2024-06-04T16:03:18Z", + "maxEndTime": "2024-06-04T16:03:18Z" + } + } + ], + "signalGroup": 6 + } + ], + "messageType": "SPAT", + "cti4501Conformant": false, + "intersectionId": 12109, + "odeReceivedAt": "2024-06-04T16:22:57.403Z", + "originIp": "10.11.81.12", + "status": { + "failureFlash": false, + "noValidSPATisAvailableAtThisTime": false, + "fixedTimeOperation": false, + "standbyOperation": false, + "trafficDependentOperation": false, + "manualControlIsEnabled": false, + "off": false, + "stopTimeIsActivated": false, + "recentChangeInMAPassignedLanesIDsUsed": false, + "recentMAPmessageUpdate": false, + "failureMode": false, + "noValidMAPisAvailableAtThisTime": false, + "signalPriorityIsActive": false, + "preemptIsActive": false + } +},{ + "_id": { + "$oid": "665f3fc18c25216ef4f53fd6" + }, + "validationMessages": [ + { + "schemaPath": "#/$defs/J2735RoadRegulatorID/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].id.region", + "message": "$.payload.data.intersectionStateList.intersectionStatelist[0].id.region: null found, integer expected" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[0].state_time_speed.movementEventList[0].timing.startTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[0].state_time_speed.movementEventList[0].timing.nextTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[1].state_time_speed.movementEventList[0].timing.startTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[1].state_time_speed.movementEventList[0].timing.nextTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[2].state_time_speed.movementEventList[0].timing.startTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[2].state_time_speed.movementEventList[0].timing.nextTime", + "message": "DE_TimeMark is missing" + } + ], + "utcTimeStamp": "2024-06-04T16:22:57.51Z", + "schemaVersion": 1, + "recordGeneratedAt": { + "$date": { + "$numberLong": "1717518177523" + } + }, + "revision": 0, + "states": [ + { + "stateTimeSpeed": [ + { + "eventState": "PROTECTED_CLEARANCE", + "timing": { + "minEndTime": "2024-06-04T16:03:18Z", + "maxEndTime": "2024-06-04T16:03:18Z" + } + } + ], + "signalGroup": 2 + }, + { + "stateTimeSpeed": [ + { + "eventState": "STOP_AND_REMAIN", + "timing": { + "minEndTime": "2024-06-04T16:03:21.1Z", + "maxEndTime": "2024-06-04T16:03:21.1Z" + } + } + ], + "signalGroup": 4 + }, + { + "stateTimeSpeed": [ + { + "eventState": "PROTECTED_CLEARANCE", + "timing": { + "minEndTime": "2024-06-04T16:03:18Z", + "maxEndTime": "2024-06-04T16:03:18Z" + } + } + ], + "signalGroup": 6 + } + ], + "messageType": "SPAT", + "cti4501Conformant": false, + "intersectionId": 12109, + "odeReceivedAt": "2024-06-04T16:22:57.51Z", + "originIp": "10.11.81.12", + "status": { + "failureFlash": false, + "noValidSPATisAvailableAtThisTime": false, + "fixedTimeOperation": false, + "standbyOperation": false, + "trafficDependentOperation": false, + "manualControlIsEnabled": false, + "off": false, + "stopTimeIsActivated": false, + "recentChangeInMAPassignedLanesIDsUsed": false, + "recentMAPmessageUpdate": false, + "failureMode": false, + "noValidMAPisAvailableAtThisTime": false, + "signalPriorityIsActive": false, + "preemptIsActive": false + } +},{ + "_id": { + "$oid": "665f3fc18c25216ef4f53fd7" + }, + "validationMessages": [ + { + "schemaPath": "#/$defs/J2735RoadRegulatorID/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].id.region", + "message": "$.payload.data.intersectionStateList.intersectionStatelist[0].id.region: null found, integer expected" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[0].state_time_speed.movementEventList[0].timing.startTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[0].state_time_speed.movementEventList[0].timing.nextTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[1].state_time_speed.movementEventList[0].timing.startTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[1].state_time_speed.movementEventList[0].timing.nextTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[2].state_time_speed.movementEventList[0].timing.startTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[2].state_time_speed.movementEventList[0].timing.nextTime", + "message": "DE_TimeMark is missing" + } + ], + "utcTimeStamp": "2024-06-04T16:22:57.618Z", + "schemaVersion": 1, + "recordGeneratedAt": { + "$date": { + "$numberLong": "1717518177631" + } + }, + "revision": 0, + "states": [ + { + "stateTimeSpeed": [ + { + "eventState": "PROTECTED_CLEARANCE", + "timing": { + "minEndTime": "2024-06-04T16:03:18Z", + "maxEndTime": "2024-06-04T16:03:18Z" + } + } + ], + "signalGroup": 2 + }, + { + "stateTimeSpeed": [ + { + "eventState": "STOP_AND_REMAIN", + "timing": { + "minEndTime": "2024-06-04T16:03:21.1Z", + "maxEndTime": "2024-06-04T16:03:21.1Z" + } + } + ], + "signalGroup": 4 + }, + { + "stateTimeSpeed": [ + { + "eventState": "PROTECTED_CLEARANCE", + "timing": { + "minEndTime": "2024-06-04T16:03:18Z", + "maxEndTime": "2024-06-04T16:03:18Z" + } + } + ], + "signalGroup": 6 + } + ], + "messageType": "SPAT", + "cti4501Conformant": false, + "intersectionId": 12109, + "odeReceivedAt": "2024-06-04T16:22:57.618Z", + "originIp": "10.11.81.12", + "status": { + "failureFlash": false, + "noValidSPATisAvailableAtThisTime": false, + "fixedTimeOperation": false, + "standbyOperation": false, + "trafficDependentOperation": false, + "manualControlIsEnabled": false, + "off": false, + "stopTimeIsActivated": false, + "recentChangeInMAPassignedLanesIDsUsed": false, + "recentMAPmessageUpdate": false, + "failureMode": false, + "noValidMAPisAvailableAtThisTime": false, + "signalPriorityIsActive": false, + "preemptIsActive": false + } +},{ + "_id": { + "$oid": "665f3fc18c25216ef4f53fd8" + }, + "validationMessages": [ + { + "schemaPath": "#/$defs/J2735RoadRegulatorID/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].id.region", + "message": "$.payload.data.intersectionStateList.intersectionStatelist[0].id.region: null found, integer expected" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[0].state_time_speed.movementEventList[0].timing.startTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[0].state_time_speed.movementEventList[0].timing.nextTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[1].state_time_speed.movementEventList[0].timing.startTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[1].state_time_speed.movementEventList[0].timing.nextTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[2].state_time_speed.movementEventList[0].timing.startTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[2].state_time_speed.movementEventList[0].timing.nextTime", + "message": "DE_TimeMark is missing" + } + ], + "utcTimeStamp": "2024-06-04T16:22:57.724Z", + "schemaVersion": 1, + "recordGeneratedAt": { + "$date": { + "$numberLong": "1717518177737" + } + }, + "revision": 0, + "states": [ + { + "stateTimeSpeed": [ + { + "eventState": "PROTECTED_CLEARANCE", + "timing": { + "minEndTime": "2024-06-04T16:03:18Z", + "maxEndTime": "2024-06-04T16:03:18Z" + } + } + ], + "signalGroup": 2 + }, + { + "stateTimeSpeed": [ + { + "eventState": "STOP_AND_REMAIN", + "timing": { + "minEndTime": "2024-06-04T16:03:21.1Z", + "maxEndTime": "2024-06-04T16:03:21.1Z" + } + } + ], + "signalGroup": 4 + }, + { + "stateTimeSpeed": [ + { + "eventState": "PROTECTED_CLEARANCE", + "timing": { + "minEndTime": "2024-06-04T16:03:18Z", + "maxEndTime": "2024-06-04T16:03:18Z" + } + } + ], + "signalGroup": 6 + } + ], + "messageType": "SPAT", + "cti4501Conformant": false, + "intersectionId": 12109, + "odeReceivedAt": "2024-06-04T16:22:57.724Z", + "originIp": "10.11.81.12", + "status": { + "failureFlash": false, + "noValidSPATisAvailableAtThisTime": false, + "fixedTimeOperation": false, + "standbyOperation": false, + "trafficDependentOperation": false, + "manualControlIsEnabled": false, + "off": false, + "stopTimeIsActivated": false, + "recentChangeInMAPassignedLanesIDsUsed": false, + "recentMAPmessageUpdate": false, + "failureMode": false, + "noValidMAPisAvailableAtThisTime": false, + "signalPriorityIsActive": false, + "preemptIsActive": false + } +},{ + "_id": { + "$oid": "665f3fc18c25216ef4f53fd9" + }, + "validationMessages": [ + { + "schemaPath": "#/$defs/J2735RoadRegulatorID/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].id.region", + "message": "$.payload.data.intersectionStateList.intersectionStatelist[0].id.region: null found, integer expected" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[0].state_time_speed.movementEventList[0].timing.startTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[0].state_time_speed.movementEventList[0].timing.nextTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[1].state_time_speed.movementEventList[0].timing.startTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[1].state_time_speed.movementEventList[0].timing.nextTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[2].state_time_speed.movementEventList[0].timing.startTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[2].state_time_speed.movementEventList[0].timing.nextTime", + "message": "DE_TimeMark is missing" + } + ], + "utcTimeStamp": "2024-06-04T16:22:57.832Z", + "schemaVersion": 1, + "recordGeneratedAt": { + "$date": { + "$numberLong": "1717518177845" + } + }, + "revision": 0, + "states": [ + { + "stateTimeSpeed": [ + { + "eventState": "PROTECTED_CLEARANCE", + "timing": { + "minEndTime": "2024-06-04T16:03:17Z", + "maxEndTime": "2024-06-04T16:03:17Z" + } + } + ], + "signalGroup": 2 + }, + { + "stateTimeSpeed": [ + { + "eventState": "STOP_AND_REMAIN", + "timing": { + "minEndTime": "2024-06-04T16:03:20.1Z", + "maxEndTime": "2024-06-04T16:03:20.1Z" + } + } + ], + "signalGroup": 4 + }, + { + "stateTimeSpeed": [ + { + "eventState": "PROTECTED_CLEARANCE", + "timing": { + "minEndTime": "2024-06-04T16:03:17Z", + "maxEndTime": "2024-06-04T16:03:17Z" + } + } + ], + "signalGroup": 6 + } + ], + "messageType": "SPAT", + "cti4501Conformant": false, + "intersectionId": 12109, + "odeReceivedAt": "2024-06-04T16:22:57.832Z", + "originIp": "10.11.81.12", + "status": { + "failureFlash": false, + "noValidSPATisAvailableAtThisTime": false, + "fixedTimeOperation": false, + "standbyOperation": false, + "trafficDependentOperation": false, + "manualControlIsEnabled": false, + "off": false, + "stopTimeIsActivated": false, + "recentChangeInMAPassignedLanesIDsUsed": false, + "recentMAPmessageUpdate": false, + "failureMode": false, + "noValidMAPisAvailableAtThisTime": false, + "signalPriorityIsActive": false, + "preemptIsActive": false + } +},{ + "_id": { + "$oid": "665f3fc18c25216ef4f53fda" + }, + "validationMessages": [ + { + "schemaPath": "#/$defs/J2735RoadRegulatorID/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].id.region", + "message": "$.payload.data.intersectionStateList.intersectionStatelist[0].id.region: null found, integer expected" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[0].state_time_speed.movementEventList[0].timing.startTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[0].state_time_speed.movementEventList[0].timing.nextTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[1].state_time_speed.movementEventList[0].timing.startTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[1].state_time_speed.movementEventList[0].timing.nextTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[2].state_time_speed.movementEventList[0].timing.startTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[2].state_time_speed.movementEventList[0].timing.nextTime", + "message": "DE_TimeMark is missing" + } + ], + "utcTimeStamp": "2024-06-04T16:22:57.924Z", + "schemaVersion": 1, + "recordGeneratedAt": { + "$date": { + "$numberLong": "1717518177936" + } + }, + "revision": 0, + "states": [ + { + "stateTimeSpeed": [ + { + "eventState": "PROTECTED_CLEARANCE", + "timing": { + "minEndTime": "2024-06-04T16:03:17Z", + "maxEndTime": "2024-06-04T16:03:17Z" + } + } + ], + "signalGroup": 2 + }, + { + "stateTimeSpeed": [ + { + "eventState": "STOP_AND_REMAIN", + "timing": { + "minEndTime": "2024-06-04T16:03:20.1Z", + "maxEndTime": "2024-06-04T16:03:20.1Z" + } + } + ], + "signalGroup": 4 + }, + { + "stateTimeSpeed": [ + { + "eventState": "PROTECTED_CLEARANCE", + "timing": { + "minEndTime": "2024-06-04T16:03:17Z", + "maxEndTime": "2024-06-04T16:03:17Z" + } + } + ], + "signalGroup": 6 + } + ], + "messageType": "SPAT", + "cti4501Conformant": false, + "intersectionId": 12109, + "odeReceivedAt": "2024-06-04T16:22:57.924Z", + "originIp": "10.11.81.12", + "status": { + "failureFlash": false, + "noValidSPATisAvailableAtThisTime": false, + "fixedTimeOperation": false, + "standbyOperation": false, + "trafficDependentOperation": false, + "manualControlIsEnabled": false, + "off": false, + "stopTimeIsActivated": false, + "recentChangeInMAPassignedLanesIDsUsed": false, + "recentMAPmessageUpdate": false, + "failureMode": false, + "noValidMAPisAvailableAtThisTime": false, + "signalPriorityIsActive": false, + "preemptIsActive": false + } +},{ + "_id": { + "$oid": "665f3fc18c25216ef4f53fdb" + }, + "validationMessages": [ + { + "schemaPath": "#/$defs/J2735RoadRegulatorID/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].id.region", + "message": "$.payload.data.intersectionStateList.intersectionStatelist[0].id.region: null found, integer expected" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[0].state_time_speed.movementEventList[0].timing.startTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[0].state_time_speed.movementEventList[0].timing.nextTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[1].state_time_speed.movementEventList[0].timing.startTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[1].state_time_speed.movementEventList[0].timing.nextTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[2].state_time_speed.movementEventList[0].timing.startTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[2].state_time_speed.movementEventList[0].timing.nextTime", + "message": "DE_TimeMark is missing" + } + ], + "utcTimeStamp": "2024-06-04T16:22:58.032Z", + "schemaVersion": 1, + "recordGeneratedAt": { + "$date": { + "$numberLong": "1717518178046" + } + }, + "revision": 0, + "states": [ + { + "stateTimeSpeed": [ + { + "eventState": "PROTECTED_CLEARANCE", + "timing": { + "minEndTime": "2024-06-04T16:03:17Z", + "maxEndTime": "2024-06-04T16:03:17Z" + } + } + ], + "signalGroup": 2 + }, + { + "stateTimeSpeed": [ + { + "eventState": "STOP_AND_REMAIN", + "timing": { + "minEndTime": "2024-06-04T16:03:20.1Z", + "maxEndTime": "2024-06-04T16:03:20.1Z" + } + } + ], + "signalGroup": 4 + }, + { + "stateTimeSpeed": [ + { + "eventState": "PROTECTED_CLEARANCE", + "timing": { + "minEndTime": "2024-06-04T16:03:17Z", + "maxEndTime": "2024-06-04T16:03:17Z" + } + } + ], + "signalGroup": 6 + } + ], + "messageType": "SPAT", + "cti4501Conformant": false, + "intersectionId": 12109, + "odeReceivedAt": "2024-06-04T16:22:58.032Z", + "originIp": "10.11.81.12", + "status": { + "failureFlash": false, + "noValidSPATisAvailableAtThisTime": false, + "fixedTimeOperation": false, + "standbyOperation": false, + "trafficDependentOperation": false, + "manualControlIsEnabled": false, + "off": false, + "stopTimeIsActivated": false, + "recentChangeInMAPassignedLanesIDsUsed": false, + "recentMAPmessageUpdate": false, + "failureMode": false, + "noValidMAPisAvailableAtThisTime": false, + "signalPriorityIsActive": false, + "preemptIsActive": false + } +},{ + "_id": { + "$oid": "665f3fc18c25216ef4f53fdc" + }, + "validationMessages": [ + { + "schemaPath": "#/$defs/J2735RoadRegulatorID/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].id.region", + "message": "$.payload.data.intersectionStateList.intersectionStatelist[0].id.region: null found, integer expected" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[0].state_time_speed.movementEventList[0].timing.startTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[0].state_time_speed.movementEventList[0].timing.nextTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[1].state_time_speed.movementEventList[0].timing.startTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[1].state_time_speed.movementEventList[0].timing.nextTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[2].state_time_speed.movementEventList[0].timing.startTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[2].state_time_speed.movementEventList[0].timing.nextTime", + "message": "DE_TimeMark is missing" + } + ], + "utcTimeStamp": "2024-06-04T16:22:58.14Z", + "schemaVersion": 1, + "recordGeneratedAt": { + "$date": { + "$numberLong": "1717518178153" + } + }, + "revision": 0, + "states": [ + { + "stateTimeSpeed": [ + { + "eventState": "PROTECTED_CLEARANCE", + "timing": { + "minEndTime": "2024-06-04T16:03:17Z", + "maxEndTime": "2024-06-04T16:03:17Z" + } + } + ], + "signalGroup": 2 + }, + { + "stateTimeSpeed": [ + { + "eventState": "STOP_AND_REMAIN", + "timing": { + "minEndTime": "2024-06-04T16:03:20.1Z", + "maxEndTime": "2024-06-04T16:03:20.1Z" + } + } + ], + "signalGroup": 4 + }, + { + "stateTimeSpeed": [ + { + "eventState": "PROTECTED_CLEARANCE", + "timing": { + "minEndTime": "2024-06-04T16:03:17Z", + "maxEndTime": "2024-06-04T16:03:17Z" + } + } + ], + "signalGroup": 6 + } + ], + "messageType": "SPAT", + "cti4501Conformant": false, + "intersectionId": 12109, + "odeReceivedAt": "2024-06-04T16:22:58.14Z", + "originIp": "10.11.81.12", + "status": { + "failureFlash": false, + "noValidSPATisAvailableAtThisTime": false, + "fixedTimeOperation": false, + "standbyOperation": false, + "trafficDependentOperation": false, + "manualControlIsEnabled": false, + "off": false, + "stopTimeIsActivated": false, + "recentChangeInMAPassignedLanesIDsUsed": false, + "recentMAPmessageUpdate": false, + "failureMode": false, + "noValidMAPisAvailableAtThisTime": false, + "signalPriorityIsActive": false, + "preemptIsActive": false + } +},{ + "_id": { + "$oid": "665f3fc18c25216ef4f53fdd" + }, + "validationMessages": [ + { + "schemaPath": "#/$defs/J2735RoadRegulatorID/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].id.region", + "message": "$.payload.data.intersectionStateList.intersectionStatelist[0].id.region: null found, integer expected" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[0].state_time_speed.movementEventList[0].timing.startTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[0].state_time_speed.movementEventList[0].timing.nextTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[1].state_time_speed.movementEventList[0].timing.startTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[1].state_time_speed.movementEventList[0].timing.nextTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[2].state_time_speed.movementEventList[0].timing.startTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[2].state_time_speed.movementEventList[0].timing.nextTime", + "message": "DE_TimeMark is missing" + } + ], + "utcTimeStamp": "2024-06-04T16:22:58.248Z", + "schemaVersion": 1, + "recordGeneratedAt": { + "$date": { + "$numberLong": "1717518178257" + } + }, + "revision": 0, + "states": [ + { + "stateTimeSpeed": [ + { + "eventState": "PROTECTED_CLEARANCE", + "timing": { + "minEndTime": "2024-06-04T16:03:17Z", + "maxEndTime": "2024-06-04T16:03:17Z" + } + } + ], + "signalGroup": 2 + }, + { + "stateTimeSpeed": [ + { + "eventState": "STOP_AND_REMAIN", + "timing": { + "minEndTime": "2024-06-04T16:03:20.1Z", + "maxEndTime": "2024-06-04T16:03:20.1Z" + } + } + ], + "signalGroup": 4 + }, + { + "stateTimeSpeed": [ + { + "eventState": "PROTECTED_CLEARANCE", + "timing": { + "minEndTime": "2024-06-04T16:03:17Z", + "maxEndTime": "2024-06-04T16:03:17Z" + } + } + ], + "signalGroup": 6 + } + ], + "messageType": "SPAT", + "cti4501Conformant": false, + "intersectionId": 12109, + "odeReceivedAt": "2024-06-04T16:22:58.248Z", + "originIp": "10.11.81.12", + "status": { + "failureFlash": false, + "noValidSPATisAvailableAtThisTime": false, + "fixedTimeOperation": false, + "standbyOperation": false, + "trafficDependentOperation": false, + "manualControlIsEnabled": false, + "off": false, + "stopTimeIsActivated": false, + "recentChangeInMAPassignedLanesIDsUsed": false, + "recentMAPmessageUpdate": false, + "failureMode": false, + "noValidMAPisAvailableAtThisTime": false, + "signalPriorityIsActive": false, + "preemptIsActive": false + } +},{ + "_id": { + "$oid": "665f3fc18c25216ef4f53fde" + }, + "validationMessages": [ + { + "schemaPath": "#/$defs/J2735RoadRegulatorID/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].id.region", + "message": "$.payload.data.intersectionStateList.intersectionStatelist[0].id.region: null found, integer expected" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[0].state_time_speed.movementEventList[0].timing.startTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[0].state_time_speed.movementEventList[0].timing.nextTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[1].state_time_speed.movementEventList[0].timing.startTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[1].state_time_speed.movementEventList[0].timing.nextTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[2].state_time_speed.movementEventList[0].timing.startTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[2].state_time_speed.movementEventList[0].timing.nextTime", + "message": "DE_TimeMark is missing" + } + ], + "utcTimeStamp": "2024-06-04T16:22:58.354Z", + "schemaVersion": 1, + "recordGeneratedAt": { + "$date": { + "$numberLong": "1717518178365" + } + }, + "revision": 0, + "states": [ + { + "stateTimeSpeed": [ + { + "eventState": "PROTECTED_CLEARANCE", + "timing": { + "minEndTime": "2024-06-04T16:03:17Z", + "maxEndTime": "2024-06-04T16:03:17Z" + } + } + ], + "signalGroup": 2 + }, + { + "stateTimeSpeed": [ + { + "eventState": "STOP_AND_REMAIN", + "timing": { + "minEndTime": "2024-06-04T16:03:20.1Z", + "maxEndTime": "2024-06-04T16:03:20.1Z" + } + } + ], + "signalGroup": 4 + }, + { + "stateTimeSpeed": [ + { + "eventState": "PROTECTED_CLEARANCE", + "timing": { + "minEndTime": "2024-06-04T16:03:17Z", + "maxEndTime": "2024-06-04T16:03:17Z" + } + } + ], + "signalGroup": 6 + } + ], + "messageType": "SPAT", + "cti4501Conformant": false, + "intersectionId": 12109, + "odeReceivedAt": "2024-06-04T16:22:58.354Z", + "originIp": "10.11.81.12", + "status": { + "failureFlash": false, + "noValidSPATisAvailableAtThisTime": false, + "fixedTimeOperation": false, + "standbyOperation": false, + "trafficDependentOperation": false, + "manualControlIsEnabled": false, + "off": false, + "stopTimeIsActivated": false, + "recentChangeInMAPassignedLanesIDsUsed": false, + "recentMAPmessageUpdate": false, + "failureMode": false, + "noValidMAPisAvailableAtThisTime": false, + "signalPriorityIsActive": false, + "preemptIsActive": false + } +},{ + "_id": { + "$oid": "665f3fc18c25216ef4f53fdf" + }, + "validationMessages": [ + { + "schemaPath": "#/$defs/J2735RoadRegulatorID/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].id.region", + "message": "$.payload.data.intersectionStateList.intersectionStatelist[0].id.region: null found, integer expected" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[0].state_time_speed.movementEventList[0].timing.startTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[0].state_time_speed.movementEventList[0].timing.nextTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[1].state_time_speed.movementEventList[0].timing.startTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[1].state_time_speed.movementEventList[0].timing.nextTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[2].state_time_speed.movementEventList[0].timing.startTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[2].state_time_speed.movementEventList[0].timing.nextTime", + "message": "DE_TimeMark is missing" + } + ], + "utcTimeStamp": "2024-06-04T16:22:58.461Z", + "schemaVersion": 1, + "recordGeneratedAt": { + "$date": { + "$numberLong": "1717518178471" + } + }, + "revision": 0, + "states": [ + { + "stateTimeSpeed": [ + { + "eventState": "PROTECTED_CLEARANCE", + "timing": { + "minEndTime": "2024-06-04T16:03:17Z", + "maxEndTime": "2024-06-04T16:03:17Z" + } + } + ], + "signalGroup": 2 + }, + { + "stateTimeSpeed": [ + { + "eventState": "STOP_AND_REMAIN", + "timing": { + "minEndTime": "2024-06-04T16:03:20.1Z", + "maxEndTime": "2024-06-04T16:03:20.1Z" + } + } + ], + "signalGroup": 4 + }, + { + "stateTimeSpeed": [ + { + "eventState": "PROTECTED_CLEARANCE", + "timing": { + "minEndTime": "2024-06-04T16:03:17Z", + "maxEndTime": "2024-06-04T16:03:17Z" + } + } + ], + "signalGroup": 6 + } + ], + "messageType": "SPAT", + "cti4501Conformant": false, + "intersectionId": 12109, + "odeReceivedAt": "2024-06-04T16:22:58.461Z", + "originIp": "10.11.81.12", + "status": { + "failureFlash": false, + "noValidSPATisAvailableAtThisTime": false, + "fixedTimeOperation": false, + "standbyOperation": false, + "trafficDependentOperation": false, + "manualControlIsEnabled": false, + "off": false, + "stopTimeIsActivated": false, + "recentChangeInMAPassignedLanesIDsUsed": false, + "recentMAPmessageUpdate": false, + "failureMode": false, + "noValidMAPisAvailableAtThisTime": false, + "signalPriorityIsActive": false, + "preemptIsActive": false + } +},{ + "_id": { + "$oid": "665f3fc18c25216ef4f53fe0" + }, + "validationMessages": [ + { + "schemaPath": "#/$defs/J2735RoadRegulatorID/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].id.region", + "message": "$.payload.data.intersectionStateList.intersectionStatelist[0].id.region: null found, integer expected" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[0].state_time_speed.movementEventList[0].timing.startTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[0].state_time_speed.movementEventList[0].timing.nextTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[1].state_time_speed.movementEventList[0].timing.startTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[1].state_time_speed.movementEventList[0].timing.nextTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[2].state_time_speed.movementEventList[0].timing.startTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[2].state_time_speed.movementEventList[0].timing.nextTime", + "message": "DE_TimeMark is missing" + } + ], + "utcTimeStamp": "2024-06-04T16:22:58.569Z", + "schemaVersion": 1, + "recordGeneratedAt": { + "$date": { + "$numberLong": "1717518178579" + } + }, + "revision": 0, + "states": [ + { + "stateTimeSpeed": [ + { + "eventState": "PROTECTED_CLEARANCE", + "timing": { + "minEndTime": "2024-06-04T16:03:17Z", + "maxEndTime": "2024-06-04T16:03:17Z" + } + } + ], + "signalGroup": 2 + }, + { + "stateTimeSpeed": [ + { + "eventState": "STOP_AND_REMAIN", + "timing": { + "minEndTime": "2024-06-04T16:03:20.1Z", + "maxEndTime": "2024-06-04T16:03:20.1Z" + } + } + ], + "signalGroup": 4 + }, + { + "stateTimeSpeed": [ + { + "eventState": "PROTECTED_CLEARANCE", + "timing": { + "minEndTime": "2024-06-04T16:03:17Z", + "maxEndTime": "2024-06-04T16:03:17Z" + } + } + ], + "signalGroup": 6 + } + ], + "messageType": "SPAT", + "cti4501Conformant": false, + "intersectionId": 12109, + "odeReceivedAt": "2024-06-04T16:22:58.569Z", + "originIp": "10.11.81.12", + "status": { + "failureFlash": false, + "noValidSPATisAvailableAtThisTime": false, + "fixedTimeOperation": false, + "standbyOperation": false, + "trafficDependentOperation": false, + "manualControlIsEnabled": false, + "off": false, + "stopTimeIsActivated": false, + "recentChangeInMAPassignedLanesIDsUsed": false, + "recentMAPmessageUpdate": false, + "failureMode": false, + "noValidMAPisAvailableAtThisTime": false, + "signalPriorityIsActive": false, + "preemptIsActive": false + } +},{ + "_id": { + "$oid": "665f3fc18c25216ef4f53fe1" + }, + "validationMessages": [ + { + "schemaPath": "#/$defs/J2735RoadRegulatorID/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].id.region", + "message": "$.payload.data.intersectionStateList.intersectionStatelist[0].id.region: null found, integer expected" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[0].state_time_speed.movementEventList[0].timing.startTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[0].state_time_speed.movementEventList[0].timing.nextTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[1].state_time_speed.movementEventList[0].timing.startTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[1].state_time_speed.movementEventList[0].timing.nextTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[2].state_time_speed.movementEventList[0].timing.startTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[2].state_time_speed.movementEventList[0].timing.nextTime", + "message": "DE_TimeMark is missing" + } + ], + "utcTimeStamp": "2024-06-04T16:22:58.676Z", + "schemaVersion": 1, + "recordGeneratedAt": { + "$date": { + "$numberLong": "1717518178686" + } + }, + "revision": 0, + "states": [ + { + "stateTimeSpeed": [ + { + "eventState": "PROTECTED_CLEARANCE", + "timing": { + "minEndTime": "2024-06-04T16:03:17Z", + "maxEndTime": "2024-06-04T16:03:17Z" + } + } + ], + "signalGroup": 2 + }, + { + "stateTimeSpeed": [ + { + "eventState": "STOP_AND_REMAIN", + "timing": { + "minEndTime": "2024-06-04T16:03:20.1Z", + "maxEndTime": "2024-06-04T16:03:20.1Z" + } + } + ], + "signalGroup": 4 + }, + { + "stateTimeSpeed": [ + { + "eventState": "PROTECTED_CLEARANCE", + "timing": { + "minEndTime": "2024-06-04T16:03:17Z", + "maxEndTime": "2024-06-04T16:03:17Z" + } + } + ], + "signalGroup": 6 + } + ], + "messageType": "SPAT", + "cti4501Conformant": false, + "intersectionId": 12109, + "odeReceivedAt": "2024-06-04T16:22:58.676Z", + "originIp": "10.11.81.12", + "status": { + "failureFlash": false, + "noValidSPATisAvailableAtThisTime": false, + "fixedTimeOperation": false, + "standbyOperation": false, + "trafficDependentOperation": false, + "manualControlIsEnabled": false, + "off": false, + "stopTimeIsActivated": false, + "recentChangeInMAPassignedLanesIDsUsed": false, + "recentMAPmessageUpdate": false, + "failureMode": false, + "noValidMAPisAvailableAtThisTime": false, + "signalPriorityIsActive": false, + "preemptIsActive": false + } +},{ + "_id": { + "$oid": "665f3fc18c25216ef4f53fe2" + }, + "validationMessages": [ + { + "schemaPath": "#/$defs/J2735RoadRegulatorID/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].id.region", + "message": "$.payload.data.intersectionStateList.intersectionStatelist[0].id.region: null found, integer expected" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[0].state_time_speed.movementEventList[0].timing.startTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[0].state_time_speed.movementEventList[0].timing.nextTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[1].state_time_speed.movementEventList[0].timing.startTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[1].state_time_speed.movementEventList[0].timing.nextTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[2].state_time_speed.movementEventList[0].timing.startTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[2].state_time_speed.movementEventList[0].timing.nextTime", + "message": "DE_TimeMark is missing" + } + ], + "utcTimeStamp": "2024-06-04T16:22:58.783Z", + "schemaVersion": 1, + "recordGeneratedAt": { + "$date": { + "$numberLong": "1717518178792" + } + }, + "revision": 0, + "states": [ + { + "stateTimeSpeed": [ + { + "eventState": "PROTECTED_CLEARANCE", + "timing": { + "minEndTime": "2024-06-04T16:03:17Z", + "maxEndTime": "2024-06-04T16:03:17Z" + } + } + ], + "signalGroup": 2 + }, + { + "stateTimeSpeed": [ + { + "eventState": "STOP_AND_REMAIN", + "timing": { + "minEndTime": "2024-06-04T16:03:20.1Z", + "maxEndTime": "2024-06-04T16:03:20.1Z" + } + } + ], + "signalGroup": 4 + }, + { + "stateTimeSpeed": [ + { + "eventState": "PROTECTED_CLEARANCE", + "timing": { + "minEndTime": "2024-06-04T16:03:17Z", + "maxEndTime": "2024-06-04T16:03:17Z" + } + } + ], + "signalGroup": 6 + } + ], + "messageType": "SPAT", + "cti4501Conformant": false, + "intersectionId": 12109, + "odeReceivedAt": "2024-06-04T16:22:58.783Z", + "originIp": "10.11.81.12", + "status": { + "failureFlash": false, + "noValidSPATisAvailableAtThisTime": false, + "fixedTimeOperation": false, + "standbyOperation": false, + "trafficDependentOperation": false, + "manualControlIsEnabled": false, + "off": false, + "stopTimeIsActivated": false, + "recentChangeInMAPassignedLanesIDsUsed": false, + "recentMAPmessageUpdate": false, + "failureMode": false, + "noValidMAPisAvailableAtThisTime": false, + "signalPriorityIsActive": false, + "preemptIsActive": false + } +},{ + "_id": { + "$oid": "665f3fc18c25216ef4f53fe3" + }, + "validationMessages": [ + { + "schemaPath": "#/$defs/J2735RoadRegulatorID/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].id.region", + "message": "$.payload.data.intersectionStateList.intersectionStatelist[0].id.region: null found, integer expected" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[0].state_time_speed.movementEventList[0].timing.startTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[0].state_time_speed.movementEventList[0].timing.nextTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[1].state_time_speed.movementEventList[0].timing.startTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[1].state_time_speed.movementEventList[0].timing.nextTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[2].state_time_speed.movementEventList[0].timing.startTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[2].state_time_speed.movementEventList[0].timing.nextTime", + "message": "DE_TimeMark is missing" + } + ], + "utcTimeStamp": "2024-06-04T16:22:58.89Z", + "schemaVersion": 1, + "recordGeneratedAt": { + "$date": { + "$numberLong": "1717518178899" + } + }, + "revision": 0, + "states": [ + { + "stateTimeSpeed": [ + { + "eventState": "PROTECTED_CLEARANCE", + "timing": { + "minEndTime": "2024-06-04T16:03:19Z", + "maxEndTime": "2024-06-04T16:03:19Z" + } + } + ], + "signalGroup": 2 + }, + { + "stateTimeSpeed": [ + { + "eventState": "STOP_AND_REMAIN", + "timing": { + "minEndTime": "2024-06-04T16:03:22.1Z", + "maxEndTime": "2024-06-04T16:03:22.1Z" + } + } + ], + "signalGroup": 4 + }, + { + "stateTimeSpeed": [ + { + "eventState": "PROTECTED_CLEARANCE", + "timing": { + "minEndTime": "2024-06-04T16:03:19Z", + "maxEndTime": "2024-06-04T16:03:19Z" + } + } + ], + "signalGroup": 6 + } + ], + "messageType": "SPAT", + "cti4501Conformant": false, + "intersectionId": 12109, + "odeReceivedAt": "2024-06-04T16:22:58.89Z", + "originIp": "10.11.81.12", + "status": { + "failureFlash": false, + "noValidSPATisAvailableAtThisTime": false, + "fixedTimeOperation": false, + "standbyOperation": false, + "trafficDependentOperation": false, + "manualControlIsEnabled": false, + "off": false, + "stopTimeIsActivated": false, + "recentChangeInMAPassignedLanesIDsUsed": false, + "recentMAPmessageUpdate": false, + "failureMode": false, + "noValidMAPisAvailableAtThisTime": false, + "signalPriorityIsActive": false, + "preemptIsActive": false + } +},{ + "_id": { + "$oid": "665f3fc18c25216ef4f53fe4" + }, + "validationMessages": [ + { + "schemaPath": "#/$defs/J2735RoadRegulatorID/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].id.region", + "message": "$.payload.data.intersectionStateList.intersectionStatelist[0].id.region: null found, integer expected" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[0].state_time_speed.movementEventList[0].timing.startTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[0].state_time_speed.movementEventList[0].timing.nextTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[1].state_time_speed.movementEventList[0].timing.startTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[1].state_time_speed.movementEventList[0].timing.nextTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[2].state_time_speed.movementEventList[0].timing.startTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[2].state_time_speed.movementEventList[0].timing.nextTime", + "message": "DE_TimeMark is missing" + } + ], + "utcTimeStamp": "2024-06-04T16:22:58.996Z", + "schemaVersion": 1, + "recordGeneratedAt": { + "$date": { + "$numberLong": "1717518179006" + } + }, + "revision": 0, + "states": [ + { + "stateTimeSpeed": [ + { + "eventState": "PROTECTED_CLEARANCE", + "timing": { + "minEndTime": "2024-06-04T16:03:19Z", + "maxEndTime": "2024-06-04T16:03:19Z" + } + } + ], + "signalGroup": 2 + }, + { + "stateTimeSpeed": [ + { + "eventState": "STOP_AND_REMAIN", + "timing": { + "minEndTime": "2024-06-04T16:03:22.1Z", + "maxEndTime": "2024-06-04T16:03:22.1Z" + } + } + ], + "signalGroup": 4 + }, + { + "stateTimeSpeed": [ + { + "eventState": "PROTECTED_CLEARANCE", + "timing": { + "minEndTime": "2024-06-04T16:03:19Z", + "maxEndTime": "2024-06-04T16:03:19Z" + } + } + ], + "signalGroup": 6 + } + ], + "messageType": "SPAT", + "cti4501Conformant": false, + "intersectionId": 12109, + "odeReceivedAt": "2024-06-04T16:22:58.996Z", + "originIp": "10.11.81.12", + "status": { + "failureFlash": false, + "noValidSPATisAvailableAtThisTime": false, + "fixedTimeOperation": false, + "standbyOperation": false, + "trafficDependentOperation": false, + "manualControlIsEnabled": false, + "off": false, + "stopTimeIsActivated": false, + "recentChangeInMAPassignedLanesIDsUsed": false, + "recentMAPmessageUpdate": false, + "failureMode": false, + "noValidMAPisAvailableAtThisTime": false, + "signalPriorityIsActive": false, + "preemptIsActive": false + } +},{ + "_id": { + "$oid": "665f3fc18c25216ef4f53fe5" + }, + "validationMessages": [ + { + "schemaPath": "#/$defs/J2735RoadRegulatorID/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].id.region", + "message": "$.payload.data.intersectionStateList.intersectionStatelist[0].id.region: null found, integer expected" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[0].state_time_speed.movementEventList[0].timing.startTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[0].state_time_speed.movementEventList[0].timing.nextTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[1].state_time_speed.movementEventList[0].timing.startTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[1].state_time_speed.movementEventList[0].timing.nextTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[2].state_time_speed.movementEventList[0].timing.startTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[2].state_time_speed.movementEventList[0].timing.nextTime", + "message": "DE_TimeMark is missing" + } + ], + "utcTimeStamp": "2024-06-04T16:22:59.104Z", + "schemaVersion": 1, + "recordGeneratedAt": { + "$date": { + "$numberLong": "1717518179113" + } + }, + "revision": 0, + "states": [ + { + "stateTimeSpeed": [ + { + "eventState": "PROTECTED_CLEARANCE", + "timing": { + "minEndTime": "2024-06-04T16:03:19Z", + "maxEndTime": "2024-06-04T16:03:19Z" + } + } + ], + "signalGroup": 2 + }, + { + "stateTimeSpeed": [ + { + "eventState": "STOP_AND_REMAIN", + "timing": { + "minEndTime": "2024-06-04T16:03:22.1Z", + "maxEndTime": "2024-06-04T16:03:22.1Z" + } + } + ], + "signalGroup": 4 + }, + { + "stateTimeSpeed": [ + { + "eventState": "PROTECTED_CLEARANCE", + "timing": { + "minEndTime": "2024-06-04T16:03:19Z", + "maxEndTime": "2024-06-04T16:03:19Z" + } + } + ], + "signalGroup": 6 + } + ], + "messageType": "SPAT", + "cti4501Conformant": false, + "intersectionId": 12109, + "odeReceivedAt": "2024-06-04T16:22:59.104Z", + "originIp": "10.11.81.12", + "status": { + "failureFlash": false, + "noValidSPATisAvailableAtThisTime": false, + "fixedTimeOperation": false, + "standbyOperation": false, + "trafficDependentOperation": false, + "manualControlIsEnabled": false, + "off": false, + "stopTimeIsActivated": false, + "recentChangeInMAPassignedLanesIDsUsed": false, + "recentMAPmessageUpdate": false, + "failureMode": false, + "noValidMAPisAvailableAtThisTime": false, + "signalPriorityIsActive": false, + "preemptIsActive": false + } +},{ + "_id": { + "$oid": "665f3fc18c25216ef4f53fe6" + }, + "validationMessages": [ + { + "schemaPath": "#/$defs/J2735RoadRegulatorID/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].id.region", + "message": "$.payload.data.intersectionStateList.intersectionStatelist[0].id.region: null found, integer expected" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[0].state_time_speed.movementEventList[0].timing.startTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[0].state_time_speed.movementEventList[0].timing.nextTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[1].state_time_speed.movementEventList[0].timing.startTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[1].state_time_speed.movementEventList[0].timing.nextTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[2].state_time_speed.movementEventList[0].timing.startTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[2].state_time_speed.movementEventList[0].timing.nextTime", + "message": "DE_TimeMark is missing" + } + ], + "utcTimeStamp": "2024-06-04T16:22:59.21Z", + "schemaVersion": 1, + "recordGeneratedAt": { + "$date": { + "$numberLong": "1717518179220" + } + }, + "revision": 0, + "states": [ + { + "stateTimeSpeed": [ + { + "eventState": "PROTECTED_CLEARANCE", + "timing": { + "minEndTime": "2024-06-04T16:03:19Z", + "maxEndTime": "2024-06-04T16:03:19Z" + } + } + ], + "signalGroup": 2 + }, + { + "stateTimeSpeed": [ + { + "eventState": "STOP_AND_REMAIN", + "timing": { + "minEndTime": "2024-06-04T16:03:22.1Z", + "maxEndTime": "2024-06-04T16:03:22.1Z" + } + } + ], + "signalGroup": 4 + }, + { + "stateTimeSpeed": [ + { + "eventState": "PROTECTED_CLEARANCE", + "timing": { + "minEndTime": "2024-06-04T16:03:19Z", + "maxEndTime": "2024-06-04T16:03:19Z" + } + } + ], + "signalGroup": 6 + } + ], + "messageType": "SPAT", + "cti4501Conformant": false, + "intersectionId": 12109, + "odeReceivedAt": "2024-06-04T16:22:59.21Z", + "originIp": "10.11.81.12", + "status": { + "failureFlash": false, + "noValidSPATisAvailableAtThisTime": false, + "fixedTimeOperation": false, + "standbyOperation": false, + "trafficDependentOperation": false, + "manualControlIsEnabled": false, + "off": false, + "stopTimeIsActivated": false, + "recentChangeInMAPassignedLanesIDsUsed": false, + "recentMAPmessageUpdate": false, + "failureMode": false, + "noValidMAPisAvailableAtThisTime": false, + "signalPriorityIsActive": false, + "preemptIsActive": false + } +},{ + "_id": { + "$oid": "665f3fc18c25216ef4f53fe7" + }, + "validationMessages": [ + { + "schemaPath": "#/$defs/J2735RoadRegulatorID/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].id.region", + "message": "$.payload.data.intersectionStateList.intersectionStatelist[0].id.region: null found, integer expected" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[0].state_time_speed.movementEventList[0].timing.startTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[0].state_time_speed.movementEventList[0].timing.nextTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[1].state_time_speed.movementEventList[0].timing.startTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[1].state_time_speed.movementEventList[0].timing.nextTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[2].state_time_speed.movementEventList[0].timing.startTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[2].state_time_speed.movementEventList[0].timing.nextTime", + "message": "DE_TimeMark is missing" + } + ], + "utcTimeStamp": "2024-06-04T16:22:59.318Z", + "schemaVersion": 1, + "recordGeneratedAt": { + "$date": { + "$numberLong": "1717518179328" + } + }, + "revision": 0, + "states": [ + { + "stateTimeSpeed": [ + { + "eventState": "PROTECTED_CLEARANCE", + "timing": { + "minEndTime": "2024-06-04T16:03:19Z", + "maxEndTime": "2024-06-04T16:03:19Z" + } + } + ], + "signalGroup": 2 + }, + { + "stateTimeSpeed": [ + { + "eventState": "STOP_AND_REMAIN", + "timing": { + "minEndTime": "2024-06-04T16:03:22.1Z", + "maxEndTime": "2024-06-04T16:03:22.1Z" + } + } + ], + "signalGroup": 4 + }, + { + "stateTimeSpeed": [ + { + "eventState": "PROTECTED_CLEARANCE", + "timing": { + "minEndTime": "2024-06-04T16:03:19Z", + "maxEndTime": "2024-06-04T16:03:19Z" + } + } + ], + "signalGroup": 6 + } + ], + "messageType": "SPAT", + "cti4501Conformant": false, + "intersectionId": 12109, + "odeReceivedAt": "2024-06-04T16:22:59.318Z", + "originIp": "10.11.81.12", + "status": { + "failureFlash": false, + "noValidSPATisAvailableAtThisTime": false, + "fixedTimeOperation": false, + "standbyOperation": false, + "trafficDependentOperation": false, + "manualControlIsEnabled": false, + "off": false, + "stopTimeIsActivated": false, + "recentChangeInMAPassignedLanesIDsUsed": false, + "recentMAPmessageUpdate": false, + "failureMode": false, + "noValidMAPisAvailableAtThisTime": false, + "signalPriorityIsActive": false, + "preemptIsActive": false + } +},{ + "_id": { + "$oid": "665f3fc18c25216ef4f53fe8" + }, + "validationMessages": [ + { + "schemaPath": "#/$defs/J2735RoadRegulatorID/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].id.region", + "message": "$.payload.data.intersectionStateList.intersectionStatelist[0].id.region: null found, integer expected" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[0].state_time_speed.movementEventList[0].timing.startTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[0].state_time_speed.movementEventList[0].timing.nextTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[1].state_time_speed.movementEventList[0].timing.startTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[1].state_time_speed.movementEventList[0].timing.nextTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[2].state_time_speed.movementEventList[0].timing.startTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[2].state_time_speed.movementEventList[0].timing.nextTime", + "message": "DE_TimeMark is missing" + } + ], + "utcTimeStamp": "2024-06-04T16:22:59.425Z", + "schemaVersion": 1, + "recordGeneratedAt": { + "$date": { + "$numberLong": "1717518179435" + } + }, + "revision": 0, + "states": [ + { + "stateTimeSpeed": [ + { + "eventState": "PROTECTED_CLEARANCE", + "timing": { + "minEndTime": "2024-06-04T16:03:19Z", + "maxEndTime": "2024-06-04T16:03:19Z" + } + } + ], + "signalGroup": 2 + }, + { + "stateTimeSpeed": [ + { + "eventState": "STOP_AND_REMAIN", + "timing": { + "minEndTime": "2024-06-04T16:03:22.1Z", + "maxEndTime": "2024-06-04T16:03:22.1Z" + } + } + ], + "signalGroup": 4 + }, + { + "stateTimeSpeed": [ + { + "eventState": "PROTECTED_CLEARANCE", + "timing": { + "minEndTime": "2024-06-04T16:03:19Z", + "maxEndTime": "2024-06-04T16:03:19Z" + } + } + ], + "signalGroup": 6 + } + ], + "messageType": "SPAT", + "cti4501Conformant": false, + "intersectionId": 12109, + "odeReceivedAt": "2024-06-04T16:22:59.425Z", + "originIp": "10.11.81.12", + "status": { + "failureFlash": false, + "noValidSPATisAvailableAtThisTime": false, + "fixedTimeOperation": false, + "standbyOperation": false, + "trafficDependentOperation": false, + "manualControlIsEnabled": false, + "off": false, + "stopTimeIsActivated": false, + "recentChangeInMAPassignedLanesIDsUsed": false, + "recentMAPmessageUpdate": false, + "failureMode": false, + "noValidMAPisAvailableAtThisTime": false, + "signalPriorityIsActive": false, + "preemptIsActive": false + } +},{ + "_id": { + "$oid": "665f3fc18c25216ef4f53fe9" + }, + "validationMessages": [ + { + "schemaPath": "#/$defs/J2735RoadRegulatorID/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].id.region", + "message": "$.payload.data.intersectionStateList.intersectionStatelist[0].id.region: null found, integer expected" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[0].state_time_speed.movementEventList[0].timing.startTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[0].state_time_speed.movementEventList[0].timing.nextTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[1].state_time_speed.movementEventList[0].timing.startTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[1].state_time_speed.movementEventList[0].timing.nextTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[2].state_time_speed.movementEventList[0].timing.startTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[2].state_time_speed.movementEventList[0].timing.nextTime", + "message": "DE_TimeMark is missing" + } + ], + "utcTimeStamp": "2024-06-04T16:22:59.531Z", + "schemaVersion": 1, + "recordGeneratedAt": { + "$date": { + "$numberLong": "1717518179541" + } + }, + "revision": 0, + "states": [ + { + "stateTimeSpeed": [ + { + "eventState": "PROTECTED_CLEARANCE", + "timing": { + "minEndTime": "2024-06-04T16:03:19Z", + "maxEndTime": "2024-06-04T16:03:19Z" + } + } + ], + "signalGroup": 2 + }, + { + "stateTimeSpeed": [ + { + "eventState": "STOP_AND_REMAIN", + "timing": { + "minEndTime": "2024-06-04T16:03:22.1Z", + "maxEndTime": "2024-06-04T16:03:22.1Z" + } + } + ], + "signalGroup": 4 + }, + { + "stateTimeSpeed": [ + { + "eventState": "PROTECTED_CLEARANCE", + "timing": { + "minEndTime": "2024-06-04T16:03:19Z", + "maxEndTime": "2024-06-04T16:03:19Z" + } + } + ], + "signalGroup": 6 + } + ], + "messageType": "SPAT", + "cti4501Conformant": false, + "intersectionId": 12109, + "odeReceivedAt": "2024-06-04T16:22:59.531Z", + "originIp": "10.11.81.12", + "status": { + "failureFlash": false, + "noValidSPATisAvailableAtThisTime": false, + "fixedTimeOperation": false, + "standbyOperation": false, + "trafficDependentOperation": false, + "manualControlIsEnabled": false, + "off": false, + "stopTimeIsActivated": false, + "recentChangeInMAPassignedLanesIDsUsed": false, + "recentMAPmessageUpdate": false, + "failureMode": false, + "noValidMAPisAvailableAtThisTime": false, + "signalPriorityIsActive": false, + "preemptIsActive": false + } +},{ + "_id": { + "$oid": "665f3fc18c25216ef4f53fea" + }, + "validationMessages": [ + { + "schemaPath": "#/$defs/J2735RoadRegulatorID/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].id.region", + "message": "$.payload.data.intersectionStateList.intersectionStatelist[0].id.region: null found, integer expected" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[0].state_time_speed.movementEventList[0].timing.startTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[0].state_time_speed.movementEventList[0].timing.nextTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[1].state_time_speed.movementEventList[0].timing.startTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[1].state_time_speed.movementEventList[0].timing.nextTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[2].state_time_speed.movementEventList[0].timing.startTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[2].state_time_speed.movementEventList[0].timing.nextTime", + "message": "DE_TimeMark is missing" + } + ], + "utcTimeStamp": "2024-06-04T16:22:59.64Z", + "schemaVersion": 1, + "recordGeneratedAt": { + "$date": { + "$numberLong": "1717518179649" + } + }, + "revision": 0, + "states": [ + { + "stateTimeSpeed": [ + { + "eventState": "PROTECTED_CLEARANCE", + "timing": { + "minEndTime": "2024-06-04T16:03:19Z", + "maxEndTime": "2024-06-04T16:03:19Z" + } + } + ], + "signalGroup": 2 + }, + { + "stateTimeSpeed": [ + { + "eventState": "STOP_AND_REMAIN", + "timing": { + "minEndTime": "2024-06-04T16:03:22.1Z", + "maxEndTime": "2024-06-04T16:03:22.1Z" + } + } + ], + "signalGroup": 4 + }, + { + "stateTimeSpeed": [ + { + "eventState": "PROTECTED_CLEARANCE", + "timing": { + "minEndTime": "2024-06-04T16:03:19Z", + "maxEndTime": "2024-06-04T16:03:19Z" + } + } + ], + "signalGroup": 6 + } + ], + "messageType": "SPAT", + "cti4501Conformant": false, + "intersectionId": 12109, + "odeReceivedAt": "2024-06-04T16:22:59.64Z", + "originIp": "10.11.81.12", + "status": { + "failureFlash": false, + "noValidSPATisAvailableAtThisTime": false, + "fixedTimeOperation": false, + "standbyOperation": false, + "trafficDependentOperation": false, + "manualControlIsEnabled": false, + "off": false, + "stopTimeIsActivated": false, + "recentChangeInMAPassignedLanesIDsUsed": false, + "recentMAPmessageUpdate": false, + "failureMode": false, + "noValidMAPisAvailableAtThisTime": false, + "signalPriorityIsActive": false, + "preemptIsActive": false + } +},{ + "_id": { + "$oid": "665f3fc18c25216ef4f53feb" + }, + "validationMessages": [ + { + "schemaPath": "#/$defs/J2735RoadRegulatorID/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].id.region", + "message": "$.payload.data.intersectionStateList.intersectionStatelist[0].id.region: null found, integer expected" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[0].state_time_speed.movementEventList[0].timing.startTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[0].state_time_speed.movementEventList[0].timing.nextTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[1].state_time_speed.movementEventList[0].timing.startTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[1].state_time_speed.movementEventList[0].timing.nextTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[2].state_time_speed.movementEventList[0].timing.startTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[2].state_time_speed.movementEventList[0].timing.nextTime", + "message": "DE_TimeMark is missing" + } + ], + "utcTimeStamp": "2024-06-04T16:22:59.747Z", + "schemaVersion": 1, + "recordGeneratedAt": { + "$date": { + "$numberLong": "1717518179754" + } + }, + "revision": 0, + "states": [ + { + "stateTimeSpeed": [ + { + "eventState": "PROTECTED_CLEARANCE", + "timing": { + "minEndTime": "2024-06-04T16:03:19Z", + "maxEndTime": "2024-06-04T16:03:19Z" + } + } + ], + "signalGroup": 2 + }, + { + "stateTimeSpeed": [ + { + "eventState": "STOP_AND_REMAIN", + "timing": { + "minEndTime": "2024-06-04T16:03:22.1Z", + "maxEndTime": "2024-06-04T16:03:22.1Z" + } + } + ], + "signalGroup": 4 + }, + { + "stateTimeSpeed": [ + { + "eventState": "PROTECTED_CLEARANCE", + "timing": { + "minEndTime": "2024-06-04T16:03:19Z", + "maxEndTime": "2024-06-04T16:03:19Z" + } + } + ], + "signalGroup": 6 + } + ], + "messageType": "SPAT", + "cti4501Conformant": false, + "intersectionId": 12109, + "odeReceivedAt": "2024-06-04T16:22:59.747Z", + "originIp": "10.11.81.12", + "status": { + "failureFlash": false, + "noValidSPATisAvailableAtThisTime": false, + "fixedTimeOperation": false, + "standbyOperation": false, + "trafficDependentOperation": false, + "manualControlIsEnabled": false, + "off": false, + "stopTimeIsActivated": false, + "recentChangeInMAPassignedLanesIDsUsed": false, + "recentMAPmessageUpdate": false, + "failureMode": false, + "noValidMAPisAvailableAtThisTime": false, + "signalPriorityIsActive": false, + "preemptIsActive": false + } +},{ + "_id": { + "$oid": "665f3fc18c25216ef4f53fec" + }, + "validationMessages": [ + { + "schemaPath": "#/$defs/J2735RoadRegulatorID/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].id.region", + "message": "$.payload.data.intersectionStateList.intersectionStatelist[0].id.region: null found, integer expected" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[0].state_time_speed.movementEventList[0].timing.startTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[0].state_time_speed.movementEventList[0].timing.nextTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[1].state_time_speed.movementEventList[0].timing.startTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[1].state_time_speed.movementEventList[0].timing.nextTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[2].state_time_speed.movementEventList[0].timing.startTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[2].state_time_speed.movementEventList[0].timing.nextTime", + "message": "DE_TimeMark is missing" + } + ], + "utcTimeStamp": "2024-06-04T16:22:59.853Z", + "schemaVersion": 1, + "recordGeneratedAt": { + "$date": { + "$numberLong": "1717518179860" + } + }, + "revision": 0, + "states": [ + { + "stateTimeSpeed": [ + { + "eventState": "PROTECTED_CLEARANCE", + "timing": { + "minEndTime": "2024-06-04T16:03:19Z", + "maxEndTime": "2024-06-04T16:03:19Z" + } + } + ], + "signalGroup": 2 + }, + { + "stateTimeSpeed": [ + { + "eventState": "STOP_AND_REMAIN", + "timing": { + "minEndTime": "2024-06-04T16:03:22.1Z", + "maxEndTime": "2024-06-04T16:03:22.1Z" + } + } + ], + "signalGroup": 4 + }, + { + "stateTimeSpeed": [ + { + "eventState": "PROTECTED_CLEARANCE", + "timing": { + "minEndTime": "2024-06-04T16:03:19Z", + "maxEndTime": "2024-06-04T16:03:19Z" + } + } + ], + "signalGroup": 6 + } + ], + "messageType": "SPAT", + "cti4501Conformant": false, + "intersectionId": 12109, + "odeReceivedAt": "2024-06-04T16:22:59.853Z", + "originIp": "10.11.81.12", + "status": { + "failureFlash": false, + "noValidSPATisAvailableAtThisTime": false, + "fixedTimeOperation": false, + "standbyOperation": false, + "trafficDependentOperation": false, + "manualControlIsEnabled": false, + "off": false, + "stopTimeIsActivated": false, + "recentChangeInMAPassignedLanesIDsUsed": false, + "recentMAPmessageUpdate": false, + "failureMode": false, + "noValidMAPisAvailableAtThisTime": false, + "signalPriorityIsActive": false, + "preemptIsActive": false + } +},{ + "_id": { + "$oid": "665f3fc18c25216ef4f53fed" + }, + "validationMessages": [ + { + "schemaPath": "#/$defs/J2735RoadRegulatorID/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].id.region", + "message": "$.payload.data.intersectionStateList.intersectionStatelist[0].id.region: null found, integer expected" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[0].state_time_speed.movementEventList[0].timing.startTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[0].state_time_speed.movementEventList[0].timing.nextTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[1].state_time_speed.movementEventList[0].timing.startTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[1].state_time_speed.movementEventList[0].timing.nextTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[2].state_time_speed.movementEventList[0].timing.startTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[2].state_time_speed.movementEventList[0].timing.nextTime", + "message": "DE_TimeMark is missing" + } + ], + "utcTimeStamp": "2024-06-04T16:22:59.96Z", + "schemaVersion": 1, + "recordGeneratedAt": { + "$date": { + "$numberLong": "1717518179967" + } + }, + "revision": 0, + "states": [ + { + "stateTimeSpeed": [ + { + "eventState": "PROTECTED_CLEARANCE", + "timing": { + "minEndTime": "2024-06-04T16:03:19Z", + "maxEndTime": "2024-06-04T16:03:19Z" + } + } + ], + "signalGroup": 2 + }, + { + "stateTimeSpeed": [ + { + "eventState": "STOP_AND_REMAIN", + "timing": { + "minEndTime": "2024-06-04T16:03:22.1Z", + "maxEndTime": "2024-06-04T16:03:22.1Z" + } + } + ], + "signalGroup": 4 + }, + { + "stateTimeSpeed": [ + { + "eventState": "PROTECTED_CLEARANCE", + "timing": { + "minEndTime": "2024-06-04T16:03:19Z", + "maxEndTime": "2024-06-04T16:03:19Z" + } + } + ], + "signalGroup": 6 + } + ], + "messageType": "SPAT", + "cti4501Conformant": false, + "intersectionId": 12109, + "odeReceivedAt": "2024-06-04T16:22:59.96Z", + "originIp": "10.11.81.12", + "status": { + "failureFlash": false, + "noValidSPATisAvailableAtThisTime": false, + "fixedTimeOperation": false, + "standbyOperation": false, + "trafficDependentOperation": false, + "manualControlIsEnabled": false, + "off": false, + "stopTimeIsActivated": false, + "recentChangeInMAPassignedLanesIDsUsed": false, + "recentMAPmessageUpdate": false, + "failureMode": false, + "noValidMAPisAvailableAtThisTime": false, + "signalPriorityIsActive": false, + "preemptIsActive": false + } +},{ + "_id": { + "$oid": "665f3fc18c25216ef4f53fee" + }, + "validationMessages": [ + { + "schemaPath": "#/$defs/J2735RoadRegulatorID/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].id.region", + "message": "$.payload.data.intersectionStateList.intersectionStatelist[0].id.region: null found, integer expected" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[0].state_time_speed.movementEventList[0].timing.startTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[0].state_time_speed.movementEventList[0].timing.nextTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[1].state_time_speed.movementEventList[0].timing.startTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[1].state_time_speed.movementEventList[0].timing.nextTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[2].state_time_speed.movementEventList[0].timing.startTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[2].state_time_speed.movementEventList[0].timing.nextTime", + "message": "DE_TimeMark is missing" + } + ], + "utcTimeStamp": "2024-06-04T16:23:00.052Z", + "schemaVersion": 1, + "recordGeneratedAt": { + "$date": { + "$numberLong": "1717518180059" + } + }, + "revision": 0, + "states": [ + { + "stateTimeSpeed": [ + { + "eventState": "PROTECTED_CLEARANCE", + "timing": { + "minEndTime": "2024-06-04T16:03:19Z", + "maxEndTime": "2024-06-04T16:03:19Z" + } + } + ], + "signalGroup": 2 + }, + { + "stateTimeSpeed": [ + { + "eventState": "STOP_AND_REMAIN", + "timing": { + "minEndTime": "2024-06-04T16:03:22.1Z", + "maxEndTime": "2024-06-04T16:03:22.1Z" + } + } + ], + "signalGroup": 4 + }, + { + "stateTimeSpeed": [ + { + "eventState": "PROTECTED_CLEARANCE", + "timing": { + "minEndTime": "2024-06-04T16:03:19Z", + "maxEndTime": "2024-06-04T16:03:19Z" + } + } + ], + "signalGroup": 6 + } + ], + "messageType": "SPAT", + "cti4501Conformant": false, + "intersectionId": 12109, + "odeReceivedAt": "2024-06-04T16:23:00.052Z", + "originIp": "10.11.81.12", + "status": { + "failureFlash": false, + "noValidSPATisAvailableAtThisTime": false, + "fixedTimeOperation": false, + "standbyOperation": false, + "trafficDependentOperation": false, + "manualControlIsEnabled": false, + "off": false, + "stopTimeIsActivated": false, + "recentChangeInMAPassignedLanesIDsUsed": false, + "recentMAPmessageUpdate": false, + "failureMode": false, + "noValidMAPisAvailableAtThisTime": false, + "signalPriorityIsActive": false, + "preemptIsActive": false + } +},{ + "_id": { + "$oid": "665f3fc18c25216ef4f53fef" + }, + "validationMessages": [ + { + "schemaPath": "#/$defs/J2735RoadRegulatorID/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].id.region", + "message": "$.payload.data.intersectionStateList.intersectionStatelist[0].id.region: null found, integer expected" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[0].state_time_speed.movementEventList[0].timing.startTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[0].state_time_speed.movementEventList[0].timing.nextTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[1].state_time_speed.movementEventList[0].timing.startTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[1].state_time_speed.movementEventList[0].timing.nextTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[2].state_time_speed.movementEventList[0].timing.startTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[2].state_time_speed.movementEventList[0].timing.nextTime", + "message": "DE_TimeMark is missing" + } + ], + "utcTimeStamp": "2024-06-04T16:23:00.16Z", + "schemaVersion": 1, + "recordGeneratedAt": { + "$date": { + "$numberLong": "1717518180166" + } + }, + "revision": 0, + "states": [ + { + "stateTimeSpeed": [ + { + "eventState": "PROTECTED_CLEARANCE", + "timing": { + "minEndTime": "2024-06-04T16:03:19Z", + "maxEndTime": "2024-06-04T16:03:19Z" + } + } + ], + "signalGroup": 2 + }, + { + "stateTimeSpeed": [ + { + "eventState": "STOP_AND_REMAIN", + "timing": { + "minEndTime": "2024-06-04T16:03:22.1Z", + "maxEndTime": "2024-06-04T16:03:22.1Z" + } + } + ], + "signalGroup": 4 + }, + { + "stateTimeSpeed": [ + { + "eventState": "PROTECTED_CLEARANCE", + "timing": { + "minEndTime": "2024-06-04T16:03:19Z", + "maxEndTime": "2024-06-04T16:03:19Z" + } + } + ], + "signalGroup": 6 + } + ], + "messageType": "SPAT", + "cti4501Conformant": false, + "intersectionId": 12109, + "odeReceivedAt": "2024-06-04T16:23:00.16Z", + "originIp": "10.11.81.12", + "status": { + "failureFlash": false, + "noValidSPATisAvailableAtThisTime": false, + "fixedTimeOperation": false, + "standbyOperation": false, + "trafficDependentOperation": false, + "manualControlIsEnabled": false, + "off": false, + "stopTimeIsActivated": false, + "recentChangeInMAPassignedLanesIDsUsed": false, + "recentMAPmessageUpdate": false, + "failureMode": false, + "noValidMAPisAvailableAtThisTime": false, + "signalPriorityIsActive": false, + "preemptIsActive": false + } +},{ + "_id": { + "$oid": "665f3fc18c25216ef4f53ff0" + }, + "validationMessages": [ + { + "schemaPath": "#/$defs/J2735RoadRegulatorID/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].id.region", + "message": "$.payload.data.intersectionStateList.intersectionStatelist[0].id.region: null found, integer expected" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[0].state_time_speed.movementEventList[0].timing.startTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[0].state_time_speed.movementEventList[0].timing.nextTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[1].state_time_speed.movementEventList[0].timing.startTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[1].state_time_speed.movementEventList[0].timing.nextTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[2].state_time_speed.movementEventList[0].timing.startTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[2].state_time_speed.movementEventList[0].timing.nextTime", + "message": "DE_TimeMark is missing" + } + ], + "utcTimeStamp": "2024-06-04T16:23:00.268Z", + "schemaVersion": 1, + "recordGeneratedAt": { + "$date": { + "$numberLong": "1717518180273" + } + }, + "revision": 0, + "states": [ + { + "stateTimeSpeed": [ + { + "eventState": "PROTECTED_CLEARANCE", + "timing": { + "minEndTime": "2024-06-04T16:03:19Z", + "maxEndTime": "2024-06-04T16:03:19Z" + } + } + ], + "signalGroup": 2 + }, + { + "stateTimeSpeed": [ + { + "eventState": "STOP_AND_REMAIN", + "timing": { + "minEndTime": "2024-06-04T16:03:22.1Z", + "maxEndTime": "2024-06-04T16:03:22.1Z" + } + } + ], + "signalGroup": 4 + }, + { + "stateTimeSpeed": [ + { + "eventState": "PROTECTED_CLEARANCE", + "timing": { + "minEndTime": "2024-06-04T16:03:19Z", + "maxEndTime": "2024-06-04T16:03:19Z" + } + } + ], + "signalGroup": 6 + } + ], + "messageType": "SPAT", + "cti4501Conformant": false, + "intersectionId": 12109, + "odeReceivedAt": "2024-06-04T16:23:00.268Z", + "originIp": "10.11.81.12", + "status": { + "failureFlash": false, + "noValidSPATisAvailableAtThisTime": false, + "fixedTimeOperation": false, + "standbyOperation": false, + "trafficDependentOperation": false, + "manualControlIsEnabled": false, + "off": false, + "stopTimeIsActivated": false, + "recentChangeInMAPassignedLanesIDsUsed": false, + "recentMAPmessageUpdate": false, + "failureMode": false, + "noValidMAPisAvailableAtThisTime": false, + "signalPriorityIsActive": false, + "preemptIsActive": false + } +},{ + "_id": { + "$oid": "665f3fc18c25216ef4f53ff1" + }, + "validationMessages": [ + { + "schemaPath": "#/$defs/J2735RoadRegulatorID/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].id.region", + "message": "$.payload.data.intersectionStateList.intersectionStatelist[0].id.region: null found, integer expected" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[0].state_time_speed.movementEventList[0].timing.startTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[0].state_time_speed.movementEventList[0].timing.nextTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[1].state_time_speed.movementEventList[0].timing.startTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[1].state_time_speed.movementEventList[0].timing.nextTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[2].state_time_speed.movementEventList[0].timing.startTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[2].state_time_speed.movementEventList[0].timing.nextTime", + "message": "DE_TimeMark is missing" + } + ], + "utcTimeStamp": "2024-06-04T16:23:00.375Z", + "schemaVersion": 1, + "recordGeneratedAt": { + "$date": { + "$numberLong": "1717518180381" + } + }, + "revision": 0, + "states": [ + { + "stateTimeSpeed": [ + { + "eventState": "PROTECTED_CLEARANCE", + "timing": { + "minEndTime": "2024-06-04T16:03:19Z", + "maxEndTime": "2024-06-04T16:03:19Z" + } + } + ], + "signalGroup": 2 + }, + { + "stateTimeSpeed": [ + { + "eventState": "STOP_AND_REMAIN", + "timing": { + "minEndTime": "2024-06-04T16:03:22.1Z", + "maxEndTime": "2024-06-04T16:03:22.1Z" + } + } + ], + "signalGroup": 4 + }, + { + "stateTimeSpeed": [ + { + "eventState": "PROTECTED_CLEARANCE", + "timing": { + "minEndTime": "2024-06-04T16:03:19Z", + "maxEndTime": "2024-06-04T16:03:19Z" + } + } + ], + "signalGroup": 6 + } + ], + "messageType": "SPAT", + "cti4501Conformant": false, + "intersectionId": 12109, + "odeReceivedAt": "2024-06-04T16:23:00.375Z", + "originIp": "10.11.81.12", + "status": { + "failureFlash": false, + "noValidSPATisAvailableAtThisTime": false, + "fixedTimeOperation": false, + "standbyOperation": false, + "trafficDependentOperation": false, + "manualControlIsEnabled": false, + "off": false, + "stopTimeIsActivated": false, + "recentChangeInMAPassignedLanesIDsUsed": false, + "recentMAPmessageUpdate": false, + "failureMode": false, + "noValidMAPisAvailableAtThisTime": false, + "signalPriorityIsActive": false, + "preemptIsActive": false + } +},{ + "_id": { + "$oid": "665f3fc18c25216ef4f53ff2" + }, + "validationMessages": [ + { + "schemaPath": "#/$defs/J2735RoadRegulatorID/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].id.region", + "message": "$.payload.data.intersectionStateList.intersectionStatelist[0].id.region: null found, integer expected" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[0].state_time_speed.movementEventList[0].timing.startTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[0].state_time_speed.movementEventList[0].timing.nextTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[1].state_time_speed.movementEventList[0].timing.startTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[1].state_time_speed.movementEventList[0].timing.nextTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[2].state_time_speed.movementEventList[0].timing.startTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[2].state_time_speed.movementEventList[0].timing.nextTime", + "message": "DE_TimeMark is missing" + } + ], + "utcTimeStamp": "2024-06-04T16:23:00.481Z", + "schemaVersion": 1, + "recordGeneratedAt": { + "$date": { + "$numberLong": "1717518180489" + } + }, + "revision": 0, + "states": [ + { + "stateTimeSpeed": [ + { + "eventState": "PROTECTED_CLEARANCE", + "timing": { + "minEndTime": "2024-06-04T16:03:19Z", + "maxEndTime": "2024-06-04T16:03:19Z" + } + } + ], + "signalGroup": 2 + }, + { + "stateTimeSpeed": [ + { + "eventState": "STOP_AND_REMAIN", + "timing": { + "minEndTime": "2024-06-04T16:03:22.1Z", + "maxEndTime": "2024-06-04T16:03:22.1Z" + } + } + ], + "signalGroup": 4 + }, + { + "stateTimeSpeed": [ + { + "eventState": "PROTECTED_CLEARANCE", + "timing": { + "minEndTime": "2024-06-04T16:03:19Z", + "maxEndTime": "2024-06-04T16:03:19Z" + } + } + ], + "signalGroup": 6 + } + ], + "messageType": "SPAT", + "cti4501Conformant": false, + "intersectionId": 12109, + "odeReceivedAt": "2024-06-04T16:23:00.481Z", + "originIp": "10.11.81.12", + "status": { + "failureFlash": false, + "noValidSPATisAvailableAtThisTime": false, + "fixedTimeOperation": false, + "standbyOperation": false, + "trafficDependentOperation": false, + "manualControlIsEnabled": false, + "off": false, + "stopTimeIsActivated": false, + "recentChangeInMAPassignedLanesIDsUsed": false, + "recentMAPmessageUpdate": false, + "failureMode": false, + "noValidMAPisAvailableAtThisTime": false, + "signalPriorityIsActive": false, + "preemptIsActive": false + } +},{ + "_id": { + "$oid": "665f3fc18c25216ef4f53ff3" + }, + "validationMessages": [ + { + "schemaPath": "#/$defs/J2735RoadRegulatorID/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].id.region", + "message": "$.payload.data.intersectionStateList.intersectionStatelist[0].id.region: null found, integer expected" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[0].state_time_speed.movementEventList[0].timing.startTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[0].state_time_speed.movementEventList[0].timing.nextTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[1].state_time_speed.movementEventList[0].timing.startTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[1].state_time_speed.movementEventList[0].timing.nextTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[2].state_time_speed.movementEventList[0].timing.startTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[2].state_time_speed.movementEventList[0].timing.nextTime", + "message": "DE_TimeMark is missing" + } + ], + "utcTimeStamp": "2024-06-04T16:23:00.589Z", + "schemaVersion": 1, + "recordGeneratedAt": { + "$date": { + "$numberLong": "1717518180596" + } + }, + "revision": 0, + "states": [ + { + "stateTimeSpeed": [ + { + "eventState": "PROTECTED_CLEARANCE", + "timing": { + "minEndTime": "2024-06-04T16:03:19Z", + "maxEndTime": "2024-06-04T16:03:19Z" + } + } + ], + "signalGroup": 2 + }, + { + "stateTimeSpeed": [ + { + "eventState": "STOP_AND_REMAIN", + "timing": { + "minEndTime": "2024-06-04T16:03:22.1Z", + "maxEndTime": "2024-06-04T16:03:22.1Z" + } + } + ], + "signalGroup": 4 + }, + { + "stateTimeSpeed": [ + { + "eventState": "PROTECTED_CLEARANCE", + "timing": { + "minEndTime": "2024-06-04T16:03:19Z", + "maxEndTime": "2024-06-04T16:03:19Z" + } + } + ], + "signalGroup": 6 + } + ], + "messageType": "SPAT", + "cti4501Conformant": false, + "intersectionId": 12109, + "odeReceivedAt": "2024-06-04T16:23:00.589Z", + "originIp": "10.11.81.12", + "status": { + "failureFlash": false, + "noValidSPATisAvailableAtThisTime": false, + "fixedTimeOperation": false, + "standbyOperation": false, + "trafficDependentOperation": false, + "manualControlIsEnabled": false, + "off": false, + "stopTimeIsActivated": false, + "recentChangeInMAPassignedLanesIDsUsed": false, + "recentMAPmessageUpdate": false, + "failureMode": false, + "noValidMAPisAvailableAtThisTime": false, + "signalPriorityIsActive": false, + "preemptIsActive": false + } +},{ + "_id": { + "$oid": "665f3fc18c25216ef4f53ff4" + }, + "validationMessages": [ + { + "schemaPath": "#/$defs/J2735RoadRegulatorID/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].id.region", + "message": "$.payload.data.intersectionStateList.intersectionStatelist[0].id.region: null found, integer expected" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[0].state_time_speed.movementEventList[0].timing.startTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[0].state_time_speed.movementEventList[0].timing.nextTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[1].state_time_speed.movementEventList[0].timing.startTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[1].state_time_speed.movementEventList[0].timing.nextTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[2].state_time_speed.movementEventList[0].timing.startTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[2].state_time_speed.movementEventList[0].timing.nextTime", + "message": "DE_TimeMark is missing" + } + ], + "utcTimeStamp": "2024-06-04T16:23:00.696Z", + "schemaVersion": 1, + "recordGeneratedAt": { + "$date": { + "$numberLong": "1717518180703" + } + }, + "revision": 0, + "states": [ + { + "stateTimeSpeed": [ + { + "eventState": "PROTECTED_CLEARANCE", + "timing": { + "minEndTime": "2024-06-04T16:03:19Z", + "maxEndTime": "2024-06-04T16:03:19Z" + } + } + ], + "signalGroup": 2 + }, + { + "stateTimeSpeed": [ + { + "eventState": "STOP_AND_REMAIN", + "timing": { + "minEndTime": "2024-06-04T16:03:22.1Z", + "maxEndTime": "2024-06-04T16:03:22.1Z" + } + } + ], + "signalGroup": 4 + }, + { + "stateTimeSpeed": [ + { + "eventState": "PROTECTED_CLEARANCE", + "timing": { + "minEndTime": "2024-06-04T16:03:19Z", + "maxEndTime": "2024-06-04T16:03:19Z" + } + } + ], + "signalGroup": 6 + } + ], + "messageType": "SPAT", + "cti4501Conformant": false, + "intersectionId": 12109, + "odeReceivedAt": "2024-06-04T16:23:00.696Z", + "originIp": "10.11.81.12", + "status": { + "failureFlash": false, + "noValidSPATisAvailableAtThisTime": false, + "fixedTimeOperation": false, + "standbyOperation": false, + "trafficDependentOperation": false, + "manualControlIsEnabled": false, + "off": false, + "stopTimeIsActivated": false, + "recentChangeInMAPassignedLanesIDsUsed": false, + "recentMAPmessageUpdate": false, + "failureMode": false, + "noValidMAPisAvailableAtThisTime": false, + "signalPriorityIsActive": false, + "preemptIsActive": false + } +},{ + "_id": { + "$oid": "665f3fc18c25216ef4f53ff5" + }, + "validationMessages": [ + { + "schemaPath": "#/$defs/J2735RoadRegulatorID/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].id.region", + "message": "$.payload.data.intersectionStateList.intersectionStatelist[0].id.region: null found, integer expected" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[0].state_time_speed.movementEventList[0].timing.startTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[0].state_time_speed.movementEventList[0].timing.nextTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[1].state_time_speed.movementEventList[0].timing.startTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[1].state_time_speed.movementEventList[0].timing.nextTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[2].state_time_speed.movementEventList[0].timing.startTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[2].state_time_speed.movementEventList[0].timing.nextTime", + "message": "DE_TimeMark is missing" + } + ], + "utcTimeStamp": "2024-06-04T16:23:00.804Z", + "schemaVersion": 1, + "recordGeneratedAt": { + "$date": { + "$numberLong": "1717518180809" + } + }, + "revision": 0, + "states": [ + { + "stateTimeSpeed": [ + { + "eventState": "PROTECTED_CLEARANCE", + "timing": { + "minEndTime": "2024-06-04T16:03:19Z", + "maxEndTime": "2024-06-04T16:03:19Z" + } + } + ], + "signalGroup": 2 + }, + { + "stateTimeSpeed": [ + { + "eventState": "STOP_AND_REMAIN", + "timing": { + "minEndTime": "2024-06-04T16:03:22.1Z", + "maxEndTime": "2024-06-04T16:03:22.1Z" + } + } + ], + "signalGroup": 4 + }, + { + "stateTimeSpeed": [ + { + "eventState": "PROTECTED_CLEARANCE", + "timing": { + "minEndTime": "2024-06-04T16:03:19Z", + "maxEndTime": "2024-06-04T16:03:19Z" + } + } + ], + "signalGroup": 6 + } + ], + "messageType": "SPAT", + "cti4501Conformant": false, + "intersectionId": 12109, + "odeReceivedAt": "2024-06-04T16:23:00.804Z", + "originIp": "10.11.81.12", + "status": { + "failureFlash": false, + "noValidSPATisAvailableAtThisTime": false, + "fixedTimeOperation": false, + "standbyOperation": false, + "trafficDependentOperation": false, + "manualControlIsEnabled": false, + "off": false, + "stopTimeIsActivated": false, + "recentChangeInMAPassignedLanesIDsUsed": false, + "recentMAPmessageUpdate": false, + "failureMode": false, + "noValidMAPisAvailableAtThisTime": false, + "signalPriorityIsActive": false, + "preemptIsActive": false + } +},{ + "_id": { + "$oid": "665f3fc18c25216ef4f53ff6" + }, + "validationMessages": [ + { + "schemaPath": "#/$defs/J2735RoadRegulatorID/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].id.region", + "message": "$.payload.data.intersectionStateList.intersectionStatelist[0].id.region: null found, integer expected" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[0].state_time_speed.movementEventList[0].timing.startTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[0].state_time_speed.movementEventList[0].timing.nextTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[1].state_time_speed.movementEventList[0].timing.startTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[1].state_time_speed.movementEventList[0].timing.nextTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[2].state_time_speed.movementEventList[0].timing.startTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[2].state_time_speed.movementEventList[0].timing.nextTime", + "message": "DE_TimeMark is missing" + } + ], + "utcTimeStamp": "2024-06-04T16:23:00.911Z", + "schemaVersion": 1, + "recordGeneratedAt": { + "$date": { + "$numberLong": "1717518180915" + } + }, + "revision": 0, + "states": [ + { + "stateTimeSpeed": [ + { + "eventState": "PROTECTED_CLEARANCE", + "timing": { + "minEndTime": "2024-06-04T16:03:19Z", + "maxEndTime": "2024-06-04T16:03:19Z" + } + } + ], + "signalGroup": 2 + }, + { + "stateTimeSpeed": [ + { + "eventState": "STOP_AND_REMAIN", + "timing": { + "minEndTime": "2024-06-04T16:03:22.1Z", + "maxEndTime": "2024-06-04T16:03:22.1Z" + } + } + ], + "signalGroup": 4 + }, + { + "stateTimeSpeed": [ + { + "eventState": "PROTECTED_CLEARANCE", + "timing": { + "minEndTime": "2024-06-04T16:03:19Z", + "maxEndTime": "2024-06-04T16:03:19Z" + } + } + ], + "signalGroup": 6 + } + ], + "messageType": "SPAT", + "cti4501Conformant": false, + "intersectionId": 12109, + "odeReceivedAt": "2024-06-04T16:23:00.911Z", + "originIp": "10.11.81.12", + "status": { + "failureFlash": false, + "noValidSPATisAvailableAtThisTime": false, + "fixedTimeOperation": false, + "standbyOperation": false, + "trafficDependentOperation": false, + "manualControlIsEnabled": false, + "off": false, + "stopTimeIsActivated": false, + "recentChangeInMAPassignedLanesIDsUsed": false, + "recentMAPmessageUpdate": false, + "failureMode": false, + "noValidMAPisAvailableAtThisTime": false, + "signalPriorityIsActive": false, + "preemptIsActive": false + } +},{ + "_id": { + "$oid": "665f3fc18c25216ef4f53ff7" + }, + "validationMessages": [ + { + "schemaPath": "#/$defs/J2735RoadRegulatorID/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].id.region", + "message": "$.payload.data.intersectionStateList.intersectionStatelist[0].id.region: null found, integer expected" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[0].state_time_speed.movementEventList[0].timing.startTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[0].state_time_speed.movementEventList[0].timing.nextTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[1].state_time_speed.movementEventList[0].timing.startTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[1].state_time_speed.movementEventList[0].timing.nextTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[2].state_time_speed.movementEventList[0].timing.startTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[2].state_time_speed.movementEventList[0].timing.nextTime", + "message": "DE_TimeMark is missing" + } + ], + "utcTimeStamp": "2024-06-04T16:23:01.004Z", + "schemaVersion": 1, + "recordGeneratedAt": { + "$date": { + "$numberLong": "1717518181007" + } + }, + "revision": 0, + "states": [ + { + "stateTimeSpeed": [ + { + "eventState": "PROTECTED_CLEARANCE", + "timing": { + "minEndTime": "2024-06-04T16:03:18Z", + "maxEndTime": "2024-06-04T16:03:18Z" + } + } + ], + "signalGroup": 2 + }, + { + "stateTimeSpeed": [ + { + "eventState": "STOP_AND_REMAIN", + "timing": { + "minEndTime": "2024-06-04T16:03:21.1Z", + "maxEndTime": "2024-06-04T16:03:21.1Z" + } + } + ], + "signalGroup": 4 + }, + { + "stateTimeSpeed": [ + { + "eventState": "PROTECTED_CLEARANCE", + "timing": { + "minEndTime": "2024-06-04T16:03:18Z", + "maxEndTime": "2024-06-04T16:03:18Z" + } + } + ], + "signalGroup": 6 + } + ], + "messageType": "SPAT", + "cti4501Conformant": false, + "intersectionId": 12109, + "odeReceivedAt": "2024-06-04T16:23:01.004Z", + "originIp": "10.11.81.12", + "status": { + "failureFlash": false, + "noValidSPATisAvailableAtThisTime": false, + "fixedTimeOperation": false, + "standbyOperation": false, + "trafficDependentOperation": false, + "manualControlIsEnabled": false, + "off": false, + "stopTimeIsActivated": false, + "recentChangeInMAPassignedLanesIDsUsed": false, + "recentMAPmessageUpdate": false, + "failureMode": false, + "noValidMAPisAvailableAtThisTime": false, + "signalPriorityIsActive": false, + "preemptIsActive": false + } +},{ + "_id": { + "$oid": "665f3fc18c25216ef4f53ff8" + }, + "validationMessages": [ + { + "schemaPath": "#/$defs/J2735RoadRegulatorID/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].id.region", + "message": "$.payload.data.intersectionStateList.intersectionStatelist[0].id.region: null found, integer expected" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[0].state_time_speed.movementEventList[0].timing.startTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[0].state_time_speed.movementEventList[0].timing.nextTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[1].state_time_speed.movementEventList[0].timing.startTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[1].state_time_speed.movementEventList[0].timing.nextTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[2].state_time_speed.movementEventList[0].timing.startTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[2].state_time_speed.movementEventList[0].timing.nextTime", + "message": "DE_TimeMark is missing" + } + ], + "utcTimeStamp": "2024-06-04T16:23:01.113Z", + "schemaVersion": 1, + "recordGeneratedAt": { + "$date": { + "$numberLong": "1717518181128" + } + }, + "revision": 0, + "states": [ + { + "stateTimeSpeed": [ + { + "eventState": "PROTECTED_CLEARANCE", + "timing": { + "minEndTime": "2024-06-04T16:03:18Z", + "maxEndTime": "2024-06-04T16:03:18Z" + } + } + ], + "signalGroup": 2 + }, + { + "stateTimeSpeed": [ + { + "eventState": "STOP_AND_REMAIN", + "timing": { + "minEndTime": "2024-06-04T16:03:21.1Z", + "maxEndTime": "2024-06-04T16:03:21.1Z" + } + } + ], + "signalGroup": 4 + }, + { + "stateTimeSpeed": [ + { + "eventState": "PROTECTED_CLEARANCE", + "timing": { + "minEndTime": "2024-06-04T16:03:18Z", + "maxEndTime": "2024-06-04T16:03:18Z" + } + } + ], + "signalGroup": 6 + } + ], + "messageType": "SPAT", + "cti4501Conformant": false, + "intersectionId": 12109, + "odeReceivedAt": "2024-06-04T16:23:01.113Z", + "originIp": "10.11.81.12", + "status": { + "failureFlash": false, + "noValidSPATisAvailableAtThisTime": false, + "fixedTimeOperation": false, + "standbyOperation": false, + "trafficDependentOperation": false, + "manualControlIsEnabled": false, + "off": false, + "stopTimeIsActivated": false, + "recentChangeInMAPassignedLanesIDsUsed": false, + "recentMAPmessageUpdate": false, + "failureMode": false, + "noValidMAPisAvailableAtThisTime": false, + "signalPriorityIsActive": false, + "preemptIsActive": false + } +},{ + "_id": { + "$oid": "665f3fc18c25216ef4f53ff9" + }, + "validationMessages": [ + { + "schemaPath": "#/$defs/J2735RoadRegulatorID/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].id.region", + "message": "$.payload.data.intersectionStateList.intersectionStatelist[0].id.region: null found, integer expected" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[0].state_time_speed.movementEventList[0].timing.startTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[0].state_time_speed.movementEventList[0].timing.nextTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[1].state_time_speed.movementEventList[0].timing.startTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[1].state_time_speed.movementEventList[0].timing.nextTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[2].state_time_speed.movementEventList[0].timing.startTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[2].state_time_speed.movementEventList[0].timing.nextTime", + "message": "DE_TimeMark is missing" + } + ], + "utcTimeStamp": "2024-06-04T16:23:11.018Z", + "schemaVersion": 1, + "recordGeneratedAt": { + "$date": { + "$numberLong": "1717518191203" + } + }, + "revision": 0, + "states": [ + { + "stateTimeSpeed": [ + { + "eventState": "PROTECTED_CLEARANCE", + "timing": { + "minEndTime": "2024-06-04T16:03:17Z", + "maxEndTime": "2024-06-04T16:03:17Z" + } + } + ], + "signalGroup": 2 + }, + { + "stateTimeSpeed": [ + { + "eventState": "STOP_AND_REMAIN", + "timing": { + "minEndTime": "2024-06-04T16:03:20.1Z", + "maxEndTime": "2024-06-04T16:03:20.1Z" + } + } + ], + "signalGroup": 4 + }, + { + "stateTimeSpeed": [ + { + "eventState": "PROTECTED_CLEARANCE", + "timing": { + "minEndTime": "2024-06-04T16:03:17Z", + "maxEndTime": "2024-06-04T16:03:17Z" + } + } + ], + "signalGroup": 6 + } + ], + "messageType": "SPAT", + "cti4501Conformant": false, + "intersectionId": 12109, + "odeReceivedAt": "2024-06-04T16:23:11.018Z", + "originIp": "10.11.81.12", + "status": { + "failureFlash": false, + "noValidSPATisAvailableAtThisTime": false, + "fixedTimeOperation": false, + "standbyOperation": false, + "trafficDependentOperation": false, + "manualControlIsEnabled": false, + "off": false, + "stopTimeIsActivated": false, + "recentChangeInMAPassignedLanesIDsUsed": false, + "recentMAPmessageUpdate": false, + "failureMode": false, + "noValidMAPisAvailableAtThisTime": false, + "signalPriorityIsActive": false, + "preemptIsActive": false + } +},{ + "_id": { + "$oid": "665f3fc18c25216ef4f53ffa" + }, + "validationMessages": [ + { + "schemaPath": "#/$defs/J2735RoadRegulatorID/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].id.region", + "message": "$.payload.data.intersectionStateList.intersectionStatelist[0].id.region: null found, integer expected" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[0].state_time_speed.movementEventList[0].timing.startTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[0].state_time_speed.movementEventList[0].timing.nextTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[1].state_time_speed.movementEventList[0].timing.startTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[1].state_time_speed.movementEventList[0].timing.nextTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[2].state_time_speed.movementEventList[0].timing.startTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[2].state_time_speed.movementEventList[0].timing.nextTime", + "message": "DE_TimeMark is missing" + } + ], + "utcTimeStamp": "2024-06-04T16:23:10.801Z", + "schemaVersion": 1, + "recordGeneratedAt": { + "$date": { + "$numberLong": "1717518191202" + } + }, + "revision": 0, + "states": [ + { + "stateTimeSpeed": [ + { + "eventState": "PROTECTED_CLEARANCE", + "timing": { + "minEndTime": "2024-06-04T16:03:17Z", + "maxEndTime": "2024-06-04T16:03:17Z" + } + } + ], + "signalGroup": 2 + }, + { + "stateTimeSpeed": [ + { + "eventState": "STOP_AND_REMAIN", + "timing": { + "minEndTime": "2024-06-04T16:03:20.1Z", + "maxEndTime": "2024-06-04T16:03:20.1Z" + } + } + ], + "signalGroup": 4 + }, + { + "stateTimeSpeed": [ + { + "eventState": "PROTECTED_CLEARANCE", + "timing": { + "minEndTime": "2024-06-04T16:03:17Z", + "maxEndTime": "2024-06-04T16:03:17Z" + } + } + ], + "signalGroup": 6 + } + ], + "messageType": "SPAT", + "cti4501Conformant": false, + "intersectionId": 12109, + "odeReceivedAt": "2024-06-04T16:23:10.801Z", + "originIp": "10.11.81.12", + "status": { + "failureFlash": false, + "noValidSPATisAvailableAtThisTime": false, + "fixedTimeOperation": false, + "standbyOperation": false, + "trafficDependentOperation": false, + "manualControlIsEnabled": false, + "off": false, + "stopTimeIsActivated": false, + "recentChangeInMAPassignedLanesIDsUsed": false, + "recentMAPmessageUpdate": false, + "failureMode": false, + "noValidMAPisAvailableAtThisTime": false, + "signalPriorityIsActive": false, + "preemptIsActive": false + } +},{ + "_id": { + "$oid": "665f3fc18c25216ef4f53ffb" + }, + "validationMessages": [ + { + "schemaPath": "#/$defs/J2735RoadRegulatorID/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].id.region", + "message": "$.payload.data.intersectionStateList.intersectionStatelist[0].id.region: null found, integer expected" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[0].state_time_speed.movementEventList[0].timing.startTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[0].state_time_speed.movementEventList[0].timing.nextTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[1].state_time_speed.movementEventList[0].timing.startTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[1].state_time_speed.movementEventList[0].timing.nextTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[2].state_time_speed.movementEventList[0].timing.startTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[2].state_time_speed.movementEventList[0].timing.nextTime", + "message": "DE_TimeMark is missing" + } + ], + "utcTimeStamp": "2024-06-04T16:23:10.91Z", + "schemaVersion": 1, + "recordGeneratedAt": { + "$date": { + "$numberLong": "1717518191203" + } + }, + "revision": 0, + "states": [ + { + "stateTimeSpeed": [ + { + "eventState": "PROTECTED_CLEARANCE", + "timing": { + "minEndTime": "2024-06-04T16:03:17Z", + "maxEndTime": "2024-06-04T16:03:17Z" + } + } + ], + "signalGroup": 2 + }, + { + "stateTimeSpeed": [ + { + "eventState": "STOP_AND_REMAIN", + "timing": { + "minEndTime": "2024-06-04T16:03:20.1Z", + "maxEndTime": "2024-06-04T16:03:20.1Z" + } + } + ], + "signalGroup": 4 + }, + { + "stateTimeSpeed": [ + { + "eventState": "PROTECTED_CLEARANCE", + "timing": { + "minEndTime": "2024-06-04T16:03:17Z", + "maxEndTime": "2024-06-04T16:03:17Z" + } + } + ], + "signalGroup": 6 + } + ], + "messageType": "SPAT", + "cti4501Conformant": false, + "intersectionId": 12109, + "odeReceivedAt": "2024-06-04T16:23:10.91Z", + "originIp": "10.11.81.12", + "status": { + "failureFlash": false, + "noValidSPATisAvailableAtThisTime": false, + "fixedTimeOperation": false, + "standbyOperation": false, + "trafficDependentOperation": false, + "manualControlIsEnabled": false, + "off": false, + "stopTimeIsActivated": false, + "recentChangeInMAPassignedLanesIDsUsed": false, + "recentMAPmessageUpdate": false, + "failureMode": false, + "noValidMAPisAvailableAtThisTime": false, + "signalPriorityIsActive": false, + "preemptIsActive": false + } +},{ + "_id": { + "$oid": "665f3fc18c25216ef4f53ffc" + }, + "validationMessages": [ + { + "schemaPath": "#/$defs/J2735RoadRegulatorID/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].id.region", + "message": "$.payload.data.intersectionStateList.intersectionStatelist[0].id.region: null found, integer expected" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[0].state_time_speed.movementEventList[0].timing.startTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[0].state_time_speed.movementEventList[0].timing.nextTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[1].state_time_speed.movementEventList[0].timing.startTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[1].state_time_speed.movementEventList[0].timing.nextTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[2].state_time_speed.movementEventList[0].timing.startTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[2].state_time_speed.movementEventList[0].timing.nextTime", + "message": "DE_TimeMark is missing" + } + ], + "utcTimeStamp": "2024-06-04T16:23:11.125Z", + "schemaVersion": 1, + "recordGeneratedAt": { + "$date": { + "$numberLong": "1717518191226" + } + }, + "revision": 0, + "states": [ + { + "stateTimeSpeed": [ + { + "eventState": "PROTECTED_CLEARANCE", + "timing": { + "minEndTime": "2024-06-04T16:03:17Z", + "maxEndTime": "2024-06-04T16:03:17Z" + } + } + ], + "signalGroup": 2 + }, + { + "stateTimeSpeed": [ + { + "eventState": "STOP_AND_REMAIN", + "timing": { + "minEndTime": "2024-06-04T16:03:20.1Z", + "maxEndTime": "2024-06-04T16:03:20.1Z" + } + } + ], + "signalGroup": 4 + }, + { + "stateTimeSpeed": [ + { + "eventState": "PROTECTED_CLEARANCE", + "timing": { + "minEndTime": "2024-06-04T16:03:17Z", + "maxEndTime": "2024-06-04T16:03:17Z" + } + } + ], + "signalGroup": 6 + } + ], + "messageType": "SPAT", + "cti4501Conformant": false, + "intersectionId": 12109, + "odeReceivedAt": "2024-06-04T16:23:11.125Z", + "originIp": "10.11.81.12", + "status": { + "failureFlash": false, + "noValidSPATisAvailableAtThisTime": false, + "fixedTimeOperation": false, + "standbyOperation": false, + "trafficDependentOperation": false, + "manualControlIsEnabled": false, + "off": false, + "stopTimeIsActivated": false, + "recentChangeInMAPassignedLanesIDsUsed": false, + "recentMAPmessageUpdate": false, + "failureMode": false, + "noValidMAPisAvailableAtThisTime": false, + "signalPriorityIsActive": false, + "preemptIsActive": false + } +},{ + "_id": { + "$oid": "665f3fc18c25216ef4f53ffd" + }, + "validationMessages": [ + { + "schemaPath": "#/$defs/J2735RoadRegulatorID/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].id.region", + "message": "$.payload.data.intersectionStateList.intersectionStatelist[0].id.region: null found, integer expected" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[0].state_time_speed.movementEventList[0].timing.startTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[0].state_time_speed.movementEventList[0].timing.nextTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[1].state_time_speed.movementEventList[0].timing.startTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[1].state_time_speed.movementEventList[0].timing.nextTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[2].state_time_speed.movementEventList[0].timing.startTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[2].state_time_speed.movementEventList[0].timing.nextTime", + "message": "DE_TimeMark is missing" + } + ], + "utcTimeStamp": "2024-06-04T16:23:11.232Z", + "schemaVersion": 1, + "recordGeneratedAt": { + "$date": { + "$numberLong": "1717518191242" + } + }, + "revision": 0, + "states": [ + { + "stateTimeSpeed": [ + { + "eventState": "PROTECTED_CLEARANCE", + "timing": { + "minEndTime": "2024-06-04T16:03:17Z", + "maxEndTime": "2024-06-04T16:03:17Z" + } + } + ], + "signalGroup": 2 + }, + { + "stateTimeSpeed": [ + { + "eventState": "STOP_AND_REMAIN", + "timing": { + "minEndTime": "2024-06-04T16:03:20.1Z", + "maxEndTime": "2024-06-04T16:03:20.1Z" + } + } + ], + "signalGroup": 4 + }, + { + "stateTimeSpeed": [ + { + "eventState": "PROTECTED_CLEARANCE", + "timing": { + "minEndTime": "2024-06-04T16:03:17Z", + "maxEndTime": "2024-06-04T16:03:17Z" + } + } + ], + "signalGroup": 6 + } + ], + "messageType": "SPAT", + "cti4501Conformant": false, + "intersectionId": 12109, + "odeReceivedAt": "2024-06-04T16:23:11.232Z", + "originIp": "10.11.81.12", + "status": { + "failureFlash": false, + "noValidSPATisAvailableAtThisTime": false, + "fixedTimeOperation": false, + "standbyOperation": false, + "trafficDependentOperation": false, + "manualControlIsEnabled": false, + "off": false, + "stopTimeIsActivated": false, + "recentChangeInMAPassignedLanesIDsUsed": false, + "recentMAPmessageUpdate": false, + "failureMode": false, + "noValidMAPisAvailableAtThisTime": false, + "signalPriorityIsActive": false, + "preemptIsActive": false + } +},{ + "_id": { + "$oid": "665f3fc18c25216ef4f53ffe" + }, + "validationMessages": [ + { + "schemaPath": "#/$defs/J2735RoadRegulatorID/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].id.region", + "message": "$.payload.data.intersectionStateList.intersectionStatelist[0].id.region: null found, integer expected" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[0].state_time_speed.movementEventList[0].timing.startTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[0].state_time_speed.movementEventList[0].timing.nextTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[1].state_time_speed.movementEventList[0].timing.startTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[1].state_time_speed.movementEventList[0].timing.nextTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[2].state_time_speed.movementEventList[0].timing.startTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[2].state_time_speed.movementEventList[0].timing.nextTime", + "message": "DE_TimeMark is missing" + } + ], + "utcTimeStamp": "2024-06-04T16:23:11.34Z", + "schemaVersion": 1, + "recordGeneratedAt": { + "$date": { + "$numberLong": "1717518191349" + } + }, + "revision": 0, + "states": [ + { + "stateTimeSpeed": [ + { + "eventState": "PROTECTED_CLEARANCE", + "timing": { + "minEndTime": "2024-06-04T16:03:17Z", + "maxEndTime": "2024-06-04T16:03:17Z" + } + } + ], + "signalGroup": 2 + }, + { + "stateTimeSpeed": [ + { + "eventState": "STOP_AND_REMAIN", + "timing": { + "minEndTime": "2024-06-04T16:03:20.1Z", + "maxEndTime": "2024-06-04T16:03:20.1Z" + } + } + ], + "signalGroup": 4 + }, + { + "stateTimeSpeed": [ + { + "eventState": "PROTECTED_CLEARANCE", + "timing": { + "minEndTime": "2024-06-04T16:03:17Z", + "maxEndTime": "2024-06-04T16:03:17Z" + } + } + ], + "signalGroup": 6 + } + ], + "messageType": "SPAT", + "cti4501Conformant": false, + "intersectionId": 12109, + "odeReceivedAt": "2024-06-04T16:23:11.34Z", + "originIp": "10.11.81.12", + "status": { + "failureFlash": false, + "noValidSPATisAvailableAtThisTime": false, + "fixedTimeOperation": false, + "standbyOperation": false, + "trafficDependentOperation": false, + "manualControlIsEnabled": false, + "off": false, + "stopTimeIsActivated": false, + "recentChangeInMAPassignedLanesIDsUsed": false, + "recentMAPmessageUpdate": false, + "failureMode": false, + "noValidMAPisAvailableAtThisTime": false, + "signalPriorityIsActive": false, + "preemptIsActive": false + } +},{ + "_id": { + "$oid": "665f3fc18c25216ef4f53fff" + }, + "validationMessages": [ + { + "schemaPath": "#/$defs/J2735RoadRegulatorID/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].id.region", + "message": "$.payload.data.intersectionStateList.intersectionStatelist[0].id.region: null found, integer expected" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[0].state_time_speed.movementEventList[0].timing.startTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[0].state_time_speed.movementEventList[0].timing.nextTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[1].state_time_speed.movementEventList[0].timing.startTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[1].state_time_speed.movementEventList[0].timing.nextTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[2].state_time_speed.movementEventList[0].timing.startTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[2].state_time_speed.movementEventList[0].timing.nextTime", + "message": "DE_TimeMark is missing" + } + ], + "utcTimeStamp": "2024-06-04T16:23:11.447Z", + "schemaVersion": 1, + "recordGeneratedAt": { + "$date": { + "$numberLong": "1717518191456" + } + }, + "revision": 0, + "states": [ + { + "stateTimeSpeed": [ + { + "eventState": "PROTECTED_CLEARANCE", + "timing": { + "minEndTime": "2024-06-04T16:03:17Z", + "maxEndTime": "2024-06-04T16:03:17Z" + } + } + ], + "signalGroup": 2 + }, + { + "stateTimeSpeed": [ + { + "eventState": "STOP_AND_REMAIN", + "timing": { + "minEndTime": "2024-06-04T16:03:20.1Z", + "maxEndTime": "2024-06-04T16:03:20.1Z" + } + } + ], + "signalGroup": 4 + }, + { + "stateTimeSpeed": [ + { + "eventState": "PROTECTED_CLEARANCE", + "timing": { + "minEndTime": "2024-06-04T16:03:17Z", + "maxEndTime": "2024-06-04T16:03:17Z" + } + } + ], + "signalGroup": 6 + } + ], + "messageType": "SPAT", + "cti4501Conformant": false, + "intersectionId": 12109, + "odeReceivedAt": "2024-06-04T16:23:11.447Z", + "originIp": "10.11.81.12", + "status": { + "failureFlash": false, + "noValidSPATisAvailableAtThisTime": false, + "fixedTimeOperation": false, + "standbyOperation": false, + "trafficDependentOperation": false, + "manualControlIsEnabled": false, + "off": false, + "stopTimeIsActivated": false, + "recentChangeInMAPassignedLanesIDsUsed": false, + "recentMAPmessageUpdate": false, + "failureMode": false, + "noValidMAPisAvailableAtThisTime": false, + "signalPriorityIsActive": false, + "preemptIsActive": false + } +},{ + "_id": { + "$oid": "665f3fc18c25216ef4f54000" + }, + "validationMessages": [ + { + "schemaPath": "#/$defs/J2735RoadRegulatorID/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].id.region", + "message": "$.payload.data.intersectionStateList.intersectionStatelist[0].id.region: null found, integer expected" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[0].state_time_speed.movementEventList[0].timing.startTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[0].state_time_speed.movementEventList[0].timing.nextTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[1].state_time_speed.movementEventList[0].timing.startTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[1].state_time_speed.movementEventList[0].timing.nextTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[2].state_time_speed.movementEventList[0].timing.startTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[2].state_time_speed.movementEventList[0].timing.nextTime", + "message": "DE_TimeMark is missing" + } + ], + "utcTimeStamp": "2024-06-04T16:23:11.555Z", + "schemaVersion": 1, + "recordGeneratedAt": { + "$date": { + "$numberLong": "1717518191562" + } + }, + "revision": 0, + "states": [ + { + "stateTimeSpeed": [ + { + "eventState": "PROTECTED_CLEARANCE", + "timing": { + "minEndTime": "2024-06-04T16:03:17Z", + "maxEndTime": "2024-06-04T16:03:17Z" + } + } + ], + "signalGroup": 2 + }, + { + "stateTimeSpeed": [ + { + "eventState": "STOP_AND_REMAIN", + "timing": { + "minEndTime": "2024-06-04T16:03:20.1Z", + "maxEndTime": "2024-06-04T16:03:20.1Z" + } + } + ], + "signalGroup": 4 + }, + { + "stateTimeSpeed": [ + { + "eventState": "PROTECTED_CLEARANCE", + "timing": { + "minEndTime": "2024-06-04T16:03:17Z", + "maxEndTime": "2024-06-04T16:03:17Z" + } + } + ], + "signalGroup": 6 + } + ], + "messageType": "SPAT", + "cti4501Conformant": false, + "intersectionId": 12109, + "odeReceivedAt": "2024-06-04T16:23:11.555Z", + "originIp": "10.11.81.12", + "status": { + "failureFlash": false, + "noValidSPATisAvailableAtThisTime": false, + "fixedTimeOperation": false, + "standbyOperation": false, + "trafficDependentOperation": false, + "manualControlIsEnabled": false, + "off": false, + "stopTimeIsActivated": false, + "recentChangeInMAPassignedLanesIDsUsed": false, + "recentMAPmessageUpdate": false, + "failureMode": false, + "noValidMAPisAvailableAtThisTime": false, + "signalPriorityIsActive": false, + "preemptIsActive": false + } +},{ + "_id": { + "$oid": "665f3fc18c25216ef4f54001" + }, + "validationMessages": [ + { + "schemaPath": "#/$defs/J2735RoadRegulatorID/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].id.region", + "message": "$.payload.data.intersectionStateList.intersectionStatelist[0].id.region: null found, integer expected" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[0].state_time_speed.movementEventList[0].timing.startTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[0].state_time_speed.movementEventList[0].timing.nextTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[1].state_time_speed.movementEventList[0].timing.startTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[1].state_time_speed.movementEventList[0].timing.nextTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[2].state_time_speed.movementEventList[0].timing.startTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[2].state_time_speed.movementEventList[0].timing.nextTime", + "message": "DE_TimeMark is missing" + } + ], + "utcTimeStamp": "2024-06-04T16:23:11.661Z", + "schemaVersion": 1, + "recordGeneratedAt": { + "$date": { + "$numberLong": "1717518191669" + } + }, + "revision": 0, + "states": [ + { + "stateTimeSpeed": [ + { + "eventState": "PROTECTED_CLEARANCE", + "timing": { + "minEndTime": "2024-06-04T16:03:19Z", + "maxEndTime": "2024-06-04T16:03:19Z" + } + } + ], + "signalGroup": 2 + }, + { + "stateTimeSpeed": [ + { + "eventState": "STOP_AND_REMAIN", + "timing": { + "minEndTime": "2024-06-04T16:03:22.1Z", + "maxEndTime": "2024-06-04T16:03:22.1Z" + } + } + ], + "signalGroup": 4 + }, + { + "stateTimeSpeed": [ + { + "eventState": "PROTECTED_CLEARANCE", + "timing": { + "minEndTime": "2024-06-04T16:03:19Z", + "maxEndTime": "2024-06-04T16:03:19Z" + } + } + ], + "signalGroup": 6 + } + ], + "messageType": "SPAT", + "cti4501Conformant": false, + "intersectionId": 12109, + "odeReceivedAt": "2024-06-04T16:23:11.661Z", + "originIp": "10.11.81.12", + "status": { + "failureFlash": false, + "noValidSPATisAvailableAtThisTime": false, + "fixedTimeOperation": false, + "standbyOperation": false, + "trafficDependentOperation": false, + "manualControlIsEnabled": false, + "off": false, + "stopTimeIsActivated": false, + "recentChangeInMAPassignedLanesIDsUsed": false, + "recentMAPmessageUpdate": false, + "failureMode": false, + "noValidMAPisAvailableAtThisTime": false, + "signalPriorityIsActive": false, + "preemptIsActive": false + } +},{ + "_id": { + "$oid": "665f3fc18c25216ef4f54002" + }, + "validationMessages": [ + { + "schemaPath": "#/$defs/J2735RoadRegulatorID/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].id.region", + "message": "$.payload.data.intersectionStateList.intersectionStatelist[0].id.region: null found, integer expected" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[0].state_time_speed.movementEventList[0].timing.startTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[0].state_time_speed.movementEventList[0].timing.nextTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[1].state_time_speed.movementEventList[0].timing.startTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[1].state_time_speed.movementEventList[0].timing.nextTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[2].state_time_speed.movementEventList[0].timing.startTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[2].state_time_speed.movementEventList[0].timing.nextTime", + "message": "DE_TimeMark is missing" + } + ], + "utcTimeStamp": "2024-06-04T16:23:11.768Z", + "schemaVersion": 1, + "recordGeneratedAt": { + "$date": { + "$numberLong": "1717518191776" + } + }, + "revision": 0, + "states": [ + { + "stateTimeSpeed": [ + { + "eventState": "PROTECTED_CLEARANCE", + "timing": { + "minEndTime": "2024-06-04T16:03:19Z", + "maxEndTime": "2024-06-04T16:03:19Z" + } + } + ], + "signalGroup": 2 + }, + { + "stateTimeSpeed": [ + { + "eventState": "STOP_AND_REMAIN", + "timing": { + "minEndTime": "2024-06-04T16:03:22.1Z", + "maxEndTime": "2024-06-04T16:03:22.1Z" + } + } + ], + "signalGroup": 4 + }, + { + "stateTimeSpeed": [ + { + "eventState": "PROTECTED_CLEARANCE", + "timing": { + "minEndTime": "2024-06-04T16:03:19Z", + "maxEndTime": "2024-06-04T16:03:19Z" + } + } + ], + "signalGroup": 6 + } + ], + "messageType": "SPAT", + "cti4501Conformant": false, + "intersectionId": 12109, + "odeReceivedAt": "2024-06-04T16:23:11.768Z", + "originIp": "10.11.81.12", + "status": { + "failureFlash": false, + "noValidSPATisAvailableAtThisTime": false, + "fixedTimeOperation": false, + "standbyOperation": false, + "trafficDependentOperation": false, + "manualControlIsEnabled": false, + "off": false, + "stopTimeIsActivated": false, + "recentChangeInMAPassignedLanesIDsUsed": false, + "recentMAPmessageUpdate": false, + "failureMode": false, + "noValidMAPisAvailableAtThisTime": false, + "signalPriorityIsActive": false, + "preemptIsActive": false + } +},{ + "_id": { + "$oid": "665f3fc18c25216ef4f54003" + }, + "validationMessages": [ + { + "schemaPath": "#/$defs/J2735RoadRegulatorID/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].id.region", + "message": "$.payload.data.intersectionStateList.intersectionStatelist[0].id.region: null found, integer expected" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[0].state_time_speed.movementEventList[0].timing.startTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[0].state_time_speed.movementEventList[0].timing.nextTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[1].state_time_speed.movementEventList[0].timing.startTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[1].state_time_speed.movementEventList[0].timing.nextTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[2].state_time_speed.movementEventList[0].timing.startTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[2].state_time_speed.movementEventList[0].timing.nextTime", + "message": "DE_TimeMark is missing" + } + ], + "utcTimeStamp": "2024-06-04T16:23:11.876Z", + "schemaVersion": 1, + "recordGeneratedAt": { + "$date": { + "$numberLong": "1717518191883" + } + }, + "revision": 0, + "states": [ + { + "stateTimeSpeed": [ + { + "eventState": "PROTECTED_CLEARANCE", + "timing": { + "minEndTime": "2024-06-04T16:03:19Z", + "maxEndTime": "2024-06-04T16:03:19Z" + } + } + ], + "signalGroup": 2 + }, + { + "stateTimeSpeed": [ + { + "eventState": "STOP_AND_REMAIN", + "timing": { + "minEndTime": "2024-06-04T16:03:22.1Z", + "maxEndTime": "2024-06-04T16:03:22.1Z" + } + } + ], + "signalGroup": 4 + }, + { + "stateTimeSpeed": [ + { + "eventState": "PROTECTED_CLEARANCE", + "timing": { + "minEndTime": "2024-06-04T16:03:19Z", + "maxEndTime": "2024-06-04T16:03:19Z" + } + } + ], + "signalGroup": 6 + } + ], + "messageType": "SPAT", + "cti4501Conformant": false, + "intersectionId": 12109, + "odeReceivedAt": "2024-06-04T16:23:11.876Z", + "originIp": "10.11.81.12", + "status": { + "failureFlash": false, + "noValidSPATisAvailableAtThisTime": false, + "fixedTimeOperation": false, + "standbyOperation": false, + "trafficDependentOperation": false, + "manualControlIsEnabled": false, + "off": false, + "stopTimeIsActivated": false, + "recentChangeInMAPassignedLanesIDsUsed": false, + "recentMAPmessageUpdate": false, + "failureMode": false, + "noValidMAPisAvailableAtThisTime": false, + "signalPriorityIsActive": false, + "preemptIsActive": false + } +},{ + "_id": { + "$oid": "665f3fc18c25216ef4f54004" + }, + "validationMessages": [ + { + "schemaPath": "#/$defs/J2735RoadRegulatorID/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].id.region", + "message": "$.payload.data.intersectionStateList.intersectionStatelist[0].id.region: null found, integer expected" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[0].state_time_speed.movementEventList[0].timing.startTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[0].state_time_speed.movementEventList[0].timing.nextTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[1].state_time_speed.movementEventList[0].timing.startTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[1].state_time_speed.movementEventList[0].timing.nextTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[2].state_time_speed.movementEventList[0].timing.startTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[2].state_time_speed.movementEventList[0].timing.nextTime", + "message": "DE_TimeMark is missing" + } + ], + "utcTimeStamp": "2024-06-04T16:23:11.981Z", + "schemaVersion": 1, + "recordGeneratedAt": { + "$date": { + "$numberLong": "1717518191991" + } + }, + "revision": 0, + "states": [ + { + "stateTimeSpeed": [ + { + "eventState": "PROTECTED_CLEARANCE", + "timing": { + "minEndTime": "2024-06-04T16:03:19Z", + "maxEndTime": "2024-06-04T16:03:19Z" + } + } + ], + "signalGroup": 2 + }, + { + "stateTimeSpeed": [ + { + "eventState": "STOP_AND_REMAIN", + "timing": { + "minEndTime": "2024-06-04T16:03:22.1Z", + "maxEndTime": "2024-06-04T16:03:22.1Z" + } + } + ], + "signalGroup": 4 + }, + { + "stateTimeSpeed": [ + { + "eventState": "PROTECTED_CLEARANCE", + "timing": { + "minEndTime": "2024-06-04T16:03:19Z", + "maxEndTime": "2024-06-04T16:03:19Z" + } + } + ], + "signalGroup": 6 + } + ], + "messageType": "SPAT", + "cti4501Conformant": false, + "intersectionId": 12109, + "odeReceivedAt": "2024-06-04T16:23:11.981Z", + "originIp": "10.11.81.12", + "status": { + "failureFlash": false, + "noValidSPATisAvailableAtThisTime": false, + "fixedTimeOperation": false, + "standbyOperation": false, + "trafficDependentOperation": false, + "manualControlIsEnabled": false, + "off": false, + "stopTimeIsActivated": false, + "recentChangeInMAPassignedLanesIDsUsed": false, + "recentMAPmessageUpdate": false, + "failureMode": false, + "noValidMAPisAvailableAtThisTime": false, + "signalPriorityIsActive": false, + "preemptIsActive": false + } +},{ + "_id": { + "$oid": "665f3fc18c25216ef4f54005" + }, + "validationMessages": [ + { + "schemaPath": "#/$defs/J2735RoadRegulatorID/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].id.region", + "message": "$.payload.data.intersectionStateList.intersectionStatelist[0].id.region: null found, integer expected" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[0].state_time_speed.movementEventList[0].timing.startTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[0].state_time_speed.movementEventList[0].timing.nextTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[1].state_time_speed.movementEventList[0].timing.startTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[1].state_time_speed.movementEventList[0].timing.nextTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[2].state_time_speed.movementEventList[0].timing.startTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[2].state_time_speed.movementEventList[0].timing.nextTime", + "message": "DE_TimeMark is missing" + } + ], + "utcTimeStamp": "2024-06-04T16:23:12.088Z", + "schemaVersion": 1, + "recordGeneratedAt": { + "$date": { + "$numberLong": "1717518192098" + } + }, + "revision": 0, + "states": [ + { + "stateTimeSpeed": [ + { + "eventState": "PROTECTED_CLEARANCE", + "timing": { + "minEndTime": "2024-06-04T16:03:19Z", + "maxEndTime": "2024-06-04T16:03:19Z" + } + } + ], + "signalGroup": 2 + }, + { + "stateTimeSpeed": [ + { + "eventState": "STOP_AND_REMAIN", + "timing": { + "minEndTime": "2024-06-04T16:03:22.1Z", + "maxEndTime": "2024-06-04T16:03:22.1Z" + } + } + ], + "signalGroup": 4 + }, + { + "stateTimeSpeed": [ + { + "eventState": "PROTECTED_CLEARANCE", + "timing": { + "minEndTime": "2024-06-04T16:03:19Z", + "maxEndTime": "2024-06-04T16:03:19Z" + } + } + ], + "signalGroup": 6 + } + ], + "messageType": "SPAT", + "cti4501Conformant": false, + "intersectionId": 12109, + "odeReceivedAt": "2024-06-04T16:23:12.088Z", + "originIp": "10.11.81.12", + "status": { + "failureFlash": false, + "noValidSPATisAvailableAtThisTime": false, + "fixedTimeOperation": false, + "standbyOperation": false, + "trafficDependentOperation": false, + "manualControlIsEnabled": false, + "off": false, + "stopTimeIsActivated": false, + "recentChangeInMAPassignedLanesIDsUsed": false, + "recentMAPmessageUpdate": false, + "failureMode": false, + "noValidMAPisAvailableAtThisTime": false, + "signalPriorityIsActive": false, + "preemptIsActive": false + } +},{ + "_id": { + "$oid": "665f3fc18c25216ef4f54006" + }, + "validationMessages": [ + { + "schemaPath": "#/$defs/J2735RoadRegulatorID/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].id.region", + "message": "$.payload.data.intersectionStateList.intersectionStatelist[0].id.region: null found, integer expected" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[0].state_time_speed.movementEventList[0].timing.startTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[0].state_time_speed.movementEventList[0].timing.nextTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[1].state_time_speed.movementEventList[0].timing.startTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[1].state_time_speed.movementEventList[0].timing.nextTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[2].state_time_speed.movementEventList[0].timing.startTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[2].state_time_speed.movementEventList[0].timing.nextTime", + "message": "DE_TimeMark is missing" + } + ], + "utcTimeStamp": "2024-06-04T16:23:12.196Z", + "schemaVersion": 1, + "recordGeneratedAt": { + "$date": { + "$numberLong": "1717518192204" + } + }, + "revision": 0, + "states": [ + { + "stateTimeSpeed": [ + { + "eventState": "PROTECTED_CLEARANCE", + "timing": { + "minEndTime": "2024-06-04T16:03:19Z", + "maxEndTime": "2024-06-04T16:03:19Z" + } + } + ], + "signalGroup": 2 + }, + { + "stateTimeSpeed": [ + { + "eventState": "STOP_AND_REMAIN", + "timing": { + "minEndTime": "2024-06-04T16:03:22.1Z", + "maxEndTime": "2024-06-04T16:03:22.1Z" + } + } + ], + "signalGroup": 4 + }, + { + "stateTimeSpeed": [ + { + "eventState": "PROTECTED_CLEARANCE", + "timing": { + "minEndTime": "2024-06-04T16:03:19Z", + "maxEndTime": "2024-06-04T16:03:19Z" + } + } + ], + "signalGroup": 6 + } + ], + "messageType": "SPAT", + "cti4501Conformant": false, + "intersectionId": 12109, + "odeReceivedAt": "2024-06-04T16:23:12.196Z", + "originIp": "10.11.81.12", + "status": { + "failureFlash": false, + "noValidSPATisAvailableAtThisTime": false, + "fixedTimeOperation": false, + "standbyOperation": false, + "trafficDependentOperation": false, + "manualControlIsEnabled": false, + "off": false, + "stopTimeIsActivated": false, + "recentChangeInMAPassignedLanesIDsUsed": false, + "recentMAPmessageUpdate": false, + "failureMode": false, + "noValidMAPisAvailableAtThisTime": false, + "signalPriorityIsActive": false, + "preemptIsActive": false + } +},{ + "_id": { + "$oid": "665f3fc18c25216ef4f54007" + }, + "validationMessages": [ + { + "schemaPath": "#/$defs/J2735RoadRegulatorID/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].id.region", + "message": "$.payload.data.intersectionStateList.intersectionStatelist[0].id.region: null found, integer expected" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[0].state_time_speed.movementEventList[0].timing.startTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[0].state_time_speed.movementEventList[0].timing.nextTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[1].state_time_speed.movementEventList[0].timing.startTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[1].state_time_speed.movementEventList[0].timing.nextTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[2].state_time_speed.movementEventList[0].timing.startTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[2].state_time_speed.movementEventList[0].timing.nextTime", + "message": "DE_TimeMark is missing" + } + ], + "utcTimeStamp": "2024-06-04T16:23:12.302Z", + "schemaVersion": 1, + "recordGeneratedAt": { + "$date": { + "$numberLong": "1717518192311" + } + }, + "revision": 0, + "states": [ + { + "stateTimeSpeed": [ + { + "eventState": "PROTECTED_CLEARANCE", + "timing": { + "minEndTime": "2024-06-04T16:03:19Z", + "maxEndTime": "2024-06-04T16:03:19Z" + } + } + ], + "signalGroup": 2 + }, + { + "stateTimeSpeed": [ + { + "eventState": "STOP_AND_REMAIN", + "timing": { + "minEndTime": "2024-06-04T16:03:22.1Z", + "maxEndTime": "2024-06-04T16:03:22.1Z" + } + } + ], + "signalGroup": 4 + }, + { + "stateTimeSpeed": [ + { + "eventState": "PROTECTED_CLEARANCE", + "timing": { + "minEndTime": "2024-06-04T16:03:19Z", + "maxEndTime": "2024-06-04T16:03:19Z" + } + } + ], + "signalGroup": 6 + } + ], + "messageType": "SPAT", + "cti4501Conformant": false, + "intersectionId": 12109, + "odeReceivedAt": "2024-06-04T16:23:12.302Z", + "originIp": "10.11.81.12", + "status": { + "failureFlash": false, + "noValidSPATisAvailableAtThisTime": false, + "fixedTimeOperation": false, + "standbyOperation": false, + "trafficDependentOperation": false, + "manualControlIsEnabled": false, + "off": false, + "stopTimeIsActivated": false, + "recentChangeInMAPassignedLanesIDsUsed": false, + "recentMAPmessageUpdate": false, + "failureMode": false, + "noValidMAPisAvailableAtThisTime": false, + "signalPriorityIsActive": false, + "preemptIsActive": false + } +},{ + "_id": { + "$oid": "665f3fc18c25216ef4f54008" + }, + "validationMessages": [ + { + "schemaPath": "#/$defs/J2735RoadRegulatorID/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].id.region", + "message": "$.payload.data.intersectionStateList.intersectionStatelist[0].id.region: null found, integer expected" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[0].state_time_speed.movementEventList[0].timing.startTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[0].state_time_speed.movementEventList[0].timing.nextTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[1].state_time_speed.movementEventList[0].timing.startTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[1].state_time_speed.movementEventList[0].timing.nextTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[2].state_time_speed.movementEventList[0].timing.startTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[2].state_time_speed.movementEventList[0].timing.nextTime", + "message": "DE_TimeMark is missing" + } + ], + "utcTimeStamp": "2024-06-04T16:23:12.41Z", + "schemaVersion": 1, + "recordGeneratedAt": { + "$date": { + "$numberLong": "1717518192418" + } + }, + "revision": 0, + "states": [ + { + "stateTimeSpeed": [ + { + "eventState": "PROTECTED_CLEARANCE", + "timing": { + "minEndTime": "2024-06-04T16:03:19Z", + "maxEndTime": "2024-06-04T16:03:19Z" + } + } + ], + "signalGroup": 2 + }, + { + "stateTimeSpeed": [ + { + "eventState": "STOP_AND_REMAIN", + "timing": { + "minEndTime": "2024-06-04T16:03:22.1Z", + "maxEndTime": "2024-06-04T16:03:22.1Z" + } + } + ], + "signalGroup": 4 + }, + { + "stateTimeSpeed": [ + { + "eventState": "PROTECTED_CLEARANCE", + "timing": { + "minEndTime": "2024-06-04T16:03:19Z", + "maxEndTime": "2024-06-04T16:03:19Z" + } + } + ], + "signalGroup": 6 + } + ], + "messageType": "SPAT", + "cti4501Conformant": false, + "intersectionId": 12109, + "odeReceivedAt": "2024-06-04T16:23:12.41Z", + "originIp": "10.11.81.12", + "status": { + "failureFlash": false, + "noValidSPATisAvailableAtThisTime": false, + "fixedTimeOperation": false, + "standbyOperation": false, + "trafficDependentOperation": false, + "manualControlIsEnabled": false, + "off": false, + "stopTimeIsActivated": false, + "recentChangeInMAPassignedLanesIDsUsed": false, + "recentMAPmessageUpdate": false, + "failureMode": false, + "noValidMAPisAvailableAtThisTime": false, + "signalPriorityIsActive": false, + "preemptIsActive": false + } +},{ + "_id": { + "$oid": "665f3fc18c25216ef4f54009" + }, + "validationMessages": [ + { + "schemaPath": "#/$defs/J2735RoadRegulatorID/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].id.region", + "message": "$.payload.data.intersectionStateList.intersectionStatelist[0].id.region: null found, integer expected" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[0].state_time_speed.movementEventList[0].timing.startTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[0].state_time_speed.movementEventList[0].timing.nextTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[1].state_time_speed.movementEventList[0].timing.startTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[1].state_time_speed.movementEventList[0].timing.nextTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[2].state_time_speed.movementEventList[0].timing.startTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[2].state_time_speed.movementEventList[0].timing.nextTime", + "message": "DE_TimeMark is missing" + } + ], + "utcTimeStamp": "2024-06-04T16:23:12.517Z", + "schemaVersion": 1, + "recordGeneratedAt": { + "$date": { + "$numberLong": "1717518192525" + } + }, + "revision": 0, + "states": [ + { + "stateTimeSpeed": [ + { + "eventState": "PROTECTED_CLEARANCE", + "timing": { + "minEndTime": "2024-06-04T16:03:19Z", + "maxEndTime": "2024-06-04T16:03:19Z" + } + } + ], + "signalGroup": 2 + }, + { + "stateTimeSpeed": [ + { + "eventState": "STOP_AND_REMAIN", + "timing": { + "minEndTime": "2024-06-04T16:03:22.1Z", + "maxEndTime": "2024-06-04T16:03:22.1Z" + } + } + ], + "signalGroup": 4 + }, + { + "stateTimeSpeed": [ + { + "eventState": "PROTECTED_CLEARANCE", + "timing": { + "minEndTime": "2024-06-04T16:03:19Z", + "maxEndTime": "2024-06-04T16:03:19Z" + } + } + ], + "signalGroup": 6 + } + ], + "messageType": "SPAT", + "cti4501Conformant": false, + "intersectionId": 12109, + "odeReceivedAt": "2024-06-04T16:23:12.517Z", + "originIp": "10.11.81.12", + "status": { + "failureFlash": false, + "noValidSPATisAvailableAtThisTime": false, + "fixedTimeOperation": false, + "standbyOperation": false, + "trafficDependentOperation": false, + "manualControlIsEnabled": false, + "off": false, + "stopTimeIsActivated": false, + "recentChangeInMAPassignedLanesIDsUsed": false, + "recentMAPmessageUpdate": false, + "failureMode": false, + "noValidMAPisAvailableAtThisTime": false, + "signalPriorityIsActive": false, + "preemptIsActive": false + } +},{ + "_id": { + "$oid": "665f3fc18c25216ef4f5400a" + }, + "validationMessages": [ + { + "schemaPath": "#/$defs/J2735RoadRegulatorID/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].id.region", + "message": "$.payload.data.intersectionStateList.intersectionStatelist[0].id.region: null found, integer expected" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[0].state_time_speed.movementEventList[0].timing.startTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[0].state_time_speed.movementEventList[0].timing.nextTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[1].state_time_speed.movementEventList[0].timing.startTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[1].state_time_speed.movementEventList[0].timing.nextTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[2].state_time_speed.movementEventList[0].timing.startTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[2].state_time_speed.movementEventList[0].timing.nextTime", + "message": "DE_TimeMark is missing" + } + ], + "utcTimeStamp": "2024-06-04T16:23:12.624Z", + "schemaVersion": 1, + "recordGeneratedAt": { + "$date": { + "$numberLong": "1717518192631" + } + }, + "revision": 0, + "states": [ + { + "stateTimeSpeed": [ + { + "eventState": "PROTECTED_CLEARANCE", + "timing": { + "minEndTime": "2024-06-04T16:03:19Z", + "maxEndTime": "2024-06-04T16:03:19Z" + } + } + ], + "signalGroup": 2 + }, + { + "stateTimeSpeed": [ + { + "eventState": "STOP_AND_REMAIN", + "timing": { + "minEndTime": "2024-06-04T16:03:22.1Z", + "maxEndTime": "2024-06-04T16:03:22.1Z" + } + } + ], + "signalGroup": 4 + }, + { + "stateTimeSpeed": [ + { + "eventState": "PROTECTED_CLEARANCE", + "timing": { + "minEndTime": "2024-06-04T16:03:19Z", + "maxEndTime": "2024-06-04T16:03:19Z" + } + } + ], + "signalGroup": 6 + } + ], + "messageType": "SPAT", + "cti4501Conformant": false, + "intersectionId": 12109, + "odeReceivedAt": "2024-06-04T16:23:12.624Z", + "originIp": "10.11.81.12", + "status": { + "failureFlash": false, + "noValidSPATisAvailableAtThisTime": false, + "fixedTimeOperation": false, + "standbyOperation": false, + "trafficDependentOperation": false, + "manualControlIsEnabled": false, + "off": false, + "stopTimeIsActivated": false, + "recentChangeInMAPassignedLanesIDsUsed": false, + "recentMAPmessageUpdate": false, + "failureMode": false, + "noValidMAPisAvailableAtThisTime": false, + "signalPriorityIsActive": false, + "preemptIsActive": false + } +},{ + "_id": { + "$oid": "665f3fc18c25216ef4f5400b" + }, + "validationMessages": [ + { + "schemaPath": "#/$defs/J2735RoadRegulatorID/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].id.region", + "message": "$.payload.data.intersectionStateList.intersectionStatelist[0].id.region: null found, integer expected" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[0].state_time_speed.movementEventList[0].timing.startTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[0].state_time_speed.movementEventList[0].timing.nextTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[1].state_time_speed.movementEventList[0].timing.startTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[1].state_time_speed.movementEventList[0].timing.nextTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[2].state_time_speed.movementEventList[0].timing.startTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[2].state_time_speed.movementEventList[0].timing.nextTime", + "message": "DE_TimeMark is missing" + } + ], + "utcTimeStamp": "2024-06-04T16:23:12.731Z", + "schemaVersion": 1, + "recordGeneratedAt": { + "$date": { + "$numberLong": "1717518192739" + } + }, + "revision": 0, + "states": [ + { + "stateTimeSpeed": [ + { + "eventState": "PROTECTED_CLEARANCE", + "timing": { + "minEndTime": "2024-06-04T16:03:19Z", + "maxEndTime": "2024-06-04T16:03:19Z" + } + } + ], + "signalGroup": 2 + }, + { + "stateTimeSpeed": [ + { + "eventState": "STOP_AND_REMAIN", + "timing": { + "minEndTime": "2024-06-04T16:03:22.1Z", + "maxEndTime": "2024-06-04T16:03:22.1Z" + } + } + ], + "signalGroup": 4 + }, + { + "stateTimeSpeed": [ + { + "eventState": "PROTECTED_CLEARANCE", + "timing": { + "minEndTime": "2024-06-04T16:03:19Z", + "maxEndTime": "2024-06-04T16:03:19Z" + } + } + ], + "signalGroup": 6 + } + ], + "messageType": "SPAT", + "cti4501Conformant": false, + "intersectionId": 12109, + "odeReceivedAt": "2024-06-04T16:23:12.731Z", + "originIp": "10.11.81.12", + "status": { + "failureFlash": false, + "noValidSPATisAvailableAtThisTime": false, + "fixedTimeOperation": false, + "standbyOperation": false, + "trafficDependentOperation": false, + "manualControlIsEnabled": false, + "off": false, + "stopTimeIsActivated": false, + "recentChangeInMAPassignedLanesIDsUsed": false, + "recentMAPmessageUpdate": false, + "failureMode": false, + "noValidMAPisAvailableAtThisTime": false, + "signalPriorityIsActive": false, + "preemptIsActive": false + } +},{ + "_id": { + "$oid": "665f3fc18c25216ef4f5400c" + }, + "validationMessages": [ + { + "schemaPath": "#/$defs/J2735RoadRegulatorID/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].id.region", + "message": "$.payload.data.intersectionStateList.intersectionStatelist[0].id.region: null found, integer expected" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[0].state_time_speed.movementEventList[0].timing.startTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[0].state_time_speed.movementEventList[0].timing.nextTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[1].state_time_speed.movementEventList[0].timing.startTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[1].state_time_speed.movementEventList[0].timing.nextTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[2].state_time_speed.movementEventList[0].timing.startTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[2].state_time_speed.movementEventList[0].timing.nextTime", + "message": "DE_TimeMark is missing" + } + ], + "utcTimeStamp": "2024-06-04T16:23:12.825Z", + "schemaVersion": 1, + "recordGeneratedAt": { + "$date": { + "$numberLong": "1717518192831" + } + }, + "revision": 0, + "states": [ + { + "stateTimeSpeed": [ + { + "eventState": "PROTECTED_CLEARANCE", + "timing": { + "minEndTime": "2024-06-04T16:03:19Z", + "maxEndTime": "2024-06-04T16:03:19Z" + } + } + ], + "signalGroup": 2 + }, + { + "stateTimeSpeed": [ + { + "eventState": "STOP_AND_REMAIN", + "timing": { + "minEndTime": "2024-06-04T16:03:22.1Z", + "maxEndTime": "2024-06-04T16:03:22.1Z" + } + } + ], + "signalGroup": 4 + }, + { + "stateTimeSpeed": [ + { + "eventState": "PROTECTED_CLEARANCE", + "timing": { + "minEndTime": "2024-06-04T16:03:19Z", + "maxEndTime": "2024-06-04T16:03:19Z" + } + } + ], + "signalGroup": 6 + } + ], + "messageType": "SPAT", + "cti4501Conformant": false, + "intersectionId": 12109, + "odeReceivedAt": "2024-06-04T16:23:12.825Z", + "originIp": "10.11.81.12", + "status": { + "failureFlash": false, + "noValidSPATisAvailableAtThisTime": false, + "fixedTimeOperation": false, + "standbyOperation": false, + "trafficDependentOperation": false, + "manualControlIsEnabled": false, + "off": false, + "stopTimeIsActivated": false, + "recentChangeInMAPassignedLanesIDsUsed": false, + "recentMAPmessageUpdate": false, + "failureMode": false, + "noValidMAPisAvailableAtThisTime": false, + "signalPriorityIsActive": false, + "preemptIsActive": false + } +},{ + "_id": { + "$oid": "665f3fc18c25216ef4f5400d" + }, + "validationMessages": [ + { + "schemaPath": "#/$defs/J2735RoadRegulatorID/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].id.region", + "message": "$.payload.data.intersectionStateList.intersectionStatelist[0].id.region: null found, integer expected" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[0].state_time_speed.movementEventList[0].timing.startTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[0].state_time_speed.movementEventList[0].timing.nextTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[1].state_time_speed.movementEventList[0].timing.startTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[1].state_time_speed.movementEventList[0].timing.nextTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[2].state_time_speed.movementEventList[0].timing.startTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[2].state_time_speed.movementEventList[0].timing.nextTime", + "message": "DE_TimeMark is missing" + } + ], + "utcTimeStamp": "2024-06-04T16:23:12.932Z", + "schemaVersion": 1, + "recordGeneratedAt": { + "$date": { + "$numberLong": "1717518192938" + } + }, + "revision": 0, + "states": [ + { + "stateTimeSpeed": [ + { + "eventState": "PROTECTED_CLEARANCE", + "timing": { + "minEndTime": "2024-06-04T16:03:19Z", + "maxEndTime": "2024-06-04T16:03:19Z" + } + } + ], + "signalGroup": 2 + }, + { + "stateTimeSpeed": [ + { + "eventState": "STOP_AND_REMAIN", + "timing": { + "minEndTime": "2024-06-04T16:03:22.1Z", + "maxEndTime": "2024-06-04T16:03:22.1Z" + } + } + ], + "signalGroup": 4 + }, + { + "stateTimeSpeed": [ + { + "eventState": "PROTECTED_CLEARANCE", + "timing": { + "minEndTime": "2024-06-04T16:03:19Z", + "maxEndTime": "2024-06-04T16:03:19Z" + } + } + ], + "signalGroup": 6 + } + ], + "messageType": "SPAT", + "cti4501Conformant": false, + "intersectionId": 12109, + "odeReceivedAt": "2024-06-04T16:23:12.932Z", + "originIp": "10.11.81.12", + "status": { + "failureFlash": false, + "noValidSPATisAvailableAtThisTime": false, + "fixedTimeOperation": false, + "standbyOperation": false, + "trafficDependentOperation": false, + "manualControlIsEnabled": false, + "off": false, + "stopTimeIsActivated": false, + "recentChangeInMAPassignedLanesIDsUsed": false, + "recentMAPmessageUpdate": false, + "failureMode": false, + "noValidMAPisAvailableAtThisTime": false, + "signalPriorityIsActive": false, + "preemptIsActive": false + } +},{ + "_id": { + "$oid": "665f3fc18c25216ef4f5400e" + }, + "validationMessages": [ + { + "schemaPath": "#/$defs/J2735RoadRegulatorID/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].id.region", + "message": "$.payload.data.intersectionStateList.intersectionStatelist[0].id.region: null found, integer expected" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[0].state_time_speed.movementEventList[0].timing.startTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[0].state_time_speed.movementEventList[0].timing.nextTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[1].state_time_speed.movementEventList[0].timing.startTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[1].state_time_speed.movementEventList[0].timing.nextTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[2].state_time_speed.movementEventList[0].timing.startTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[2].state_time_speed.movementEventList[0].timing.nextTime", + "message": "DE_TimeMark is missing" + } + ], + "utcTimeStamp": "2024-06-04T16:23:13.037Z", + "schemaVersion": 1, + "recordGeneratedAt": { + "$date": { + "$numberLong": "1717518193045" + } + }, + "revision": 0, + "states": [ + { + "stateTimeSpeed": [ + { + "eventState": "PROTECTED_CLEARANCE", + "timing": { + "minEndTime": "2024-06-04T16:03:19Z", + "maxEndTime": "2024-06-04T16:03:19Z" + } + } + ], + "signalGroup": 2 + }, + { + "stateTimeSpeed": [ + { + "eventState": "STOP_AND_REMAIN", + "timing": { + "minEndTime": "2024-06-04T16:03:22.1Z", + "maxEndTime": "2024-06-04T16:03:22.1Z" + } + } + ], + "signalGroup": 4 + }, + { + "stateTimeSpeed": [ + { + "eventState": "PROTECTED_CLEARANCE", + "timing": { + "minEndTime": "2024-06-04T16:03:19Z", + "maxEndTime": "2024-06-04T16:03:19Z" + } + } + ], + "signalGroup": 6 + } + ], + "messageType": "SPAT", + "cti4501Conformant": false, + "intersectionId": 12109, + "odeReceivedAt": "2024-06-04T16:23:13.037Z", + "originIp": "10.11.81.12", + "status": { + "failureFlash": false, + "noValidSPATisAvailableAtThisTime": false, + "fixedTimeOperation": false, + "standbyOperation": false, + "trafficDependentOperation": false, + "manualControlIsEnabled": false, + "off": false, + "stopTimeIsActivated": false, + "recentChangeInMAPassignedLanesIDsUsed": false, + "recentMAPmessageUpdate": false, + "failureMode": false, + "noValidMAPisAvailableAtThisTime": false, + "signalPriorityIsActive": false, + "preemptIsActive": false + } +},{ + "_id": { + "$oid": "665f3fc18c25216ef4f5400f" + }, + "validationMessages": [ + { + "schemaPath": "#/$defs/J2735RoadRegulatorID/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].id.region", + "message": "$.payload.data.intersectionStateList.intersectionStatelist[0].id.region: null found, integer expected" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[0].state_time_speed.movementEventList[0].timing.startTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[0].state_time_speed.movementEventList[0].timing.nextTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[1].state_time_speed.movementEventList[0].timing.startTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[1].state_time_speed.movementEventList[0].timing.nextTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[2].state_time_speed.movementEventList[0].timing.startTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[2].state_time_speed.movementEventList[0].timing.nextTime", + "message": "DE_TimeMark is missing" + } + ], + "utcTimeStamp": "2024-06-04T16:23:13.146Z", + "schemaVersion": 1, + "recordGeneratedAt": { + "$date": { + "$numberLong": "1717518193152" + } + }, + "revision": 0, + "states": [ + { + "stateTimeSpeed": [ + { + "eventState": "PROTECTED_CLEARANCE", + "timing": { + "minEndTime": "2024-06-04T16:03:19Z", + "maxEndTime": "2024-06-04T16:03:19Z" + } + } + ], + "signalGroup": 2 + }, + { + "stateTimeSpeed": [ + { + "eventState": "STOP_AND_REMAIN", + "timing": { + "minEndTime": "2024-06-04T16:03:22.1Z", + "maxEndTime": "2024-06-04T16:03:22.1Z" + } + } + ], + "signalGroup": 4 + }, + { + "stateTimeSpeed": [ + { + "eventState": "PROTECTED_CLEARANCE", + "timing": { + "minEndTime": "2024-06-04T16:03:19Z", + "maxEndTime": "2024-06-04T16:03:19Z" + } + } + ], + "signalGroup": 6 + } + ], + "messageType": "SPAT", + "cti4501Conformant": false, + "intersectionId": 12109, + "odeReceivedAt": "2024-06-04T16:23:13.146Z", + "originIp": "10.11.81.12", + "status": { + "failureFlash": false, + "noValidSPATisAvailableAtThisTime": false, + "fixedTimeOperation": false, + "standbyOperation": false, + "trafficDependentOperation": false, + "manualControlIsEnabled": false, + "off": false, + "stopTimeIsActivated": false, + "recentChangeInMAPassignedLanesIDsUsed": false, + "recentMAPmessageUpdate": false, + "failureMode": false, + "noValidMAPisAvailableAtThisTime": false, + "signalPriorityIsActive": false, + "preemptIsActive": false + } +},{ + "_id": { + "$oid": "665f3fc18c25216ef4f54010" + }, + "validationMessages": [ + { + "schemaPath": "#/$defs/J2735RoadRegulatorID/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].id.region", + "message": "$.payload.data.intersectionStateList.intersectionStatelist[0].id.region: null found, integer expected" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[0].state_time_speed.movementEventList[0].timing.startTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[0].state_time_speed.movementEventList[0].timing.nextTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[1].state_time_speed.movementEventList[0].timing.startTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[1].state_time_speed.movementEventList[0].timing.nextTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[2].state_time_speed.movementEventList[0].timing.startTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[2].state_time_speed.movementEventList[0].timing.nextTime", + "message": "DE_TimeMark is missing" + } + ], + "utcTimeStamp": "2024-06-04T16:23:13.253Z", + "schemaVersion": 1, + "recordGeneratedAt": { + "$date": { + "$numberLong": "1717518193258" + } + }, + "revision": 0, + "states": [ + { + "stateTimeSpeed": [ + { + "eventState": "PROTECTED_CLEARANCE", + "timing": { + "minEndTime": "2024-06-04T16:03:19Z", + "maxEndTime": "2024-06-04T16:03:19Z" + } + } + ], + "signalGroup": 2 + }, + { + "stateTimeSpeed": [ + { + "eventState": "STOP_AND_REMAIN", + "timing": { + "minEndTime": "2024-06-04T16:03:22.1Z", + "maxEndTime": "2024-06-04T16:03:22.1Z" + } + } + ], + "signalGroup": 4 + }, + { + "stateTimeSpeed": [ + { + "eventState": "PROTECTED_CLEARANCE", + "timing": { + "minEndTime": "2024-06-04T16:03:19Z", + "maxEndTime": "2024-06-04T16:03:19Z" + } + } + ], + "signalGroup": 6 + } + ], + "messageType": "SPAT", + "cti4501Conformant": false, + "intersectionId": 12109, + "odeReceivedAt": "2024-06-04T16:23:13.253Z", + "originIp": "10.11.81.12", + "status": { + "failureFlash": false, + "noValidSPATisAvailableAtThisTime": false, + "fixedTimeOperation": false, + "standbyOperation": false, + "trafficDependentOperation": false, + "manualControlIsEnabled": false, + "off": false, + "stopTimeIsActivated": false, + "recentChangeInMAPassignedLanesIDsUsed": false, + "recentMAPmessageUpdate": false, + "failureMode": false, + "noValidMAPisAvailableAtThisTime": false, + "signalPriorityIsActive": false, + "preemptIsActive": false + } +},{ + "_id": { + "$oid": "665f3fc18c25216ef4f54011" + }, + "validationMessages": [ + { + "schemaPath": "#/$defs/J2735RoadRegulatorID/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].id.region", + "message": "$.payload.data.intersectionStateList.intersectionStatelist[0].id.region: null found, integer expected" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[0].state_time_speed.movementEventList[0].timing.startTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[0].state_time_speed.movementEventList[0].timing.nextTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[1].state_time_speed.movementEventList[0].timing.startTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[1].state_time_speed.movementEventList[0].timing.nextTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[2].state_time_speed.movementEventList[0].timing.startTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[2].state_time_speed.movementEventList[0].timing.nextTime", + "message": "DE_TimeMark is missing" + } + ], + "utcTimeStamp": "2024-06-04T16:23:13.359Z", + "schemaVersion": 1, + "recordGeneratedAt": { + "$date": { + "$numberLong": "1717518193365" + } + }, + "revision": 0, + "states": [ + { + "stateTimeSpeed": [ + { + "eventState": "PROTECTED_CLEARANCE", + "timing": { + "minEndTime": "2024-06-04T16:03:19Z", + "maxEndTime": "2024-06-04T16:03:19Z" + } + } + ], + "signalGroup": 2 + }, + { + "stateTimeSpeed": [ + { + "eventState": "STOP_AND_REMAIN", + "timing": { + "minEndTime": "2024-06-04T16:03:22.1Z", + "maxEndTime": "2024-06-04T16:03:22.1Z" + } + } + ], + "signalGroup": 4 + }, + { + "stateTimeSpeed": [ + { + "eventState": "PROTECTED_CLEARANCE", + "timing": { + "minEndTime": "2024-06-04T16:03:19Z", + "maxEndTime": "2024-06-04T16:03:19Z" + } + } + ], + "signalGroup": 6 + } + ], + "messageType": "SPAT", + "cti4501Conformant": false, + "intersectionId": 12109, + "odeReceivedAt": "2024-06-04T16:23:13.359Z", + "originIp": "10.11.81.12", + "status": { + "failureFlash": false, + "noValidSPATisAvailableAtThisTime": false, + "fixedTimeOperation": false, + "standbyOperation": false, + "trafficDependentOperation": false, + "manualControlIsEnabled": false, + "off": false, + "stopTimeIsActivated": false, + "recentChangeInMAPassignedLanesIDsUsed": false, + "recentMAPmessageUpdate": false, + "failureMode": false, + "noValidMAPisAvailableAtThisTime": false, + "signalPriorityIsActive": false, + "preemptIsActive": false + } +},{ + "_id": { + "$oid": "665f3fc18c25216ef4f54012" + }, + "validationMessages": [ + { + "schemaPath": "#/$defs/J2735RoadRegulatorID/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].id.region", + "message": "$.payload.data.intersectionStateList.intersectionStatelist[0].id.region: null found, integer expected" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[0].state_time_speed.movementEventList[0].timing.startTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[0].state_time_speed.movementEventList[0].timing.nextTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[1].state_time_speed.movementEventList[0].timing.startTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[1].state_time_speed.movementEventList[0].timing.nextTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[2].state_time_speed.movementEventList[0].timing.startTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[2].state_time_speed.movementEventList[0].timing.nextTime", + "message": "DE_TimeMark is missing" + } + ], + "utcTimeStamp": "2024-06-04T16:23:13.467Z", + "schemaVersion": 1, + "recordGeneratedAt": { + "$date": { + "$numberLong": "1717518193473" + } + }, + "revision": 0, + "states": [ + { + "stateTimeSpeed": [ + { + "eventState": "PROTECTED_CLEARANCE", + "timing": { + "minEndTime": "2024-06-04T16:03:19Z", + "maxEndTime": "2024-06-04T16:03:19Z" + } + } + ], + "signalGroup": 2 + }, + { + "stateTimeSpeed": [ + { + "eventState": "STOP_AND_REMAIN", + "timing": { + "minEndTime": "2024-06-04T16:03:22.1Z", + "maxEndTime": "2024-06-04T16:03:22.1Z" + } + } + ], + "signalGroup": 4 + }, + { + "stateTimeSpeed": [ + { + "eventState": "PROTECTED_CLEARANCE", + "timing": { + "minEndTime": "2024-06-04T16:03:19Z", + "maxEndTime": "2024-06-04T16:03:19Z" + } + } + ], + "signalGroup": 6 + } + ], + "messageType": "SPAT", + "cti4501Conformant": false, + "intersectionId": 12109, + "odeReceivedAt": "2024-06-04T16:23:13.467Z", + "originIp": "10.11.81.12", + "status": { + "failureFlash": false, + "noValidSPATisAvailableAtThisTime": false, + "fixedTimeOperation": false, + "standbyOperation": false, + "trafficDependentOperation": false, + "manualControlIsEnabled": false, + "off": false, + "stopTimeIsActivated": false, + "recentChangeInMAPassignedLanesIDsUsed": false, + "recentMAPmessageUpdate": false, + "failureMode": false, + "noValidMAPisAvailableAtThisTime": false, + "signalPriorityIsActive": false, + "preemptIsActive": false + } +},{ + "_id": { + "$oid": "665f3fc18c25216ef4f54013" + }, + "validationMessages": [ + { + "schemaPath": "#/$defs/J2735RoadRegulatorID/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].id.region", + "message": "$.payload.data.intersectionStateList.intersectionStatelist[0].id.region: null found, integer expected" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[0].state_time_speed.movementEventList[0].timing.startTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[0].state_time_speed.movementEventList[0].timing.nextTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[1].state_time_speed.movementEventList[0].timing.startTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[1].state_time_speed.movementEventList[0].timing.nextTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[2].state_time_speed.movementEventList[0].timing.startTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[2].state_time_speed.movementEventList[0].timing.nextTime", + "message": "DE_TimeMark is missing" + } + ], + "utcTimeStamp": "2024-06-04T16:23:13.574Z", + "schemaVersion": 1, + "recordGeneratedAt": { + "$date": { + "$numberLong": "1717518193580" + } + }, + "revision": 0, + "states": [ + { + "stateTimeSpeed": [ + { + "eventState": "PROTECTED_CLEARANCE", + "timing": { + "minEndTime": "2024-06-04T16:03:19Z", + "maxEndTime": "2024-06-04T16:03:19Z" + } + } + ], + "signalGroup": 2 + }, + { + "stateTimeSpeed": [ + { + "eventState": "STOP_AND_REMAIN", + "timing": { + "minEndTime": "2024-06-04T16:03:22.1Z", + "maxEndTime": "2024-06-04T16:03:22.1Z" + } + } + ], + "signalGroup": 4 + }, + { + "stateTimeSpeed": [ + { + "eventState": "PROTECTED_CLEARANCE", + "timing": { + "minEndTime": "2024-06-04T16:03:19Z", + "maxEndTime": "2024-06-04T16:03:19Z" + } + } + ], + "signalGroup": 6 + } + ], + "messageType": "SPAT", + "cti4501Conformant": false, + "intersectionId": 12109, + "odeReceivedAt": "2024-06-04T16:23:13.574Z", + "originIp": "10.11.81.12", + "status": { + "failureFlash": false, + "noValidSPATisAvailableAtThisTime": false, + "fixedTimeOperation": false, + "standbyOperation": false, + "trafficDependentOperation": false, + "manualControlIsEnabled": false, + "off": false, + "stopTimeIsActivated": false, + "recentChangeInMAPassignedLanesIDsUsed": false, + "recentMAPmessageUpdate": false, + "failureMode": false, + "noValidMAPisAvailableAtThisTime": false, + "signalPriorityIsActive": false, + "preemptIsActive": false + } +},{ + "_id": { + "$oid": "665f3fc18c25216ef4f54014" + }, + "validationMessages": [ + { + "schemaPath": "#/$defs/J2735RoadRegulatorID/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].id.region", + "message": "$.payload.data.intersectionStateList.intersectionStatelist[0].id.region: null found, integer expected" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[0].state_time_speed.movementEventList[0].timing.startTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[0].state_time_speed.movementEventList[0].timing.nextTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[1].state_time_speed.movementEventList[0].timing.startTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[1].state_time_speed.movementEventList[0].timing.nextTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[2].state_time_speed.movementEventList[0].timing.startTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[2].state_time_speed.movementEventList[0].timing.nextTime", + "message": "DE_TimeMark is missing" + } + ], + "utcTimeStamp": "2024-06-04T16:23:13.681Z", + "schemaVersion": 1, + "recordGeneratedAt": { + "$date": { + "$numberLong": "1717518193687" + } + }, + "revision": 0, + "states": [ + { + "stateTimeSpeed": [ + { + "eventState": "PROTECTED_CLEARANCE", + "timing": { + "minEndTime": "2024-06-04T16:03:19Z", + "maxEndTime": "2024-06-04T16:03:19Z" + } + } + ], + "signalGroup": 2 + }, + { + "stateTimeSpeed": [ + { + "eventState": "STOP_AND_REMAIN", + "timing": { + "minEndTime": "2024-06-04T16:03:22.1Z", + "maxEndTime": "2024-06-04T16:03:22.1Z" + } + } + ], + "signalGroup": 4 + }, + { + "stateTimeSpeed": [ + { + "eventState": "PROTECTED_CLEARANCE", + "timing": { + "minEndTime": "2024-06-04T16:03:19Z", + "maxEndTime": "2024-06-04T16:03:19Z" + } + } + ], + "signalGroup": 6 + } + ], + "messageType": "SPAT", + "cti4501Conformant": false, + "intersectionId": 12109, + "odeReceivedAt": "2024-06-04T16:23:13.681Z", + "originIp": "10.11.81.12", + "status": { + "failureFlash": false, + "noValidSPATisAvailableAtThisTime": false, + "fixedTimeOperation": false, + "standbyOperation": false, + "trafficDependentOperation": false, + "manualControlIsEnabled": false, + "off": false, + "stopTimeIsActivated": false, + "recentChangeInMAPassignedLanesIDsUsed": false, + "recentMAPmessageUpdate": false, + "failureMode": false, + "noValidMAPisAvailableAtThisTime": false, + "signalPriorityIsActive": false, + "preemptIsActive": false + } +},{ + "_id": { + "$oid": "665f3fc18c25216ef4f54015" + }, + "validationMessages": [ + { + "schemaPath": "#/$defs/J2735RoadRegulatorID/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].id.region", + "message": "$.payload.data.intersectionStateList.intersectionStatelist[0].id.region: null found, integer expected" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[0].state_time_speed.movementEventList[0].timing.startTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[0].state_time_speed.movementEventList[0].timing.nextTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[1].state_time_speed.movementEventList[0].timing.startTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[1].state_time_speed.movementEventList[0].timing.nextTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[2].state_time_speed.movementEventList[0].timing.startTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[2].state_time_speed.movementEventList[0].timing.nextTime", + "message": "DE_TimeMark is missing" + } + ], + "utcTimeStamp": "2024-06-04T16:23:13.788Z", + "schemaVersion": 1, + "recordGeneratedAt": { + "$date": { + "$numberLong": "1717518193795" + } + }, + "revision": 0, + "states": [ + { + "stateTimeSpeed": [ + { + "eventState": "PROTECTED_CLEARANCE", + "timing": { + "minEndTime": "2024-06-04T16:03:18Z", + "maxEndTime": "2024-06-04T16:03:18Z" + } + } + ], + "signalGroup": 2 + }, + { + "stateTimeSpeed": [ + { + "eventState": "STOP_AND_REMAIN", + "timing": { + "minEndTime": "2024-06-04T16:03:21.1Z", + "maxEndTime": "2024-06-04T16:03:21.1Z" + } + } + ], + "signalGroup": 4 + }, + { + "stateTimeSpeed": [ + { + "eventState": "PROTECTED_CLEARANCE", + "timing": { + "minEndTime": "2024-06-04T16:03:18Z", + "maxEndTime": "2024-06-04T16:03:18Z" + } + } + ], + "signalGroup": 6 + } + ], + "messageType": "SPAT", + "cti4501Conformant": false, + "intersectionId": 12109, + "odeReceivedAt": "2024-06-04T16:23:13.788Z", + "originIp": "10.11.81.12", + "status": { + "failureFlash": false, + "noValidSPATisAvailableAtThisTime": false, + "fixedTimeOperation": false, + "standbyOperation": false, + "trafficDependentOperation": false, + "manualControlIsEnabled": false, + "off": false, + "stopTimeIsActivated": false, + "recentChangeInMAPassignedLanesIDsUsed": false, + "recentMAPmessageUpdate": false, + "failureMode": false, + "noValidMAPisAvailableAtThisTime": false, + "signalPriorityIsActive": false, + "preemptIsActive": false + } +},{ + "_id": { + "$oid": "665f3fc18c25216ef4f54016" + }, + "validationMessages": [ + { + "schemaPath": "#/$defs/J2735RoadRegulatorID/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].id.region", + "message": "$.payload.data.intersectionStateList.intersectionStatelist[0].id.region: null found, integer expected" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[0].state_time_speed.movementEventList[0].timing.startTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[0].state_time_speed.movementEventList[0].timing.nextTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[1].state_time_speed.movementEventList[0].timing.startTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[1].state_time_speed.movementEventList[0].timing.nextTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[2].state_time_speed.movementEventList[0].timing.startTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[2].state_time_speed.movementEventList[0].timing.nextTime", + "message": "DE_TimeMark is missing" + } + ], + "utcTimeStamp": "2024-06-04T16:23:13.894Z", + "schemaVersion": 1, + "recordGeneratedAt": { + "$date": { + "$numberLong": "1717518193902" + } + }, + "revision": 0, + "states": [ + { + "stateTimeSpeed": [ + { + "eventState": "PROTECTED_CLEARANCE", + "timing": { + "minEndTime": "2024-06-04T16:03:18Z", + "maxEndTime": "2024-06-04T16:03:18Z" + } + } + ], + "signalGroup": 2 + }, + { + "stateTimeSpeed": [ + { + "eventState": "STOP_AND_REMAIN", + "timing": { + "minEndTime": "2024-06-04T16:03:21.1Z", + "maxEndTime": "2024-06-04T16:03:21.1Z" + } + } + ], + "signalGroup": 4 + }, + { + "stateTimeSpeed": [ + { + "eventState": "PROTECTED_CLEARANCE", + "timing": { + "minEndTime": "2024-06-04T16:03:18Z", + "maxEndTime": "2024-06-04T16:03:18Z" + } + } + ], + "signalGroup": 6 + } + ], + "messageType": "SPAT", + "cti4501Conformant": false, + "intersectionId": 12109, + "odeReceivedAt": "2024-06-04T16:23:13.894Z", + "originIp": "10.11.81.12", + "status": { + "failureFlash": false, + "noValidSPATisAvailableAtThisTime": false, + "fixedTimeOperation": false, + "standbyOperation": false, + "trafficDependentOperation": false, + "manualControlIsEnabled": false, + "off": false, + "stopTimeIsActivated": false, + "recentChangeInMAPassignedLanesIDsUsed": false, + "recentMAPmessageUpdate": false, + "failureMode": false, + "noValidMAPisAvailableAtThisTime": false, + "signalPriorityIsActive": false, + "preemptIsActive": false + } +},{ + "_id": { + "$oid": "665f3fc18c25216ef4f54017" + }, + "validationMessages": [ + { + "schemaPath": "#/$defs/J2735RoadRegulatorID/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].id.region", + "message": "$.payload.data.intersectionStateList.intersectionStatelist[0].id.region: null found, integer expected" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[0].state_time_speed.movementEventList[0].timing.startTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[0].state_time_speed.movementEventList[0].timing.nextTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[1].state_time_speed.movementEventList[0].timing.startTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[1].state_time_speed.movementEventList[0].timing.nextTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[2].state_time_speed.movementEventList[0].timing.startTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[2].state_time_speed.movementEventList[0].timing.nextTime", + "message": "DE_TimeMark is missing" + } + ], + "utcTimeStamp": "2024-06-04T16:23:14.003Z", + "schemaVersion": 1, + "recordGeneratedAt": { + "$date": { + "$numberLong": "1717518194008" + } + }, + "revision": 0, + "states": [ + { + "stateTimeSpeed": [ + { + "eventState": "PROTECTED_CLEARANCE", + "timing": { + "minEndTime": "2024-06-04T16:03:18Z", + "maxEndTime": "2024-06-04T16:03:18Z" + } + } + ], + "signalGroup": 2 + }, + { + "stateTimeSpeed": [ + { + "eventState": "STOP_AND_REMAIN", + "timing": { + "minEndTime": "2024-06-04T16:03:21.1Z", + "maxEndTime": "2024-06-04T16:03:21.1Z" + } + } + ], + "signalGroup": 4 + }, + { + "stateTimeSpeed": [ + { + "eventState": "PROTECTED_CLEARANCE", + "timing": { + "minEndTime": "2024-06-04T16:03:18Z", + "maxEndTime": "2024-06-04T16:03:18Z" + } + } + ], + "signalGroup": 6 + } + ], + "messageType": "SPAT", + "cti4501Conformant": false, + "intersectionId": 12109, + "odeReceivedAt": "2024-06-04T16:23:14.003Z", + "originIp": "10.11.81.12", + "status": { + "failureFlash": false, + "noValidSPATisAvailableAtThisTime": false, + "fixedTimeOperation": false, + "standbyOperation": false, + "trafficDependentOperation": false, + "manualControlIsEnabled": false, + "off": false, + "stopTimeIsActivated": false, + "recentChangeInMAPassignedLanesIDsUsed": false, + "recentMAPmessageUpdate": false, + "failureMode": false, + "noValidMAPisAvailableAtThisTime": false, + "signalPriorityIsActive": false, + "preemptIsActive": false + } +},{ + "_id": { + "$oid": "665f3fc18c25216ef4f54018" + }, + "validationMessages": [ + { + "schemaPath": "#/$defs/J2735RoadRegulatorID/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].id.region", + "message": "$.payload.data.intersectionStateList.intersectionStatelist[0].id.region: null found, integer expected" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[0].state_time_speed.movementEventList[0].timing.startTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[0].state_time_speed.movementEventList[0].timing.nextTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[1].state_time_speed.movementEventList[0].timing.startTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[1].state_time_speed.movementEventList[0].timing.nextTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[2].state_time_speed.movementEventList[0].timing.startTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[2].state_time_speed.movementEventList[0].timing.nextTime", + "message": "DE_TimeMark is missing" + } + ], + "utcTimeStamp": "2024-06-04T16:23:14.11Z", + "schemaVersion": 1, + "recordGeneratedAt": { + "$date": { + "$numberLong": "1717518194116" + } + }, + "revision": 0, + "states": [ + { + "stateTimeSpeed": [ + { + "eventState": "PROTECTED_CLEARANCE", + "timing": { + "minEndTime": "2024-06-04T16:03:18Z", + "maxEndTime": "2024-06-04T16:03:18Z" + } + } + ], + "signalGroup": 2 + }, + { + "stateTimeSpeed": [ + { + "eventState": "STOP_AND_REMAIN", + "timing": { + "minEndTime": "2024-06-04T16:03:21.1Z", + "maxEndTime": "2024-06-04T16:03:21.1Z" + } + } + ], + "signalGroup": 4 + }, + { + "stateTimeSpeed": [ + { + "eventState": "PROTECTED_CLEARANCE", + "timing": { + "minEndTime": "2024-06-04T16:03:18Z", + "maxEndTime": "2024-06-04T16:03:18Z" + } + } + ], + "signalGroup": 6 + } + ], + "messageType": "SPAT", + "cti4501Conformant": false, + "intersectionId": 12109, + "odeReceivedAt": "2024-06-04T16:23:14.11Z", + "originIp": "10.11.81.12", + "status": { + "failureFlash": false, + "noValidSPATisAvailableAtThisTime": false, + "fixedTimeOperation": false, + "standbyOperation": false, + "trafficDependentOperation": false, + "manualControlIsEnabled": false, + "off": false, + "stopTimeIsActivated": false, + "recentChangeInMAPassignedLanesIDsUsed": false, + "recentMAPmessageUpdate": false, + "failureMode": false, + "noValidMAPisAvailableAtThisTime": false, + "signalPriorityIsActive": false, + "preemptIsActive": false + } +},{ + "_id": { + "$oid": "665f3fc18c25216ef4f54019" + }, + "validationMessages": [ + { + "schemaPath": "#/$defs/J2735RoadRegulatorID/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].id.region", + "message": "$.payload.data.intersectionStateList.intersectionStatelist[0].id.region: null found, integer expected" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[0].state_time_speed.movementEventList[0].timing.startTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[0].state_time_speed.movementEventList[0].timing.nextTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[1].state_time_speed.movementEventList[0].timing.startTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[1].state_time_speed.movementEventList[0].timing.nextTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[2].state_time_speed.movementEventList[0].timing.startTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[2].state_time_speed.movementEventList[0].timing.nextTime", + "message": "DE_TimeMark is missing" + } + ], + "utcTimeStamp": "2024-06-04T16:23:14.218Z", + "schemaVersion": 1, + "recordGeneratedAt": { + "$date": { + "$numberLong": "1717518194221" + } + }, + "revision": 0, + "states": [ + { + "stateTimeSpeed": [ + { + "eventState": "PROTECTED_CLEARANCE", + "timing": { + "minEndTime": "2024-06-04T16:03:18Z", + "maxEndTime": "2024-06-04T16:03:18Z" + } + } + ], + "signalGroup": 2 + }, + { + "stateTimeSpeed": [ + { + "eventState": "STOP_AND_REMAIN", + "timing": { + "minEndTime": "2024-06-04T16:03:21.1Z", + "maxEndTime": "2024-06-04T16:03:21.1Z" + } + } + ], + "signalGroup": 4 + }, + { + "stateTimeSpeed": [ + { + "eventState": "PROTECTED_CLEARANCE", + "timing": { + "minEndTime": "2024-06-04T16:03:18Z", + "maxEndTime": "2024-06-04T16:03:18Z" + } + } + ], + "signalGroup": 6 + } + ], + "messageType": "SPAT", + "cti4501Conformant": false, + "intersectionId": 12109, + "odeReceivedAt": "2024-06-04T16:23:14.218Z", + "originIp": "10.11.81.12", + "status": { + "failureFlash": false, + "noValidSPATisAvailableAtThisTime": false, + "fixedTimeOperation": false, + "standbyOperation": false, + "trafficDependentOperation": false, + "manualControlIsEnabled": false, + "off": false, + "stopTimeIsActivated": false, + "recentChangeInMAPassignedLanesIDsUsed": false, + "recentMAPmessageUpdate": false, + "failureMode": false, + "noValidMAPisAvailableAtThisTime": false, + "signalPriorityIsActive": false, + "preemptIsActive": false + } +},{ + "_id": { + "$oid": "665f3fc18c25216ef4f5401a" + }, + "validationMessages": [ + { + "schemaPath": "#/$defs/J2735RoadRegulatorID/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].id.region", + "message": "$.payload.data.intersectionStateList.intersectionStatelist[0].id.region: null found, integer expected" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[0].state_time_speed.movementEventList[0].timing.startTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[0].state_time_speed.movementEventList[0].timing.nextTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[1].state_time_speed.movementEventList[0].timing.startTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[1].state_time_speed.movementEventList[0].timing.nextTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[2].state_time_speed.movementEventList[0].timing.startTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[2].state_time_speed.movementEventList[0].timing.nextTime", + "message": "DE_TimeMark is missing" + } + ], + "utcTimeStamp": "2024-06-04T16:23:14.325Z", + "schemaVersion": 1, + "recordGeneratedAt": { + "$date": { + "$numberLong": "1717518194328" + } + }, + "revision": 0, + "states": [ + { + "stateTimeSpeed": [ + { + "eventState": "PROTECTED_CLEARANCE", + "timing": { + "minEndTime": "2024-06-04T16:03:18Z", + "maxEndTime": "2024-06-04T16:03:18Z" + } + } + ], + "signalGroup": 2 + }, + { + "stateTimeSpeed": [ + { + "eventState": "STOP_AND_REMAIN", + "timing": { + "minEndTime": "2024-06-04T16:03:21.1Z", + "maxEndTime": "2024-06-04T16:03:21.1Z" + } + } + ], + "signalGroup": 4 + }, + { + "stateTimeSpeed": [ + { + "eventState": "PROTECTED_CLEARANCE", + "timing": { + "minEndTime": "2024-06-04T16:03:18Z", + "maxEndTime": "2024-06-04T16:03:18Z" + } + } + ], + "signalGroup": 6 + } + ], + "messageType": "SPAT", + "cti4501Conformant": false, + "intersectionId": 12109, + "odeReceivedAt": "2024-06-04T16:23:14.325Z", + "originIp": "10.11.81.12", + "status": { + "failureFlash": false, + "noValidSPATisAvailableAtThisTime": false, + "fixedTimeOperation": false, + "standbyOperation": false, + "trafficDependentOperation": false, + "manualControlIsEnabled": false, + "off": false, + "stopTimeIsActivated": false, + "recentChangeInMAPassignedLanesIDsUsed": false, + "recentMAPmessageUpdate": false, + "failureMode": false, + "noValidMAPisAvailableAtThisTime": false, + "signalPriorityIsActive": false, + "preemptIsActive": false + } +},{ + "_id": { + "$oid": "665f3fc18c25216ef4f5401b" + }, + "validationMessages": [ + { + "schemaPath": "#/$defs/J2735RoadRegulatorID/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].id.region", + "message": "$.payload.data.intersectionStateList.intersectionStatelist[0].id.region: null found, integer expected" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[0].state_time_speed.movementEventList[0].timing.startTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[0].state_time_speed.movementEventList[0].timing.nextTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[1].state_time_speed.movementEventList[0].timing.startTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[1].state_time_speed.movementEventList[0].timing.nextTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[2].state_time_speed.movementEventList[0].timing.startTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[2].state_time_speed.movementEventList[0].timing.nextTime", + "message": "DE_TimeMark is missing" + } + ], + "utcTimeStamp": "2024-06-04T16:23:14.433Z", + "schemaVersion": 1, + "recordGeneratedAt": { + "$date": { + "$numberLong": "1717518194436" + } + }, + "revision": 0, + "states": [ + { + "stateTimeSpeed": [ + { + "eventState": "PROTECTED_CLEARANCE", + "timing": { + "minEndTime": "2024-06-04T16:03:18Z", + "maxEndTime": "2024-06-04T16:03:18Z" + } + } + ], + "signalGroup": 2 + }, + { + "stateTimeSpeed": [ + { + "eventState": "STOP_AND_REMAIN", + "timing": { + "minEndTime": "2024-06-04T16:03:21.1Z", + "maxEndTime": "2024-06-04T16:03:21.1Z" + } + } + ], + "signalGroup": 4 + }, + { + "stateTimeSpeed": [ + { + "eventState": "PROTECTED_CLEARANCE", + "timing": { + "minEndTime": "2024-06-04T16:03:18Z", + "maxEndTime": "2024-06-04T16:03:18Z" + } + } + ], + "signalGroup": 6 + } + ], + "messageType": "SPAT", + "cti4501Conformant": false, + "intersectionId": 12109, + "odeReceivedAt": "2024-06-04T16:23:14.433Z", + "originIp": "10.11.81.12", + "status": { + "failureFlash": false, + "noValidSPATisAvailableAtThisTime": false, + "fixedTimeOperation": false, + "standbyOperation": false, + "trafficDependentOperation": false, + "manualControlIsEnabled": false, + "off": false, + "stopTimeIsActivated": false, + "recentChangeInMAPassignedLanesIDsUsed": false, + "recentMAPmessageUpdate": false, + "failureMode": false, + "noValidMAPisAvailableAtThisTime": false, + "signalPriorityIsActive": false, + "preemptIsActive": false + } +},{ + "_id": { + "$oid": "665f3fc18c25216ef4f5401c" + }, + "validationMessages": [ + { + "schemaPath": "#/$defs/J2735RoadRegulatorID/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].id.region", + "message": "$.payload.data.intersectionStateList.intersectionStatelist[0].id.region: null found, integer expected" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[0].state_time_speed.movementEventList[0].timing.startTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[0].state_time_speed.movementEventList[0].timing.nextTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[1].state_time_speed.movementEventList[0].timing.startTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[1].state_time_speed.movementEventList[0].timing.nextTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[2].state_time_speed.movementEventList[0].timing.startTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[2].state_time_speed.movementEventList[0].timing.nextTime", + "message": "DE_TimeMark is missing" + } + ], + "utcTimeStamp": "2024-06-04T16:23:14.54Z", + "schemaVersion": 1, + "recordGeneratedAt": { + "$date": { + "$numberLong": "1717518194542" + } + }, + "revision": 0, + "states": [ + { + "stateTimeSpeed": [ + { + "eventState": "PROTECTED_CLEARANCE", + "timing": { + "minEndTime": "2024-06-04T16:03:18Z", + "maxEndTime": "2024-06-04T16:03:18Z" + } + } + ], + "signalGroup": 2 + }, + { + "stateTimeSpeed": [ + { + "eventState": "STOP_AND_REMAIN", + "timing": { + "minEndTime": "2024-06-04T16:03:21.1Z", + "maxEndTime": "2024-06-04T16:03:21.1Z" + } + } + ], + "signalGroup": 4 + }, + { + "stateTimeSpeed": [ + { + "eventState": "PROTECTED_CLEARANCE", + "timing": { + "minEndTime": "2024-06-04T16:03:18Z", + "maxEndTime": "2024-06-04T16:03:18Z" + } + } + ], + "signalGroup": 6 + } + ], + "messageType": "SPAT", + "cti4501Conformant": false, + "intersectionId": 12109, + "odeReceivedAt": "2024-06-04T16:23:14.54Z", + "originIp": "10.11.81.12", + "status": { + "failureFlash": false, + "noValidSPATisAvailableAtThisTime": false, + "fixedTimeOperation": false, + "standbyOperation": false, + "trafficDependentOperation": false, + "manualControlIsEnabled": false, + "off": false, + "stopTimeIsActivated": false, + "recentChangeInMAPassignedLanesIDsUsed": false, + "recentMAPmessageUpdate": false, + "failureMode": false, + "noValidMAPisAvailableAtThisTime": false, + "signalPriorityIsActive": false, + "preemptIsActive": false + } +},{ + "_id": { + "$oid": "665f3fc18c25216ef4f5401d" + }, + "validationMessages": [ + { + "schemaPath": "#/$defs/J2735RoadRegulatorID/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].id.region", + "message": "$.payload.data.intersectionStateList.intersectionStatelist[0].id.region: null found, integer expected" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[0].state_time_speed.movementEventList[0].timing.startTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[0].state_time_speed.movementEventList[0].timing.nextTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[1].state_time_speed.movementEventList[0].timing.startTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[1].state_time_speed.movementEventList[0].timing.nextTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[2].state_time_speed.movementEventList[0].timing.startTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[2].state_time_speed.movementEventList[0].timing.nextTime", + "message": "DE_TimeMark is missing" + } + ], + "utcTimeStamp": "2024-06-04T16:23:14.648Z", + "schemaVersion": 1, + "recordGeneratedAt": { + "$date": { + "$numberLong": "1717518194650" + } + }, + "revision": 0, + "states": [ + { + "stateTimeSpeed": [ + { + "eventState": "PROTECTED_CLEARANCE", + "timing": { + "minEndTime": "2024-06-04T16:03:18Z", + "maxEndTime": "2024-06-04T16:03:18Z" + } + } + ], + "signalGroup": 2 + }, + { + "stateTimeSpeed": [ + { + "eventState": "STOP_AND_REMAIN", + "timing": { + "minEndTime": "2024-06-04T16:03:21.1Z", + "maxEndTime": "2024-06-04T16:03:21.1Z" + } + } + ], + "signalGroup": 4 + }, + { + "stateTimeSpeed": [ + { + "eventState": "PROTECTED_CLEARANCE", + "timing": { + "minEndTime": "2024-06-04T16:03:18Z", + "maxEndTime": "2024-06-04T16:03:18Z" + } + } + ], + "signalGroup": 6 + } + ], + "messageType": "SPAT", + "cti4501Conformant": false, + "intersectionId": 12109, + "odeReceivedAt": "2024-06-04T16:23:14.648Z", + "originIp": "10.11.81.12", + "status": { + "failureFlash": false, + "noValidSPATisAvailableAtThisTime": false, + "fixedTimeOperation": false, + "standbyOperation": false, + "trafficDependentOperation": false, + "manualControlIsEnabled": false, + "off": false, + "stopTimeIsActivated": false, + "recentChangeInMAPassignedLanesIDsUsed": false, + "recentMAPmessageUpdate": false, + "failureMode": false, + "noValidMAPisAvailableAtThisTime": false, + "signalPriorityIsActive": false, + "preemptIsActive": false + } +},{ + "_id": { + "$oid": "665f3fc18c25216ef4f5401e" + }, + "validationMessages": [ + { + "schemaPath": "#/$defs/J2735RoadRegulatorID/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].id.region", + "message": "$.payload.data.intersectionStateList.intersectionStatelist[0].id.region: null found, integer expected" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[0].state_time_speed.movementEventList[0].timing.startTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[0].state_time_speed.movementEventList[0].timing.nextTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[1].state_time_speed.movementEventList[0].timing.startTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[1].state_time_speed.movementEventList[0].timing.nextTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[2].state_time_speed.movementEventList[0].timing.startTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[2].state_time_speed.movementEventList[0].timing.nextTime", + "message": "DE_TimeMark is missing" + } + ], + "utcTimeStamp": "2024-06-04T16:23:14.754Z", + "schemaVersion": 1, + "recordGeneratedAt": { + "$date": { + "$numberLong": "1717518194756" + } + }, + "revision": 0, + "states": [ + { + "stateTimeSpeed": [ + { + "eventState": "PROTECTED_CLEARANCE", + "timing": { + "minEndTime": "2024-06-04T16:03:18Z", + "maxEndTime": "2024-06-04T16:03:18Z" + } + } + ], + "signalGroup": 2 + }, + { + "stateTimeSpeed": [ + { + "eventState": "STOP_AND_REMAIN", + "timing": { + "minEndTime": "2024-06-04T16:03:21.1Z", + "maxEndTime": "2024-06-04T16:03:21.1Z" + } + } + ], + "signalGroup": 4 + }, + { + "stateTimeSpeed": [ + { + "eventState": "PROTECTED_CLEARANCE", + "timing": { + "minEndTime": "2024-06-04T16:03:18Z", + "maxEndTime": "2024-06-04T16:03:18Z" + } + } + ], + "signalGroup": 6 + } + ], + "messageType": "SPAT", + "cti4501Conformant": false, + "intersectionId": 12109, + "odeReceivedAt": "2024-06-04T16:23:14.754Z", + "originIp": "10.11.81.12", + "status": { + "failureFlash": false, + "noValidSPATisAvailableAtThisTime": false, + "fixedTimeOperation": false, + "standbyOperation": false, + "trafficDependentOperation": false, + "manualControlIsEnabled": false, + "off": false, + "stopTimeIsActivated": false, + "recentChangeInMAPassignedLanesIDsUsed": false, + "recentMAPmessageUpdate": false, + "failureMode": false, + "noValidMAPisAvailableAtThisTime": false, + "signalPriorityIsActive": false, + "preemptIsActive": false + } +},{ + "_id": { + "$oid": "665f3fc18c25216ef4f5401f" + }, + "validationMessages": [ + { + "schemaPath": "#/$defs/J2735RoadRegulatorID/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].id.region", + "message": "$.payload.data.intersectionStateList.intersectionStatelist[0].id.region: null found, integer expected" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[0].state_time_speed.movementEventList[0].timing.startTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[0].state_time_speed.movementEventList[0].timing.nextTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[1].state_time_speed.movementEventList[0].timing.startTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[1].state_time_speed.movementEventList[0].timing.nextTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[2].state_time_speed.movementEventList[0].timing.startTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[2].state_time_speed.movementEventList[0].timing.nextTime", + "message": "DE_TimeMark is missing" + } + ], + "utcTimeStamp": "2024-06-04T16:23:14.862Z", + "schemaVersion": 1, + "recordGeneratedAt": { + "$date": { + "$numberLong": "1717518194862" + } + }, + "revision": 0, + "states": [ + { + "stateTimeSpeed": [ + { + "eventState": "PROTECTED_CLEARANCE", + "timing": { + "minEndTime": "2024-06-04T16:03:17Z", + "maxEndTime": "2024-06-04T16:03:17Z" + } + } + ], + "signalGroup": 2 + }, + { + "stateTimeSpeed": [ + { + "eventState": "STOP_AND_REMAIN", + "timing": { + "minEndTime": "2024-06-04T16:03:20.1Z", + "maxEndTime": "2024-06-04T16:03:20.1Z" + } + } + ], + "signalGroup": 4 + }, + { + "stateTimeSpeed": [ + { + "eventState": "PROTECTED_CLEARANCE", + "timing": { + "minEndTime": "2024-06-04T16:03:17Z", + "maxEndTime": "2024-06-04T16:03:17Z" + } + } + ], + "signalGroup": 6 + } + ], + "messageType": "SPAT", + "cti4501Conformant": false, + "intersectionId": 12109, + "odeReceivedAt": "2024-06-04T16:23:14.862Z", + "originIp": "10.11.81.12", + "status": { + "failureFlash": false, + "noValidSPATisAvailableAtThisTime": false, + "fixedTimeOperation": false, + "standbyOperation": false, + "trafficDependentOperation": false, + "manualControlIsEnabled": false, + "off": false, + "stopTimeIsActivated": false, + "recentChangeInMAPassignedLanesIDsUsed": false, + "recentMAPmessageUpdate": false, + "failureMode": false, + "noValidMAPisAvailableAtThisTime": false, + "signalPriorityIsActive": false, + "preemptIsActive": false + } +},{ + "_id": { + "$oid": "665f3fc18c25216ef4f54020" + }, + "validationMessages": [ + { + "schemaPath": "#/$defs/J2735RoadRegulatorID/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].id.region", + "message": "$.payload.data.intersectionStateList.intersectionStatelist[0].id.region: null found, integer expected" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[0].state_time_speed.movementEventList[0].timing.startTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[0].state_time_speed.movementEventList[0].timing.nextTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[1].state_time_speed.movementEventList[0].timing.startTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[1].state_time_speed.movementEventList[0].timing.nextTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[2].state_time_speed.movementEventList[0].timing.startTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[2].state_time_speed.movementEventList[0].timing.nextTime", + "message": "DE_TimeMark is missing" + } + ], + "utcTimeStamp": "2024-06-04T16:23:14.954Z", + "schemaVersion": 1, + "recordGeneratedAt": { + "$date": { + "$numberLong": "1717518194969" + } + }, + "revision": 0, + "states": [ + { + "stateTimeSpeed": [ + { + "eventState": "PROTECTED_CLEARANCE", + "timing": { + "minEndTime": "2024-06-04T16:03:17Z", + "maxEndTime": "2024-06-04T16:03:17Z" + } + } + ], + "signalGroup": 2 + }, + { + "stateTimeSpeed": [ + { + "eventState": "STOP_AND_REMAIN", + "timing": { + "minEndTime": "2024-06-04T16:03:20.1Z", + "maxEndTime": "2024-06-04T16:03:20.1Z" + } + } + ], + "signalGroup": 4 + }, + { + "stateTimeSpeed": [ + { + "eventState": "PROTECTED_CLEARANCE", + "timing": { + "minEndTime": "2024-06-04T16:03:17Z", + "maxEndTime": "2024-06-04T16:03:17Z" + } + } + ], + "signalGroup": 6 + } + ], + "messageType": "SPAT", + "cti4501Conformant": false, + "intersectionId": 12109, + "odeReceivedAt": "2024-06-04T16:23:14.954Z", + "originIp": "10.11.81.12", + "status": { + "failureFlash": false, + "noValidSPATisAvailableAtThisTime": false, + "fixedTimeOperation": false, + "standbyOperation": false, + "trafficDependentOperation": false, + "manualControlIsEnabled": false, + "off": false, + "stopTimeIsActivated": false, + "recentChangeInMAPassignedLanesIDsUsed": false, + "recentMAPmessageUpdate": false, + "failureMode": false, + "noValidMAPisAvailableAtThisTime": false, + "signalPriorityIsActive": false, + "preemptIsActive": false + } +},{ + "_id": { + "$oid": "665f3fc18c25216ef4f54021" + }, + "validationMessages": [ + { + "schemaPath": "#/$defs/J2735RoadRegulatorID/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].id.region", + "message": "$.payload.data.intersectionStateList.intersectionStatelist[0].id.region: null found, integer expected" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[0].state_time_speed.movementEventList[0].timing.startTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[0].state_time_speed.movementEventList[0].timing.nextTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[1].state_time_speed.movementEventList[0].timing.startTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[1].state_time_speed.movementEventList[0].timing.nextTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[2].state_time_speed.movementEventList[0].timing.startTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[2].state_time_speed.movementEventList[0].timing.nextTime", + "message": "DE_TimeMark is missing" + } + ], + "utcTimeStamp": "2024-06-04T16:23:15.062Z", + "schemaVersion": 1, + "recordGeneratedAt": { + "$date": { + "$numberLong": "1717518195075" + } + }, + "revision": 0, + "states": [ + { + "stateTimeSpeed": [ + { + "eventState": "PROTECTED_CLEARANCE", + "timing": { + "minEndTime": "2024-06-04T16:03:17Z", + "maxEndTime": "2024-06-04T16:03:17Z" + } + } + ], + "signalGroup": 2 + }, + { + "stateTimeSpeed": [ + { + "eventState": "STOP_AND_REMAIN", + "timing": { + "minEndTime": "2024-06-04T16:03:20.1Z", + "maxEndTime": "2024-06-04T16:03:20.1Z" + } + } + ], + "signalGroup": 4 + }, + { + "stateTimeSpeed": [ + { + "eventState": "PROTECTED_CLEARANCE", + "timing": { + "minEndTime": "2024-06-04T16:03:17Z", + "maxEndTime": "2024-06-04T16:03:17Z" + } + } + ], + "signalGroup": 6 + } + ], + "messageType": "SPAT", + "cti4501Conformant": false, + "intersectionId": 12109, + "odeReceivedAt": "2024-06-04T16:23:15.062Z", + "originIp": "10.11.81.12", + "status": { + "failureFlash": false, + "noValidSPATisAvailableAtThisTime": false, + "fixedTimeOperation": false, + "standbyOperation": false, + "trafficDependentOperation": false, + "manualControlIsEnabled": false, + "off": false, + "stopTimeIsActivated": false, + "recentChangeInMAPassignedLanesIDsUsed": false, + "recentMAPmessageUpdate": false, + "failureMode": false, + "noValidMAPisAvailableAtThisTime": false, + "signalPriorityIsActive": false, + "preemptIsActive": false + } +},{ + "_id": { + "$oid": "665f3fc18c25216ef4f54022" + }, + "validationMessages": [ + { + "schemaPath": "#/$defs/J2735RoadRegulatorID/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].id.region", + "message": "$.payload.data.intersectionStateList.intersectionStatelist[0].id.region: null found, integer expected" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[0].state_time_speed.movementEventList[0].timing.startTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[0].state_time_speed.movementEventList[0].timing.nextTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[1].state_time_speed.movementEventList[0].timing.startTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[1].state_time_speed.movementEventList[0].timing.nextTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[2].state_time_speed.movementEventList[0].timing.startTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[2].state_time_speed.movementEventList[0].timing.nextTime", + "message": "DE_TimeMark is missing" + } + ], + "utcTimeStamp": "2024-06-04T16:23:15.17Z", + "schemaVersion": 1, + "recordGeneratedAt": { + "$date": { + "$numberLong": "1717518195182" + } + }, + "revision": 0, + "states": [ + { + "stateTimeSpeed": [ + { + "eventState": "PROTECTED_CLEARANCE", + "timing": { + "minEndTime": "2024-06-04T16:03:17Z", + "maxEndTime": "2024-06-04T16:03:17Z" + } + } + ], + "signalGroup": 2 + }, + { + "stateTimeSpeed": [ + { + "eventState": "STOP_AND_REMAIN", + "timing": { + "minEndTime": "2024-06-04T16:03:20.1Z", + "maxEndTime": "2024-06-04T16:03:20.1Z" + } + } + ], + "signalGroup": 4 + }, + { + "stateTimeSpeed": [ + { + "eventState": "PROTECTED_CLEARANCE", + "timing": { + "minEndTime": "2024-06-04T16:03:17Z", + "maxEndTime": "2024-06-04T16:03:17Z" + } + } + ], + "signalGroup": 6 + } + ], + "messageType": "SPAT", + "cti4501Conformant": false, + "intersectionId": 12109, + "odeReceivedAt": "2024-06-04T16:23:15.17Z", + "originIp": "10.11.81.12", + "status": { + "failureFlash": false, + "noValidSPATisAvailableAtThisTime": false, + "fixedTimeOperation": false, + "standbyOperation": false, + "trafficDependentOperation": false, + "manualControlIsEnabled": false, + "off": false, + "stopTimeIsActivated": false, + "recentChangeInMAPassignedLanesIDsUsed": false, + "recentMAPmessageUpdate": false, + "failureMode": false, + "noValidMAPisAvailableAtThisTime": false, + "signalPriorityIsActive": false, + "preemptIsActive": false + } +},{ + "_id": { + "$oid": "665f3fc18c25216ef4f54023" + }, + "validationMessages": [ + { + "schemaPath": "#/$defs/J2735RoadRegulatorID/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].id.region", + "message": "$.payload.data.intersectionStateList.intersectionStatelist[0].id.region: null found, integer expected" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[0].state_time_speed.movementEventList[0].timing.startTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[0].state_time_speed.movementEventList[0].timing.nextTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[1].state_time_speed.movementEventList[0].timing.startTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[1].state_time_speed.movementEventList[0].timing.nextTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[2].state_time_speed.movementEventList[0].timing.startTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[2].state_time_speed.movementEventList[0].timing.nextTime", + "message": "DE_TimeMark is missing" + } + ], + "utcTimeStamp": "2024-06-04T16:23:15.278Z", + "schemaVersion": 1, + "recordGeneratedAt": { + "$date": { + "$numberLong": "1717518195289" + } + }, + "revision": 0, + "states": [ + { + "stateTimeSpeed": [ + { + "eventState": "PROTECTED_CLEARANCE", + "timing": { + "minEndTime": "2024-06-04T16:03:17Z", + "maxEndTime": "2024-06-04T16:03:17Z" + } + } + ], + "signalGroup": 2 + }, + { + "stateTimeSpeed": [ + { + "eventState": "STOP_AND_REMAIN", + "timing": { + "minEndTime": "2024-06-04T16:03:20.1Z", + "maxEndTime": "2024-06-04T16:03:20.1Z" + } + } + ], + "signalGroup": 4 + }, + { + "stateTimeSpeed": [ + { + "eventState": "PROTECTED_CLEARANCE", + "timing": { + "minEndTime": "2024-06-04T16:03:17Z", + "maxEndTime": "2024-06-04T16:03:17Z" + } + } + ], + "signalGroup": 6 + } + ], + "messageType": "SPAT", + "cti4501Conformant": false, + "intersectionId": 12109, + "odeReceivedAt": "2024-06-04T16:23:15.278Z", + "originIp": "10.11.81.12", + "status": { + "failureFlash": false, + "noValidSPATisAvailableAtThisTime": false, + "fixedTimeOperation": false, + "standbyOperation": false, + "trafficDependentOperation": false, + "manualControlIsEnabled": false, + "off": false, + "stopTimeIsActivated": false, + "recentChangeInMAPassignedLanesIDsUsed": false, + "recentMAPmessageUpdate": false, + "failureMode": false, + "noValidMAPisAvailableAtThisTime": false, + "signalPriorityIsActive": false, + "preemptIsActive": false + } +},{ + "_id": { + "$oid": "665f3fc18c25216ef4f54024" + }, + "validationMessages": [ + { + "schemaPath": "#/$defs/J2735RoadRegulatorID/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].id.region", + "message": "$.payload.data.intersectionStateList.intersectionStatelist[0].id.region: null found, integer expected" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[0].state_time_speed.movementEventList[0].timing.startTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[0].state_time_speed.movementEventList[0].timing.nextTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[1].state_time_speed.movementEventList[0].timing.startTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[1].state_time_speed.movementEventList[0].timing.nextTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[2].state_time_speed.movementEventList[0].timing.startTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[2].state_time_speed.movementEventList[0].timing.nextTime", + "message": "DE_TimeMark is missing" + } + ], + "utcTimeStamp": "2024-06-04T16:23:15.384Z", + "schemaVersion": 1, + "recordGeneratedAt": { + "$date": { + "$numberLong": "1717518195395" + } + }, + "revision": 0, + "states": [ + { + "stateTimeSpeed": [ + { + "eventState": "PROTECTED_CLEARANCE", + "timing": { + "minEndTime": "2024-06-04T16:03:17Z", + "maxEndTime": "2024-06-04T16:03:17Z" + } + } + ], + "signalGroup": 2 + }, + { + "stateTimeSpeed": [ + { + "eventState": "STOP_AND_REMAIN", + "timing": { + "minEndTime": "2024-06-04T16:03:20.1Z", + "maxEndTime": "2024-06-04T16:03:20.1Z" + } + } + ], + "signalGroup": 4 + }, + { + "stateTimeSpeed": [ + { + "eventState": "PROTECTED_CLEARANCE", + "timing": { + "minEndTime": "2024-06-04T16:03:17Z", + "maxEndTime": "2024-06-04T16:03:17Z" + } + } + ], + "signalGroup": 6 + } + ], + "messageType": "SPAT", + "cti4501Conformant": false, + "intersectionId": 12109, + "odeReceivedAt": "2024-06-04T16:23:15.384Z", + "originIp": "10.11.81.12", + "status": { + "failureFlash": false, + "noValidSPATisAvailableAtThisTime": false, + "fixedTimeOperation": false, + "standbyOperation": false, + "trafficDependentOperation": false, + "manualControlIsEnabled": false, + "off": false, + "stopTimeIsActivated": false, + "recentChangeInMAPassignedLanesIDsUsed": false, + "recentMAPmessageUpdate": false, + "failureMode": false, + "noValidMAPisAvailableAtThisTime": false, + "signalPriorityIsActive": false, + "preemptIsActive": false + } +},{ + "_id": { + "$oid": "665f3fc18c25216ef4f54025" + }, + "validationMessages": [ + { + "schemaPath": "#/$defs/J2735RoadRegulatorID/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].id.region", + "message": "$.payload.data.intersectionStateList.intersectionStatelist[0].id.region: null found, integer expected" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[0].state_time_speed.movementEventList[0].timing.startTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[0].state_time_speed.movementEventList[0].timing.nextTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[1].state_time_speed.movementEventList[0].timing.startTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[1].state_time_speed.movementEventList[0].timing.nextTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[2].state_time_speed.movementEventList[0].timing.startTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[2].state_time_speed.movementEventList[0].timing.nextTime", + "message": "DE_TimeMark is missing" + } + ], + "utcTimeStamp": "2024-06-04T16:23:15.491Z", + "schemaVersion": 1, + "recordGeneratedAt": { + "$date": { + "$numberLong": "1717518195502" + } + }, + "revision": 0, + "states": [ + { + "stateTimeSpeed": [ + { + "eventState": "PROTECTED_CLEARANCE", + "timing": { + "minEndTime": "2024-06-04T16:03:17Z", + "maxEndTime": "2024-06-04T16:03:17Z" + } + } + ], + "signalGroup": 2 + }, + { + "stateTimeSpeed": [ + { + "eventState": "STOP_AND_REMAIN", + "timing": { + "minEndTime": "2024-06-04T16:03:20.1Z", + "maxEndTime": "2024-06-04T16:03:20.1Z" + } + } + ], + "signalGroup": 4 + }, + { + "stateTimeSpeed": [ + { + "eventState": "PROTECTED_CLEARANCE", + "timing": { + "minEndTime": "2024-06-04T16:03:17Z", + "maxEndTime": "2024-06-04T16:03:17Z" + } + } + ], + "signalGroup": 6 + } + ], + "messageType": "SPAT", + "cti4501Conformant": false, + "intersectionId": 12109, + "odeReceivedAt": "2024-06-04T16:23:15.491Z", + "originIp": "10.11.81.12", + "status": { + "failureFlash": false, + "noValidSPATisAvailableAtThisTime": false, + "fixedTimeOperation": false, + "standbyOperation": false, + "trafficDependentOperation": false, + "manualControlIsEnabled": false, + "off": false, + "stopTimeIsActivated": false, + "recentChangeInMAPassignedLanesIDsUsed": false, + "recentMAPmessageUpdate": false, + "failureMode": false, + "noValidMAPisAvailableAtThisTime": false, + "signalPriorityIsActive": false, + "preemptIsActive": false + } +},{ + "_id": { + "$oid": "665f3fc18c25216ef4f54026" + }, + "validationMessages": [ + { + "schemaPath": "#/$defs/J2735RoadRegulatorID/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].id.region", + "message": "$.payload.data.intersectionStateList.intersectionStatelist[0].id.region: null found, integer expected" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[0].state_time_speed.movementEventList[0].timing.startTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[0].state_time_speed.movementEventList[0].timing.nextTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[1].state_time_speed.movementEventList[0].timing.startTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[1].state_time_speed.movementEventList[0].timing.nextTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[2].state_time_speed.movementEventList[0].timing.startTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[2].state_time_speed.movementEventList[0].timing.nextTime", + "message": "DE_TimeMark is missing" + } + ], + "utcTimeStamp": "2024-06-04T16:23:15.599Z", + "schemaVersion": 1, + "recordGeneratedAt": { + "$date": { + "$numberLong": "1717518195609" + } + }, + "revision": 0, + "states": [ + { + "stateTimeSpeed": [ + { + "eventState": "PROTECTED_CLEARANCE", + "timing": { + "minEndTime": "2024-06-04T16:03:17Z", + "maxEndTime": "2024-06-04T16:03:17Z" + } + } + ], + "signalGroup": 2 + }, + { + "stateTimeSpeed": [ + { + "eventState": "STOP_AND_REMAIN", + "timing": { + "minEndTime": "2024-06-04T16:03:20.1Z", + "maxEndTime": "2024-06-04T16:03:20.1Z" + } + } + ], + "signalGroup": 4 + }, + { + "stateTimeSpeed": [ + { + "eventState": "PROTECTED_CLEARANCE", + "timing": { + "minEndTime": "2024-06-04T16:03:17Z", + "maxEndTime": "2024-06-04T16:03:17Z" + } + } + ], + "signalGroup": 6 + } + ], + "messageType": "SPAT", + "cti4501Conformant": false, + "intersectionId": 12109, + "odeReceivedAt": "2024-06-04T16:23:15.599Z", + "originIp": "10.11.81.12", + "status": { + "failureFlash": false, + "noValidSPATisAvailableAtThisTime": false, + "fixedTimeOperation": false, + "standbyOperation": false, + "trafficDependentOperation": false, + "manualControlIsEnabled": false, + "off": false, + "stopTimeIsActivated": false, + "recentChangeInMAPassignedLanesIDsUsed": false, + "recentMAPmessageUpdate": false, + "failureMode": false, + "noValidMAPisAvailableAtThisTime": false, + "signalPriorityIsActive": false, + "preemptIsActive": false + } +},{ + "_id": { + "$oid": "665f3fc18c25216ef4f54027" + }, + "validationMessages": [ + { + "schemaPath": "#/$defs/J2735RoadRegulatorID/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].id.region", + "message": "$.payload.data.intersectionStateList.intersectionStatelist[0].id.region: null found, integer expected" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[0].state_time_speed.movementEventList[0].timing.startTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[0].state_time_speed.movementEventList[0].timing.nextTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[1].state_time_speed.movementEventList[0].timing.startTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[1].state_time_speed.movementEventList[0].timing.nextTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[2].state_time_speed.movementEventList[0].timing.startTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[2].state_time_speed.movementEventList[0].timing.nextTime", + "message": "DE_TimeMark is missing" + } + ], + "utcTimeStamp": "2024-06-04T16:23:15.706Z", + "schemaVersion": 1, + "recordGeneratedAt": { + "$date": { + "$numberLong": "1717518195717" + } + }, + "revision": 0, + "states": [ + { + "stateTimeSpeed": [ + { + "eventState": "PROTECTED_CLEARANCE", + "timing": { + "minEndTime": "2024-06-04T16:03:17Z", + "maxEndTime": "2024-06-04T16:03:17Z" + } + } + ], + "signalGroup": 2 + }, + { + "stateTimeSpeed": [ + { + "eventState": "STOP_AND_REMAIN", + "timing": { + "minEndTime": "2024-06-04T16:03:20.1Z", + "maxEndTime": "2024-06-04T16:03:20.1Z" + } + } + ], + "signalGroup": 4 + }, + { + "stateTimeSpeed": [ + { + "eventState": "PROTECTED_CLEARANCE", + "timing": { + "minEndTime": "2024-06-04T16:03:17Z", + "maxEndTime": "2024-06-04T16:03:17Z" + } + } + ], + "signalGroup": 6 + } + ], + "messageType": "SPAT", + "cti4501Conformant": false, + "intersectionId": 12109, + "odeReceivedAt": "2024-06-04T16:23:15.706Z", + "originIp": "10.11.81.12", + "status": { + "failureFlash": false, + "noValidSPATisAvailableAtThisTime": false, + "fixedTimeOperation": false, + "standbyOperation": false, + "trafficDependentOperation": false, + "manualControlIsEnabled": false, + "off": false, + "stopTimeIsActivated": false, + "recentChangeInMAPassignedLanesIDsUsed": false, + "recentMAPmessageUpdate": false, + "failureMode": false, + "noValidMAPisAvailableAtThisTime": false, + "signalPriorityIsActive": false, + "preemptIsActive": false + } +},{ + "_id": { + "$oid": "665f3fc18c25216ef4f54028" + }, + "validationMessages": [ + { + "schemaPath": "#/$defs/J2735RoadRegulatorID/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].id.region", + "message": "$.payload.data.intersectionStateList.intersectionStatelist[0].id.region: null found, integer expected" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[0].state_time_speed.movementEventList[0].timing.startTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[0].state_time_speed.movementEventList[0].timing.nextTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[1].state_time_speed.movementEventList[0].timing.startTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[1].state_time_speed.movementEventList[0].timing.nextTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[2].state_time_speed.movementEventList[0].timing.startTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[2].state_time_speed.movementEventList[0].timing.nextTime", + "message": "DE_TimeMark is missing" + } + ], + "utcTimeStamp": "2024-06-04T16:23:15.813Z", + "schemaVersion": 1, + "recordGeneratedAt": { + "$date": { + "$numberLong": "1717518195824" + } + }, + "revision": 0, + "states": [ + { + "stateTimeSpeed": [ + { + "eventState": "PROTECTED_CLEARANCE", + "timing": { + "minEndTime": "2024-06-04T16:03:17Z", + "maxEndTime": "2024-06-04T16:03:17Z" + } + } + ], + "signalGroup": 2 + }, + { + "stateTimeSpeed": [ + { + "eventState": "STOP_AND_REMAIN", + "timing": { + "minEndTime": "2024-06-04T16:03:20.1Z", + "maxEndTime": "2024-06-04T16:03:20.1Z" + } + } + ], + "signalGroup": 4 + }, + { + "stateTimeSpeed": [ + { + "eventState": "PROTECTED_CLEARANCE", + "timing": { + "minEndTime": "2024-06-04T16:03:17Z", + "maxEndTime": "2024-06-04T16:03:17Z" + } + } + ], + "signalGroup": 6 + } + ], + "messageType": "SPAT", + "cti4501Conformant": false, + "intersectionId": 12109, + "odeReceivedAt": "2024-06-04T16:23:15.813Z", + "originIp": "10.11.81.12", + "status": { + "failureFlash": false, + "noValidSPATisAvailableAtThisTime": false, + "fixedTimeOperation": false, + "standbyOperation": false, + "trafficDependentOperation": false, + "manualControlIsEnabled": false, + "off": false, + "stopTimeIsActivated": false, + "recentChangeInMAPassignedLanesIDsUsed": false, + "recentMAPmessageUpdate": false, + "failureMode": false, + "noValidMAPisAvailableAtThisTime": false, + "signalPriorityIsActive": false, + "preemptIsActive": false + } +},{ + "_id": { + "$oid": "665f3fc18c25216ef4f54029" + }, + "validationMessages": [ + { + "schemaPath": "#/$defs/J2735RoadRegulatorID/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].id.region", + "message": "$.payload.data.intersectionStateList.intersectionStatelist[0].id.region: null found, integer expected" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[0].state_time_speed.movementEventList[0].timing.startTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[0].state_time_speed.movementEventList[0].timing.nextTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[1].state_time_speed.movementEventList[0].timing.startTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[1].state_time_speed.movementEventList[0].timing.nextTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[2].state_time_speed.movementEventList[0].timing.startTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[2].state_time_speed.movementEventList[0].timing.nextTime", + "message": "DE_TimeMark is missing" + } + ], + "utcTimeStamp": "2024-06-04T16:23:15.92Z", + "schemaVersion": 1, + "recordGeneratedAt": { + "$date": { + "$numberLong": "1717518195931" + } + }, + "revision": 0, + "states": [ + { + "stateTimeSpeed": [ + { + "eventState": "PROTECTED_CLEARANCE", + "timing": { + "minEndTime": "2024-06-04T16:03:19Z", + "maxEndTime": "2024-06-04T16:03:19Z" + } + } + ], + "signalGroup": 2 + }, + { + "stateTimeSpeed": [ + { + "eventState": "STOP_AND_REMAIN", + "timing": { + "minEndTime": "2024-06-04T16:03:22.1Z", + "maxEndTime": "2024-06-04T16:03:22.1Z" + } + } + ], + "signalGroup": 4 + }, + { + "stateTimeSpeed": [ + { + "eventState": "PROTECTED_CLEARANCE", + "timing": { + "minEndTime": "2024-06-04T16:03:19Z", + "maxEndTime": "2024-06-04T16:03:19Z" + } + } + ], + "signalGroup": 6 + } + ], + "messageType": "SPAT", + "cti4501Conformant": false, + "intersectionId": 12109, + "odeReceivedAt": "2024-06-04T16:23:15.92Z", + "originIp": "10.11.81.12", + "status": { + "failureFlash": false, + "noValidSPATisAvailableAtThisTime": false, + "fixedTimeOperation": false, + "standbyOperation": false, + "trafficDependentOperation": false, + "manualControlIsEnabled": false, + "off": false, + "stopTimeIsActivated": false, + "recentChangeInMAPassignedLanesIDsUsed": false, + "recentMAPmessageUpdate": false, + "failureMode": false, + "noValidMAPisAvailableAtThisTime": false, + "signalPriorityIsActive": false, + "preemptIsActive": false + } +},{ + "_id": { + "$oid": "665f3fc18c25216ef4f5402a" + }, + "validationMessages": [ + { + "schemaPath": "#/$defs/J2735RoadRegulatorID/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].id.region", + "message": "$.payload.data.intersectionStateList.intersectionStatelist[0].id.region: null found, integer expected" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[0].state_time_speed.movementEventList[0].timing.startTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[0].state_time_speed.movementEventList[0].timing.nextTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[1].state_time_speed.movementEventList[0].timing.startTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[1].state_time_speed.movementEventList[0].timing.nextTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[2].state_time_speed.movementEventList[0].timing.startTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[2].state_time_speed.movementEventList[0].timing.nextTime", + "message": "DE_TimeMark is missing" + } + ], + "utcTimeStamp": "2024-06-04T16:23:16.026Z", + "schemaVersion": 1, + "recordGeneratedAt": { + "$date": { + "$numberLong": "1717518196037" + } + }, + "revision": 0, + "states": [ + { + "stateTimeSpeed": [ + { + "eventState": "PROTECTED_CLEARANCE", + "timing": { + "minEndTime": "2024-06-04T16:03:19Z", + "maxEndTime": "2024-06-04T16:03:19Z" + } + } + ], + "signalGroup": 2 + }, + { + "stateTimeSpeed": [ + { + "eventState": "STOP_AND_REMAIN", + "timing": { + "minEndTime": "2024-06-04T16:03:22.1Z", + "maxEndTime": "2024-06-04T16:03:22.1Z" + } + } + ], + "signalGroup": 4 + }, + { + "stateTimeSpeed": [ + { + "eventState": "PROTECTED_CLEARANCE", + "timing": { + "minEndTime": "2024-06-04T16:03:19Z", + "maxEndTime": "2024-06-04T16:03:19Z" + } + } + ], + "signalGroup": 6 + } + ], + "messageType": "SPAT", + "cti4501Conformant": false, + "intersectionId": 12109, + "odeReceivedAt": "2024-06-04T16:23:16.026Z", + "originIp": "10.11.81.12", + "status": { + "failureFlash": false, + "noValidSPATisAvailableAtThisTime": false, + "fixedTimeOperation": false, + "standbyOperation": false, + "trafficDependentOperation": false, + "manualControlIsEnabled": false, + "off": false, + "stopTimeIsActivated": false, + "recentChangeInMAPassignedLanesIDsUsed": false, + "recentMAPmessageUpdate": false, + "failureMode": false, + "noValidMAPisAvailableAtThisTime": false, + "signalPriorityIsActive": false, + "preemptIsActive": false + } +},{ + "_id": { + "$oid": "665f3fc18c25216ef4f5402b" + }, + "validationMessages": [ + { + "schemaPath": "#/$defs/J2735RoadRegulatorID/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].id.region", + "message": "$.payload.data.intersectionStateList.intersectionStatelist[0].id.region: null found, integer expected" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[0].state_time_speed.movementEventList[0].timing.startTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[0].state_time_speed.movementEventList[0].timing.nextTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[1].state_time_speed.movementEventList[0].timing.startTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[1].state_time_speed.movementEventList[0].timing.nextTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[2].state_time_speed.movementEventList[0].timing.startTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[2].state_time_speed.movementEventList[0].timing.nextTime", + "message": "DE_TimeMark is missing" + } + ], + "utcTimeStamp": "2024-06-04T16:23:16.134Z", + "schemaVersion": 1, + "recordGeneratedAt": { + "$date": { + "$numberLong": "1717518196144" + } + }, + "revision": 0, + "states": [ + { + "stateTimeSpeed": [ + { + "eventState": "PROTECTED_CLEARANCE", + "timing": { + "minEndTime": "2024-06-04T16:03:19Z", + "maxEndTime": "2024-06-04T16:03:19Z" + } + } + ], + "signalGroup": 2 + }, + { + "stateTimeSpeed": [ + { + "eventState": "STOP_AND_REMAIN", + "timing": { + "minEndTime": "2024-06-04T16:03:22.1Z", + "maxEndTime": "2024-06-04T16:03:22.1Z" + } + } + ], + "signalGroup": 4 + }, + { + "stateTimeSpeed": [ + { + "eventState": "PROTECTED_CLEARANCE", + "timing": { + "minEndTime": "2024-06-04T16:03:19Z", + "maxEndTime": "2024-06-04T16:03:19Z" + } + } + ], + "signalGroup": 6 + } + ], + "messageType": "SPAT", + "cti4501Conformant": false, + "intersectionId": 12109, + "odeReceivedAt": "2024-06-04T16:23:16.134Z", + "originIp": "10.11.81.12", + "status": { + "failureFlash": false, + "noValidSPATisAvailableAtThisTime": false, + "fixedTimeOperation": false, + "standbyOperation": false, + "trafficDependentOperation": false, + "manualControlIsEnabled": false, + "off": false, + "stopTimeIsActivated": false, + "recentChangeInMAPassignedLanesIDsUsed": false, + "recentMAPmessageUpdate": false, + "failureMode": false, + "noValidMAPisAvailableAtThisTime": false, + "signalPriorityIsActive": false, + "preemptIsActive": false + } +},{ + "_id": { + "$oid": "665f3fc18c25216ef4f5402c" + }, + "validationMessages": [ + { + "schemaPath": "#/$defs/J2735RoadRegulatorID/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].id.region", + "message": "$.payload.data.intersectionStateList.intersectionStatelist[0].id.region: null found, integer expected" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[0].state_time_speed.movementEventList[0].timing.startTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[0].state_time_speed.movementEventList[0].timing.nextTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[1].state_time_speed.movementEventList[0].timing.startTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[1].state_time_speed.movementEventList[0].timing.nextTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[2].state_time_speed.movementEventList[0].timing.startTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[2].state_time_speed.movementEventList[0].timing.nextTime", + "message": "DE_TimeMark is missing" + } + ], + "utcTimeStamp": "2024-06-04T16:23:16.24Z", + "schemaVersion": 1, + "recordGeneratedAt": { + "$date": { + "$numberLong": "1717518196251" + } + }, + "revision": 0, + "states": [ + { + "stateTimeSpeed": [ + { + "eventState": "PROTECTED_CLEARANCE", + "timing": { + "minEndTime": "2024-06-04T16:03:19Z", + "maxEndTime": "2024-06-04T16:03:19Z" + } + } + ], + "signalGroup": 2 + }, + { + "stateTimeSpeed": [ + { + "eventState": "STOP_AND_REMAIN", + "timing": { + "minEndTime": "2024-06-04T16:03:22.1Z", + "maxEndTime": "2024-06-04T16:03:22.1Z" + } + } + ], + "signalGroup": 4 + }, + { + "stateTimeSpeed": [ + { + "eventState": "PROTECTED_CLEARANCE", + "timing": { + "minEndTime": "2024-06-04T16:03:19Z", + "maxEndTime": "2024-06-04T16:03:19Z" + } + } + ], + "signalGroup": 6 + } + ], + "messageType": "SPAT", + "cti4501Conformant": false, + "intersectionId": 12109, + "odeReceivedAt": "2024-06-04T16:23:16.24Z", + "originIp": "10.11.81.12", + "status": { + "failureFlash": false, + "noValidSPATisAvailableAtThisTime": false, + "fixedTimeOperation": false, + "standbyOperation": false, + "trafficDependentOperation": false, + "manualControlIsEnabled": false, + "off": false, + "stopTimeIsActivated": false, + "recentChangeInMAPassignedLanesIDsUsed": false, + "recentMAPmessageUpdate": false, + "failureMode": false, + "noValidMAPisAvailableAtThisTime": false, + "signalPriorityIsActive": false, + "preemptIsActive": false + } +},{ + "_id": { + "$oid": "665f3fc18c25216ef4f5402d" + }, + "validationMessages": [ + { + "schemaPath": "#/$defs/J2735RoadRegulatorID/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].id.region", + "message": "$.payload.data.intersectionStateList.intersectionStatelist[0].id.region: null found, integer expected" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[0].state_time_speed.movementEventList[0].timing.startTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[0].state_time_speed.movementEventList[0].timing.nextTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[1].state_time_speed.movementEventList[0].timing.startTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[1].state_time_speed.movementEventList[0].timing.nextTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[2].state_time_speed.movementEventList[0].timing.startTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[2].state_time_speed.movementEventList[0].timing.nextTime", + "message": "DE_TimeMark is missing" + } + ], + "utcTimeStamp": "2024-06-04T16:23:16.348Z", + "schemaVersion": 1, + "recordGeneratedAt": { + "$date": { + "$numberLong": "1717518196357" + } + }, + "revision": 0, + "states": [ + { + "stateTimeSpeed": [ + { + "eventState": "PROTECTED_CLEARANCE", + "timing": { + "minEndTime": "2024-06-04T16:03:19Z", + "maxEndTime": "2024-06-04T16:03:19Z" + } + } + ], + "signalGroup": 2 + }, + { + "stateTimeSpeed": [ + { + "eventState": "STOP_AND_REMAIN", + "timing": { + "minEndTime": "2024-06-04T16:03:22.1Z", + "maxEndTime": "2024-06-04T16:03:22.1Z" + } + } + ], + "signalGroup": 4 + }, + { + "stateTimeSpeed": [ + { + "eventState": "PROTECTED_CLEARANCE", + "timing": { + "minEndTime": "2024-06-04T16:03:19Z", + "maxEndTime": "2024-06-04T16:03:19Z" + } + } + ], + "signalGroup": 6 + } + ], + "messageType": "SPAT", + "cti4501Conformant": false, + "intersectionId": 12109, + "odeReceivedAt": "2024-06-04T16:23:16.348Z", + "originIp": "10.11.81.12", + "status": { + "failureFlash": false, + "noValidSPATisAvailableAtThisTime": false, + "fixedTimeOperation": false, + "standbyOperation": false, + "trafficDependentOperation": false, + "manualControlIsEnabled": false, + "off": false, + "stopTimeIsActivated": false, + "recentChangeInMAPassignedLanesIDsUsed": false, + "recentMAPmessageUpdate": false, + "failureMode": false, + "noValidMAPisAvailableAtThisTime": false, + "signalPriorityIsActive": false, + "preemptIsActive": false + } +},{ + "_id": { + "$oid": "665f3fc18c25216ef4f5402e" + }, + "validationMessages": [ + { + "schemaPath": "#/$defs/J2735RoadRegulatorID/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].id.region", + "message": "$.payload.data.intersectionStateList.intersectionStatelist[0].id.region: null found, integer expected" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[0].state_time_speed.movementEventList[0].timing.startTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[0].state_time_speed.movementEventList[0].timing.nextTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[1].state_time_speed.movementEventList[0].timing.startTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[1].state_time_speed.movementEventList[0].timing.nextTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[2].state_time_speed.movementEventList[0].timing.startTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[2].state_time_speed.movementEventList[0].timing.nextTime", + "message": "DE_TimeMark is missing" + } + ], + "utcTimeStamp": "2024-06-04T16:23:16.455Z", + "schemaVersion": 1, + "recordGeneratedAt": { + "$date": { + "$numberLong": "1717518196466" + } + }, + "revision": 0, + "states": [ + { + "stateTimeSpeed": [ + { + "eventState": "PROTECTED_CLEARANCE", + "timing": { + "minEndTime": "2024-06-04T16:03:19Z", + "maxEndTime": "2024-06-04T16:03:19Z" + } + } + ], + "signalGroup": 2 + }, + { + "stateTimeSpeed": [ + { + "eventState": "STOP_AND_REMAIN", + "timing": { + "minEndTime": "2024-06-04T16:03:22.1Z", + "maxEndTime": "2024-06-04T16:03:22.1Z" + } + } + ], + "signalGroup": 4 + }, + { + "stateTimeSpeed": [ + { + "eventState": "PROTECTED_CLEARANCE", + "timing": { + "minEndTime": "2024-06-04T16:03:19Z", + "maxEndTime": "2024-06-04T16:03:19Z" + } + } + ], + "signalGroup": 6 + } + ], + "messageType": "SPAT", + "cti4501Conformant": false, + "intersectionId": 12109, + "odeReceivedAt": "2024-06-04T16:23:16.455Z", + "originIp": "10.11.81.12", + "status": { + "failureFlash": false, + "noValidSPATisAvailableAtThisTime": false, + "fixedTimeOperation": false, + "standbyOperation": false, + "trafficDependentOperation": false, + "manualControlIsEnabled": false, + "off": false, + "stopTimeIsActivated": false, + "recentChangeInMAPassignedLanesIDsUsed": false, + "recentMAPmessageUpdate": false, + "failureMode": false, + "noValidMAPisAvailableAtThisTime": false, + "signalPriorityIsActive": false, + "preemptIsActive": false + } +},{ + "_id": { + "$oid": "665f3fc18c25216ef4f5402f" + }, + "validationMessages": [ + { + "schemaPath": "#/$defs/J2735RoadRegulatorID/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].id.region", + "message": "$.payload.data.intersectionStateList.intersectionStatelist[0].id.region: null found, integer expected" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[0].state_time_speed.movementEventList[0].timing.startTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[0].state_time_speed.movementEventList[0].timing.nextTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[1].state_time_speed.movementEventList[0].timing.startTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[1].state_time_speed.movementEventList[0].timing.nextTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[2].state_time_speed.movementEventList[0].timing.startTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[2].state_time_speed.movementEventList[0].timing.nextTime", + "message": "DE_TimeMark is missing" + } + ], + "utcTimeStamp": "2024-06-04T16:23:16.561Z", + "schemaVersion": 1, + "recordGeneratedAt": { + "$date": { + "$numberLong": "1717518196571" + } + }, + "revision": 0, + "states": [ + { + "stateTimeSpeed": [ + { + "eventState": "PROTECTED_CLEARANCE", + "timing": { + "minEndTime": "2024-06-04T16:03:19Z", + "maxEndTime": "2024-06-04T16:03:19Z" + } + } + ], + "signalGroup": 2 + }, + { + "stateTimeSpeed": [ + { + "eventState": "STOP_AND_REMAIN", + "timing": { + "minEndTime": "2024-06-04T16:03:22.1Z", + "maxEndTime": "2024-06-04T16:03:22.1Z" + } + } + ], + "signalGroup": 4 + }, + { + "stateTimeSpeed": [ + { + "eventState": "PROTECTED_CLEARANCE", + "timing": { + "minEndTime": "2024-06-04T16:03:19Z", + "maxEndTime": "2024-06-04T16:03:19Z" + } + } + ], + "signalGroup": 6 + } + ], + "messageType": "SPAT", + "cti4501Conformant": false, + "intersectionId": 12109, + "odeReceivedAt": "2024-06-04T16:23:16.561Z", + "originIp": "10.11.81.12", + "status": { + "failureFlash": false, + "noValidSPATisAvailableAtThisTime": false, + "fixedTimeOperation": false, + "standbyOperation": false, + "trafficDependentOperation": false, + "manualControlIsEnabled": false, + "off": false, + "stopTimeIsActivated": false, + "recentChangeInMAPassignedLanesIDsUsed": false, + "recentMAPmessageUpdate": false, + "failureMode": false, + "noValidMAPisAvailableAtThisTime": false, + "signalPriorityIsActive": false, + "preemptIsActive": false + } +},{ + "_id": { + "$oid": "665f3fc18c25216ef4f54030" + }, + "validationMessages": [ + { + "schemaPath": "#/$defs/J2735RoadRegulatorID/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].id.region", + "message": "$.payload.data.intersectionStateList.intersectionStatelist[0].id.region: null found, integer expected" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[0].state_time_speed.movementEventList[0].timing.startTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[0].state_time_speed.movementEventList[0].timing.nextTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[1].state_time_speed.movementEventList[0].timing.startTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[1].state_time_speed.movementEventList[0].timing.nextTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[2].state_time_speed.movementEventList[0].timing.startTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[2].state_time_speed.movementEventList[0].timing.nextTime", + "message": "DE_TimeMark is missing" + } + ], + "utcTimeStamp": "2024-06-04T16:23:16.67Z", + "schemaVersion": 1, + "recordGeneratedAt": { + "$date": { + "$numberLong": "1717518196678" + } + }, + "revision": 0, + "states": [ + { + "stateTimeSpeed": [ + { + "eventState": "PROTECTED_CLEARANCE", + "timing": { + "minEndTime": "2024-06-04T16:03:19Z", + "maxEndTime": "2024-06-04T16:03:19Z" + } + } + ], + "signalGroup": 2 + }, + { + "stateTimeSpeed": [ + { + "eventState": "STOP_AND_REMAIN", + "timing": { + "minEndTime": "2024-06-04T16:03:22.1Z", + "maxEndTime": "2024-06-04T16:03:22.1Z" + } + } + ], + "signalGroup": 4 + }, + { + "stateTimeSpeed": [ + { + "eventState": "PROTECTED_CLEARANCE", + "timing": { + "minEndTime": "2024-06-04T16:03:19Z", + "maxEndTime": "2024-06-04T16:03:19Z" + } + } + ], + "signalGroup": 6 + } + ], + "messageType": "SPAT", + "cti4501Conformant": false, + "intersectionId": 12109, + "odeReceivedAt": "2024-06-04T16:23:16.67Z", + "originIp": "10.11.81.12", + "status": { + "failureFlash": false, + "noValidSPATisAvailableAtThisTime": false, + "fixedTimeOperation": false, + "standbyOperation": false, + "trafficDependentOperation": false, + "manualControlIsEnabled": false, + "off": false, + "stopTimeIsActivated": false, + "recentChangeInMAPassignedLanesIDsUsed": false, + "recentMAPmessageUpdate": false, + "failureMode": false, + "noValidMAPisAvailableAtThisTime": false, + "signalPriorityIsActive": false, + "preemptIsActive": false + } +},{ + "_id": { + "$oid": "665f3fc18c25216ef4f54031" + }, + "validationMessages": [ + { + "schemaPath": "#/$defs/J2735RoadRegulatorID/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].id.region", + "message": "$.payload.data.intersectionStateList.intersectionStatelist[0].id.region: null found, integer expected" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[0].state_time_speed.movementEventList[0].timing.startTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[0].state_time_speed.movementEventList[0].timing.nextTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[1].state_time_speed.movementEventList[0].timing.startTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[1].state_time_speed.movementEventList[0].timing.nextTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[2].state_time_speed.movementEventList[0].timing.startTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[2].state_time_speed.movementEventList[0].timing.nextTime", + "message": "DE_TimeMark is missing" + } + ], + "utcTimeStamp": "2024-06-04T16:23:16.777Z", + "schemaVersion": 1, + "recordGeneratedAt": { + "$date": { + "$numberLong": "1717518196784" + } + }, + "revision": 0, + "states": [ + { + "stateTimeSpeed": [ + { + "eventState": "PROTECTED_CLEARANCE", + "timing": { + "minEndTime": "2024-06-04T16:03:19Z", + "maxEndTime": "2024-06-04T16:03:19Z" + } + } + ], + "signalGroup": 2 + }, + { + "stateTimeSpeed": [ + { + "eventState": "STOP_AND_REMAIN", + "timing": { + "minEndTime": "2024-06-04T16:03:22.1Z", + "maxEndTime": "2024-06-04T16:03:22.1Z" + } + } + ], + "signalGroup": 4 + }, + { + "stateTimeSpeed": [ + { + "eventState": "PROTECTED_CLEARANCE", + "timing": { + "minEndTime": "2024-06-04T16:03:19Z", + "maxEndTime": "2024-06-04T16:03:19Z" + } + } + ], + "signalGroup": 6 + } + ], + "messageType": "SPAT", + "cti4501Conformant": false, + "intersectionId": 12109, + "odeReceivedAt": "2024-06-04T16:23:16.777Z", + "originIp": "10.11.81.12", + "status": { + "failureFlash": false, + "noValidSPATisAvailableAtThisTime": false, + "fixedTimeOperation": false, + "standbyOperation": false, + "trafficDependentOperation": false, + "manualControlIsEnabled": false, + "off": false, + "stopTimeIsActivated": false, + "recentChangeInMAPassignedLanesIDsUsed": false, + "recentMAPmessageUpdate": false, + "failureMode": false, + "noValidMAPisAvailableAtThisTime": false, + "signalPriorityIsActive": false, + "preemptIsActive": false + } +},{ + "_id": { + "$oid": "665f3fc18c25216ef4f54032" + }, + "validationMessages": [ + { + "schemaPath": "#/$defs/J2735RoadRegulatorID/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].id.region", + "message": "$.payload.data.intersectionStateList.intersectionStatelist[0].id.region: null found, integer expected" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[0].state_time_speed.movementEventList[0].timing.startTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[0].state_time_speed.movementEventList[0].timing.nextTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[1].state_time_speed.movementEventList[0].timing.startTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[1].state_time_speed.movementEventList[0].timing.nextTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[2].state_time_speed.movementEventList[0].timing.startTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[2].state_time_speed.movementEventList[0].timing.nextTime", + "message": "DE_TimeMark is missing" + } + ], + "utcTimeStamp": "2024-06-04T16:23:16.883Z", + "schemaVersion": 1, + "recordGeneratedAt": { + "$date": { + "$numberLong": "1717518196891" + } + }, + "revision": 0, + "states": [ + { + "stateTimeSpeed": [ + { + "eventState": "PROTECTED_CLEARANCE", + "timing": { + "minEndTime": "2024-06-04T16:03:19Z", + "maxEndTime": "2024-06-04T16:03:19Z" + } + } + ], + "signalGroup": 2 + }, + { + "stateTimeSpeed": [ + { + "eventState": "STOP_AND_REMAIN", + "timing": { + "minEndTime": "2024-06-04T16:03:22.1Z", + "maxEndTime": "2024-06-04T16:03:22.1Z" + } + } + ], + "signalGroup": 4 + }, + { + "stateTimeSpeed": [ + { + "eventState": "PROTECTED_CLEARANCE", + "timing": { + "minEndTime": "2024-06-04T16:03:19Z", + "maxEndTime": "2024-06-04T16:03:19Z" + } + } + ], + "signalGroup": 6 + } + ], + "messageType": "SPAT", + "cti4501Conformant": false, + "intersectionId": 12109, + "odeReceivedAt": "2024-06-04T16:23:16.883Z", + "originIp": "10.11.81.12", + "status": { + "failureFlash": false, + "noValidSPATisAvailableAtThisTime": false, + "fixedTimeOperation": false, + "standbyOperation": false, + "trafficDependentOperation": false, + "manualControlIsEnabled": false, + "off": false, + "stopTimeIsActivated": false, + "recentChangeInMAPassignedLanesIDsUsed": false, + "recentMAPmessageUpdate": false, + "failureMode": false, + "noValidMAPisAvailableAtThisTime": false, + "signalPriorityIsActive": false, + "preemptIsActive": false + } +},{ + "_id": { + "$oid": "665f3fc18c25216ef4f54033" + }, + "validationMessages": [ + { + "schemaPath": "#/$defs/J2735RoadRegulatorID/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].id.region", + "message": "$.payload.data.intersectionStateList.intersectionStatelist[0].id.region: null found, integer expected" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[0].state_time_speed.movementEventList[0].timing.startTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[0].state_time_speed.movementEventList[0].timing.nextTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[1].state_time_speed.movementEventList[0].timing.startTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[1].state_time_speed.movementEventList[0].timing.nextTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[2].state_time_speed.movementEventList[0].timing.startTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[2].state_time_speed.movementEventList[0].timing.nextTime", + "message": "DE_TimeMark is missing" + } + ], + "utcTimeStamp": "2024-06-04T16:23:16.99Z", + "schemaVersion": 1, + "recordGeneratedAt": { + "$date": { + "$numberLong": "1717518196997" + } + }, + "revision": 0, + "states": [ + { + "stateTimeSpeed": [ + { + "eventState": "PROTECTED_CLEARANCE", + "timing": { + "minEndTime": "2024-06-04T16:03:19Z", + "maxEndTime": "2024-06-04T16:03:19Z" + } + } + ], + "signalGroup": 2 + }, + { + "stateTimeSpeed": [ + { + "eventState": "STOP_AND_REMAIN", + "timing": { + "minEndTime": "2024-06-04T16:03:22.1Z", + "maxEndTime": "2024-06-04T16:03:22.1Z" + } + } + ], + "signalGroup": 4 + }, + { + "stateTimeSpeed": [ + { + "eventState": "PROTECTED_CLEARANCE", + "timing": { + "minEndTime": "2024-06-04T16:03:19Z", + "maxEndTime": "2024-06-04T16:03:19Z" + } + } + ], + "signalGroup": 6 + } + ], + "messageType": "SPAT", + "cti4501Conformant": false, + "intersectionId": 12109, + "odeReceivedAt": "2024-06-04T16:23:16.99Z", + "originIp": "10.11.81.12", + "status": { + "failureFlash": false, + "noValidSPATisAvailableAtThisTime": false, + "fixedTimeOperation": false, + "standbyOperation": false, + "trafficDependentOperation": false, + "manualControlIsEnabled": false, + "off": false, + "stopTimeIsActivated": false, + "recentChangeInMAPassignedLanesIDsUsed": false, + "recentMAPmessageUpdate": false, + "failureMode": false, + "noValidMAPisAvailableAtThisTime": false, + "signalPriorityIsActive": false, + "preemptIsActive": false + } +},{ + "_id": { + "$oid": "665f3fc18c25216ef4f54034" + }, + "validationMessages": [ + { + "schemaPath": "#/$defs/J2735RoadRegulatorID/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].id.region", + "message": "$.payload.data.intersectionStateList.intersectionStatelist[0].id.region: null found, integer expected" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[0].state_time_speed.movementEventList[0].timing.startTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[0].state_time_speed.movementEventList[0].timing.nextTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[1].state_time_speed.movementEventList[0].timing.startTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[1].state_time_speed.movementEventList[0].timing.nextTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[2].state_time_speed.movementEventList[0].timing.startTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[2].state_time_speed.movementEventList[0].timing.nextTime", + "message": "DE_TimeMark is missing" + } + ], + "utcTimeStamp": "2024-06-04T16:23:17.082Z", + "schemaVersion": 1, + "recordGeneratedAt": { + "$date": { + "$numberLong": "1717518197089" + } + }, + "revision": 0, + "states": [ + { + "stateTimeSpeed": [ + { + "eventState": "PROTECTED_CLEARANCE", + "timing": { + "minEndTime": "2024-06-04T16:03:19Z", + "maxEndTime": "2024-06-04T16:03:19Z" + } + } + ], + "signalGroup": 2 + }, + { + "stateTimeSpeed": [ + { + "eventState": "STOP_AND_REMAIN", + "timing": { + "minEndTime": "2024-06-04T16:03:22.1Z", + "maxEndTime": "2024-06-04T16:03:22.1Z" + } + } + ], + "signalGroup": 4 + }, + { + "stateTimeSpeed": [ + { + "eventState": "PROTECTED_CLEARANCE", + "timing": { + "minEndTime": "2024-06-04T16:03:19Z", + "maxEndTime": "2024-06-04T16:03:19Z" + } + } + ], + "signalGroup": 6 + } + ], + "messageType": "SPAT", + "cti4501Conformant": false, + "intersectionId": 12109, + "odeReceivedAt": "2024-06-04T16:23:17.082Z", + "originIp": "10.11.81.12", + "status": { + "failureFlash": false, + "noValidSPATisAvailableAtThisTime": false, + "fixedTimeOperation": false, + "standbyOperation": false, + "trafficDependentOperation": false, + "manualControlIsEnabled": false, + "off": false, + "stopTimeIsActivated": false, + "recentChangeInMAPassignedLanesIDsUsed": false, + "recentMAPmessageUpdate": false, + "failureMode": false, + "noValidMAPisAvailableAtThisTime": false, + "signalPriorityIsActive": false, + "preemptIsActive": false + } +},{ + "_id": { + "$oid": "665f3fc18c25216ef4f54035" + }, + "validationMessages": [ + { + "schemaPath": "#/$defs/J2735RoadRegulatorID/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].id.region", + "message": "$.payload.data.intersectionStateList.intersectionStatelist[0].id.region: null found, integer expected" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[0].state_time_speed.movementEventList[0].timing.startTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[0].state_time_speed.movementEventList[0].timing.nextTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[1].state_time_speed.movementEventList[0].timing.startTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[1].state_time_speed.movementEventList[0].timing.nextTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[2].state_time_speed.movementEventList[0].timing.startTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[2].state_time_speed.movementEventList[0].timing.nextTime", + "message": "DE_TimeMark is missing" + } + ], + "utcTimeStamp": "2024-06-04T16:23:17.19Z", + "schemaVersion": 1, + "recordGeneratedAt": { + "$date": { + "$numberLong": "1717518197196" + } + }, + "revision": 0, + "states": [ + { + "stateTimeSpeed": [ + { + "eventState": "PROTECTED_CLEARANCE", + "timing": { + "minEndTime": "2024-06-04T16:03:19Z", + "maxEndTime": "2024-06-04T16:03:19Z" + } + } + ], + "signalGroup": 2 + }, + { + "stateTimeSpeed": [ + { + "eventState": "STOP_AND_REMAIN", + "timing": { + "minEndTime": "2024-06-04T16:03:22.1Z", + "maxEndTime": "2024-06-04T16:03:22.1Z" + } + } + ], + "signalGroup": 4 + }, + { + "stateTimeSpeed": [ + { + "eventState": "PROTECTED_CLEARANCE", + "timing": { + "minEndTime": "2024-06-04T16:03:19Z", + "maxEndTime": "2024-06-04T16:03:19Z" + } + } + ], + "signalGroup": 6 + } + ], + "messageType": "SPAT", + "cti4501Conformant": false, + "intersectionId": 12109, + "odeReceivedAt": "2024-06-04T16:23:17.19Z", + "originIp": "10.11.81.12", + "status": { + "failureFlash": false, + "noValidSPATisAvailableAtThisTime": false, + "fixedTimeOperation": false, + "standbyOperation": false, + "trafficDependentOperation": false, + "manualControlIsEnabled": false, + "off": false, + "stopTimeIsActivated": false, + "recentChangeInMAPassignedLanesIDsUsed": false, + "recentMAPmessageUpdate": false, + "failureMode": false, + "noValidMAPisAvailableAtThisTime": false, + "signalPriorityIsActive": false, + "preemptIsActive": false + } +},{ + "_id": { + "$oid": "665f3fc18c25216ef4f54036" + }, + "validationMessages": [ + { + "schemaPath": "#/$defs/J2735RoadRegulatorID/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].id.region", + "message": "$.payload.data.intersectionStateList.intersectionStatelist[0].id.region: null found, integer expected" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[0].state_time_speed.movementEventList[0].timing.startTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[0].state_time_speed.movementEventList[0].timing.nextTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[1].state_time_speed.movementEventList[0].timing.startTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[1].state_time_speed.movementEventList[0].timing.nextTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[2].state_time_speed.movementEventList[0].timing.startTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[2].state_time_speed.movementEventList[0].timing.nextTime", + "message": "DE_TimeMark is missing" + } + ], + "utcTimeStamp": "2024-06-04T16:23:17.298Z", + "schemaVersion": 1, + "recordGeneratedAt": { + "$date": { + "$numberLong": "1717518197303" + } + }, + "revision": 0, + "states": [ + { + "stateTimeSpeed": [ + { + "eventState": "PROTECTED_CLEARANCE", + "timing": { + "minEndTime": "2024-06-04T16:03:19Z", + "maxEndTime": "2024-06-04T16:03:19Z" + } + } + ], + "signalGroup": 2 + }, + { + "stateTimeSpeed": [ + { + "eventState": "STOP_AND_REMAIN", + "timing": { + "minEndTime": "2024-06-04T16:03:22.1Z", + "maxEndTime": "2024-06-04T16:03:22.1Z" + } + } + ], + "signalGroup": 4 + }, + { + "stateTimeSpeed": [ + { + "eventState": "PROTECTED_CLEARANCE", + "timing": { + "minEndTime": "2024-06-04T16:03:19Z", + "maxEndTime": "2024-06-04T16:03:19Z" + } + } + ], + "signalGroup": 6 + } + ], + "messageType": "SPAT", + "cti4501Conformant": false, + "intersectionId": 12109, + "odeReceivedAt": "2024-06-04T16:23:17.298Z", + "originIp": "10.11.81.12", + "status": { + "failureFlash": false, + "noValidSPATisAvailableAtThisTime": false, + "fixedTimeOperation": false, + "standbyOperation": false, + "trafficDependentOperation": false, + "manualControlIsEnabled": false, + "off": false, + "stopTimeIsActivated": false, + "recentChangeInMAPassignedLanesIDsUsed": false, + "recentMAPmessageUpdate": false, + "failureMode": false, + "noValidMAPisAvailableAtThisTime": false, + "signalPriorityIsActive": false, + "preemptIsActive": false + } +},{ + "_id": { + "$oid": "665f3fc18c25216ef4f54037" + }, + "validationMessages": [ + { + "schemaPath": "#/$defs/J2735RoadRegulatorID/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].id.region", + "message": "$.payload.data.intersectionStateList.intersectionStatelist[0].id.region: null found, integer expected" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[0].state_time_speed.movementEventList[0].timing.startTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[0].state_time_speed.movementEventList[0].timing.nextTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[1].state_time_speed.movementEventList[0].timing.startTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[1].state_time_speed.movementEventList[0].timing.nextTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[2].state_time_speed.movementEventList[0].timing.startTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[2].state_time_speed.movementEventList[0].timing.nextTime", + "message": "DE_TimeMark is missing" + } + ], + "utcTimeStamp": "2024-06-04T16:23:17.405Z", + "schemaVersion": 1, + "recordGeneratedAt": { + "$date": { + "$numberLong": "1717518197412" + } + }, + "revision": 0, + "states": [ + { + "stateTimeSpeed": [ + { + "eventState": "PROTECTED_CLEARANCE", + "timing": { + "minEndTime": "2024-06-04T16:03:19Z", + "maxEndTime": "2024-06-04T16:03:19Z" + } + } + ], + "signalGroup": 2 + }, + { + "stateTimeSpeed": [ + { + "eventState": "STOP_AND_REMAIN", + "timing": { + "minEndTime": "2024-06-04T16:03:22.1Z", + "maxEndTime": "2024-06-04T16:03:22.1Z" + } + } + ], + "signalGroup": 4 + }, + { + "stateTimeSpeed": [ + { + "eventState": "PROTECTED_CLEARANCE", + "timing": { + "minEndTime": "2024-06-04T16:03:19Z", + "maxEndTime": "2024-06-04T16:03:19Z" + } + } + ], + "signalGroup": 6 + } + ], + "messageType": "SPAT", + "cti4501Conformant": false, + "intersectionId": 12109, + "odeReceivedAt": "2024-06-04T16:23:17.405Z", + "originIp": "10.11.81.12", + "status": { + "failureFlash": false, + "noValidSPATisAvailableAtThisTime": false, + "fixedTimeOperation": false, + "standbyOperation": false, + "trafficDependentOperation": false, + "manualControlIsEnabled": false, + "off": false, + "stopTimeIsActivated": false, + "recentChangeInMAPassignedLanesIDsUsed": false, + "recentMAPmessageUpdate": false, + "failureMode": false, + "noValidMAPisAvailableAtThisTime": false, + "signalPriorityIsActive": false, + "preemptIsActive": false + } +},{ + "_id": { + "$oid": "665f3fc18c25216ef4f54038" + }, + "validationMessages": [ + { + "schemaPath": "#/$defs/J2735RoadRegulatorID/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].id.region", + "message": "$.payload.data.intersectionStateList.intersectionStatelist[0].id.region: null found, integer expected" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[0].state_time_speed.movementEventList[0].timing.startTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[0].state_time_speed.movementEventList[0].timing.nextTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[1].state_time_speed.movementEventList[0].timing.startTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[1].state_time_speed.movementEventList[0].timing.nextTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[2].state_time_speed.movementEventList[0].timing.startTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[2].state_time_speed.movementEventList[0].timing.nextTime", + "message": "DE_TimeMark is missing" + } + ], + "utcTimeStamp": "2024-06-04T16:23:17.511Z", + "schemaVersion": 1, + "recordGeneratedAt": { + "$date": { + "$numberLong": "1717518197518" + } + }, + "revision": 0, + "states": [ + { + "stateTimeSpeed": [ + { + "eventState": "PROTECTED_CLEARANCE", + "timing": { + "minEndTime": "2024-06-04T16:03:19Z", + "maxEndTime": "2024-06-04T16:03:19Z" + } + } + ], + "signalGroup": 2 + }, + { + "stateTimeSpeed": [ + { + "eventState": "STOP_AND_REMAIN", + "timing": { + "minEndTime": "2024-06-04T16:03:22.1Z", + "maxEndTime": "2024-06-04T16:03:22.1Z" + } + } + ], + "signalGroup": 4 + }, + { + "stateTimeSpeed": [ + { + "eventState": "PROTECTED_CLEARANCE", + "timing": { + "minEndTime": "2024-06-04T16:03:19Z", + "maxEndTime": "2024-06-04T16:03:19Z" + } + } + ], + "signalGroup": 6 + } + ], + "messageType": "SPAT", + "cti4501Conformant": false, + "intersectionId": 12109, + "odeReceivedAt": "2024-06-04T16:23:17.511Z", + "originIp": "10.11.81.12", + "status": { + "failureFlash": false, + "noValidSPATisAvailableAtThisTime": false, + "fixedTimeOperation": false, + "standbyOperation": false, + "trafficDependentOperation": false, + "manualControlIsEnabled": false, + "off": false, + "stopTimeIsActivated": false, + "recentChangeInMAPassignedLanesIDsUsed": false, + "recentMAPmessageUpdate": false, + "failureMode": false, + "noValidMAPisAvailableAtThisTime": false, + "signalPriorityIsActive": false, + "preemptIsActive": false + } +},{ + "_id": { + "$oid": "665f3fc18c25216ef4f54039" + }, + "validationMessages": [ + { + "schemaPath": "#/$defs/J2735RoadRegulatorID/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].id.region", + "message": "$.payload.data.intersectionStateList.intersectionStatelist[0].id.region: null found, integer expected" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[0].state_time_speed.movementEventList[0].timing.startTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[0].state_time_speed.movementEventList[0].timing.nextTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[1].state_time_speed.movementEventList[0].timing.startTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[1].state_time_speed.movementEventList[0].timing.nextTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[2].state_time_speed.movementEventList[0].timing.startTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[2].state_time_speed.movementEventList[0].timing.nextTime", + "message": "DE_TimeMark is missing" + } + ], + "utcTimeStamp": "2024-06-04T16:23:17.619Z", + "schemaVersion": 1, + "recordGeneratedAt": { + "$date": { + "$numberLong": "1717518197625" + } + }, + "revision": 0, + "states": [ + { + "stateTimeSpeed": [ + { + "eventState": "PROTECTED_CLEARANCE", + "timing": { + "minEndTime": "2024-06-04T16:03:19Z", + "maxEndTime": "2024-06-04T16:03:19Z" + } + } + ], + "signalGroup": 2 + }, + { + "stateTimeSpeed": [ + { + "eventState": "STOP_AND_REMAIN", + "timing": { + "minEndTime": "2024-06-04T16:03:22.1Z", + "maxEndTime": "2024-06-04T16:03:22.1Z" + } + } + ], + "signalGroup": 4 + }, + { + "stateTimeSpeed": [ + { + "eventState": "PROTECTED_CLEARANCE", + "timing": { + "minEndTime": "2024-06-04T16:03:19Z", + "maxEndTime": "2024-06-04T16:03:19Z" + } + } + ], + "signalGroup": 6 + } + ], + "messageType": "SPAT", + "cti4501Conformant": false, + "intersectionId": 12109, + "odeReceivedAt": "2024-06-04T16:23:17.619Z", + "originIp": "10.11.81.12", + "status": { + "failureFlash": false, + "noValidSPATisAvailableAtThisTime": false, + "fixedTimeOperation": false, + "standbyOperation": false, + "trafficDependentOperation": false, + "manualControlIsEnabled": false, + "off": false, + "stopTimeIsActivated": false, + "recentChangeInMAPassignedLanesIDsUsed": false, + "recentMAPmessageUpdate": false, + "failureMode": false, + "noValidMAPisAvailableAtThisTime": false, + "signalPriorityIsActive": false, + "preemptIsActive": false + } +},{ + "_id": { + "$oid": "665f3fc18c25216ef4f5403a" + }, + "validationMessages": [ + { + "schemaPath": "#/$defs/J2735RoadRegulatorID/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].id.region", + "message": "$.payload.data.intersectionStateList.intersectionStatelist[0].id.region: null found, integer expected" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[0].state_time_speed.movementEventList[0].timing.startTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[0].state_time_speed.movementEventList[0].timing.nextTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[1].state_time_speed.movementEventList[0].timing.startTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[1].state_time_speed.movementEventList[0].timing.nextTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[2].state_time_speed.movementEventList[0].timing.startTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[2].state_time_speed.movementEventList[0].timing.nextTime", + "message": "DE_TimeMark is missing" + } + ], + "utcTimeStamp": "2024-06-04T16:23:17.726Z", + "schemaVersion": 1, + "recordGeneratedAt": { + "$date": { + "$numberLong": "1717518197731" + } + }, + "revision": 0, + "states": [ + { + "stateTimeSpeed": [ + { + "eventState": "PROTECTED_CLEARANCE", + "timing": { + "minEndTime": "2024-06-04T16:03:19Z", + "maxEndTime": "2024-06-04T16:03:19Z" + } + } + ], + "signalGroup": 2 + }, + { + "stateTimeSpeed": [ + { + "eventState": "STOP_AND_REMAIN", + "timing": { + "minEndTime": "2024-06-04T16:03:22.1Z", + "maxEndTime": "2024-06-04T16:03:22.1Z" + } + } + ], + "signalGroup": 4 + }, + { + "stateTimeSpeed": [ + { + "eventState": "PROTECTED_CLEARANCE", + "timing": { + "minEndTime": "2024-06-04T16:03:19Z", + "maxEndTime": "2024-06-04T16:03:19Z" + } + } + ], + "signalGroup": 6 + } + ], + "messageType": "SPAT", + "cti4501Conformant": false, + "intersectionId": 12109, + "odeReceivedAt": "2024-06-04T16:23:17.726Z", + "originIp": "10.11.81.12", + "status": { + "failureFlash": false, + "noValidSPATisAvailableAtThisTime": false, + "fixedTimeOperation": false, + "standbyOperation": false, + "trafficDependentOperation": false, + "manualControlIsEnabled": false, + "off": false, + "stopTimeIsActivated": false, + "recentChangeInMAPassignedLanesIDsUsed": false, + "recentMAPmessageUpdate": false, + "failureMode": false, + "noValidMAPisAvailableAtThisTime": false, + "signalPriorityIsActive": false, + "preemptIsActive": false + } +},{ + "_id": { + "$oid": "665f3fc18c25216ef4f5403b" + }, + "validationMessages": [ + { + "schemaPath": "#/$defs/J2735RoadRegulatorID/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].id.region", + "message": "$.payload.data.intersectionStateList.intersectionStatelist[0].id.region: null found, integer expected" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[0].state_time_speed.movementEventList[0].timing.startTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[0].state_time_speed.movementEventList[0].timing.nextTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[1].state_time_speed.movementEventList[0].timing.startTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[1].state_time_speed.movementEventList[0].timing.nextTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[2].state_time_speed.movementEventList[0].timing.startTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[2].state_time_speed.movementEventList[0].timing.nextTime", + "message": "DE_TimeMark is missing" + } + ], + "utcTimeStamp": "2024-06-04T16:23:17.834Z", + "schemaVersion": 1, + "recordGeneratedAt": { + "$date": { + "$numberLong": "1717518197838" + } + }, + "revision": 0, + "states": [ + { + "stateTimeSpeed": [ + { + "eventState": "PROTECTED_CLEARANCE", + "timing": { + "minEndTime": "2024-06-04T16:03:19Z", + "maxEndTime": "2024-06-04T16:03:19Z" + } + } + ], + "signalGroup": 2 + }, + { + "stateTimeSpeed": [ + { + "eventState": "STOP_AND_REMAIN", + "timing": { + "minEndTime": "2024-06-04T16:03:22.1Z", + "maxEndTime": "2024-06-04T16:03:22.1Z" + } + } + ], + "signalGroup": 4 + }, + { + "stateTimeSpeed": [ + { + "eventState": "PROTECTED_CLEARANCE", + "timing": { + "minEndTime": "2024-06-04T16:03:19Z", + "maxEndTime": "2024-06-04T16:03:19Z" + } + } + ], + "signalGroup": 6 + } + ], + "messageType": "SPAT", + "cti4501Conformant": false, + "intersectionId": 12109, + "odeReceivedAt": "2024-06-04T16:23:17.834Z", + "originIp": "10.11.81.12", + "status": { + "failureFlash": false, + "noValidSPATisAvailableAtThisTime": false, + "fixedTimeOperation": false, + "standbyOperation": false, + "trafficDependentOperation": false, + "manualControlIsEnabled": false, + "off": false, + "stopTimeIsActivated": false, + "recentChangeInMAPassignedLanesIDsUsed": false, + "recentMAPmessageUpdate": false, + "failureMode": false, + "noValidMAPisAvailableAtThisTime": false, + "signalPriorityIsActive": false, + "preemptIsActive": false + } +},{ + "_id": { + "$oid": "665f3fc18c25216ef4f5403c" + }, + "validationMessages": [ + { + "schemaPath": "#/$defs/J2735RoadRegulatorID/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].id.region", + "message": "$.payload.data.intersectionStateList.intersectionStatelist[0].id.region: null found, integer expected" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[0].state_time_speed.movementEventList[0].timing.startTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[0].state_time_speed.movementEventList[0].timing.nextTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[1].state_time_speed.movementEventList[0].timing.startTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[1].state_time_speed.movementEventList[0].timing.nextTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[2].state_time_speed.movementEventList[0].timing.startTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[2].state_time_speed.movementEventList[0].timing.nextTime", + "message": "DE_TimeMark is missing" + } + ], + "utcTimeStamp": "2024-06-04T16:23:17.941Z", + "schemaVersion": 1, + "recordGeneratedAt": { + "$date": { + "$numberLong": "1717518197944" + } + }, + "revision": 0, + "states": [ + { + "stateTimeSpeed": [ + { + "eventState": "PROTECTED_CLEARANCE", + "timing": { + "minEndTime": "2024-06-04T16:03:19Z", + "maxEndTime": "2024-06-04T16:03:19Z" + } + } + ], + "signalGroup": 2 + }, + { + "stateTimeSpeed": [ + { + "eventState": "STOP_AND_REMAIN", + "timing": { + "minEndTime": "2024-06-04T16:03:22.1Z", + "maxEndTime": "2024-06-04T16:03:22.1Z" + } + } + ], + "signalGroup": 4 + }, + { + "stateTimeSpeed": [ + { + "eventState": "PROTECTED_CLEARANCE", + "timing": { + "minEndTime": "2024-06-04T16:03:19Z", + "maxEndTime": "2024-06-04T16:03:19Z" + } + } + ], + "signalGroup": 6 + } + ], + "messageType": "SPAT", + "cti4501Conformant": false, + "intersectionId": 12109, + "odeReceivedAt": "2024-06-04T16:23:17.941Z", + "originIp": "10.11.81.12", + "status": { + "failureFlash": false, + "noValidSPATisAvailableAtThisTime": false, + "fixedTimeOperation": false, + "standbyOperation": false, + "trafficDependentOperation": false, + "manualControlIsEnabled": false, + "off": false, + "stopTimeIsActivated": false, + "recentChangeInMAPassignedLanesIDsUsed": false, + "recentMAPmessageUpdate": false, + "failureMode": false, + "noValidMAPisAvailableAtThisTime": false, + "signalPriorityIsActive": false, + "preemptIsActive": false + } +},{ + "_id": { + "$oid": "665f3fc18c25216ef4f5403d" + }, + "validationMessages": [ + { + "schemaPath": "#/$defs/J2735RoadRegulatorID/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].id.region", + "message": "$.payload.data.intersectionStateList.intersectionStatelist[0].id.region: null found, integer expected" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[0].state_time_speed.movementEventList[0].timing.startTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[0].state_time_speed.movementEventList[0].timing.nextTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[1].state_time_speed.movementEventList[0].timing.startTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[1].state_time_speed.movementEventList[0].timing.nextTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[2].state_time_speed.movementEventList[0].timing.startTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[2].state_time_speed.movementEventList[0].timing.nextTime", + "message": "DE_TimeMark is missing" + } + ], + "utcTimeStamp": "2024-06-04T16:23:18.034Z", + "schemaVersion": 1, + "recordGeneratedAt": { + "$date": { + "$numberLong": "1717518198035" + } + }, + "revision": 0, + "states": [ + { + "stateTimeSpeed": [ + { + "eventState": "PROTECTED_CLEARANCE", + "timing": { + "minEndTime": "2024-06-04T16:03:18Z", + "maxEndTime": "2024-06-04T16:03:18Z" + } + } + ], + "signalGroup": 2 + }, + { + "stateTimeSpeed": [ + { + "eventState": "STOP_AND_REMAIN", + "timing": { + "minEndTime": "2024-06-04T16:03:21.1Z", + "maxEndTime": "2024-06-04T16:03:21.1Z" + } + } + ], + "signalGroup": 4 + }, + { + "stateTimeSpeed": [ + { + "eventState": "PROTECTED_CLEARANCE", + "timing": { + "minEndTime": "2024-06-04T16:03:18Z", + "maxEndTime": "2024-06-04T16:03:18Z" + } + } + ], + "signalGroup": 6 + } + ], + "messageType": "SPAT", + "cti4501Conformant": false, + "intersectionId": 12109, + "odeReceivedAt": "2024-06-04T16:23:18.034Z", + "originIp": "10.11.81.12", + "status": { + "failureFlash": false, + "noValidSPATisAvailableAtThisTime": false, + "fixedTimeOperation": false, + "standbyOperation": false, + "trafficDependentOperation": false, + "manualControlIsEnabled": false, + "off": false, + "stopTimeIsActivated": false, + "recentChangeInMAPassignedLanesIDsUsed": false, + "recentMAPmessageUpdate": false, + "failureMode": false, + "noValidMAPisAvailableAtThisTime": false, + "signalPriorityIsActive": false, + "preemptIsActive": false + } +},{ + "_id": { + "$oid": "665f3fc18c25216ef4f5403e" + }, + "validationMessages": [ + { + "schemaPath": "#/$defs/J2735RoadRegulatorID/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].id.region", + "message": "$.payload.data.intersectionStateList.intersectionStatelist[0].id.region: null found, integer expected" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[0].state_time_speed.movementEventList[0].timing.startTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[0].state_time_speed.movementEventList[0].timing.nextTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[1].state_time_speed.movementEventList[0].timing.startTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[1].state_time_speed.movementEventList[0].timing.nextTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[2].state_time_speed.movementEventList[0].timing.startTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[2].state_time_speed.movementEventList[0].timing.nextTime", + "message": "DE_TimeMark is missing" + } + ], + "utcTimeStamp": "2024-06-04T16:23:18.143Z", + "schemaVersion": 1, + "recordGeneratedAt": { + "$date": { + "$numberLong": "1717518198158" + } + }, + "revision": 0, + "states": [ + { + "stateTimeSpeed": [ + { + "eventState": "PROTECTED_CLEARANCE", + "timing": { + "minEndTime": "2024-06-04T16:03:18Z", + "maxEndTime": "2024-06-04T16:03:18Z" + } + } + ], + "signalGroup": 2 + }, + { + "stateTimeSpeed": [ + { + "eventState": "STOP_AND_REMAIN", + "timing": { + "minEndTime": "2024-06-04T16:03:21.1Z", + "maxEndTime": "2024-06-04T16:03:21.1Z" + } + } + ], + "signalGroup": 4 + }, + { + "stateTimeSpeed": [ + { + "eventState": "PROTECTED_CLEARANCE", + "timing": { + "minEndTime": "2024-06-04T16:03:18Z", + "maxEndTime": "2024-06-04T16:03:18Z" + } + } + ], + "signalGroup": 6 + } + ], + "messageType": "SPAT", + "cti4501Conformant": false, + "intersectionId": 12109, + "odeReceivedAt": "2024-06-04T16:23:18.143Z", + "originIp": "10.11.81.12", + "status": { + "failureFlash": false, + "noValidSPATisAvailableAtThisTime": false, + "fixedTimeOperation": false, + "standbyOperation": false, + "trafficDependentOperation": false, + "manualControlIsEnabled": false, + "off": false, + "stopTimeIsActivated": false, + "recentChangeInMAPassignedLanesIDsUsed": false, + "recentMAPmessageUpdate": false, + "failureMode": false, + "noValidMAPisAvailableAtThisTime": false, + "signalPriorityIsActive": false, + "preemptIsActive": false + } +},{ + "_id": { + "$oid": "665f3fc18c25216ef4f5403f" + }, + "validationMessages": [ + { + "schemaPath": "#/$defs/J2735RoadRegulatorID/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].id.region", + "message": "$.payload.data.intersectionStateList.intersectionStatelist[0].id.region: null found, integer expected" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[0].state_time_speed.movementEventList[0].timing.startTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[0].state_time_speed.movementEventList[0].timing.nextTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[1].state_time_speed.movementEventList[0].timing.startTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[1].state_time_speed.movementEventList[0].timing.nextTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[2].state_time_speed.movementEventList[0].timing.startTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[2].state_time_speed.movementEventList[0].timing.nextTime", + "message": "DE_TimeMark is missing" + } + ], + "utcTimeStamp": "2024-06-04T16:23:26.394Z", + "schemaVersion": 1, + "recordGeneratedAt": { + "$date": { + "$numberLong": "1717518206575" + } + }, + "revision": 0, + "states": [ + { + "stateTimeSpeed": [ + { + "eventState": "PROTECTED_CLEARANCE", + "timing": { + "minEndTime": "2024-06-04T16:03:17Z", + "maxEndTime": "2024-06-04T16:03:17Z" + } + } + ], + "signalGroup": 2 + }, + { + "stateTimeSpeed": [ + { + "eventState": "STOP_AND_REMAIN", + "timing": { + "minEndTime": "2024-06-04T16:03:20.1Z", + "maxEndTime": "2024-06-04T16:03:20.1Z" + } + } + ], + "signalGroup": 4 + }, + { + "stateTimeSpeed": [ + { + "eventState": "PROTECTED_CLEARANCE", + "timing": { + "minEndTime": "2024-06-04T16:03:17Z", + "maxEndTime": "2024-06-04T16:03:17Z" + } + } + ], + "signalGroup": 6 + } + ], + "messageType": "SPAT", + "cti4501Conformant": false, + "intersectionId": 12109, + "odeReceivedAt": "2024-06-04T16:23:26.394Z", + "originIp": "10.11.81.12", + "status": { + "failureFlash": false, + "noValidSPATisAvailableAtThisTime": false, + "fixedTimeOperation": false, + "standbyOperation": false, + "trafficDependentOperation": false, + "manualControlIsEnabled": false, + "off": false, + "stopTimeIsActivated": false, + "recentChangeInMAPassignedLanesIDsUsed": false, + "recentMAPmessageUpdate": false, + "failureMode": false, + "noValidMAPisAvailableAtThisTime": false, + "signalPriorityIsActive": false, + "preemptIsActive": false + } +},{ + "_id": { + "$oid": "665f3fc18c25216ef4f54040" + }, + "validationMessages": [ + { + "schemaPath": "#/$defs/J2735RoadRegulatorID/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].id.region", + "message": "$.payload.data.intersectionStateList.intersectionStatelist[0].id.region: null found, integer expected" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[0].state_time_speed.movementEventList[0].timing.startTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[0].state_time_speed.movementEventList[0].timing.nextTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[1].state_time_speed.movementEventList[0].timing.startTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[1].state_time_speed.movementEventList[0].timing.nextTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[2].state_time_speed.movementEventList[0].timing.startTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[2].state_time_speed.movementEventList[0].timing.nextTime", + "message": "DE_TimeMark is missing" + } + ], + "utcTimeStamp": "2024-06-04T16:23:26.286Z", + "schemaVersion": 1, + "recordGeneratedAt": { + "$date": { + "$numberLong": "1717518206575" + } + }, + "revision": 0, + "states": [ + { + "stateTimeSpeed": [ + { + "eventState": "PROTECTED_CLEARANCE", + "timing": { + "minEndTime": "2024-06-04T16:03:17Z", + "maxEndTime": "2024-06-04T16:03:17Z" + } + } + ], + "signalGroup": 2 + }, + { + "stateTimeSpeed": [ + { + "eventState": "STOP_AND_REMAIN", + "timing": { + "minEndTime": "2024-06-04T16:03:20.1Z", + "maxEndTime": "2024-06-04T16:03:20.1Z" + } + } + ], + "signalGroup": 4 + }, + { + "stateTimeSpeed": [ + { + "eventState": "PROTECTED_CLEARANCE", + "timing": { + "minEndTime": "2024-06-04T16:03:17Z", + "maxEndTime": "2024-06-04T16:03:17Z" + } + } + ], + "signalGroup": 6 + } + ], + "messageType": "SPAT", + "cti4501Conformant": false, + "intersectionId": 12109, + "odeReceivedAt": "2024-06-04T16:23:26.286Z", + "originIp": "10.11.81.12", + "status": { + "failureFlash": false, + "noValidSPATisAvailableAtThisTime": false, + "fixedTimeOperation": false, + "standbyOperation": false, + "trafficDependentOperation": false, + "manualControlIsEnabled": false, + "off": false, + "stopTimeIsActivated": false, + "recentChangeInMAPassignedLanesIDsUsed": false, + "recentMAPmessageUpdate": false, + "failureMode": false, + "noValidMAPisAvailableAtThisTime": false, + "signalPriorityIsActive": false, + "preemptIsActive": false + } +},{ + "_id": { + "$oid": "665f3fc18c25216ef4f54041" + }, + "validationMessages": [ + { + "schemaPath": "#/$defs/J2735RoadRegulatorID/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].id.region", + "message": "$.payload.data.intersectionStateList.intersectionStatelist[0].id.region: null found, integer expected" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[0].state_time_speed.movementEventList[0].timing.startTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[0].state_time_speed.movementEventList[0].timing.nextTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[1].state_time_speed.movementEventList[0].timing.startTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[1].state_time_speed.movementEventList[0].timing.nextTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[2].state_time_speed.movementEventList[0].timing.startTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[2].state_time_speed.movementEventList[0].timing.nextTime", + "message": "DE_TimeMark is missing" + } + ], + "utcTimeStamp": "2024-06-04T16:23:26.177Z", + "schemaVersion": 1, + "recordGeneratedAt": { + "$date": { + "$numberLong": "1717518206575" + } + }, + "revision": 0, + "states": [ + { + "stateTimeSpeed": [ + { + "eventState": "PROTECTED_CLEARANCE", + "timing": { + "minEndTime": "2024-06-04T16:03:17Z", + "maxEndTime": "2024-06-04T16:03:17Z" + } + } + ], + "signalGroup": 2 + }, + { + "stateTimeSpeed": [ + { + "eventState": "STOP_AND_REMAIN", + "timing": { + "minEndTime": "2024-06-04T16:03:20.1Z", + "maxEndTime": "2024-06-04T16:03:20.1Z" + } + } + ], + "signalGroup": 4 + }, + { + "stateTimeSpeed": [ + { + "eventState": "PROTECTED_CLEARANCE", + "timing": { + "minEndTime": "2024-06-04T16:03:17Z", + "maxEndTime": "2024-06-04T16:03:17Z" + } + } + ], + "signalGroup": 6 + } + ], + "messageType": "SPAT", + "cti4501Conformant": false, + "intersectionId": 12109, + "odeReceivedAt": "2024-06-04T16:23:26.177Z", + "originIp": "10.11.81.12", + "status": { + "failureFlash": false, + "noValidSPATisAvailableAtThisTime": false, + "fixedTimeOperation": false, + "standbyOperation": false, + "trafficDependentOperation": false, + "manualControlIsEnabled": false, + "off": false, + "stopTimeIsActivated": false, + "recentChangeInMAPassignedLanesIDsUsed": false, + "recentMAPmessageUpdate": false, + "failureMode": false, + "noValidMAPisAvailableAtThisTime": false, + "signalPriorityIsActive": false, + "preemptIsActive": false + } +},{ + "_id": { + "$oid": "665f3fc18c25216ef4f54042" + }, + "validationMessages": [ + { + "schemaPath": "#/$defs/J2735RoadRegulatorID/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].id.region", + "message": "$.payload.data.intersectionStateList.intersectionStatelist[0].id.region: null found, integer expected" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[0].state_time_speed.movementEventList[0].timing.startTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[0].state_time_speed.movementEventList[0].timing.nextTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[1].state_time_speed.movementEventList[0].timing.startTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[1].state_time_speed.movementEventList[0].timing.nextTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[2].state_time_speed.movementEventList[0].timing.startTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[2].state_time_speed.movementEventList[0].timing.nextTime", + "message": "DE_TimeMark is missing" + } + ], + "utcTimeStamp": "2024-06-04T16:23:26.501Z", + "schemaVersion": 1, + "recordGeneratedAt": { + "$date": { + "$numberLong": "1717518206604" + } + }, + "revision": 0, + "states": [ + { + "stateTimeSpeed": [ + { + "eventState": "PROTECTED_CLEARANCE", + "timing": { + "minEndTime": "2024-06-04T16:03:17Z", + "maxEndTime": "2024-06-04T16:03:17Z" + } + } + ], + "signalGroup": 2 + }, + { + "stateTimeSpeed": [ + { + "eventState": "STOP_AND_REMAIN", + "timing": { + "minEndTime": "2024-06-04T16:03:20.1Z", + "maxEndTime": "2024-06-04T16:03:20.1Z" + } + } + ], + "signalGroup": 4 + }, + { + "stateTimeSpeed": [ + { + "eventState": "PROTECTED_CLEARANCE", + "timing": { + "minEndTime": "2024-06-04T16:03:17Z", + "maxEndTime": "2024-06-04T16:03:17Z" + } + } + ], + "signalGroup": 6 + } + ], + "messageType": "SPAT", + "cti4501Conformant": false, + "intersectionId": 12109, + "odeReceivedAt": "2024-06-04T16:23:26.501Z", + "originIp": "10.11.81.12", + "status": { + "failureFlash": false, + "noValidSPATisAvailableAtThisTime": false, + "fixedTimeOperation": false, + "standbyOperation": false, + "trafficDependentOperation": false, + "manualControlIsEnabled": false, + "off": false, + "stopTimeIsActivated": false, + "recentChangeInMAPassignedLanesIDsUsed": false, + "recentMAPmessageUpdate": false, + "failureMode": false, + "noValidMAPisAvailableAtThisTime": false, + "signalPriorityIsActive": false, + "preemptIsActive": false + } +},{ + "_id": { + "$oid": "665f3fc18c25216ef4f54043" + }, + "validationMessages": [ + { + "schemaPath": "#/$defs/J2735RoadRegulatorID/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].id.region", + "message": "$.payload.data.intersectionStateList.intersectionStatelist[0].id.region: null found, integer expected" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[0].state_time_speed.movementEventList[0].timing.startTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[0].state_time_speed.movementEventList[0].timing.nextTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[1].state_time_speed.movementEventList[0].timing.startTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[1].state_time_speed.movementEventList[0].timing.nextTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[2].state_time_speed.movementEventList[0].timing.startTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[2].state_time_speed.movementEventList[0].timing.nextTime", + "message": "DE_TimeMark is missing" + } + ], + "utcTimeStamp": "2024-06-04T16:23:26.608Z", + "schemaVersion": 1, + "recordGeneratedAt": { + "$date": { + "$numberLong": "1717518206619" + } + }, + "revision": 0, + "states": [ + { + "stateTimeSpeed": [ + { + "eventState": "PROTECTED_CLEARANCE", + "timing": { + "minEndTime": "2024-06-04T16:03:17Z", + "maxEndTime": "2024-06-04T16:03:17Z" + } + } + ], + "signalGroup": 2 + }, + { + "stateTimeSpeed": [ + { + "eventState": "STOP_AND_REMAIN", + "timing": { + "minEndTime": "2024-06-04T16:03:20.1Z", + "maxEndTime": "2024-06-04T16:03:20.1Z" + } + } + ], + "signalGroup": 4 + }, + { + "stateTimeSpeed": [ + { + "eventState": "PROTECTED_CLEARANCE", + "timing": { + "minEndTime": "2024-06-04T16:03:17Z", + "maxEndTime": "2024-06-04T16:03:17Z" + } + } + ], + "signalGroup": 6 + } + ], + "messageType": "SPAT", + "cti4501Conformant": false, + "intersectionId": 12109, + "odeReceivedAt": "2024-06-04T16:23:26.608Z", + "originIp": "10.11.81.12", + "status": { + "failureFlash": false, + "noValidSPATisAvailableAtThisTime": false, + "fixedTimeOperation": false, + "standbyOperation": false, + "trafficDependentOperation": false, + "manualControlIsEnabled": false, + "off": false, + "stopTimeIsActivated": false, + "recentChangeInMAPassignedLanesIDsUsed": false, + "recentMAPmessageUpdate": false, + "failureMode": false, + "noValidMAPisAvailableAtThisTime": false, + "signalPriorityIsActive": false, + "preemptIsActive": false + } +},{ + "_id": { + "$oid": "665f3fc18c25216ef4f54044" + }, + "validationMessages": [ + { + "schemaPath": "#/$defs/J2735RoadRegulatorID/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].id.region", + "message": "$.payload.data.intersectionStateList.intersectionStatelist[0].id.region: null found, integer expected" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[0].state_time_speed.movementEventList[0].timing.startTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[0].state_time_speed.movementEventList[0].timing.nextTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[1].state_time_speed.movementEventList[0].timing.startTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[1].state_time_speed.movementEventList[0].timing.nextTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[2].state_time_speed.movementEventList[0].timing.startTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[2].state_time_speed.movementEventList[0].timing.nextTime", + "message": "DE_TimeMark is missing" + } + ], + "utcTimeStamp": "2024-06-04T16:23:26.716Z", + "schemaVersion": 1, + "recordGeneratedAt": { + "$date": { + "$numberLong": "1717518206727" + } + }, + "revision": 0, + "states": [ + { + "stateTimeSpeed": [ + { + "eventState": "PROTECTED_CLEARANCE", + "timing": { + "minEndTime": "2024-06-04T16:03:17Z", + "maxEndTime": "2024-06-04T16:03:17Z" + } + } + ], + "signalGroup": 2 + }, + { + "stateTimeSpeed": [ + { + "eventState": "STOP_AND_REMAIN", + "timing": { + "minEndTime": "2024-06-04T16:03:20.1Z", + "maxEndTime": "2024-06-04T16:03:20.1Z" + } + } + ], + "signalGroup": 4 + }, + { + "stateTimeSpeed": [ + { + "eventState": "PROTECTED_CLEARANCE", + "timing": { + "minEndTime": "2024-06-04T16:03:17Z", + "maxEndTime": "2024-06-04T16:03:17Z" + } + } + ], + "signalGroup": 6 + } + ], + "messageType": "SPAT", + "cti4501Conformant": false, + "intersectionId": 12109, + "odeReceivedAt": "2024-06-04T16:23:26.716Z", + "originIp": "10.11.81.12", + "status": { + "failureFlash": false, + "noValidSPATisAvailableAtThisTime": false, + "fixedTimeOperation": false, + "standbyOperation": false, + "trafficDependentOperation": false, + "manualControlIsEnabled": false, + "off": false, + "stopTimeIsActivated": false, + "recentChangeInMAPassignedLanesIDsUsed": false, + "recentMAPmessageUpdate": false, + "failureMode": false, + "noValidMAPisAvailableAtThisTime": false, + "signalPriorityIsActive": false, + "preemptIsActive": false + } +},{ + "_id": { + "$oid": "665f3fc18c25216ef4f54045" + }, + "validationMessages": [ + { + "schemaPath": "#/$defs/J2735RoadRegulatorID/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].id.region", + "message": "$.payload.data.intersectionStateList.intersectionStatelist[0].id.region: null found, integer expected" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[0].state_time_speed.movementEventList[0].timing.startTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[0].state_time_speed.movementEventList[0].timing.nextTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[1].state_time_speed.movementEventList[0].timing.startTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[1].state_time_speed.movementEventList[0].timing.nextTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[2].state_time_speed.movementEventList[0].timing.startTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[2].state_time_speed.movementEventList[0].timing.nextTime", + "message": "DE_TimeMark is missing" + } + ], + "utcTimeStamp": "2024-06-04T16:23:26.823Z", + "schemaVersion": 1, + "recordGeneratedAt": { + "$date": { + "$numberLong": "1717518206834" + } + }, + "revision": 0, + "states": [ + { + "stateTimeSpeed": [ + { + "eventState": "PROTECTED_CLEARANCE", + "timing": { + "minEndTime": "2024-06-04T16:03:17Z", + "maxEndTime": "2024-06-04T16:03:17Z" + } + } + ], + "signalGroup": 2 + }, + { + "stateTimeSpeed": [ + { + "eventState": "STOP_AND_REMAIN", + "timing": { + "minEndTime": "2024-06-04T16:03:20.1Z", + "maxEndTime": "2024-06-04T16:03:20.1Z" + } + } + ], + "signalGroup": 4 + }, + { + "stateTimeSpeed": [ + { + "eventState": "PROTECTED_CLEARANCE", + "timing": { + "minEndTime": "2024-06-04T16:03:17Z", + "maxEndTime": "2024-06-04T16:03:17Z" + } + } + ], + "signalGroup": 6 + } + ], + "messageType": "SPAT", + "cti4501Conformant": false, + "intersectionId": 12109, + "odeReceivedAt": "2024-06-04T16:23:26.823Z", + "originIp": "10.11.81.12", + "status": { + "failureFlash": false, + "noValidSPATisAvailableAtThisTime": false, + "fixedTimeOperation": false, + "standbyOperation": false, + "trafficDependentOperation": false, + "manualControlIsEnabled": false, + "off": false, + "stopTimeIsActivated": false, + "recentChangeInMAPassignedLanesIDsUsed": false, + "recentMAPmessageUpdate": false, + "failureMode": false, + "noValidMAPisAvailableAtThisTime": false, + "signalPriorityIsActive": false, + "preemptIsActive": false + } +},{ + "_id": { + "$oid": "665f3fc18c25216ef4f54046" + }, + "validationMessages": [ + { + "schemaPath": "#/$defs/J2735RoadRegulatorID/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].id.region", + "message": "$.payload.data.intersectionStateList.intersectionStatelist[0].id.region: null found, integer expected" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[0].state_time_speed.movementEventList[0].timing.startTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[0].state_time_speed.movementEventList[0].timing.nextTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[1].state_time_speed.movementEventList[0].timing.startTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[1].state_time_speed.movementEventList[0].timing.nextTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[2].state_time_speed.movementEventList[0].timing.startTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[2].state_time_speed.movementEventList[0].timing.nextTime", + "message": "DE_TimeMark is missing" + } + ], + "utcTimeStamp": "2024-06-04T16:23:26.931Z", + "schemaVersion": 1, + "recordGeneratedAt": { + "$date": { + "$numberLong": "1717518206942" + } + }, + "revision": 0, + "states": [ + { + "stateTimeSpeed": [ + { + "eventState": "PROTECTED_CLEARANCE", + "timing": { + "minEndTime": "2024-06-04T16:03:17Z", + "maxEndTime": "2024-06-04T16:03:17Z" + } + } + ], + "signalGroup": 2 + }, + { + "stateTimeSpeed": [ + { + "eventState": "STOP_AND_REMAIN", + "timing": { + "minEndTime": "2024-06-04T16:03:20.1Z", + "maxEndTime": "2024-06-04T16:03:20.1Z" + } + } + ], + "signalGroup": 4 + }, + { + "stateTimeSpeed": [ + { + "eventState": "PROTECTED_CLEARANCE", + "timing": { + "minEndTime": "2024-06-04T16:03:17Z", + "maxEndTime": "2024-06-04T16:03:17Z" + } + } + ], + "signalGroup": 6 + } + ], + "messageType": "SPAT", + "cti4501Conformant": false, + "intersectionId": 12109, + "odeReceivedAt": "2024-06-04T16:23:26.931Z", + "originIp": "10.11.81.12", + "status": { + "failureFlash": false, + "noValidSPATisAvailableAtThisTime": false, + "fixedTimeOperation": false, + "standbyOperation": false, + "trafficDependentOperation": false, + "manualControlIsEnabled": false, + "off": false, + "stopTimeIsActivated": false, + "recentChangeInMAPassignedLanesIDsUsed": false, + "recentMAPmessageUpdate": false, + "failureMode": false, + "noValidMAPisAvailableAtThisTime": false, + "signalPriorityIsActive": false, + "preemptIsActive": false + } +},{ + "_id": { + "$oid": "665f3fc18c25216ef4f54047" + }, + "validationMessages": [ + { + "schemaPath": "#/$defs/J2735RoadRegulatorID/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].id.region", + "message": "$.payload.data.intersectionStateList.intersectionStatelist[0].id.region: null found, integer expected" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[0].state_time_speed.movementEventList[0].timing.startTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[0].state_time_speed.movementEventList[0].timing.nextTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[1].state_time_speed.movementEventList[0].timing.startTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[1].state_time_speed.movementEventList[0].timing.nextTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[2].state_time_speed.movementEventList[0].timing.startTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[2].state_time_speed.movementEventList[0].timing.nextTime", + "message": "DE_TimeMark is missing" + } + ], + "utcTimeStamp": "2024-06-04T16:23:27.037Z", + "schemaVersion": 1, + "recordGeneratedAt": { + "$date": { + "$numberLong": "1717518207049" + } + }, + "revision": 0, + "states": [ + { + "stateTimeSpeed": [ + { + "eventState": "PROTECTED_CLEARANCE", + "timing": { + "minEndTime": "2024-06-04T16:03:19Z", + "maxEndTime": "2024-06-04T16:03:19Z" + } + } + ], + "signalGroup": 2 + }, + { + "stateTimeSpeed": [ + { + "eventState": "STOP_AND_REMAIN", + "timing": { + "minEndTime": "2024-06-04T16:03:22.1Z", + "maxEndTime": "2024-06-04T16:03:22.1Z" + } + } + ], + "signalGroup": 4 + }, + { + "stateTimeSpeed": [ + { + "eventState": "PROTECTED_CLEARANCE", + "timing": { + "minEndTime": "2024-06-04T16:03:19Z", + "maxEndTime": "2024-06-04T16:03:19Z" + } + } + ], + "signalGroup": 6 + } + ], + "messageType": "SPAT", + "cti4501Conformant": false, + "intersectionId": 12109, + "odeReceivedAt": "2024-06-04T16:23:27.037Z", + "originIp": "10.11.81.12", + "status": { + "failureFlash": false, + "noValidSPATisAvailableAtThisTime": false, + "fixedTimeOperation": false, + "standbyOperation": false, + "trafficDependentOperation": false, + "manualControlIsEnabled": false, + "off": false, + "stopTimeIsActivated": false, + "recentChangeInMAPassignedLanesIDsUsed": false, + "recentMAPmessageUpdate": false, + "failureMode": false, + "noValidMAPisAvailableAtThisTime": false, + "signalPriorityIsActive": false, + "preemptIsActive": false + } +},{ + "_id": { + "$oid": "665f3fc18c25216ef4f54048" + }, + "validationMessages": [ + { + "schemaPath": "#/$defs/J2735RoadRegulatorID/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].id.region", + "message": "$.payload.data.intersectionStateList.intersectionStatelist[0].id.region: null found, integer expected" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[0].state_time_speed.movementEventList[0].timing.startTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[0].state_time_speed.movementEventList[0].timing.nextTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[1].state_time_speed.movementEventList[0].timing.startTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[1].state_time_speed.movementEventList[0].timing.nextTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[2].state_time_speed.movementEventList[0].timing.startTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[2].state_time_speed.movementEventList[0].timing.nextTime", + "message": "DE_TimeMark is missing" + } + ], + "utcTimeStamp": "2024-06-04T16:23:27.144Z", + "schemaVersion": 1, + "recordGeneratedAt": { + "$date": { + "$numberLong": "1717518207154" + } + }, + "revision": 0, + "states": [ + { + "stateTimeSpeed": [ + { + "eventState": "PROTECTED_CLEARANCE", + "timing": { + "minEndTime": "2024-06-04T16:03:19Z", + "maxEndTime": "2024-06-04T16:03:19Z" + } + } + ], + "signalGroup": 2 + }, + { + "stateTimeSpeed": [ + { + "eventState": "STOP_AND_REMAIN", + "timing": { + "minEndTime": "2024-06-04T16:03:22.1Z", + "maxEndTime": "2024-06-04T16:03:22.1Z" + } + } + ], + "signalGroup": 4 + }, + { + "stateTimeSpeed": [ + { + "eventState": "PROTECTED_CLEARANCE", + "timing": { + "minEndTime": "2024-06-04T16:03:19Z", + "maxEndTime": "2024-06-04T16:03:19Z" + } + } + ], + "signalGroup": 6 + } + ], + "messageType": "SPAT", + "cti4501Conformant": false, + "intersectionId": 12109, + "odeReceivedAt": "2024-06-04T16:23:27.144Z", + "originIp": "10.11.81.12", + "status": { + "failureFlash": false, + "noValidSPATisAvailableAtThisTime": false, + "fixedTimeOperation": false, + "standbyOperation": false, + "trafficDependentOperation": false, + "manualControlIsEnabled": false, + "off": false, + "stopTimeIsActivated": false, + "recentChangeInMAPassignedLanesIDsUsed": false, + "recentMAPmessageUpdate": false, + "failureMode": false, + "noValidMAPisAvailableAtThisTime": false, + "signalPriorityIsActive": false, + "preemptIsActive": false + } +},{ + "_id": { + "$oid": "665f3fc18c25216ef4f54049" + }, + "validationMessages": [ + { + "schemaPath": "#/$defs/J2735RoadRegulatorID/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].id.region", + "message": "$.payload.data.intersectionStateList.intersectionStatelist[0].id.region: null found, integer expected" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[0].state_time_speed.movementEventList[0].timing.startTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[0].state_time_speed.movementEventList[0].timing.nextTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[1].state_time_speed.movementEventList[0].timing.startTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[1].state_time_speed.movementEventList[0].timing.nextTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[2].state_time_speed.movementEventList[0].timing.startTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[2].state_time_speed.movementEventList[0].timing.nextTime", + "message": "DE_TimeMark is missing" + } + ], + "utcTimeStamp": "2024-06-04T16:23:27.252Z", + "schemaVersion": 1, + "recordGeneratedAt": { + "$date": { + "$numberLong": "1717518207262" + } + }, + "revision": 0, + "states": [ + { + "stateTimeSpeed": [ + { + "eventState": "PROTECTED_CLEARANCE", + "timing": { + "minEndTime": "2024-06-04T16:03:19Z", + "maxEndTime": "2024-06-04T16:03:19Z" + } + } + ], + "signalGroup": 2 + }, + { + "stateTimeSpeed": [ + { + "eventState": "STOP_AND_REMAIN", + "timing": { + "minEndTime": "2024-06-04T16:03:22.1Z", + "maxEndTime": "2024-06-04T16:03:22.1Z" + } + } + ], + "signalGroup": 4 + }, + { + "stateTimeSpeed": [ + { + "eventState": "PROTECTED_CLEARANCE", + "timing": { + "minEndTime": "2024-06-04T16:03:19Z", + "maxEndTime": "2024-06-04T16:03:19Z" + } + } + ], + "signalGroup": 6 + } + ], + "messageType": "SPAT", + "cti4501Conformant": false, + "intersectionId": 12109, + "odeReceivedAt": "2024-06-04T16:23:27.252Z", + "originIp": "10.11.81.12", + "status": { + "failureFlash": false, + "noValidSPATisAvailableAtThisTime": false, + "fixedTimeOperation": false, + "standbyOperation": false, + "trafficDependentOperation": false, + "manualControlIsEnabled": false, + "off": false, + "stopTimeIsActivated": false, + "recentChangeInMAPassignedLanesIDsUsed": false, + "recentMAPmessageUpdate": false, + "failureMode": false, + "noValidMAPisAvailableAtThisTime": false, + "signalPriorityIsActive": false, + "preemptIsActive": false + } +},{ + "_id": { + "$oid": "665f3fc18c25216ef4f5404a" + }, + "validationMessages": [ + { + "schemaPath": "#/$defs/J2735RoadRegulatorID/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].id.region", + "message": "$.payload.data.intersectionStateList.intersectionStatelist[0].id.region: null found, integer expected" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[0].state_time_speed.movementEventList[0].timing.startTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[0].state_time_speed.movementEventList[0].timing.nextTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[1].state_time_speed.movementEventList[0].timing.startTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[1].state_time_speed.movementEventList[0].timing.nextTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[2].state_time_speed.movementEventList[0].timing.startTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[2].state_time_speed.movementEventList[0].timing.nextTime", + "message": "DE_TimeMark is missing" + } + ], + "utcTimeStamp": "2024-06-04T16:23:27.357Z", + "schemaVersion": 1, + "recordGeneratedAt": { + "$date": { + "$numberLong": "1717518207369" + } + }, + "revision": 0, + "states": [ + { + "stateTimeSpeed": [ + { + "eventState": "PROTECTED_CLEARANCE", + "timing": { + "minEndTime": "2024-06-04T16:03:19Z", + "maxEndTime": "2024-06-04T16:03:19Z" + } + } + ], + "signalGroup": 2 + }, + { + "stateTimeSpeed": [ + { + "eventState": "STOP_AND_REMAIN", + "timing": { + "minEndTime": "2024-06-04T16:03:22.1Z", + "maxEndTime": "2024-06-04T16:03:22.1Z" + } + } + ], + "signalGroup": 4 + }, + { + "stateTimeSpeed": [ + { + "eventState": "PROTECTED_CLEARANCE", + "timing": { + "minEndTime": "2024-06-04T16:03:19Z", + "maxEndTime": "2024-06-04T16:03:19Z" + } + } + ], + "signalGroup": 6 + } + ], + "messageType": "SPAT", + "cti4501Conformant": false, + "intersectionId": 12109, + "odeReceivedAt": "2024-06-04T16:23:27.357Z", + "originIp": "10.11.81.12", + "status": { + "failureFlash": false, + "noValidSPATisAvailableAtThisTime": false, + "fixedTimeOperation": false, + "standbyOperation": false, + "trafficDependentOperation": false, + "manualControlIsEnabled": false, + "off": false, + "stopTimeIsActivated": false, + "recentChangeInMAPassignedLanesIDsUsed": false, + "recentMAPmessageUpdate": false, + "failureMode": false, + "noValidMAPisAvailableAtThisTime": false, + "signalPriorityIsActive": false, + "preemptIsActive": false + } +},{ + "_id": { + "$oid": "665f3fc18c25216ef4f5404b" + }, + "validationMessages": [ + { + "schemaPath": "#/$defs/J2735RoadRegulatorID/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].id.region", + "message": "$.payload.data.intersectionStateList.intersectionStatelist[0].id.region: null found, integer expected" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[0].state_time_speed.movementEventList[0].timing.startTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[0].state_time_speed.movementEventList[0].timing.nextTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[1].state_time_speed.movementEventList[0].timing.startTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[1].state_time_speed.movementEventList[0].timing.nextTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[2].state_time_speed.movementEventList[0].timing.startTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[2].state_time_speed.movementEventList[0].timing.nextTime", + "message": "DE_TimeMark is missing" + } + ], + "utcTimeStamp": "2024-06-04T16:23:27.464Z", + "schemaVersion": 1, + "recordGeneratedAt": { + "$date": { + "$numberLong": "1717518207475" + } + }, + "revision": 0, + "states": [ + { + "stateTimeSpeed": [ + { + "eventState": "PROTECTED_CLEARANCE", + "timing": { + "minEndTime": "2024-06-04T16:03:19Z", + "maxEndTime": "2024-06-04T16:03:19Z" + } + } + ], + "signalGroup": 2 + }, + { + "stateTimeSpeed": [ + { + "eventState": "STOP_AND_REMAIN", + "timing": { + "minEndTime": "2024-06-04T16:03:22.1Z", + "maxEndTime": "2024-06-04T16:03:22.1Z" + } + } + ], + "signalGroup": 4 + }, + { + "stateTimeSpeed": [ + { + "eventState": "PROTECTED_CLEARANCE", + "timing": { + "minEndTime": "2024-06-04T16:03:19Z", + "maxEndTime": "2024-06-04T16:03:19Z" + } + } + ], + "signalGroup": 6 + } + ], + "messageType": "SPAT", + "cti4501Conformant": false, + "intersectionId": 12109, + "odeReceivedAt": "2024-06-04T16:23:27.464Z", + "originIp": "10.11.81.12", + "status": { + "failureFlash": false, + "noValidSPATisAvailableAtThisTime": false, + "fixedTimeOperation": false, + "standbyOperation": false, + "trafficDependentOperation": false, + "manualControlIsEnabled": false, + "off": false, + "stopTimeIsActivated": false, + "recentChangeInMAPassignedLanesIDsUsed": false, + "recentMAPmessageUpdate": false, + "failureMode": false, + "noValidMAPisAvailableAtThisTime": false, + "signalPriorityIsActive": false, + "preemptIsActive": false + } +},{ + "_id": { + "$oid": "665f3fc18c25216ef4f5404c" + }, + "validationMessages": [ + { + "schemaPath": "#/$defs/J2735RoadRegulatorID/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].id.region", + "message": "$.payload.data.intersectionStateList.intersectionStatelist[0].id.region: null found, integer expected" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[0].state_time_speed.movementEventList[0].timing.startTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[0].state_time_speed.movementEventList[0].timing.nextTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[1].state_time_speed.movementEventList[0].timing.startTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[1].state_time_speed.movementEventList[0].timing.nextTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[2].state_time_speed.movementEventList[0].timing.startTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[2].state_time_speed.movementEventList[0].timing.nextTime", + "message": "DE_TimeMark is missing" + } + ], + "utcTimeStamp": "2024-06-04T16:23:27.572Z", + "schemaVersion": 1, + "recordGeneratedAt": { + "$date": { + "$numberLong": "1717518207581" + } + }, + "revision": 0, + "states": [ + { + "stateTimeSpeed": [ + { + "eventState": "PROTECTED_CLEARANCE", + "timing": { + "minEndTime": "2024-06-04T16:03:19Z", + "maxEndTime": "2024-06-04T16:03:19Z" + } + } + ], + "signalGroup": 2 + }, + { + "stateTimeSpeed": [ + { + "eventState": "STOP_AND_REMAIN", + "timing": { + "minEndTime": "2024-06-04T16:03:22.1Z", + "maxEndTime": "2024-06-04T16:03:22.1Z" + } + } + ], + "signalGroup": 4 + }, + { + "stateTimeSpeed": [ + { + "eventState": "PROTECTED_CLEARANCE", + "timing": { + "minEndTime": "2024-06-04T16:03:19Z", + "maxEndTime": "2024-06-04T16:03:19Z" + } + } + ], + "signalGroup": 6 + } + ], + "messageType": "SPAT", + "cti4501Conformant": false, + "intersectionId": 12109, + "odeReceivedAt": "2024-06-04T16:23:27.572Z", + "originIp": "10.11.81.12", + "status": { + "failureFlash": false, + "noValidSPATisAvailableAtThisTime": false, + "fixedTimeOperation": false, + "standbyOperation": false, + "trafficDependentOperation": false, + "manualControlIsEnabled": false, + "off": false, + "stopTimeIsActivated": false, + "recentChangeInMAPassignedLanesIDsUsed": false, + "recentMAPmessageUpdate": false, + "failureMode": false, + "noValidMAPisAvailableAtThisTime": false, + "signalPriorityIsActive": false, + "preemptIsActive": false + } +},{ + "_id": { + "$oid": "665f3fc18c25216ef4f5404d" + }, + "validationMessages": [ + { + "schemaPath": "#/$defs/J2735RoadRegulatorID/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].id.region", + "message": "$.payload.data.intersectionStateList.intersectionStatelist[0].id.region: null found, integer expected" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[0].state_time_speed.movementEventList[0].timing.startTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[0].state_time_speed.movementEventList[0].timing.nextTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[1].state_time_speed.movementEventList[0].timing.startTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[1].state_time_speed.movementEventList[0].timing.nextTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[2].state_time_speed.movementEventList[0].timing.startTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[2].state_time_speed.movementEventList[0].timing.nextTime", + "message": "DE_TimeMark is missing" + } + ], + "utcTimeStamp": "2024-06-04T16:23:27.678Z", + "schemaVersion": 1, + "recordGeneratedAt": { + "$date": { + "$numberLong": "1717518207688" + } + }, + "revision": 0, + "states": [ + { + "stateTimeSpeed": [ + { + "eventState": "PROTECTED_CLEARANCE", + "timing": { + "minEndTime": "2024-06-04T16:03:19Z", + "maxEndTime": "2024-06-04T16:03:19Z" + } + } + ], + "signalGroup": 2 + }, + { + "stateTimeSpeed": [ + { + "eventState": "STOP_AND_REMAIN", + "timing": { + "minEndTime": "2024-06-04T16:03:22.1Z", + "maxEndTime": "2024-06-04T16:03:22.1Z" + } + } + ], + "signalGroup": 4 + }, + { + "stateTimeSpeed": [ + { + "eventState": "PROTECTED_CLEARANCE", + "timing": { + "minEndTime": "2024-06-04T16:03:19Z", + "maxEndTime": "2024-06-04T16:03:19Z" + } + } + ], + "signalGroup": 6 + } + ], + "messageType": "SPAT", + "cti4501Conformant": false, + "intersectionId": 12109, + "odeReceivedAt": "2024-06-04T16:23:27.678Z", + "originIp": "10.11.81.12", + "status": { + "failureFlash": false, + "noValidSPATisAvailableAtThisTime": false, + "fixedTimeOperation": false, + "standbyOperation": false, + "trafficDependentOperation": false, + "manualControlIsEnabled": false, + "off": false, + "stopTimeIsActivated": false, + "recentChangeInMAPassignedLanesIDsUsed": false, + "recentMAPmessageUpdate": false, + "failureMode": false, + "noValidMAPisAvailableAtThisTime": false, + "signalPriorityIsActive": false, + "preemptIsActive": false + } +},{ + "_id": { + "$oid": "665f3fc18c25216ef4f5404e" + }, + "validationMessages": [ + { + "schemaPath": "#/$defs/J2735RoadRegulatorID/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].id.region", + "message": "$.payload.data.intersectionStateList.intersectionStatelist[0].id.region: null found, integer expected" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[0].state_time_speed.movementEventList[0].timing.startTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[0].state_time_speed.movementEventList[0].timing.nextTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[1].state_time_speed.movementEventList[0].timing.startTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[1].state_time_speed.movementEventList[0].timing.nextTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[2].state_time_speed.movementEventList[0].timing.startTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[2].state_time_speed.movementEventList[0].timing.nextTime", + "message": "DE_TimeMark is missing" + } + ], + "utcTimeStamp": "2024-06-04T16:23:27.786Z", + "schemaVersion": 1, + "recordGeneratedAt": { + "$date": { + "$numberLong": "1717518207795" + } + }, + "revision": 0, + "states": [ + { + "stateTimeSpeed": [ + { + "eventState": "PROTECTED_CLEARANCE", + "timing": { + "minEndTime": "2024-06-04T16:03:19Z", + "maxEndTime": "2024-06-04T16:03:19Z" + } + } + ], + "signalGroup": 2 + }, + { + "stateTimeSpeed": [ + { + "eventState": "STOP_AND_REMAIN", + "timing": { + "minEndTime": "2024-06-04T16:03:22.1Z", + "maxEndTime": "2024-06-04T16:03:22.1Z" + } + } + ], + "signalGroup": 4 + }, + { + "stateTimeSpeed": [ + { + "eventState": "PROTECTED_CLEARANCE", + "timing": { + "minEndTime": "2024-06-04T16:03:19Z", + "maxEndTime": "2024-06-04T16:03:19Z" + } + } + ], + "signalGroup": 6 + } + ], + "messageType": "SPAT", + "cti4501Conformant": false, + "intersectionId": 12109, + "odeReceivedAt": "2024-06-04T16:23:27.786Z", + "originIp": "10.11.81.12", + "status": { + "failureFlash": false, + "noValidSPATisAvailableAtThisTime": false, + "fixedTimeOperation": false, + "standbyOperation": false, + "trafficDependentOperation": false, + "manualControlIsEnabled": false, + "off": false, + "stopTimeIsActivated": false, + "recentChangeInMAPassignedLanesIDsUsed": false, + "recentMAPmessageUpdate": false, + "failureMode": false, + "noValidMAPisAvailableAtThisTime": false, + "signalPriorityIsActive": false, + "preemptIsActive": false + } +},{ + "_id": { + "$oid": "665f3fc18c25216ef4f5404f" + }, + "validationMessages": [ + { + "schemaPath": "#/$defs/J2735RoadRegulatorID/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].id.region", + "message": "$.payload.data.intersectionStateList.intersectionStatelist[0].id.region: null found, integer expected" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[0].state_time_speed.movementEventList[0].timing.startTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[0].state_time_speed.movementEventList[0].timing.nextTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[1].state_time_speed.movementEventList[0].timing.startTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[1].state_time_speed.movementEventList[0].timing.nextTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[2].state_time_speed.movementEventList[0].timing.startTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[2].state_time_speed.movementEventList[0].timing.nextTime", + "message": "DE_TimeMark is missing" + } + ], + "utcTimeStamp": "2024-06-04T16:23:27.893Z", + "schemaVersion": 1, + "recordGeneratedAt": { + "$date": { + "$numberLong": "1717518207900" + } + }, + "revision": 0, + "states": [ + { + "stateTimeSpeed": [ + { + "eventState": "PROTECTED_CLEARANCE", + "timing": { + "minEndTime": "2024-06-04T16:03:19Z", + "maxEndTime": "2024-06-04T16:03:19Z" + } + } + ], + "signalGroup": 2 + }, + { + "stateTimeSpeed": [ + { + "eventState": "STOP_AND_REMAIN", + "timing": { + "minEndTime": "2024-06-04T16:03:22.1Z", + "maxEndTime": "2024-06-04T16:03:22.1Z" + } + } + ], + "signalGroup": 4 + }, + { + "stateTimeSpeed": [ + { + "eventState": "PROTECTED_CLEARANCE", + "timing": { + "minEndTime": "2024-06-04T16:03:19Z", + "maxEndTime": "2024-06-04T16:03:19Z" + } + } + ], + "signalGroup": 6 + } + ], + "messageType": "SPAT", + "cti4501Conformant": false, + "intersectionId": 12109, + "odeReceivedAt": "2024-06-04T16:23:27.893Z", + "originIp": "10.11.81.12", + "status": { + "failureFlash": false, + "noValidSPATisAvailableAtThisTime": false, + "fixedTimeOperation": false, + "standbyOperation": false, + "trafficDependentOperation": false, + "manualControlIsEnabled": false, + "off": false, + "stopTimeIsActivated": false, + "recentChangeInMAPassignedLanesIDsUsed": false, + "recentMAPmessageUpdate": false, + "failureMode": false, + "noValidMAPisAvailableAtThisTime": false, + "signalPriorityIsActive": false, + "preemptIsActive": false + } +},{ + "_id": { + "$oid": "665f3fc18c25216ef4f54050" + }, + "validationMessages": [ + { + "schemaPath": "#/$defs/J2735RoadRegulatorID/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].id.region", + "message": "$.payload.data.intersectionStateList.intersectionStatelist[0].id.region: null found, integer expected" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[0].state_time_speed.movementEventList[0].timing.startTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[0].state_time_speed.movementEventList[0].timing.nextTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[1].state_time_speed.movementEventList[0].timing.startTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[1].state_time_speed.movementEventList[0].timing.nextTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[2].state_time_speed.movementEventList[0].timing.startTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[2].state_time_speed.movementEventList[0].timing.nextTime", + "message": "DE_TimeMark is missing" + } + ], + "utcTimeStamp": "2024-06-04T16:23:28Z", + "schemaVersion": 1, + "recordGeneratedAt": { + "$date": { + "$numberLong": "1717518208006" + } + }, + "revision": 0, + "states": [ + { + "stateTimeSpeed": [ + { + "eventState": "PROTECTED_CLEARANCE", + "timing": { + "minEndTime": "2024-06-04T16:03:19Z", + "maxEndTime": "2024-06-04T16:03:19Z" + } + } + ], + "signalGroup": 2 + }, + { + "stateTimeSpeed": [ + { + "eventState": "STOP_AND_REMAIN", + "timing": { + "minEndTime": "2024-06-04T16:03:22.1Z", + "maxEndTime": "2024-06-04T16:03:22.1Z" + } + } + ], + "signalGroup": 4 + }, + { + "stateTimeSpeed": [ + { + "eventState": "PROTECTED_CLEARANCE", + "timing": { + "minEndTime": "2024-06-04T16:03:19Z", + "maxEndTime": "2024-06-04T16:03:19Z" + } + } + ], + "signalGroup": 6 + } + ], + "messageType": "SPAT", + "cti4501Conformant": false, + "intersectionId": 12109, + "odeReceivedAt": "2024-06-04T16:23:28Z", + "originIp": "10.11.81.12", + "status": { + "failureFlash": false, + "noValidSPATisAvailableAtThisTime": false, + "fixedTimeOperation": false, + "standbyOperation": false, + "trafficDependentOperation": false, + "manualControlIsEnabled": false, + "off": false, + "stopTimeIsActivated": false, + "recentChangeInMAPassignedLanesIDsUsed": false, + "recentMAPmessageUpdate": false, + "failureMode": false, + "noValidMAPisAvailableAtThisTime": false, + "signalPriorityIsActive": false, + "preemptIsActive": false + } +},{ + "_id": { + "$oid": "665f3fc18c25216ef4f54051" + }, + "validationMessages": [ + { + "schemaPath": "#/$defs/J2735RoadRegulatorID/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].id.region", + "message": "$.payload.data.intersectionStateList.intersectionStatelist[0].id.region: null found, integer expected" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[0].state_time_speed.movementEventList[0].timing.startTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[0].state_time_speed.movementEventList[0].timing.nextTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[1].state_time_speed.movementEventList[0].timing.startTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[1].state_time_speed.movementEventList[0].timing.nextTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[2].state_time_speed.movementEventList[0].timing.startTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[2].state_time_speed.movementEventList[0].timing.nextTime", + "message": "DE_TimeMark is missing" + } + ], + "utcTimeStamp": "2024-06-04T16:23:28.107Z", + "schemaVersion": 1, + "recordGeneratedAt": { + "$date": { + "$numberLong": "1717518208114" + } + }, + "revision": 0, + "states": [ + { + "stateTimeSpeed": [ + { + "eventState": "PROTECTED_CLEARANCE", + "timing": { + "minEndTime": "2024-06-04T16:03:19Z", + "maxEndTime": "2024-06-04T16:03:19Z" + } + } + ], + "signalGroup": 2 + }, + { + "stateTimeSpeed": [ + { + "eventState": "STOP_AND_REMAIN", + "timing": { + "minEndTime": "2024-06-04T16:03:22.1Z", + "maxEndTime": "2024-06-04T16:03:22.1Z" + } + } + ], + "signalGroup": 4 + }, + { + "stateTimeSpeed": [ + { + "eventState": "PROTECTED_CLEARANCE", + "timing": { + "minEndTime": "2024-06-04T16:03:19Z", + "maxEndTime": "2024-06-04T16:03:19Z" + } + } + ], + "signalGroup": 6 + } + ], + "messageType": "SPAT", + "cti4501Conformant": false, + "intersectionId": 12109, + "odeReceivedAt": "2024-06-04T16:23:28.107Z", + "originIp": "10.11.81.12", + "status": { + "failureFlash": false, + "noValidSPATisAvailableAtThisTime": false, + "fixedTimeOperation": false, + "standbyOperation": false, + "trafficDependentOperation": false, + "manualControlIsEnabled": false, + "off": false, + "stopTimeIsActivated": false, + "recentChangeInMAPassignedLanesIDsUsed": false, + "recentMAPmessageUpdate": false, + "failureMode": false, + "noValidMAPisAvailableAtThisTime": false, + "signalPriorityIsActive": false, + "preemptIsActive": false + } +},{ + "_id": { + "$oid": "665f3fc18c25216ef4f54052" + }, + "validationMessages": [ + { + "schemaPath": "#/$defs/J2735RoadRegulatorID/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].id.region", + "message": "$.payload.data.intersectionStateList.intersectionStatelist[0].id.region: null found, integer expected" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[0].state_time_speed.movementEventList[0].timing.startTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[0].state_time_speed.movementEventList[0].timing.nextTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[1].state_time_speed.movementEventList[0].timing.startTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[1].state_time_speed.movementEventList[0].timing.nextTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[2].state_time_speed.movementEventList[0].timing.startTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[2].state_time_speed.movementEventList[0].timing.nextTime", + "message": "DE_TimeMark is missing" + } + ], + "utcTimeStamp": "2024-06-04T16:23:28.201Z", + "schemaVersion": 1, + "recordGeneratedAt": { + "$date": { + "$numberLong": "1717518208207" + } + }, + "revision": 0, + "states": [ + { + "stateTimeSpeed": [ + { + "eventState": "PROTECTED_CLEARANCE", + "timing": { + "minEndTime": "2024-06-04T16:03:19Z", + "maxEndTime": "2024-06-04T16:03:19Z" + } + } + ], + "signalGroup": 2 + }, + { + "stateTimeSpeed": [ + { + "eventState": "STOP_AND_REMAIN", + "timing": { + "minEndTime": "2024-06-04T16:03:22.1Z", + "maxEndTime": "2024-06-04T16:03:22.1Z" + } + } + ], + "signalGroup": 4 + }, + { + "stateTimeSpeed": [ + { + "eventState": "PROTECTED_CLEARANCE", + "timing": { + "minEndTime": "2024-06-04T16:03:19Z", + "maxEndTime": "2024-06-04T16:03:19Z" + } + } + ], + "signalGroup": 6 + } + ], + "messageType": "SPAT", + "cti4501Conformant": false, + "intersectionId": 12109, + "odeReceivedAt": "2024-06-04T16:23:28.201Z", + "originIp": "10.11.81.12", + "status": { + "failureFlash": false, + "noValidSPATisAvailableAtThisTime": false, + "fixedTimeOperation": false, + "standbyOperation": false, + "trafficDependentOperation": false, + "manualControlIsEnabled": false, + "off": false, + "stopTimeIsActivated": false, + "recentChangeInMAPassignedLanesIDsUsed": false, + "recentMAPmessageUpdate": false, + "failureMode": false, + "noValidMAPisAvailableAtThisTime": false, + "signalPriorityIsActive": false, + "preemptIsActive": false + } +},{ + "_id": { + "$oid": "665f3fc18c25216ef4f54053" + }, + "validationMessages": [ + { + "schemaPath": "#/$defs/J2735RoadRegulatorID/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].id.region", + "message": "$.payload.data.intersectionStateList.intersectionStatelist[0].id.region: null found, integer expected" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[0].state_time_speed.movementEventList[0].timing.startTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[0].state_time_speed.movementEventList[0].timing.nextTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[1].state_time_speed.movementEventList[0].timing.startTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[1].state_time_speed.movementEventList[0].timing.nextTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[2].state_time_speed.movementEventList[0].timing.startTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[2].state_time_speed.movementEventList[0].timing.nextTime", + "message": "DE_TimeMark is missing" + } + ], + "utcTimeStamp": "2024-06-04T16:23:28.308Z", + "schemaVersion": 1, + "recordGeneratedAt": { + "$date": { + "$numberLong": "1717518208314" + } + }, + "revision": 0, + "states": [ + { + "stateTimeSpeed": [ + { + "eventState": "PROTECTED_CLEARANCE", + "timing": { + "minEndTime": "2024-06-04T16:03:19Z", + "maxEndTime": "2024-06-04T16:03:19Z" + } + } + ], + "signalGroup": 2 + }, + { + "stateTimeSpeed": [ + { + "eventState": "STOP_AND_REMAIN", + "timing": { + "minEndTime": "2024-06-04T16:03:22.1Z", + "maxEndTime": "2024-06-04T16:03:22.1Z" + } + } + ], + "signalGroup": 4 + }, + { + "stateTimeSpeed": [ + { + "eventState": "PROTECTED_CLEARANCE", + "timing": { + "minEndTime": "2024-06-04T16:03:19Z", + "maxEndTime": "2024-06-04T16:03:19Z" + } + } + ], + "signalGroup": 6 + } + ], + "messageType": "SPAT", + "cti4501Conformant": false, + "intersectionId": 12109, + "odeReceivedAt": "2024-06-04T16:23:28.308Z", + "originIp": "10.11.81.12", + "status": { + "failureFlash": false, + "noValidSPATisAvailableAtThisTime": false, + "fixedTimeOperation": false, + "standbyOperation": false, + "trafficDependentOperation": false, + "manualControlIsEnabled": false, + "off": false, + "stopTimeIsActivated": false, + "recentChangeInMAPassignedLanesIDsUsed": false, + "recentMAPmessageUpdate": false, + "failureMode": false, + "noValidMAPisAvailableAtThisTime": false, + "signalPriorityIsActive": false, + "preemptIsActive": false + } +},{ + "_id": { + "$oid": "665f3fc18c25216ef4f54054" + }, + "validationMessages": [ + { + "schemaPath": "#/$defs/J2735RoadRegulatorID/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].id.region", + "message": "$.payload.data.intersectionStateList.intersectionStatelist[0].id.region: null found, integer expected" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[0].state_time_speed.movementEventList[0].timing.startTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[0].state_time_speed.movementEventList[0].timing.nextTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[1].state_time_speed.movementEventList[0].timing.startTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[1].state_time_speed.movementEventList[0].timing.nextTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[2].state_time_speed.movementEventList[0].timing.startTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[2].state_time_speed.movementEventList[0].timing.nextTime", + "message": "DE_TimeMark is missing" + } + ], + "utcTimeStamp": "2024-06-04T16:23:28.413Z", + "schemaVersion": 1, + "recordGeneratedAt": { + "$date": { + "$numberLong": "1717518208421" + } + }, + "revision": 0, + "states": [ + { + "stateTimeSpeed": [ + { + "eventState": "PROTECTED_CLEARANCE", + "timing": { + "minEndTime": "2024-06-04T16:03:19Z", + "maxEndTime": "2024-06-04T16:03:19Z" + } + } + ], + "signalGroup": 2 + }, + { + "stateTimeSpeed": [ + { + "eventState": "STOP_AND_REMAIN", + "timing": { + "minEndTime": "2024-06-04T16:03:22.1Z", + "maxEndTime": "2024-06-04T16:03:22.1Z" + } + } + ], + "signalGroup": 4 + }, + { + "stateTimeSpeed": [ + { + "eventState": "PROTECTED_CLEARANCE", + "timing": { + "minEndTime": "2024-06-04T16:03:19Z", + "maxEndTime": "2024-06-04T16:03:19Z" + } + } + ], + "signalGroup": 6 + } + ], + "messageType": "SPAT", + "cti4501Conformant": false, + "intersectionId": 12109, + "odeReceivedAt": "2024-06-04T16:23:28.413Z", + "originIp": "10.11.81.12", + "status": { + "failureFlash": false, + "noValidSPATisAvailableAtThisTime": false, + "fixedTimeOperation": false, + "standbyOperation": false, + "trafficDependentOperation": false, + "manualControlIsEnabled": false, + "off": false, + "stopTimeIsActivated": false, + "recentChangeInMAPassignedLanesIDsUsed": false, + "recentMAPmessageUpdate": false, + "failureMode": false, + "noValidMAPisAvailableAtThisTime": false, + "signalPriorityIsActive": false, + "preemptIsActive": false + } +},{ + "_id": { + "$oid": "665f3fc18c25216ef4f54055" + }, + "validationMessages": [ + { + "schemaPath": "#/$defs/J2735RoadRegulatorID/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].id.region", + "message": "$.payload.data.intersectionStateList.intersectionStatelist[0].id.region: null found, integer expected" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[0].state_time_speed.movementEventList[0].timing.startTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[0].state_time_speed.movementEventList[0].timing.nextTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[1].state_time_speed.movementEventList[0].timing.startTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[1].state_time_speed.movementEventList[0].timing.nextTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[2].state_time_speed.movementEventList[0].timing.startTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[2].state_time_speed.movementEventList[0].timing.nextTime", + "message": "DE_TimeMark is missing" + } + ], + "utcTimeStamp": "2024-06-04T16:23:28.522Z", + "schemaVersion": 1, + "recordGeneratedAt": { + "$date": { + "$numberLong": "1717518208528" + } + }, + "revision": 0, + "states": [ + { + "stateTimeSpeed": [ + { + "eventState": "PROTECTED_CLEARANCE", + "timing": { + "minEndTime": "2024-06-04T16:03:19Z", + "maxEndTime": "2024-06-04T16:03:19Z" + } + } + ], + "signalGroup": 2 + }, + { + "stateTimeSpeed": [ + { + "eventState": "STOP_AND_REMAIN", + "timing": { + "minEndTime": "2024-06-04T16:03:22.1Z", + "maxEndTime": "2024-06-04T16:03:22.1Z" + } + } + ], + "signalGroup": 4 + }, + { + "stateTimeSpeed": [ + { + "eventState": "PROTECTED_CLEARANCE", + "timing": { + "minEndTime": "2024-06-04T16:03:19Z", + "maxEndTime": "2024-06-04T16:03:19Z" + } + } + ], + "signalGroup": 6 + } + ], + "messageType": "SPAT", + "cti4501Conformant": false, + "intersectionId": 12109, + "odeReceivedAt": "2024-06-04T16:23:28.522Z", + "originIp": "10.11.81.12", + "status": { + "failureFlash": false, + "noValidSPATisAvailableAtThisTime": false, + "fixedTimeOperation": false, + "standbyOperation": false, + "trafficDependentOperation": false, + "manualControlIsEnabled": false, + "off": false, + "stopTimeIsActivated": false, + "recentChangeInMAPassignedLanesIDsUsed": false, + "recentMAPmessageUpdate": false, + "failureMode": false, + "noValidMAPisAvailableAtThisTime": false, + "signalPriorityIsActive": false, + "preemptIsActive": false + } +},{ + "_id": { + "$oid": "665f3fc18c25216ef4f54056" + }, + "validationMessages": [ + { + "schemaPath": "#/$defs/J2735RoadRegulatorID/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].id.region", + "message": "$.payload.data.intersectionStateList.intersectionStatelist[0].id.region: null found, integer expected" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[0].state_time_speed.movementEventList[0].timing.startTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[0].state_time_speed.movementEventList[0].timing.nextTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[1].state_time_speed.movementEventList[0].timing.startTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[1].state_time_speed.movementEventList[0].timing.nextTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[2].state_time_speed.movementEventList[0].timing.startTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[2].state_time_speed.movementEventList[0].timing.nextTime", + "message": "DE_TimeMark is missing" + } + ], + "utcTimeStamp": "2024-06-04T16:23:28.629Z", + "schemaVersion": 1, + "recordGeneratedAt": { + "$date": { + "$numberLong": "1717518208636" + } + }, + "revision": 0, + "states": [ + { + "stateTimeSpeed": [ + { + "eventState": "PROTECTED_CLEARANCE", + "timing": { + "minEndTime": "2024-06-04T16:03:19Z", + "maxEndTime": "2024-06-04T16:03:19Z" + } + } + ], + "signalGroup": 2 + }, + { + "stateTimeSpeed": [ + { + "eventState": "STOP_AND_REMAIN", + "timing": { + "minEndTime": "2024-06-04T16:03:22.1Z", + "maxEndTime": "2024-06-04T16:03:22.1Z" + } + } + ], + "signalGroup": 4 + }, + { + "stateTimeSpeed": [ + { + "eventState": "PROTECTED_CLEARANCE", + "timing": { + "minEndTime": "2024-06-04T16:03:19Z", + "maxEndTime": "2024-06-04T16:03:19Z" + } + } + ], + "signalGroup": 6 + } + ], + "messageType": "SPAT", + "cti4501Conformant": false, + "intersectionId": 12109, + "odeReceivedAt": "2024-06-04T16:23:28.629Z", + "originIp": "10.11.81.12", + "status": { + "failureFlash": false, + "noValidSPATisAvailableAtThisTime": false, + "fixedTimeOperation": false, + "standbyOperation": false, + "trafficDependentOperation": false, + "manualControlIsEnabled": false, + "off": false, + "stopTimeIsActivated": false, + "recentChangeInMAPassignedLanesIDsUsed": false, + "recentMAPmessageUpdate": false, + "failureMode": false, + "noValidMAPisAvailableAtThisTime": false, + "signalPriorityIsActive": false, + "preemptIsActive": false + } +},{ + "_id": { + "$oid": "665f3fc18c25216ef4f54057" + }, + "validationMessages": [ + { + "schemaPath": "#/$defs/J2735RoadRegulatorID/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].id.region", + "message": "$.payload.data.intersectionStateList.intersectionStatelist[0].id.region: null found, integer expected" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[0].state_time_speed.movementEventList[0].timing.startTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[0].state_time_speed.movementEventList[0].timing.nextTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[1].state_time_speed.movementEventList[0].timing.startTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[1].state_time_speed.movementEventList[0].timing.nextTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[2].state_time_speed.movementEventList[0].timing.startTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[2].state_time_speed.movementEventList[0].timing.nextTime", + "message": "DE_TimeMark is missing" + } + ], + "utcTimeStamp": "2024-06-04T16:23:28.735Z", + "schemaVersion": 1, + "recordGeneratedAt": { + "$date": { + "$numberLong": "1717518208743" + } + }, + "revision": 0, + "states": [ + { + "stateTimeSpeed": [ + { + "eventState": "PROTECTED_CLEARANCE", + "timing": { + "minEndTime": "2024-06-04T16:03:19Z", + "maxEndTime": "2024-06-04T16:03:19Z" + } + } + ], + "signalGroup": 2 + }, + { + "stateTimeSpeed": [ + { + "eventState": "STOP_AND_REMAIN", + "timing": { + "minEndTime": "2024-06-04T16:03:22.1Z", + "maxEndTime": "2024-06-04T16:03:22.1Z" + } + } + ], + "signalGroup": 4 + }, + { + "stateTimeSpeed": [ + { + "eventState": "PROTECTED_CLEARANCE", + "timing": { + "minEndTime": "2024-06-04T16:03:19Z", + "maxEndTime": "2024-06-04T16:03:19Z" + } + } + ], + "signalGroup": 6 + } + ], + "messageType": "SPAT", + "cti4501Conformant": false, + "intersectionId": 12109, + "odeReceivedAt": "2024-06-04T16:23:28.735Z", + "originIp": "10.11.81.12", + "status": { + "failureFlash": false, + "noValidSPATisAvailableAtThisTime": false, + "fixedTimeOperation": false, + "standbyOperation": false, + "trafficDependentOperation": false, + "manualControlIsEnabled": false, + "off": false, + "stopTimeIsActivated": false, + "recentChangeInMAPassignedLanesIDsUsed": false, + "recentMAPmessageUpdate": false, + "failureMode": false, + "noValidMAPisAvailableAtThisTime": false, + "signalPriorityIsActive": false, + "preemptIsActive": false + } +},{ + "_id": { + "$oid": "665f3fc18c25216ef4f54058" + }, + "validationMessages": [ + { + "schemaPath": "#/$defs/J2735RoadRegulatorID/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].id.region", + "message": "$.payload.data.intersectionStateList.intersectionStatelist[0].id.region: null found, integer expected" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[0].state_time_speed.movementEventList[0].timing.startTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[0].state_time_speed.movementEventList[0].timing.nextTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[1].state_time_speed.movementEventList[0].timing.startTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[1].state_time_speed.movementEventList[0].timing.nextTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[2].state_time_speed.movementEventList[0].timing.startTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[2].state_time_speed.movementEventList[0].timing.nextTime", + "message": "DE_TimeMark is missing" + } + ], + "utcTimeStamp": "2024-06-04T16:23:28.843Z", + "schemaVersion": 1, + "recordGeneratedAt": { + "$date": { + "$numberLong": "1717518208850" + } + }, + "revision": 0, + "states": [ + { + "stateTimeSpeed": [ + { + "eventState": "PROTECTED_CLEARANCE", + "timing": { + "minEndTime": "2024-06-04T16:03:19Z", + "maxEndTime": "2024-06-04T16:03:19Z" + } + } + ], + "signalGroup": 2 + }, + { + "stateTimeSpeed": [ + { + "eventState": "STOP_AND_REMAIN", + "timing": { + "minEndTime": "2024-06-04T16:03:22.1Z", + "maxEndTime": "2024-06-04T16:03:22.1Z" + } + } + ], + "signalGroup": 4 + }, + { + "stateTimeSpeed": [ + { + "eventState": "PROTECTED_CLEARANCE", + "timing": { + "minEndTime": "2024-06-04T16:03:19Z", + "maxEndTime": "2024-06-04T16:03:19Z" + } + } + ], + "signalGroup": 6 + } + ], + "messageType": "SPAT", + "cti4501Conformant": false, + "intersectionId": 12109, + "odeReceivedAt": "2024-06-04T16:23:28.843Z", + "originIp": "10.11.81.12", + "status": { + "failureFlash": false, + "noValidSPATisAvailableAtThisTime": false, + "fixedTimeOperation": false, + "standbyOperation": false, + "trafficDependentOperation": false, + "manualControlIsEnabled": false, + "off": false, + "stopTimeIsActivated": false, + "recentChangeInMAPassignedLanesIDsUsed": false, + "recentMAPmessageUpdate": false, + "failureMode": false, + "noValidMAPisAvailableAtThisTime": false, + "signalPriorityIsActive": false, + "preemptIsActive": false + } +},{ + "_id": { + "$oid": "665f3fc18c25216ef4f54059" + }, + "validationMessages": [ + { + "schemaPath": "#/$defs/J2735RoadRegulatorID/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].id.region", + "message": "$.payload.data.intersectionStateList.intersectionStatelist[0].id.region: null found, integer expected" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[0].state_time_speed.movementEventList[0].timing.startTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[0].state_time_speed.movementEventList[0].timing.nextTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[1].state_time_speed.movementEventList[0].timing.startTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[1].state_time_speed.movementEventList[0].timing.nextTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[2].state_time_speed.movementEventList[0].timing.startTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[2].state_time_speed.movementEventList[0].timing.nextTime", + "message": "DE_TimeMark is missing" + } + ], + "utcTimeStamp": "2024-06-04T16:23:28.95Z", + "schemaVersion": 1, + "recordGeneratedAt": { + "$date": { + "$numberLong": "1717518208957" + } + }, + "revision": 0, + "states": [ + { + "stateTimeSpeed": [ + { + "eventState": "PROTECTED_CLEARANCE", + "timing": { + "minEndTime": "2024-06-04T16:03:19Z", + "maxEndTime": "2024-06-04T16:03:19Z" + } + } + ], + "signalGroup": 2 + }, + { + "stateTimeSpeed": [ + { + "eventState": "STOP_AND_REMAIN", + "timing": { + "minEndTime": "2024-06-04T16:03:22.1Z", + "maxEndTime": "2024-06-04T16:03:22.1Z" + } + } + ], + "signalGroup": 4 + }, + { + "stateTimeSpeed": [ + { + "eventState": "PROTECTED_CLEARANCE", + "timing": { + "minEndTime": "2024-06-04T16:03:19Z", + "maxEndTime": "2024-06-04T16:03:19Z" + } + } + ], + "signalGroup": 6 + } + ], + "messageType": "SPAT", + "cti4501Conformant": false, + "intersectionId": 12109, + "odeReceivedAt": "2024-06-04T16:23:28.95Z", + "originIp": "10.11.81.12", + "status": { + "failureFlash": false, + "noValidSPATisAvailableAtThisTime": false, + "fixedTimeOperation": false, + "standbyOperation": false, + "trafficDependentOperation": false, + "manualControlIsEnabled": false, + "off": false, + "stopTimeIsActivated": false, + "recentChangeInMAPassignedLanesIDsUsed": false, + "recentMAPmessageUpdate": false, + "failureMode": false, + "noValidMAPisAvailableAtThisTime": false, + "signalPriorityIsActive": false, + "preemptIsActive": false + } +},{ + "_id": { + "$oid": "665f3fc18c25216ef4f5405a" + }, + "validationMessages": [ + { + "schemaPath": "#/$defs/J2735RoadRegulatorID/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].id.region", + "message": "$.payload.data.intersectionStateList.intersectionStatelist[0].id.region: null found, integer expected" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[0].state_time_speed.movementEventList[0].timing.startTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[0].state_time_speed.movementEventList[0].timing.nextTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[1].state_time_speed.movementEventList[0].timing.startTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[1].state_time_speed.movementEventList[0].timing.nextTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[2].state_time_speed.movementEventList[0].timing.startTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[2].state_time_speed.movementEventList[0].timing.nextTime", + "message": "DE_TimeMark is missing" + } + ], + "utcTimeStamp": "2024-06-04T16:23:29.057Z", + "schemaVersion": 1, + "recordGeneratedAt": { + "$date": { + "$numberLong": "1717518209065" + } + }, + "revision": 0, + "states": [ + { + "stateTimeSpeed": [ + { + "eventState": "PROTECTED_CLEARANCE", + "timing": { + "minEndTime": "2024-06-04T16:03:19Z", + "maxEndTime": "2024-06-04T16:03:19Z" + } + } + ], + "signalGroup": 2 + }, + { + "stateTimeSpeed": [ + { + "eventState": "STOP_AND_REMAIN", + "timing": { + "minEndTime": "2024-06-04T16:03:22.1Z", + "maxEndTime": "2024-06-04T16:03:22.1Z" + } + } + ], + "signalGroup": 4 + }, + { + "stateTimeSpeed": [ + { + "eventState": "PROTECTED_CLEARANCE", + "timing": { + "minEndTime": "2024-06-04T16:03:19Z", + "maxEndTime": "2024-06-04T16:03:19Z" + } + } + ], + "signalGroup": 6 + } + ], + "messageType": "SPAT", + "cti4501Conformant": false, + "intersectionId": 12109, + "odeReceivedAt": "2024-06-04T16:23:29.057Z", + "originIp": "10.11.81.12", + "status": { + "failureFlash": false, + "noValidSPATisAvailableAtThisTime": false, + "fixedTimeOperation": false, + "standbyOperation": false, + "trafficDependentOperation": false, + "manualControlIsEnabled": false, + "off": false, + "stopTimeIsActivated": false, + "recentChangeInMAPassignedLanesIDsUsed": false, + "recentMAPmessageUpdate": false, + "failureMode": false, + "noValidMAPisAvailableAtThisTime": false, + "signalPriorityIsActive": false, + "preemptIsActive": false + } +},{ + "_id": { + "$oid": "665f3fc18c25216ef4f5405b" + }, + "validationMessages": [ + { + "schemaPath": "#/$defs/J2735RoadRegulatorID/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].id.region", + "message": "$.payload.data.intersectionStateList.intersectionStatelist[0].id.region: null found, integer expected" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[0].state_time_speed.movementEventList[0].timing.startTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[0].state_time_speed.movementEventList[0].timing.nextTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[1].state_time_speed.movementEventList[0].timing.startTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[1].state_time_speed.movementEventList[0].timing.nextTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[2].state_time_speed.movementEventList[0].timing.startTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[2].state_time_speed.movementEventList[0].timing.nextTime", + "message": "DE_TimeMark is missing" + } + ], + "utcTimeStamp": "2024-06-04T16:23:29.164Z", + "schemaVersion": 1, + "recordGeneratedAt": { + "$date": { + "$numberLong": "1717518209172" + } + }, + "revision": 0, + "states": [ + { + "stateTimeSpeed": [ + { + "eventState": "PROTECTED_CLEARANCE", + "timing": { + "minEndTime": "2024-06-04T16:03:18Z", + "maxEndTime": "2024-06-04T16:03:18Z" + } + } + ], + "signalGroup": 2 + }, + { + "stateTimeSpeed": [ + { + "eventState": "STOP_AND_REMAIN", + "timing": { + "minEndTime": "2024-06-04T16:03:21.1Z", + "maxEndTime": "2024-06-04T16:03:21.1Z" + } + } + ], + "signalGroup": 4 + }, + { + "stateTimeSpeed": [ + { + "eventState": "PROTECTED_CLEARANCE", + "timing": { + "minEndTime": "2024-06-04T16:03:18Z", + "maxEndTime": "2024-06-04T16:03:18Z" + } + } + ], + "signalGroup": 6 + } + ], + "messageType": "SPAT", + "cti4501Conformant": false, + "intersectionId": 12109, + "odeReceivedAt": "2024-06-04T16:23:29.164Z", + "originIp": "10.11.81.12", + "status": { + "failureFlash": false, + "noValidSPATisAvailableAtThisTime": false, + "fixedTimeOperation": false, + "standbyOperation": false, + "trafficDependentOperation": false, + "manualControlIsEnabled": false, + "off": false, + "stopTimeIsActivated": false, + "recentChangeInMAPassignedLanesIDsUsed": false, + "recentMAPmessageUpdate": false, + "failureMode": false, + "noValidMAPisAvailableAtThisTime": false, + "signalPriorityIsActive": false, + "preemptIsActive": false + } +},{ + "_id": { + "$oid": "665f3fc18c25216ef4f5405c" + }, + "validationMessages": [ + { + "schemaPath": "#/$defs/J2735RoadRegulatorID/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].id.region", + "message": "$.payload.data.intersectionStateList.intersectionStatelist[0].id.region: null found, integer expected" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[0].state_time_speed.movementEventList[0].timing.startTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[0].state_time_speed.movementEventList[0].timing.nextTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[1].state_time_speed.movementEventList[0].timing.startTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[1].state_time_speed.movementEventList[0].timing.nextTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[2].state_time_speed.movementEventList[0].timing.startTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[2].state_time_speed.movementEventList[0].timing.nextTime", + "message": "DE_TimeMark is missing" + } + ], + "utcTimeStamp": "2024-06-04T16:23:29.27Z", + "schemaVersion": 1, + "recordGeneratedAt": { + "$date": { + "$numberLong": "1717518209277" + } + }, + "revision": 0, + "states": [ + { + "stateTimeSpeed": [ + { + "eventState": "PROTECTED_CLEARANCE", + "timing": { + "minEndTime": "2024-06-04T16:03:18Z", + "maxEndTime": "2024-06-04T16:03:18Z" + } + } + ], + "signalGroup": 2 + }, + { + "stateTimeSpeed": [ + { + "eventState": "STOP_AND_REMAIN", + "timing": { + "minEndTime": "2024-06-04T16:03:21.1Z", + "maxEndTime": "2024-06-04T16:03:21.1Z" + } + } + ], + "signalGroup": 4 + }, + { + "stateTimeSpeed": [ + { + "eventState": "PROTECTED_CLEARANCE", + "timing": { + "minEndTime": "2024-06-04T16:03:18Z", + "maxEndTime": "2024-06-04T16:03:18Z" + } + } + ], + "signalGroup": 6 + } + ], + "messageType": "SPAT", + "cti4501Conformant": false, + "intersectionId": 12109, + "odeReceivedAt": "2024-06-04T16:23:29.27Z", + "originIp": "10.11.81.12", + "status": { + "failureFlash": false, + "noValidSPATisAvailableAtThisTime": false, + "fixedTimeOperation": false, + "standbyOperation": false, + "trafficDependentOperation": false, + "manualControlIsEnabled": false, + "off": false, + "stopTimeIsActivated": false, + "recentChangeInMAPassignedLanesIDsUsed": false, + "recentMAPmessageUpdate": false, + "failureMode": false, + "noValidMAPisAvailableAtThisTime": false, + "signalPriorityIsActive": false, + "preemptIsActive": false + } +},{ + "_id": { + "$oid": "665f3fc18c25216ef4f5405d" + }, + "validationMessages": [ + { + "schemaPath": "#/$defs/J2735RoadRegulatorID/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].id.region", + "message": "$.payload.data.intersectionStateList.intersectionStatelist[0].id.region: null found, integer expected" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[0].state_time_speed.movementEventList[0].timing.startTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[0].state_time_speed.movementEventList[0].timing.nextTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[1].state_time_speed.movementEventList[0].timing.startTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[1].state_time_speed.movementEventList[0].timing.nextTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[2].state_time_speed.movementEventList[0].timing.startTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[2].state_time_speed.movementEventList[0].timing.nextTime", + "message": "DE_TimeMark is missing" + } + ], + "utcTimeStamp": "2024-06-04T16:23:29.379Z", + "schemaVersion": 1, + "recordGeneratedAt": { + "$date": { + "$numberLong": "1717518209385" + } + }, + "revision": 0, + "states": [ + { + "stateTimeSpeed": [ + { + "eventState": "PROTECTED_CLEARANCE", + "timing": { + "minEndTime": "2024-06-04T16:03:18Z", + "maxEndTime": "2024-06-04T16:03:18Z" + } + } + ], + "signalGroup": 2 + }, + { + "stateTimeSpeed": [ + { + "eventState": "STOP_AND_REMAIN", + "timing": { + "minEndTime": "2024-06-04T16:03:21.1Z", + "maxEndTime": "2024-06-04T16:03:21.1Z" + } + } + ], + "signalGroup": 4 + }, + { + "stateTimeSpeed": [ + { + "eventState": "PROTECTED_CLEARANCE", + "timing": { + "minEndTime": "2024-06-04T16:03:18Z", + "maxEndTime": "2024-06-04T16:03:18Z" + } + } + ], + "signalGroup": 6 + } + ], + "messageType": "SPAT", + "cti4501Conformant": false, + "intersectionId": 12109, + "odeReceivedAt": "2024-06-04T16:23:29.379Z", + "originIp": "10.11.81.12", + "status": { + "failureFlash": false, + "noValidSPATisAvailableAtThisTime": false, + "fixedTimeOperation": false, + "standbyOperation": false, + "trafficDependentOperation": false, + "manualControlIsEnabled": false, + "off": false, + "stopTimeIsActivated": false, + "recentChangeInMAPassignedLanesIDsUsed": false, + "recentMAPmessageUpdate": false, + "failureMode": false, + "noValidMAPisAvailableAtThisTime": false, + "signalPriorityIsActive": false, + "preemptIsActive": false + } +},{ + "_id": { + "$oid": "665f3fc18c25216ef4f5405e" + }, + "validationMessages": [ + { + "schemaPath": "#/$defs/J2735RoadRegulatorID/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].id.region", + "message": "$.payload.data.intersectionStateList.intersectionStatelist[0].id.region: null found, integer expected" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[0].state_time_speed.movementEventList[0].timing.startTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[0].state_time_speed.movementEventList[0].timing.nextTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[1].state_time_speed.movementEventList[0].timing.startTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[1].state_time_speed.movementEventList[0].timing.nextTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[2].state_time_speed.movementEventList[0].timing.startTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[2].state_time_speed.movementEventList[0].timing.nextTime", + "message": "DE_TimeMark is missing" + } + ], + "utcTimeStamp": "2024-06-04T16:23:29.486Z", + "schemaVersion": 1, + "recordGeneratedAt": { + "$date": { + "$numberLong": "1717518209490" + } + }, + "revision": 0, + "states": [ + { + "stateTimeSpeed": [ + { + "eventState": "PROTECTED_CLEARANCE", + "timing": { + "minEndTime": "2024-06-04T16:03:18Z", + "maxEndTime": "2024-06-04T16:03:18Z" + } + } + ], + "signalGroup": 2 + }, + { + "stateTimeSpeed": [ + { + "eventState": "STOP_AND_REMAIN", + "timing": { + "minEndTime": "2024-06-04T16:03:21.1Z", + "maxEndTime": "2024-06-04T16:03:21.1Z" + } + } + ], + "signalGroup": 4 + }, + { + "stateTimeSpeed": [ + { + "eventState": "PROTECTED_CLEARANCE", + "timing": { + "minEndTime": "2024-06-04T16:03:18Z", + "maxEndTime": "2024-06-04T16:03:18Z" + } + } + ], + "signalGroup": 6 + } + ], + "messageType": "SPAT", + "cti4501Conformant": false, + "intersectionId": 12109, + "odeReceivedAt": "2024-06-04T16:23:29.486Z", + "originIp": "10.11.81.12", + "status": { + "failureFlash": false, + "noValidSPATisAvailableAtThisTime": false, + "fixedTimeOperation": false, + "standbyOperation": false, + "trafficDependentOperation": false, + "manualControlIsEnabled": false, + "off": false, + "stopTimeIsActivated": false, + "recentChangeInMAPassignedLanesIDsUsed": false, + "recentMAPmessageUpdate": false, + "failureMode": false, + "noValidMAPisAvailableAtThisTime": false, + "signalPriorityIsActive": false, + "preemptIsActive": false + } +},{ + "_id": { + "$oid": "665f3fc18c25216ef4f5405f" + }, + "validationMessages": [ + { + "schemaPath": "#/$defs/J2735RoadRegulatorID/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].id.region", + "message": "$.payload.data.intersectionStateList.intersectionStatelist[0].id.region: null found, integer expected" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[0].state_time_speed.movementEventList[0].timing.startTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[0].state_time_speed.movementEventList[0].timing.nextTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[1].state_time_speed.movementEventList[0].timing.startTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[1].state_time_speed.movementEventList[0].timing.nextTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[2].state_time_speed.movementEventList[0].timing.startTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[2].state_time_speed.movementEventList[0].timing.nextTime", + "message": "DE_TimeMark is missing" + } + ], + "utcTimeStamp": "2024-06-04T16:23:29.594Z", + "schemaVersion": 1, + "recordGeneratedAt": { + "$date": { + "$numberLong": "1717518209598" + } + }, + "revision": 0, + "states": [ + { + "stateTimeSpeed": [ + { + "eventState": "PROTECTED_CLEARANCE", + "timing": { + "minEndTime": "2024-06-04T16:03:18Z", + "maxEndTime": "2024-06-04T16:03:18Z" + } + } + ], + "signalGroup": 2 + }, + { + "stateTimeSpeed": [ + { + "eventState": "STOP_AND_REMAIN", + "timing": { + "minEndTime": "2024-06-04T16:03:21.1Z", + "maxEndTime": "2024-06-04T16:03:21.1Z" + } + } + ], + "signalGroup": 4 + }, + { + "stateTimeSpeed": [ + { + "eventState": "PROTECTED_CLEARANCE", + "timing": { + "minEndTime": "2024-06-04T16:03:18Z", + "maxEndTime": "2024-06-04T16:03:18Z" + } + } + ], + "signalGroup": 6 + } + ], + "messageType": "SPAT", + "cti4501Conformant": false, + "intersectionId": 12109, + "odeReceivedAt": "2024-06-04T16:23:29.594Z", + "originIp": "10.11.81.12", + "status": { + "failureFlash": false, + "noValidSPATisAvailableAtThisTime": false, + "fixedTimeOperation": false, + "standbyOperation": false, + "trafficDependentOperation": false, + "manualControlIsEnabled": false, + "off": false, + "stopTimeIsActivated": false, + "recentChangeInMAPassignedLanesIDsUsed": false, + "recentMAPmessageUpdate": false, + "failureMode": false, + "noValidMAPisAvailableAtThisTime": false, + "signalPriorityIsActive": false, + "preemptIsActive": false + } +},{ + "_id": { + "$oid": "665f3fc18c25216ef4f54060" + }, + "validationMessages": [ + { + "schemaPath": "#/$defs/J2735RoadRegulatorID/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].id.region", + "message": "$.payload.data.intersectionStateList.intersectionStatelist[0].id.region: null found, integer expected" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[0].state_time_speed.movementEventList[0].timing.startTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[0].state_time_speed.movementEventList[0].timing.nextTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[1].state_time_speed.movementEventList[0].timing.startTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[1].state_time_speed.movementEventList[0].timing.nextTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[2].state_time_speed.movementEventList[0].timing.startTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[2].state_time_speed.movementEventList[0].timing.nextTime", + "message": "DE_TimeMark is missing" + } + ], + "utcTimeStamp": "2024-06-04T16:23:29.701Z", + "schemaVersion": 1, + "recordGeneratedAt": { + "$date": { + "$numberLong": "1717518209706" + } + }, + "revision": 0, + "states": [ + { + "stateTimeSpeed": [ + { + "eventState": "PROTECTED_CLEARANCE", + "timing": { + "minEndTime": "2024-06-04T16:03:18Z", + "maxEndTime": "2024-06-04T16:03:18Z" + } + } + ], + "signalGroup": 2 + }, + { + "stateTimeSpeed": [ + { + "eventState": "STOP_AND_REMAIN", + "timing": { + "minEndTime": "2024-06-04T16:03:21.1Z", + "maxEndTime": "2024-06-04T16:03:21.1Z" + } + } + ], + "signalGroup": 4 + }, + { + "stateTimeSpeed": [ + { + "eventState": "PROTECTED_CLEARANCE", + "timing": { + "minEndTime": "2024-06-04T16:03:18Z", + "maxEndTime": "2024-06-04T16:03:18Z" + } + } + ], + "signalGroup": 6 + } + ], + "messageType": "SPAT", + "cti4501Conformant": false, + "intersectionId": 12109, + "odeReceivedAt": "2024-06-04T16:23:29.701Z", + "originIp": "10.11.81.12", + "status": { + "failureFlash": false, + "noValidSPATisAvailableAtThisTime": false, + "fixedTimeOperation": false, + "standbyOperation": false, + "trafficDependentOperation": false, + "manualControlIsEnabled": false, + "off": false, + "stopTimeIsActivated": false, + "recentChangeInMAPassignedLanesIDsUsed": false, + "recentMAPmessageUpdate": false, + "failureMode": false, + "noValidMAPisAvailableAtThisTime": false, + "signalPriorityIsActive": false, + "preemptIsActive": false + } +},{ + "_id": { + "$oid": "665f3fc18c25216ef4f54061" + }, + "validationMessages": [ + { + "schemaPath": "#/$defs/J2735RoadRegulatorID/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].id.region", + "message": "$.payload.data.intersectionStateList.intersectionStatelist[0].id.region: null found, integer expected" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[0].state_time_speed.movementEventList[0].timing.startTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[0].state_time_speed.movementEventList[0].timing.nextTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[1].state_time_speed.movementEventList[0].timing.startTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[1].state_time_speed.movementEventList[0].timing.nextTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[2].state_time_speed.movementEventList[0].timing.startTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[2].state_time_speed.movementEventList[0].timing.nextTime", + "message": "DE_TimeMark is missing" + } + ], + "utcTimeStamp": "2024-06-04T16:23:29.809Z", + "schemaVersion": 1, + "recordGeneratedAt": { + "$date": { + "$numberLong": "1717518209810" + } + }, + "revision": 0, + "states": [ + { + "stateTimeSpeed": [ + { + "eventState": "PROTECTED_CLEARANCE", + "timing": { + "minEndTime": "2024-06-04T16:03:18Z", + "maxEndTime": "2024-06-04T16:03:18Z" + } + } + ], + "signalGroup": 2 + }, + { + "stateTimeSpeed": [ + { + "eventState": "STOP_AND_REMAIN", + "timing": { + "minEndTime": "2024-06-04T16:03:21.1Z", + "maxEndTime": "2024-06-04T16:03:21.1Z" + } + } + ], + "signalGroup": 4 + }, + { + "stateTimeSpeed": [ + { + "eventState": "PROTECTED_CLEARANCE", + "timing": { + "minEndTime": "2024-06-04T16:03:18Z", + "maxEndTime": "2024-06-04T16:03:18Z" + } + } + ], + "signalGroup": 6 + } + ], + "messageType": "SPAT", + "cti4501Conformant": false, + "intersectionId": 12109, + "odeReceivedAt": "2024-06-04T16:23:29.809Z", + "originIp": "10.11.81.12", + "status": { + "failureFlash": false, + "noValidSPATisAvailableAtThisTime": false, + "fixedTimeOperation": false, + "standbyOperation": false, + "trafficDependentOperation": false, + "manualControlIsEnabled": false, + "off": false, + "stopTimeIsActivated": false, + "recentChangeInMAPassignedLanesIDsUsed": false, + "recentMAPmessageUpdate": false, + "failureMode": false, + "noValidMAPisAvailableAtThisTime": false, + "signalPriorityIsActive": false, + "preemptIsActive": false + } +},{ + "_id": { + "$oid": "665f3fc18c25216ef4f54062" + }, + "validationMessages": [ + { + "schemaPath": "#/$defs/J2735RoadRegulatorID/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].id.region", + "message": "$.payload.data.intersectionStateList.intersectionStatelist[0].id.region: null found, integer expected" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[0].state_time_speed.movementEventList[0].timing.startTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[0].state_time_speed.movementEventList[0].timing.nextTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[1].state_time_speed.movementEventList[0].timing.startTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[1].state_time_speed.movementEventList[0].timing.nextTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[2].state_time_speed.movementEventList[0].timing.startTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[2].state_time_speed.movementEventList[0].timing.nextTime", + "message": "DE_TimeMark is missing" + } + ], + "utcTimeStamp": "2024-06-04T16:23:29.916Z", + "schemaVersion": 1, + "recordGeneratedAt": { + "$date": { + "$numberLong": "1717518209917" + } + }, + "revision": 0, + "states": [ + { + "stateTimeSpeed": [ + { + "eventState": "PROTECTED_CLEARANCE", + "timing": { + "minEndTime": "2024-06-04T16:03:18Z", + "maxEndTime": "2024-06-04T16:03:18Z" + } + } + ], + "signalGroup": 2 + }, + { + "stateTimeSpeed": [ + { + "eventState": "STOP_AND_REMAIN", + "timing": { + "minEndTime": "2024-06-04T16:03:21.1Z", + "maxEndTime": "2024-06-04T16:03:21.1Z" + } + } + ], + "signalGroup": 4 + }, + { + "stateTimeSpeed": [ + { + "eventState": "PROTECTED_CLEARANCE", + "timing": { + "minEndTime": "2024-06-04T16:03:18Z", + "maxEndTime": "2024-06-04T16:03:18Z" + } + } + ], + "signalGroup": 6 + } + ], + "messageType": "SPAT", + "cti4501Conformant": false, + "intersectionId": 12109, + "odeReceivedAt": "2024-06-04T16:23:29.916Z", + "originIp": "10.11.81.12", + "status": { + "failureFlash": false, + "noValidSPATisAvailableAtThisTime": false, + "fixedTimeOperation": false, + "standbyOperation": false, + "trafficDependentOperation": false, + "manualControlIsEnabled": false, + "off": false, + "stopTimeIsActivated": false, + "recentChangeInMAPassignedLanesIDsUsed": false, + "recentMAPmessageUpdate": false, + "failureMode": false, + "noValidMAPisAvailableAtThisTime": false, + "signalPriorityIsActive": false, + "preemptIsActive": false + } +},{ + "_id": { + "$oid": "665f3fc18c25216ef4f54063" + }, + "validationMessages": [ + { + "schemaPath": "#/$defs/J2735RoadRegulatorID/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].id.region", + "message": "$.payload.data.intersectionStateList.intersectionStatelist[0].id.region: null found, integer expected" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[0].state_time_speed.movementEventList[0].timing.startTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[0].state_time_speed.movementEventList[0].timing.nextTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[1].state_time_speed.movementEventList[0].timing.startTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[1].state_time_speed.movementEventList[0].timing.nextTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[2].state_time_speed.movementEventList[0].timing.startTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[2].state_time_speed.movementEventList[0].timing.nextTime", + "message": "DE_TimeMark is missing" + } + ], + "utcTimeStamp": "2024-06-04T16:23:30.024Z", + "schemaVersion": 1, + "recordGeneratedAt": { + "$date": { + "$numberLong": "1717518210040" + } + }, + "revision": 0, + "states": [ + { + "stateTimeSpeed": [ + { + "eventState": "PROTECTED_CLEARANCE", + "timing": { + "minEndTime": "2024-06-04T16:03:18Z", + "maxEndTime": "2024-06-04T16:03:18Z" + } + } + ], + "signalGroup": 2 + }, + { + "stateTimeSpeed": [ + { + "eventState": "STOP_AND_REMAIN", + "timing": { + "minEndTime": "2024-06-04T16:03:21.1Z", + "maxEndTime": "2024-06-04T16:03:21.1Z" + } + } + ], + "signalGroup": 4 + }, + { + "stateTimeSpeed": [ + { + "eventState": "PROTECTED_CLEARANCE", + "timing": { + "minEndTime": "2024-06-04T16:03:18Z", + "maxEndTime": "2024-06-04T16:03:18Z" + } + } + ], + "signalGroup": 6 + } + ], + "messageType": "SPAT", + "cti4501Conformant": false, + "intersectionId": 12109, + "odeReceivedAt": "2024-06-04T16:23:30.024Z", + "originIp": "10.11.81.12", + "status": { + "failureFlash": false, + "noValidSPATisAvailableAtThisTime": false, + "fixedTimeOperation": false, + "standbyOperation": false, + "trafficDependentOperation": false, + "manualControlIsEnabled": false, + "off": false, + "stopTimeIsActivated": false, + "recentChangeInMAPassignedLanesIDsUsed": false, + "recentMAPmessageUpdate": false, + "failureMode": false, + "noValidMAPisAvailableAtThisTime": false, + "signalPriorityIsActive": false, + "preemptIsActive": false + } +},{ + "_id": { + "$oid": "665f3fc18c25216ef4f54064" + }, + "validationMessages": [ + { + "schemaPath": "#/$defs/J2735RoadRegulatorID/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].id.region", + "message": "$.payload.data.intersectionStateList.intersectionStatelist[0].id.region: null found, integer expected" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[0].state_time_speed.movementEventList[0].timing.startTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[0].state_time_speed.movementEventList[0].timing.nextTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[1].state_time_speed.movementEventList[0].timing.startTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[1].state_time_speed.movementEventList[0].timing.nextTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[2].state_time_speed.movementEventList[0].timing.startTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[2].state_time_speed.movementEventList[0].timing.nextTime", + "message": "DE_TimeMark is missing" + } + ], + "utcTimeStamp": "2024-06-04T16:23:30.13Z", + "schemaVersion": 1, + "recordGeneratedAt": { + "$date": { + "$numberLong": "1717518210131" + } + }, + "revision": 0, + "states": [ + { + "stateTimeSpeed": [ + { + "eventState": "PROTECTED_CLEARANCE", + "timing": { + "minEndTime": "2024-06-04T16:03:18Z", + "maxEndTime": "2024-06-04T16:03:18Z" + } + } + ], + "signalGroup": 2 + }, + { + "stateTimeSpeed": [ + { + "eventState": "STOP_AND_REMAIN", + "timing": { + "minEndTime": "2024-06-04T16:03:21.1Z", + "maxEndTime": "2024-06-04T16:03:21.1Z" + } + } + ], + "signalGroup": 4 + }, + { + "stateTimeSpeed": [ + { + "eventState": "PROTECTED_CLEARANCE", + "timing": { + "minEndTime": "2024-06-04T16:03:18Z", + "maxEndTime": "2024-06-04T16:03:18Z" + } + } + ], + "signalGroup": 6 + } + ], + "messageType": "SPAT", + "cti4501Conformant": false, + "intersectionId": 12109, + "odeReceivedAt": "2024-06-04T16:23:30.13Z", + "originIp": "10.11.81.12", + "status": { + "failureFlash": false, + "noValidSPATisAvailableAtThisTime": false, + "fixedTimeOperation": false, + "standbyOperation": false, + "trafficDependentOperation": false, + "manualControlIsEnabled": false, + "off": false, + "stopTimeIsActivated": false, + "recentChangeInMAPassignedLanesIDsUsed": false, + "recentMAPmessageUpdate": false, + "failureMode": false, + "noValidMAPisAvailableAtThisTime": false, + "signalPriorityIsActive": false, + "preemptIsActive": false + } +},{ + "_id": { + "$oid": "665f3fc18c25216ef4f54065" + }, + "validationMessages": [ + { + "schemaPath": "#/$defs/J2735RoadRegulatorID/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].id.region", + "message": "$.payload.data.intersectionStateList.intersectionStatelist[0].id.region: null found, integer expected" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[0].state_time_speed.movementEventList[0].timing.startTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[0].state_time_speed.movementEventList[0].timing.nextTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[1].state_time_speed.movementEventList[0].timing.startTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[1].state_time_speed.movementEventList[0].timing.nextTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[2].state_time_speed.movementEventList[0].timing.startTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[2].state_time_speed.movementEventList[0].timing.nextTime", + "message": "DE_TimeMark is missing" + } + ], + "utcTimeStamp": "2024-06-04T16:23:30.238Z", + "schemaVersion": 1, + "recordGeneratedAt": { + "$date": { + "$numberLong": "1717518210251" + } + }, + "revision": 0, + "states": [ + { + "stateTimeSpeed": [ + { + "eventState": "PROTECTED_CLEARANCE", + "timing": { + "minEndTime": "2024-06-04T16:03:17Z", + "maxEndTime": "2024-06-04T16:03:17Z" + } + } + ], + "signalGroup": 2 + }, + { + "stateTimeSpeed": [ + { + "eventState": "STOP_AND_REMAIN", + "timing": { + "minEndTime": "2024-06-04T16:03:20.1Z", + "maxEndTime": "2024-06-04T16:03:20.1Z" + } + } + ], + "signalGroup": 4 + }, + { + "stateTimeSpeed": [ + { + "eventState": "PROTECTED_CLEARANCE", + "timing": { + "minEndTime": "2024-06-04T16:03:17Z", + "maxEndTime": "2024-06-04T16:03:17Z" + } + } + ], + "signalGroup": 6 + } + ], + "messageType": "SPAT", + "cti4501Conformant": false, + "intersectionId": 12109, + "odeReceivedAt": "2024-06-04T16:23:30.238Z", + "originIp": "10.11.81.12", + "status": { + "failureFlash": false, + "noValidSPATisAvailableAtThisTime": false, + "fixedTimeOperation": false, + "standbyOperation": false, + "trafficDependentOperation": false, + "manualControlIsEnabled": false, + "off": false, + "stopTimeIsActivated": false, + "recentChangeInMAPassignedLanesIDsUsed": false, + "recentMAPmessageUpdate": false, + "failureMode": false, + "noValidMAPisAvailableAtThisTime": false, + "signalPriorityIsActive": false, + "preemptIsActive": false + } +},{ + "_id": { + "$oid": "665f3fc18c25216ef4f54066" + }, + "validationMessages": [ + { + "schemaPath": "#/$defs/J2735RoadRegulatorID/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].id.region", + "message": "$.payload.data.intersectionStateList.intersectionStatelist[0].id.region: null found, integer expected" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[0].state_time_speed.movementEventList[0].timing.startTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[0].state_time_speed.movementEventList[0].timing.nextTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[1].state_time_speed.movementEventList[0].timing.startTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[1].state_time_speed.movementEventList[0].timing.nextTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[2].state_time_speed.movementEventList[0].timing.startTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[2].state_time_speed.movementEventList[0].timing.nextTime", + "message": "DE_TimeMark is missing" + } + ], + "utcTimeStamp": "2024-06-04T16:23:30.33Z", + "schemaVersion": 1, + "recordGeneratedAt": { + "$date": { + "$numberLong": "1717518210343" + } + }, + "revision": 0, + "states": [ + { + "stateTimeSpeed": [ + { + "eventState": "PROTECTED_CLEARANCE", + "timing": { + "minEndTime": "2024-06-04T16:03:17Z", + "maxEndTime": "2024-06-04T16:03:17Z" + } + } + ], + "signalGroup": 2 + }, + { + "stateTimeSpeed": [ + { + "eventState": "STOP_AND_REMAIN", + "timing": { + "minEndTime": "2024-06-04T16:03:20.1Z", + "maxEndTime": "2024-06-04T16:03:20.1Z" + } + } + ], + "signalGroup": 4 + }, + { + "stateTimeSpeed": [ + { + "eventState": "PROTECTED_CLEARANCE", + "timing": { + "minEndTime": "2024-06-04T16:03:17Z", + "maxEndTime": "2024-06-04T16:03:17Z" + } + } + ], + "signalGroup": 6 + } + ], + "messageType": "SPAT", + "cti4501Conformant": false, + "intersectionId": 12109, + "odeReceivedAt": "2024-06-04T16:23:30.33Z", + "originIp": "10.11.81.12", + "status": { + "failureFlash": false, + "noValidSPATisAvailableAtThisTime": false, + "fixedTimeOperation": false, + "standbyOperation": false, + "trafficDependentOperation": false, + "manualControlIsEnabled": false, + "off": false, + "stopTimeIsActivated": false, + "recentChangeInMAPassignedLanesIDsUsed": false, + "recentMAPmessageUpdate": false, + "failureMode": false, + "noValidMAPisAvailableAtThisTime": false, + "signalPriorityIsActive": false, + "preemptIsActive": false + } +},{ + "_id": { + "$oid": "665f3fc18c25216ef4f54067" + }, + "validationMessages": [ + { + "schemaPath": "#/$defs/J2735RoadRegulatorID/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].id.region", + "message": "$.payload.data.intersectionStateList.intersectionStatelist[0].id.region: null found, integer expected" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[0].state_time_speed.movementEventList[0].timing.startTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[0].state_time_speed.movementEventList[0].timing.nextTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[1].state_time_speed.movementEventList[0].timing.startTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[1].state_time_speed.movementEventList[0].timing.nextTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[2].state_time_speed.movementEventList[0].timing.startTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[2].state_time_speed.movementEventList[0].timing.nextTime", + "message": "DE_TimeMark is missing" + } + ], + "utcTimeStamp": "2024-06-04T16:23:30.438Z", + "schemaVersion": 1, + "recordGeneratedAt": { + "$date": { + "$numberLong": "1717518210450" + } + }, + "revision": 0, + "states": [ + { + "stateTimeSpeed": [ + { + "eventState": "PROTECTED_CLEARANCE", + "timing": { + "minEndTime": "2024-06-04T16:03:17Z", + "maxEndTime": "2024-06-04T16:03:17Z" + } + } + ], + "signalGroup": 2 + }, + { + "stateTimeSpeed": [ + { + "eventState": "STOP_AND_REMAIN", + "timing": { + "minEndTime": "2024-06-04T16:03:20.1Z", + "maxEndTime": "2024-06-04T16:03:20.1Z" + } + } + ], + "signalGroup": 4 + }, + { + "stateTimeSpeed": [ + { + "eventState": "PROTECTED_CLEARANCE", + "timing": { + "minEndTime": "2024-06-04T16:03:17Z", + "maxEndTime": "2024-06-04T16:03:17Z" + } + } + ], + "signalGroup": 6 + } + ], + "messageType": "SPAT", + "cti4501Conformant": false, + "intersectionId": 12109, + "odeReceivedAt": "2024-06-04T16:23:30.438Z", + "originIp": "10.11.81.12", + "status": { + "failureFlash": false, + "noValidSPATisAvailableAtThisTime": false, + "fixedTimeOperation": false, + "standbyOperation": false, + "trafficDependentOperation": false, + "manualControlIsEnabled": false, + "off": false, + "stopTimeIsActivated": false, + "recentChangeInMAPassignedLanesIDsUsed": false, + "recentMAPmessageUpdate": false, + "failureMode": false, + "noValidMAPisAvailableAtThisTime": false, + "signalPriorityIsActive": false, + "preemptIsActive": false + } +},{ + "_id": { + "$oid": "665f3fc18c25216ef4f54068" + }, + "validationMessages": [ + { + "schemaPath": "#/$defs/J2735RoadRegulatorID/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].id.region", + "message": "$.payload.data.intersectionStateList.intersectionStatelist[0].id.region: null found, integer expected" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[0].state_time_speed.movementEventList[0].timing.startTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[0].state_time_speed.movementEventList[0].timing.nextTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[1].state_time_speed.movementEventList[0].timing.startTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[1].state_time_speed.movementEventList[0].timing.nextTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[2].state_time_speed.movementEventList[0].timing.startTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[2].state_time_speed.movementEventList[0].timing.nextTime", + "message": "DE_TimeMark is missing" + } + ], + "utcTimeStamp": "2024-06-04T16:23:30.546Z", + "schemaVersion": 1, + "recordGeneratedAt": { + "$date": { + "$numberLong": "1717518210556" + } + }, + "revision": 0, + "states": [ + { + "stateTimeSpeed": [ + { + "eventState": "PROTECTED_CLEARANCE", + "timing": { + "minEndTime": "2024-06-04T16:03:17Z", + "maxEndTime": "2024-06-04T16:03:17Z" + } + } + ], + "signalGroup": 2 + }, + { + "stateTimeSpeed": [ + { + "eventState": "STOP_AND_REMAIN", + "timing": { + "minEndTime": "2024-06-04T16:03:20.1Z", + "maxEndTime": "2024-06-04T16:03:20.1Z" + } + } + ], + "signalGroup": 4 + }, + { + "stateTimeSpeed": [ + { + "eventState": "PROTECTED_CLEARANCE", + "timing": { + "minEndTime": "2024-06-04T16:03:17Z", + "maxEndTime": "2024-06-04T16:03:17Z" + } + } + ], + "signalGroup": 6 + } + ], + "messageType": "SPAT", + "cti4501Conformant": false, + "intersectionId": 12109, + "odeReceivedAt": "2024-06-04T16:23:30.546Z", + "originIp": "10.11.81.12", + "status": { + "failureFlash": false, + "noValidSPATisAvailableAtThisTime": false, + "fixedTimeOperation": false, + "standbyOperation": false, + "trafficDependentOperation": false, + "manualControlIsEnabled": false, + "off": false, + "stopTimeIsActivated": false, + "recentChangeInMAPassignedLanesIDsUsed": false, + "recentMAPmessageUpdate": false, + "failureMode": false, + "noValidMAPisAvailableAtThisTime": false, + "signalPriorityIsActive": false, + "preemptIsActive": false + } +},{ + "_id": { + "$oid": "665f3fc18c25216ef4f54069" + }, + "validationMessages": [ + { + "schemaPath": "#/$defs/J2735RoadRegulatorID/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].id.region", + "message": "$.payload.data.intersectionStateList.intersectionStatelist[0].id.region: null found, integer expected" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[0].state_time_speed.movementEventList[0].timing.startTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[0].state_time_speed.movementEventList[0].timing.nextTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[1].state_time_speed.movementEventList[0].timing.startTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[1].state_time_speed.movementEventList[0].timing.nextTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[2].state_time_speed.movementEventList[0].timing.startTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[2].state_time_speed.movementEventList[0].timing.nextTime", + "message": "DE_TimeMark is missing" + } + ], + "utcTimeStamp": "2024-06-04T16:23:30.654Z", + "schemaVersion": 1, + "recordGeneratedAt": { + "$date": { + "$numberLong": "1717518210663" + } + }, + "revision": 0, + "states": [ + { + "stateTimeSpeed": [ + { + "eventState": "PROTECTED_CLEARANCE", + "timing": { + "minEndTime": "2024-06-04T16:03:17Z", + "maxEndTime": "2024-06-04T16:03:17Z" + } + } + ], + "signalGroup": 2 + }, + { + "stateTimeSpeed": [ + { + "eventState": "STOP_AND_REMAIN", + "timing": { + "minEndTime": "2024-06-04T16:03:20.1Z", + "maxEndTime": "2024-06-04T16:03:20.1Z" + } + } + ], + "signalGroup": 4 + }, + { + "stateTimeSpeed": [ + { + "eventState": "PROTECTED_CLEARANCE", + "timing": { + "minEndTime": "2024-06-04T16:03:17Z", + "maxEndTime": "2024-06-04T16:03:17Z" + } + } + ], + "signalGroup": 6 + } + ], + "messageType": "SPAT", + "cti4501Conformant": false, + "intersectionId": 12109, + "odeReceivedAt": "2024-06-04T16:23:30.654Z", + "originIp": "10.11.81.12", + "status": { + "failureFlash": false, + "noValidSPATisAvailableAtThisTime": false, + "fixedTimeOperation": false, + "standbyOperation": false, + "trafficDependentOperation": false, + "manualControlIsEnabled": false, + "off": false, + "stopTimeIsActivated": false, + "recentChangeInMAPassignedLanesIDsUsed": false, + "recentMAPmessageUpdate": false, + "failureMode": false, + "noValidMAPisAvailableAtThisTime": false, + "signalPriorityIsActive": false, + "preemptIsActive": false + } +},{ + "_id": { + "$oid": "665f3fc18c25216ef4f5406a" + }, + "validationMessages": [ + { + "schemaPath": "#/$defs/J2735RoadRegulatorID/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].id.region", + "message": "$.payload.data.intersectionStateList.intersectionStatelist[0].id.region: null found, integer expected" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[0].state_time_speed.movementEventList[0].timing.startTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[0].state_time_speed.movementEventList[0].timing.nextTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[1].state_time_speed.movementEventList[0].timing.startTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[1].state_time_speed.movementEventList[0].timing.nextTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[2].state_time_speed.movementEventList[0].timing.startTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[2].state_time_speed.movementEventList[0].timing.nextTime", + "message": "DE_TimeMark is missing" + } + ], + "utcTimeStamp": "2024-06-04T16:23:30.76Z", + "schemaVersion": 1, + "recordGeneratedAt": { + "$date": { + "$numberLong": "1717518210771" + } + }, + "revision": 0, + "states": [ + { + "stateTimeSpeed": [ + { + "eventState": "PROTECTED_CLEARANCE", + "timing": { + "minEndTime": "2024-06-04T16:03:17Z", + "maxEndTime": "2024-06-04T16:03:17Z" + } + } + ], + "signalGroup": 2 + }, + { + "stateTimeSpeed": [ + { + "eventState": "STOP_AND_REMAIN", + "timing": { + "minEndTime": "2024-06-04T16:03:20.1Z", + "maxEndTime": "2024-06-04T16:03:20.1Z" + } + } + ], + "signalGroup": 4 + }, + { + "stateTimeSpeed": [ + { + "eventState": "PROTECTED_CLEARANCE", + "timing": { + "minEndTime": "2024-06-04T16:03:17Z", + "maxEndTime": "2024-06-04T16:03:17Z" + } + } + ], + "signalGroup": 6 + } + ], + "messageType": "SPAT", + "cti4501Conformant": false, + "intersectionId": 12109, + "odeReceivedAt": "2024-06-04T16:23:30.76Z", + "originIp": "10.11.81.12", + "status": { + "failureFlash": false, + "noValidSPATisAvailableAtThisTime": false, + "fixedTimeOperation": false, + "standbyOperation": false, + "trafficDependentOperation": false, + "manualControlIsEnabled": false, + "off": false, + "stopTimeIsActivated": false, + "recentChangeInMAPassignedLanesIDsUsed": false, + "recentMAPmessageUpdate": false, + "failureMode": false, + "noValidMAPisAvailableAtThisTime": false, + "signalPriorityIsActive": false, + "preemptIsActive": false + } +},{ + "_id": { + "$oid": "665f3fc18c25216ef4f5406b" + }, + "validationMessages": [ + { + "schemaPath": "#/$defs/J2735RoadRegulatorID/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].id.region", + "message": "$.payload.data.intersectionStateList.intersectionStatelist[0].id.region: null found, integer expected" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[0].state_time_speed.movementEventList[0].timing.startTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[0].state_time_speed.movementEventList[0].timing.nextTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[1].state_time_speed.movementEventList[0].timing.startTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[1].state_time_speed.movementEventList[0].timing.nextTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[2].state_time_speed.movementEventList[0].timing.startTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[2].state_time_speed.movementEventList[0].timing.nextTime", + "message": "DE_TimeMark is missing" + } + ], + "utcTimeStamp": "2024-06-04T16:23:30.867Z", + "schemaVersion": 1, + "recordGeneratedAt": { + "$date": { + "$numberLong": "1717518210878" + } + }, + "revision": 0, + "states": [ + { + "stateTimeSpeed": [ + { + "eventState": "PROTECTED_CLEARANCE", + "timing": { + "minEndTime": "2024-06-04T16:03:17Z", + "maxEndTime": "2024-06-04T16:03:17Z" + } + } + ], + "signalGroup": 2 + }, + { + "stateTimeSpeed": [ + { + "eventState": "STOP_AND_REMAIN", + "timing": { + "minEndTime": "2024-06-04T16:03:20.1Z", + "maxEndTime": "2024-06-04T16:03:20.1Z" + } + } + ], + "signalGroup": 4 + }, + { + "stateTimeSpeed": [ + { + "eventState": "PROTECTED_CLEARANCE", + "timing": { + "minEndTime": "2024-06-04T16:03:17Z", + "maxEndTime": "2024-06-04T16:03:17Z" + } + } + ], + "signalGroup": 6 + } + ], + "messageType": "SPAT", + "cti4501Conformant": false, + "intersectionId": 12109, + "odeReceivedAt": "2024-06-04T16:23:30.867Z", + "originIp": "10.11.81.12", + "status": { + "failureFlash": false, + "noValidSPATisAvailableAtThisTime": false, + "fixedTimeOperation": false, + "standbyOperation": false, + "trafficDependentOperation": false, + "manualControlIsEnabled": false, + "off": false, + "stopTimeIsActivated": false, + "recentChangeInMAPassignedLanesIDsUsed": false, + "recentMAPmessageUpdate": false, + "failureMode": false, + "noValidMAPisAvailableAtThisTime": false, + "signalPriorityIsActive": false, + "preemptIsActive": false + } +},{ + "_id": { + "$oid": "665f3fc18c25216ef4f5406c" + }, + "validationMessages": [ + { + "schemaPath": "#/$defs/J2735RoadRegulatorID/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].id.region", + "message": "$.payload.data.intersectionStateList.intersectionStatelist[0].id.region: null found, integer expected" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[0].state_time_speed.movementEventList[0].timing.startTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[0].state_time_speed.movementEventList[0].timing.nextTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[1].state_time_speed.movementEventList[0].timing.startTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[1].state_time_speed.movementEventList[0].timing.nextTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[2].state_time_speed.movementEventList[0].timing.startTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[2].state_time_speed.movementEventList[0].timing.nextTime", + "message": "DE_TimeMark is missing" + } + ], + "utcTimeStamp": "2024-06-04T16:23:30.975Z", + "schemaVersion": 1, + "recordGeneratedAt": { + "$date": { + "$numberLong": "1717518210985" + } + }, + "revision": 0, + "states": [ + { + "stateTimeSpeed": [ + { + "eventState": "PROTECTED_CLEARANCE", + "timing": { + "minEndTime": "2024-06-04T16:03:17Z", + "maxEndTime": "2024-06-04T16:03:17Z" + } + } + ], + "signalGroup": 2 + }, + { + "stateTimeSpeed": [ + { + "eventState": "STOP_AND_REMAIN", + "timing": { + "minEndTime": "2024-06-04T16:03:20.1Z", + "maxEndTime": "2024-06-04T16:03:20.1Z" + } + } + ], + "signalGroup": 4 + }, + { + "stateTimeSpeed": [ + { + "eventState": "PROTECTED_CLEARANCE", + "timing": { + "minEndTime": "2024-06-04T16:03:17Z", + "maxEndTime": "2024-06-04T16:03:17Z" + } + } + ], + "signalGroup": 6 + } + ], + "messageType": "SPAT", + "cti4501Conformant": false, + "intersectionId": 12109, + "odeReceivedAt": "2024-06-04T16:23:30.975Z", + "originIp": "10.11.81.12", + "status": { + "failureFlash": false, + "noValidSPATisAvailableAtThisTime": false, + "fixedTimeOperation": false, + "standbyOperation": false, + "trafficDependentOperation": false, + "manualControlIsEnabled": false, + "off": false, + "stopTimeIsActivated": false, + "recentChangeInMAPassignedLanesIDsUsed": false, + "recentMAPmessageUpdate": false, + "failureMode": false, + "noValidMAPisAvailableAtThisTime": false, + "signalPriorityIsActive": false, + "preemptIsActive": false + } +},{ + "_id": { + "$oid": "665f3fc18c25216ef4f5406d" + }, + "validationMessages": [ + { + "schemaPath": "#/$defs/J2735RoadRegulatorID/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].id.region", + "message": "$.payload.data.intersectionStateList.intersectionStatelist[0].id.region: null found, integer expected" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[0].state_time_speed.movementEventList[0].timing.startTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[0].state_time_speed.movementEventList[0].timing.nextTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[1].state_time_speed.movementEventList[0].timing.startTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[1].state_time_speed.movementEventList[0].timing.nextTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[2].state_time_speed.movementEventList[0].timing.startTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[2].state_time_speed.movementEventList[0].timing.nextTime", + "message": "DE_TimeMark is missing" + } + ], + "utcTimeStamp": "2024-06-04T16:23:31.082Z", + "schemaVersion": 1, + "recordGeneratedAt": { + "$date": { + "$numberLong": "1717518211092" + } + }, + "revision": 0, + "states": [ + { + "stateTimeSpeed": [ + { + "eventState": "PROTECTED_CLEARANCE", + "timing": { + "minEndTime": "2024-06-04T16:03:17Z", + "maxEndTime": "2024-06-04T16:03:17Z" + } + } + ], + "signalGroup": 2 + }, + { + "stateTimeSpeed": [ + { + "eventState": "STOP_AND_REMAIN", + "timing": { + "minEndTime": "2024-06-04T16:03:20.1Z", + "maxEndTime": "2024-06-04T16:03:20.1Z" + } + } + ], + "signalGroup": 4 + }, + { + "stateTimeSpeed": [ + { + "eventState": "PROTECTED_CLEARANCE", + "timing": { + "minEndTime": "2024-06-04T16:03:17Z", + "maxEndTime": "2024-06-04T16:03:17Z" + } + } + ], + "signalGroup": 6 + } + ], + "messageType": "SPAT", + "cti4501Conformant": false, + "intersectionId": 12109, + "odeReceivedAt": "2024-06-04T16:23:31.082Z", + "originIp": "10.11.81.12", + "status": { + "failureFlash": false, + "noValidSPATisAvailableAtThisTime": false, + "fixedTimeOperation": false, + "standbyOperation": false, + "trafficDependentOperation": false, + "manualControlIsEnabled": false, + "off": false, + "stopTimeIsActivated": false, + "recentChangeInMAPassignedLanesIDsUsed": false, + "recentMAPmessageUpdate": false, + "failureMode": false, + "noValidMAPisAvailableAtThisTime": false, + "signalPriorityIsActive": false, + "preemptIsActive": false + } +},{ + "_id": { + "$oid": "665f3fc18c25216ef4f5406e" + }, + "validationMessages": [ + { + "schemaPath": "#/$defs/J2735RoadRegulatorID/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].id.region", + "message": "$.payload.data.intersectionStateList.intersectionStatelist[0].id.region: null found, integer expected" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[0].state_time_speed.movementEventList[0].timing.startTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[0].state_time_speed.movementEventList[0].timing.nextTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[1].state_time_speed.movementEventList[0].timing.startTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[1].state_time_speed.movementEventList[0].timing.nextTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[2].state_time_speed.movementEventList[0].timing.startTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[2].state_time_speed.movementEventList[0].timing.nextTime", + "message": "DE_TimeMark is missing" + } + ], + "utcTimeStamp": "2024-06-04T16:23:31.189Z", + "schemaVersion": 1, + "recordGeneratedAt": { + "$date": { + "$numberLong": "1717518211199" + } + }, + "revision": 0, + "states": [ + { + "stateTimeSpeed": [ + { + "eventState": "PROTECTED_CLEARANCE", + "timing": { + "minEndTime": "2024-06-04T16:03:17Z", + "maxEndTime": "2024-06-04T16:03:17Z" + } + } + ], + "signalGroup": 2 + }, + { + "stateTimeSpeed": [ + { + "eventState": "STOP_AND_REMAIN", + "timing": { + "minEndTime": "2024-06-04T16:03:20.1Z", + "maxEndTime": "2024-06-04T16:03:20.1Z" + } + } + ], + "signalGroup": 4 + }, + { + "stateTimeSpeed": [ + { + "eventState": "PROTECTED_CLEARANCE", + "timing": { + "minEndTime": "2024-06-04T16:03:17Z", + "maxEndTime": "2024-06-04T16:03:17Z" + } + } + ], + "signalGroup": 6 + } + ], + "messageType": "SPAT", + "cti4501Conformant": false, + "intersectionId": 12109, + "odeReceivedAt": "2024-06-04T16:23:31.189Z", + "originIp": "10.11.81.12", + "status": { + "failureFlash": false, + "noValidSPATisAvailableAtThisTime": false, + "fixedTimeOperation": false, + "standbyOperation": false, + "trafficDependentOperation": false, + "manualControlIsEnabled": false, + "off": false, + "stopTimeIsActivated": false, + "recentChangeInMAPassignedLanesIDsUsed": false, + "recentMAPmessageUpdate": false, + "failureMode": false, + "noValidMAPisAvailableAtThisTime": false, + "signalPriorityIsActive": false, + "preemptIsActive": false + } +},{ + "_id": { + "$oid": "665f3fc18c25216ef4f5406f" + }, + "validationMessages": [ + { + "schemaPath": "#/$defs/J2735RoadRegulatorID/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].id.region", + "message": "$.payload.data.intersectionStateList.intersectionStatelist[0].id.region: null found, integer expected" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[0].state_time_speed.movementEventList[0].timing.startTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[0].state_time_speed.movementEventList[0].timing.nextTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[1].state_time_speed.movementEventList[0].timing.startTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[1].state_time_speed.movementEventList[0].timing.nextTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[2].state_time_speed.movementEventList[0].timing.startTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[2].state_time_speed.movementEventList[0].timing.nextTime", + "message": "DE_TimeMark is missing" + } + ], + "utcTimeStamp": "2024-06-04T16:23:31.296Z", + "schemaVersion": 1, + "recordGeneratedAt": { + "$date": { + "$numberLong": "1717518211306" + } + }, + "revision": 0, + "states": [ + { + "stateTimeSpeed": [ + { + "eventState": "PROTECTED_CLEARANCE", + "timing": { + "minEndTime": "2024-06-04T16:03:19Z", + "maxEndTime": "2024-06-04T16:03:19Z" + } + } + ], + "signalGroup": 2 + }, + { + "stateTimeSpeed": [ + { + "eventState": "STOP_AND_REMAIN", + "timing": { + "minEndTime": "2024-06-04T16:03:22.1Z", + "maxEndTime": "2024-06-04T16:03:22.1Z" + } + } + ], + "signalGroup": 4 + }, + { + "stateTimeSpeed": [ + { + "eventState": "PROTECTED_CLEARANCE", + "timing": { + "minEndTime": "2024-06-04T16:03:19Z", + "maxEndTime": "2024-06-04T16:03:19Z" + } + } + ], + "signalGroup": 6 + } + ], + "messageType": "SPAT", + "cti4501Conformant": false, + "intersectionId": 12109, + "odeReceivedAt": "2024-06-04T16:23:31.296Z", + "originIp": "10.11.81.12", + "status": { + "failureFlash": false, + "noValidSPATisAvailableAtThisTime": false, + "fixedTimeOperation": false, + "standbyOperation": false, + "trafficDependentOperation": false, + "manualControlIsEnabled": false, + "off": false, + "stopTimeIsActivated": false, + "recentChangeInMAPassignedLanesIDsUsed": false, + "recentMAPmessageUpdate": false, + "failureMode": false, + "noValidMAPisAvailableAtThisTime": false, + "signalPriorityIsActive": false, + "preemptIsActive": false + } +},{ + "_id": { + "$oid": "665f3fc18c25216ef4f54070" + }, + "validationMessages": [ + { + "schemaPath": "#/$defs/J2735RoadRegulatorID/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].id.region", + "message": "$.payload.data.intersectionStateList.intersectionStatelist[0].id.region: null found, integer expected" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[0].state_time_speed.movementEventList[0].timing.startTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[0].state_time_speed.movementEventList[0].timing.nextTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[1].state_time_speed.movementEventList[0].timing.startTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[1].state_time_speed.movementEventList[0].timing.nextTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[2].state_time_speed.movementEventList[0].timing.startTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[2].state_time_speed.movementEventList[0].timing.nextTime", + "message": "DE_TimeMark is missing" + } + ], + "utcTimeStamp": "2024-06-04T16:23:31.402Z", + "schemaVersion": 1, + "recordGeneratedAt": { + "$date": { + "$numberLong": "1717518211413" + } + }, + "revision": 0, + "states": [ + { + "stateTimeSpeed": [ + { + "eventState": "PROTECTED_CLEARANCE", + "timing": { + "minEndTime": "2024-06-04T16:03:19Z", + "maxEndTime": "2024-06-04T16:03:19Z" + } + } + ], + "signalGroup": 2 + }, + { + "stateTimeSpeed": [ + { + "eventState": "STOP_AND_REMAIN", + "timing": { + "minEndTime": "2024-06-04T16:03:22.1Z", + "maxEndTime": "2024-06-04T16:03:22.1Z" + } + } + ], + "signalGroup": 4 + }, + { + "stateTimeSpeed": [ + { + "eventState": "PROTECTED_CLEARANCE", + "timing": { + "minEndTime": "2024-06-04T16:03:19Z", + "maxEndTime": "2024-06-04T16:03:19Z" + } + } + ], + "signalGroup": 6 + } + ], + "messageType": "SPAT", + "cti4501Conformant": false, + "intersectionId": 12109, + "odeReceivedAt": "2024-06-04T16:23:31.402Z", + "originIp": "10.11.81.12", + "status": { + "failureFlash": false, + "noValidSPATisAvailableAtThisTime": false, + "fixedTimeOperation": false, + "standbyOperation": false, + "trafficDependentOperation": false, + "manualControlIsEnabled": false, + "off": false, + "stopTimeIsActivated": false, + "recentChangeInMAPassignedLanesIDsUsed": false, + "recentMAPmessageUpdate": false, + "failureMode": false, + "noValidMAPisAvailableAtThisTime": false, + "signalPriorityIsActive": false, + "preemptIsActive": false + } +},{ + "_id": { + "$oid": "665f3fc18c25216ef4f54071" + }, + "validationMessages": [ + { + "schemaPath": "#/$defs/J2735RoadRegulatorID/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].id.region", + "message": "$.payload.data.intersectionStateList.intersectionStatelist[0].id.region: null found, integer expected" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[0].state_time_speed.movementEventList[0].timing.startTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[0].state_time_speed.movementEventList[0].timing.nextTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[1].state_time_speed.movementEventList[0].timing.startTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[1].state_time_speed.movementEventList[0].timing.nextTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[2].state_time_speed.movementEventList[0].timing.startTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[2].state_time_speed.movementEventList[0].timing.nextTime", + "message": "DE_TimeMark is missing" + } + ], + "utcTimeStamp": "2024-06-04T16:23:31.51Z", + "schemaVersion": 1, + "recordGeneratedAt": { + "$date": { + "$numberLong": "1717518211520" + } + }, + "revision": 0, + "states": [ + { + "stateTimeSpeed": [ + { + "eventState": "PROTECTED_CLEARANCE", + "timing": { + "minEndTime": "2024-06-04T16:03:19Z", + "maxEndTime": "2024-06-04T16:03:19Z" + } + } + ], + "signalGroup": 2 + }, + { + "stateTimeSpeed": [ + { + "eventState": "STOP_AND_REMAIN", + "timing": { + "minEndTime": "2024-06-04T16:03:22.1Z", + "maxEndTime": "2024-06-04T16:03:22.1Z" + } + } + ], + "signalGroup": 4 + }, + { + "stateTimeSpeed": [ + { + "eventState": "PROTECTED_CLEARANCE", + "timing": { + "minEndTime": "2024-06-04T16:03:19Z", + "maxEndTime": "2024-06-04T16:03:19Z" + } + } + ], + "signalGroup": 6 + } + ], + "messageType": "SPAT", + "cti4501Conformant": false, + "intersectionId": 12109, + "odeReceivedAt": "2024-06-04T16:23:31.51Z", + "originIp": "10.11.81.12", + "status": { + "failureFlash": false, + "noValidSPATisAvailableAtThisTime": false, + "fixedTimeOperation": false, + "standbyOperation": false, + "trafficDependentOperation": false, + "manualControlIsEnabled": false, + "off": false, + "stopTimeIsActivated": false, + "recentChangeInMAPassignedLanesIDsUsed": false, + "recentMAPmessageUpdate": false, + "failureMode": false, + "noValidMAPisAvailableAtThisTime": false, + "signalPriorityIsActive": false, + "preemptIsActive": false + } +},{ + "_id": { + "$oid": "665f3fc18c25216ef4f54072" + }, + "validationMessages": [ + { + "schemaPath": "#/$defs/J2735RoadRegulatorID/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].id.region", + "message": "$.payload.data.intersectionStateList.intersectionStatelist[0].id.region: null found, integer expected" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[0].state_time_speed.movementEventList[0].timing.startTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[0].state_time_speed.movementEventList[0].timing.nextTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[1].state_time_speed.movementEventList[0].timing.startTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[1].state_time_speed.movementEventList[0].timing.nextTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[2].state_time_speed.movementEventList[0].timing.startTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[2].state_time_speed.movementEventList[0].timing.nextTime", + "message": "DE_TimeMark is missing" + } + ], + "utcTimeStamp": "2024-06-04T16:23:31.616Z", + "schemaVersion": 1, + "recordGeneratedAt": { + "$date": { + "$numberLong": "1717518211628" + } + }, + "revision": 0, + "states": [ + { + "stateTimeSpeed": [ + { + "eventState": "PROTECTED_CLEARANCE", + "timing": { + "minEndTime": "2024-06-04T16:03:19Z", + "maxEndTime": "2024-06-04T16:03:19Z" + } + } + ], + "signalGroup": 2 + }, + { + "stateTimeSpeed": [ + { + "eventState": "STOP_AND_REMAIN", + "timing": { + "minEndTime": "2024-06-04T16:03:22.1Z", + "maxEndTime": "2024-06-04T16:03:22.1Z" + } + } + ], + "signalGroup": 4 + }, + { + "stateTimeSpeed": [ + { + "eventState": "PROTECTED_CLEARANCE", + "timing": { + "minEndTime": "2024-06-04T16:03:19Z", + "maxEndTime": "2024-06-04T16:03:19Z" + } + } + ], + "signalGroup": 6 + } + ], + "messageType": "SPAT", + "cti4501Conformant": false, + "intersectionId": 12109, + "odeReceivedAt": "2024-06-04T16:23:31.616Z", + "originIp": "10.11.81.12", + "status": { + "failureFlash": false, + "noValidSPATisAvailableAtThisTime": false, + "fixedTimeOperation": false, + "standbyOperation": false, + "trafficDependentOperation": false, + "manualControlIsEnabled": false, + "off": false, + "stopTimeIsActivated": false, + "recentChangeInMAPassignedLanesIDsUsed": false, + "recentMAPmessageUpdate": false, + "failureMode": false, + "noValidMAPisAvailableAtThisTime": false, + "signalPriorityIsActive": false, + "preemptIsActive": false + } +},{ + "_id": { + "$oid": "665f3fc18c25216ef4f54073" + }, + "validationMessages": [ + { + "schemaPath": "#/$defs/J2735RoadRegulatorID/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].id.region", + "message": "$.payload.data.intersectionStateList.intersectionStatelist[0].id.region: null found, integer expected" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[0].state_time_speed.movementEventList[0].timing.startTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[0].state_time_speed.movementEventList[0].timing.nextTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[1].state_time_speed.movementEventList[0].timing.startTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[1].state_time_speed.movementEventList[0].timing.nextTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[2].state_time_speed.movementEventList[0].timing.startTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[2].state_time_speed.movementEventList[0].timing.nextTime", + "message": "DE_TimeMark is missing" + } + ], + "utcTimeStamp": "2024-06-04T16:23:31.724Z", + "schemaVersion": 1, + "recordGeneratedAt": { + "$date": { + "$numberLong": "1717518211734" + } + }, + "revision": 0, + "states": [ + { + "stateTimeSpeed": [ + { + "eventState": "PROTECTED_CLEARANCE", + "timing": { + "minEndTime": "2024-06-04T16:03:19Z", + "maxEndTime": "2024-06-04T16:03:19Z" + } + } + ], + "signalGroup": 2 + }, + { + "stateTimeSpeed": [ + { + "eventState": "STOP_AND_REMAIN", + "timing": { + "minEndTime": "2024-06-04T16:03:22.1Z", + "maxEndTime": "2024-06-04T16:03:22.1Z" + } + } + ], + "signalGroup": 4 + }, + { + "stateTimeSpeed": [ + { + "eventState": "PROTECTED_CLEARANCE", + "timing": { + "minEndTime": "2024-06-04T16:03:19Z", + "maxEndTime": "2024-06-04T16:03:19Z" + } + } + ], + "signalGroup": 6 + } + ], + "messageType": "SPAT", + "cti4501Conformant": false, + "intersectionId": 12109, + "odeReceivedAt": "2024-06-04T16:23:31.724Z", + "originIp": "10.11.81.12", + "status": { + "failureFlash": false, + "noValidSPATisAvailableAtThisTime": false, + "fixedTimeOperation": false, + "standbyOperation": false, + "trafficDependentOperation": false, + "manualControlIsEnabled": false, + "off": false, + "stopTimeIsActivated": false, + "recentChangeInMAPassignedLanesIDsUsed": false, + "recentMAPmessageUpdate": false, + "failureMode": false, + "noValidMAPisAvailableAtThisTime": false, + "signalPriorityIsActive": false, + "preemptIsActive": false + } +},{ + "_id": { + "$oid": "665f3fc18c25216ef4f54074" + }, + "validationMessages": [ + { + "schemaPath": "#/$defs/J2735RoadRegulatorID/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].id.region", + "message": "$.payload.data.intersectionStateList.intersectionStatelist[0].id.region: null found, integer expected" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[0].state_time_speed.movementEventList[0].timing.startTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[0].state_time_speed.movementEventList[0].timing.nextTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[1].state_time_speed.movementEventList[0].timing.startTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[1].state_time_speed.movementEventList[0].timing.nextTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[2].state_time_speed.movementEventList[0].timing.startTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[2].state_time_speed.movementEventList[0].timing.nextTime", + "message": "DE_TimeMark is missing" + } + ], + "utcTimeStamp": "2024-06-04T16:23:31.831Z", + "schemaVersion": 1, + "recordGeneratedAt": { + "$date": { + "$numberLong": "1717518211841" + } + }, + "revision": 0, + "states": [ + { + "stateTimeSpeed": [ + { + "eventState": "PROTECTED_CLEARANCE", + "timing": { + "minEndTime": "2024-06-04T16:03:19Z", + "maxEndTime": "2024-06-04T16:03:19Z" + } + } + ], + "signalGroup": 2 + }, + { + "stateTimeSpeed": [ + { + "eventState": "STOP_AND_REMAIN", + "timing": { + "minEndTime": "2024-06-04T16:03:22.1Z", + "maxEndTime": "2024-06-04T16:03:22.1Z" + } + } + ], + "signalGroup": 4 + }, + { + "stateTimeSpeed": [ + { + "eventState": "PROTECTED_CLEARANCE", + "timing": { + "minEndTime": "2024-06-04T16:03:19Z", + "maxEndTime": "2024-06-04T16:03:19Z" + } + } + ], + "signalGroup": 6 + } + ], + "messageType": "SPAT", + "cti4501Conformant": false, + "intersectionId": 12109, + "odeReceivedAt": "2024-06-04T16:23:31.831Z", + "originIp": "10.11.81.12", + "status": { + "failureFlash": false, + "noValidSPATisAvailableAtThisTime": false, + "fixedTimeOperation": false, + "standbyOperation": false, + "trafficDependentOperation": false, + "manualControlIsEnabled": false, + "off": false, + "stopTimeIsActivated": false, + "recentChangeInMAPassignedLanesIDsUsed": false, + "recentMAPmessageUpdate": false, + "failureMode": false, + "noValidMAPisAvailableAtThisTime": false, + "signalPriorityIsActive": false, + "preemptIsActive": false + } +},{ + "_id": { + "$oid": "665f3fc18c25216ef4f54075" + }, + "validationMessages": [ + { + "schemaPath": "#/$defs/J2735RoadRegulatorID/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].id.region", + "message": "$.payload.data.intersectionStateList.intersectionStatelist[0].id.region: null found, integer expected" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[0].state_time_speed.movementEventList[0].timing.startTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[0].state_time_speed.movementEventList[0].timing.nextTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[1].state_time_speed.movementEventList[0].timing.startTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[1].state_time_speed.movementEventList[0].timing.nextTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[2].state_time_speed.movementEventList[0].timing.startTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[2].state_time_speed.movementEventList[0].timing.nextTime", + "message": "DE_TimeMark is missing" + } + ], + "utcTimeStamp": "2024-06-04T16:23:31.937Z", + "schemaVersion": 1, + "recordGeneratedAt": { + "$date": { + "$numberLong": "1717518211949" + } + }, + "revision": 0, + "states": [ + { + "stateTimeSpeed": [ + { + "eventState": "PROTECTED_CLEARANCE", + "timing": { + "minEndTime": "2024-06-04T16:03:19Z", + "maxEndTime": "2024-06-04T16:03:19Z" + } + } + ], + "signalGroup": 2 + }, + { + "stateTimeSpeed": [ + { + "eventState": "STOP_AND_REMAIN", + "timing": { + "minEndTime": "2024-06-04T16:03:22.1Z", + "maxEndTime": "2024-06-04T16:03:22.1Z" + } + } + ], + "signalGroup": 4 + }, + { + "stateTimeSpeed": [ + { + "eventState": "PROTECTED_CLEARANCE", + "timing": { + "minEndTime": "2024-06-04T16:03:19Z", + "maxEndTime": "2024-06-04T16:03:19Z" + } + } + ], + "signalGroup": 6 + } + ], + "messageType": "SPAT", + "cti4501Conformant": false, + "intersectionId": 12109, + "odeReceivedAt": "2024-06-04T16:23:31.937Z", + "originIp": "10.11.81.12", + "status": { + "failureFlash": false, + "noValidSPATisAvailableAtThisTime": false, + "fixedTimeOperation": false, + "standbyOperation": false, + "trafficDependentOperation": false, + "manualControlIsEnabled": false, + "off": false, + "stopTimeIsActivated": false, + "recentChangeInMAPassignedLanesIDsUsed": false, + "recentMAPmessageUpdate": false, + "failureMode": false, + "noValidMAPisAvailableAtThisTime": false, + "signalPriorityIsActive": false, + "preemptIsActive": false + } +},{ + "_id": { + "$oid": "665f3fc18c25216ef4f54076" + }, + "validationMessages": [ + { + "schemaPath": "#/$defs/J2735RoadRegulatorID/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].id.region", + "message": "$.payload.data.intersectionStateList.intersectionStatelist[0].id.region: null found, integer expected" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[0].state_time_speed.movementEventList[0].timing.startTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[0].state_time_speed.movementEventList[0].timing.nextTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[1].state_time_speed.movementEventList[0].timing.startTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[1].state_time_speed.movementEventList[0].timing.nextTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[2].state_time_speed.movementEventList[0].timing.startTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[2].state_time_speed.movementEventList[0].timing.nextTime", + "message": "DE_TimeMark is missing" + } + ], + "utcTimeStamp": "2024-06-04T16:23:32.046Z", + "schemaVersion": 1, + "recordGeneratedAt": { + "$date": { + "$numberLong": "1717518212053" + } + }, + "revision": 0, + "states": [ + { + "stateTimeSpeed": [ + { + "eventState": "PROTECTED_CLEARANCE", + "timing": { + "minEndTime": "2024-06-04T16:03:19Z", + "maxEndTime": "2024-06-04T16:03:19Z" + } + } + ], + "signalGroup": 2 + }, + { + "stateTimeSpeed": [ + { + "eventState": "STOP_AND_REMAIN", + "timing": { + "minEndTime": "2024-06-04T16:03:22.1Z", + "maxEndTime": "2024-06-04T16:03:22.1Z" + } + } + ], + "signalGroup": 4 + }, + { + "stateTimeSpeed": [ + { + "eventState": "PROTECTED_CLEARANCE", + "timing": { + "minEndTime": "2024-06-04T16:03:19Z", + "maxEndTime": "2024-06-04T16:03:19Z" + } + } + ], + "signalGroup": 6 + } + ], + "messageType": "SPAT", + "cti4501Conformant": false, + "intersectionId": 12109, + "odeReceivedAt": "2024-06-04T16:23:32.046Z", + "originIp": "10.11.81.12", + "status": { + "failureFlash": false, + "noValidSPATisAvailableAtThisTime": false, + "fixedTimeOperation": false, + "standbyOperation": false, + "trafficDependentOperation": false, + "manualControlIsEnabled": false, + "off": false, + "stopTimeIsActivated": false, + "recentChangeInMAPassignedLanesIDsUsed": false, + "recentMAPmessageUpdate": false, + "failureMode": false, + "noValidMAPisAvailableAtThisTime": false, + "signalPriorityIsActive": false, + "preemptIsActive": false + } +},{ + "_id": { + "$oid": "665f3fc18c25216ef4f54077" + }, + "validationMessages": [ + { + "schemaPath": "#/$defs/J2735RoadRegulatorID/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].id.region", + "message": "$.payload.data.intersectionStateList.intersectionStatelist[0].id.region: null found, integer expected" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[0].state_time_speed.movementEventList[0].timing.startTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[0].state_time_speed.movementEventList[0].timing.nextTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[1].state_time_speed.movementEventList[0].timing.startTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[1].state_time_speed.movementEventList[0].timing.nextTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[2].state_time_speed.movementEventList[0].timing.startTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[2].state_time_speed.movementEventList[0].timing.nextTime", + "message": "DE_TimeMark is missing" + } + ], + "utcTimeStamp": "2024-06-04T16:23:32.153Z", + "schemaVersion": 1, + "recordGeneratedAt": { + "$date": { + "$numberLong": "1717518212158" + } + }, + "revision": 0, + "states": [ + { + "stateTimeSpeed": [ + { + "eventState": "PROTECTED_CLEARANCE", + "timing": { + "minEndTime": "2024-06-04T16:03:19Z", + "maxEndTime": "2024-06-04T16:03:19Z" + } + } + ], + "signalGroup": 2 + }, + { + "stateTimeSpeed": [ + { + "eventState": "STOP_AND_REMAIN", + "timing": { + "minEndTime": "2024-06-04T16:03:22.1Z", + "maxEndTime": "2024-06-04T16:03:22.1Z" + } + } + ], + "signalGroup": 4 + }, + { + "stateTimeSpeed": [ + { + "eventState": "PROTECTED_CLEARANCE", + "timing": { + "minEndTime": "2024-06-04T16:03:19Z", + "maxEndTime": "2024-06-04T16:03:19Z" + } + } + ], + "signalGroup": 6 + } + ], + "messageType": "SPAT", + "cti4501Conformant": false, + "intersectionId": 12109, + "odeReceivedAt": "2024-06-04T16:23:32.153Z", + "originIp": "10.11.81.12", + "status": { + "failureFlash": false, + "noValidSPATisAvailableAtThisTime": false, + "fixedTimeOperation": false, + "standbyOperation": false, + "trafficDependentOperation": false, + "manualControlIsEnabled": false, + "off": false, + "stopTimeIsActivated": false, + "recentChangeInMAPassignedLanesIDsUsed": false, + "recentMAPmessageUpdate": false, + "failureMode": false, + "noValidMAPisAvailableAtThisTime": false, + "signalPriorityIsActive": false, + "preemptIsActive": false + } +},{ + "_id": { + "$oid": "665f3fc18c25216ef4f54078" + }, + "validationMessages": [ + { + "schemaPath": "#/$defs/J2735RoadRegulatorID/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].id.region", + "message": "$.payload.data.intersectionStateList.intersectionStatelist[0].id.region: null found, integer expected" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[0].state_time_speed.movementEventList[0].timing.startTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[0].state_time_speed.movementEventList[0].timing.nextTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[1].state_time_speed.movementEventList[0].timing.startTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[1].state_time_speed.movementEventList[0].timing.nextTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[2].state_time_speed.movementEventList[0].timing.startTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[2].state_time_speed.movementEventList[0].timing.nextTime", + "message": "DE_TimeMark is missing" + } + ], + "utcTimeStamp": "2024-06-04T16:23:32.259Z", + "schemaVersion": 1, + "recordGeneratedAt": { + "$date": { + "$numberLong": "1717518212266" + } + }, + "revision": 0, + "states": [ + { + "stateTimeSpeed": [ + { + "eventState": "PROTECTED_CLEARANCE", + "timing": { + "minEndTime": "2024-06-04T16:03:19Z", + "maxEndTime": "2024-06-04T16:03:19Z" + } + } + ], + "signalGroup": 2 + }, + { + "stateTimeSpeed": [ + { + "eventState": "STOP_AND_REMAIN", + "timing": { + "minEndTime": "2024-06-04T16:03:22.1Z", + "maxEndTime": "2024-06-04T16:03:22.1Z" + } + } + ], + "signalGroup": 4 + }, + { + "stateTimeSpeed": [ + { + "eventState": "PROTECTED_CLEARANCE", + "timing": { + "minEndTime": "2024-06-04T16:03:19Z", + "maxEndTime": "2024-06-04T16:03:19Z" + } + } + ], + "signalGroup": 6 + } + ], + "messageType": "SPAT", + "cti4501Conformant": false, + "intersectionId": 12109, + "odeReceivedAt": "2024-06-04T16:23:32.259Z", + "originIp": "10.11.81.12", + "status": { + "failureFlash": false, + "noValidSPATisAvailableAtThisTime": false, + "fixedTimeOperation": false, + "standbyOperation": false, + "trafficDependentOperation": false, + "manualControlIsEnabled": false, + "off": false, + "stopTimeIsActivated": false, + "recentChangeInMAPassignedLanesIDsUsed": false, + "recentMAPmessageUpdate": false, + "failureMode": false, + "noValidMAPisAvailableAtThisTime": false, + "signalPriorityIsActive": false, + "preemptIsActive": false + } +},{ + "_id": { + "$oid": "665f3fc18c25216ef4f54079" + }, + "validationMessages": [ + { + "schemaPath": "#/$defs/J2735RoadRegulatorID/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].id.region", + "message": "$.payload.data.intersectionStateList.intersectionStatelist[0].id.region: null found, integer expected" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[0].state_time_speed.movementEventList[0].timing.startTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[0].state_time_speed.movementEventList[0].timing.nextTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[1].state_time_speed.movementEventList[0].timing.startTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[1].state_time_speed.movementEventList[0].timing.nextTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[2].state_time_speed.movementEventList[0].timing.startTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[2].state_time_speed.movementEventList[0].timing.nextTime", + "message": "DE_TimeMark is missing" + } + ], + "utcTimeStamp": "2024-06-04T16:23:32.366Z", + "schemaVersion": 1, + "recordGeneratedAt": { + "$date": { + "$numberLong": "1717518212373" + } + }, + "revision": 0, + "states": [ + { + "stateTimeSpeed": [ + { + "eventState": "PROTECTED_CLEARANCE", + "timing": { + "minEndTime": "2024-06-04T16:03:19Z", + "maxEndTime": "2024-06-04T16:03:19Z" + } + } + ], + "signalGroup": 2 + }, + { + "stateTimeSpeed": [ + { + "eventState": "STOP_AND_REMAIN", + "timing": { + "minEndTime": "2024-06-04T16:03:22.1Z", + "maxEndTime": "2024-06-04T16:03:22.1Z" + } + } + ], + "signalGroup": 4 + }, + { + "stateTimeSpeed": [ + { + "eventState": "PROTECTED_CLEARANCE", + "timing": { + "minEndTime": "2024-06-04T16:03:19Z", + "maxEndTime": "2024-06-04T16:03:19Z" + } + } + ], + "signalGroup": 6 + } + ], + "messageType": "SPAT", + "cti4501Conformant": false, + "intersectionId": 12109, + "odeReceivedAt": "2024-06-04T16:23:32.366Z", + "originIp": "10.11.81.12", + "status": { + "failureFlash": false, + "noValidSPATisAvailableAtThisTime": false, + "fixedTimeOperation": false, + "standbyOperation": false, + "trafficDependentOperation": false, + "manualControlIsEnabled": false, + "off": false, + "stopTimeIsActivated": false, + "recentChangeInMAPassignedLanesIDsUsed": false, + "recentMAPmessageUpdate": false, + "failureMode": false, + "noValidMAPisAvailableAtThisTime": false, + "signalPriorityIsActive": false, + "preemptIsActive": false + } +},{ + "_id": { + "$oid": "665f3fc18c25216ef4f5407a" + }, + "validationMessages": [ + { + "schemaPath": "#/$defs/J2735RoadRegulatorID/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].id.region", + "message": "$.payload.data.intersectionStateList.intersectionStatelist[0].id.region: null found, integer expected" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[0].state_time_speed.movementEventList[0].timing.startTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[0].state_time_speed.movementEventList[0].timing.nextTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[1].state_time_speed.movementEventList[0].timing.startTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[1].state_time_speed.movementEventList[0].timing.nextTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[2].state_time_speed.movementEventList[0].timing.startTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[2].state_time_speed.movementEventList[0].timing.nextTime", + "message": "DE_TimeMark is missing" + } + ], + "utcTimeStamp": "2024-06-04T16:23:32.458Z", + "schemaVersion": 1, + "recordGeneratedAt": { + "$date": { + "$numberLong": "1717518212463" + } + }, + "revision": 0, + "states": [ + { + "stateTimeSpeed": [ + { + "eventState": "PROTECTED_CLEARANCE", + "timing": { + "minEndTime": "2024-06-04T16:03:19Z", + "maxEndTime": "2024-06-04T16:03:19Z" + } + } + ], + "signalGroup": 2 + }, + { + "stateTimeSpeed": [ + { + "eventState": "STOP_AND_REMAIN", + "timing": { + "minEndTime": "2024-06-04T16:03:22.1Z", + "maxEndTime": "2024-06-04T16:03:22.1Z" + } + } + ], + "signalGroup": 4 + }, + { + "stateTimeSpeed": [ + { + "eventState": "PROTECTED_CLEARANCE", + "timing": { + "minEndTime": "2024-06-04T16:03:19Z", + "maxEndTime": "2024-06-04T16:03:19Z" + } + } + ], + "signalGroup": 6 + } + ], + "messageType": "SPAT", + "cti4501Conformant": false, + "intersectionId": 12109, + "odeReceivedAt": "2024-06-04T16:23:32.458Z", + "originIp": "10.11.81.12", + "status": { + "failureFlash": false, + "noValidSPATisAvailableAtThisTime": false, + "fixedTimeOperation": false, + "standbyOperation": false, + "trafficDependentOperation": false, + "manualControlIsEnabled": false, + "off": false, + "stopTimeIsActivated": false, + "recentChangeInMAPassignedLanesIDsUsed": false, + "recentMAPmessageUpdate": false, + "failureMode": false, + "noValidMAPisAvailableAtThisTime": false, + "signalPriorityIsActive": false, + "preemptIsActive": false + } +},{ + "_id": { + "$oid": "665f3fc18c25216ef4f5407b" + }, + "validationMessages": [ + { + "schemaPath": "#/$defs/J2735RoadRegulatorID/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].id.region", + "message": "$.payload.data.intersectionStateList.intersectionStatelist[0].id.region: null found, integer expected" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[0].state_time_speed.movementEventList[0].timing.startTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[0].state_time_speed.movementEventList[0].timing.nextTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[1].state_time_speed.movementEventList[0].timing.startTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[1].state_time_speed.movementEventList[0].timing.nextTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[2].state_time_speed.movementEventList[0].timing.startTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[2].state_time_speed.movementEventList[0].timing.nextTime", + "message": "DE_TimeMark is missing" + } + ], + "utcTimeStamp": "2024-06-04T16:23:32.566Z", + "schemaVersion": 1, + "recordGeneratedAt": { + "$date": { + "$numberLong": "1717518212570" + } + }, + "revision": 0, + "states": [ + { + "stateTimeSpeed": [ + { + "eventState": "PROTECTED_CLEARANCE", + "timing": { + "minEndTime": "2024-06-04T16:03:19Z", + "maxEndTime": "2024-06-04T16:03:19Z" + } + } + ], + "signalGroup": 2 + }, + { + "stateTimeSpeed": [ + { + "eventState": "STOP_AND_REMAIN", + "timing": { + "minEndTime": "2024-06-04T16:03:22.1Z", + "maxEndTime": "2024-06-04T16:03:22.1Z" + } + } + ], + "signalGroup": 4 + }, + { + "stateTimeSpeed": [ + { + "eventState": "PROTECTED_CLEARANCE", + "timing": { + "minEndTime": "2024-06-04T16:03:19Z", + "maxEndTime": "2024-06-04T16:03:19Z" + } + } + ], + "signalGroup": 6 + } + ], + "messageType": "SPAT", + "cti4501Conformant": false, + "intersectionId": 12109, + "odeReceivedAt": "2024-06-04T16:23:32.566Z", + "originIp": "10.11.81.12", + "status": { + "failureFlash": false, + "noValidSPATisAvailableAtThisTime": false, + "fixedTimeOperation": false, + "standbyOperation": false, + "trafficDependentOperation": false, + "manualControlIsEnabled": false, + "off": false, + "stopTimeIsActivated": false, + "recentChangeInMAPassignedLanesIDsUsed": false, + "recentMAPmessageUpdate": false, + "failureMode": false, + "noValidMAPisAvailableAtThisTime": false, + "signalPriorityIsActive": false, + "preemptIsActive": false + } +},{ + "_id": { + "$oid": "665f3fc18c25216ef4f5407c" + }, + "validationMessages": [ + { + "schemaPath": "#/$defs/J2735RoadRegulatorID/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].id.region", + "message": "$.payload.data.intersectionStateList.intersectionStatelist[0].id.region: null found, integer expected" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[0].state_time_speed.movementEventList[0].timing.startTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[0].state_time_speed.movementEventList[0].timing.nextTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[1].state_time_speed.movementEventList[0].timing.startTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[1].state_time_speed.movementEventList[0].timing.nextTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[2].state_time_speed.movementEventList[0].timing.startTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[2].state_time_speed.movementEventList[0].timing.nextTime", + "message": "DE_TimeMark is missing" + } + ], + "utcTimeStamp": "2024-06-04T16:23:32.674Z", + "schemaVersion": 1, + "recordGeneratedAt": { + "$date": { + "$numberLong": "1717518212677" + } + }, + "revision": 0, + "states": [ + { + "stateTimeSpeed": [ + { + "eventState": "PROTECTED_CLEARANCE", + "timing": { + "minEndTime": "2024-06-04T16:03:19Z", + "maxEndTime": "2024-06-04T16:03:19Z" + } + } + ], + "signalGroup": 2 + }, + { + "stateTimeSpeed": [ + { + "eventState": "STOP_AND_REMAIN", + "timing": { + "minEndTime": "2024-06-04T16:03:22.1Z", + "maxEndTime": "2024-06-04T16:03:22.1Z" + } + } + ], + "signalGroup": 4 + }, + { + "stateTimeSpeed": [ + { + "eventState": "PROTECTED_CLEARANCE", + "timing": { + "minEndTime": "2024-06-04T16:03:19Z", + "maxEndTime": "2024-06-04T16:03:19Z" + } + } + ], + "signalGroup": 6 + } + ], + "messageType": "SPAT", + "cti4501Conformant": false, + "intersectionId": 12109, + "odeReceivedAt": "2024-06-04T16:23:32.674Z", + "originIp": "10.11.81.12", + "status": { + "failureFlash": false, + "noValidSPATisAvailableAtThisTime": false, + "fixedTimeOperation": false, + "standbyOperation": false, + "trafficDependentOperation": false, + "manualControlIsEnabled": false, + "off": false, + "stopTimeIsActivated": false, + "recentChangeInMAPassignedLanesIDsUsed": false, + "recentMAPmessageUpdate": false, + "failureMode": false, + "noValidMAPisAvailableAtThisTime": false, + "signalPriorityIsActive": false, + "preemptIsActive": false + } +},{ + "_id": { + "$oid": "665f3fc18c25216ef4f5407d" + }, + "validationMessages": [ + { + "schemaPath": "#/$defs/J2735RoadRegulatorID/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].id.region", + "message": "$.payload.data.intersectionStateList.intersectionStatelist[0].id.region: null found, integer expected" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[0].state_time_speed.movementEventList[0].timing.startTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[0].state_time_speed.movementEventList[0].timing.nextTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[1].state_time_speed.movementEventList[0].timing.startTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[1].state_time_speed.movementEventList[0].timing.nextTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[2].state_time_speed.movementEventList[0].timing.startTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[2].state_time_speed.movementEventList[0].timing.nextTime", + "message": "DE_TimeMark is missing" + } + ], + "utcTimeStamp": "2024-06-04T16:23:32.781Z", + "schemaVersion": 1, + "recordGeneratedAt": { + "$date": { + "$numberLong": "1717518212786" + } + }, + "revision": 0, + "states": [ + { + "stateTimeSpeed": [ + { + "eventState": "PROTECTED_CLEARANCE", + "timing": { + "minEndTime": "2024-06-04T16:03:19Z", + "maxEndTime": "2024-06-04T16:03:19Z" + } + } + ], + "signalGroup": 2 + }, + { + "stateTimeSpeed": [ + { + "eventState": "STOP_AND_REMAIN", + "timing": { + "minEndTime": "2024-06-04T16:03:22.1Z", + "maxEndTime": "2024-06-04T16:03:22.1Z" + } + } + ], + "signalGroup": 4 + }, + { + "stateTimeSpeed": [ + { + "eventState": "PROTECTED_CLEARANCE", + "timing": { + "minEndTime": "2024-06-04T16:03:19Z", + "maxEndTime": "2024-06-04T16:03:19Z" + } + } + ], + "signalGroup": 6 + } + ], + "messageType": "SPAT", + "cti4501Conformant": false, + "intersectionId": 12109, + "odeReceivedAt": "2024-06-04T16:23:32.781Z", + "originIp": "10.11.81.12", + "status": { + "failureFlash": false, + "noValidSPATisAvailableAtThisTime": false, + "fixedTimeOperation": false, + "standbyOperation": false, + "trafficDependentOperation": false, + "manualControlIsEnabled": false, + "off": false, + "stopTimeIsActivated": false, + "recentChangeInMAPassignedLanesIDsUsed": false, + "recentMAPmessageUpdate": false, + "failureMode": false, + "noValidMAPisAvailableAtThisTime": false, + "signalPriorityIsActive": false, + "preemptIsActive": false + } +},{ + "_id": { + "$oid": "665f3fc18c25216ef4f5407e" + }, + "validationMessages": [ + { + "schemaPath": "#/$defs/J2735RoadRegulatorID/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].id.region", + "message": "$.payload.data.intersectionStateList.intersectionStatelist[0].id.region: null found, integer expected" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[0].state_time_speed.movementEventList[0].timing.startTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[0].state_time_speed.movementEventList[0].timing.nextTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[1].state_time_speed.movementEventList[0].timing.startTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[1].state_time_speed.movementEventList[0].timing.nextTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[2].state_time_speed.movementEventList[0].timing.startTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[2].state_time_speed.movementEventList[0].timing.nextTime", + "message": "DE_TimeMark is missing" + } + ], + "utcTimeStamp": "2024-06-04T16:23:32.887Z", + "schemaVersion": 1, + "recordGeneratedAt": { + "$date": { + "$numberLong": "1717518212893" + } + }, + "revision": 0, + "states": [ + { + "stateTimeSpeed": [ + { + "eventState": "PROTECTED_CLEARANCE", + "timing": { + "minEndTime": "2024-06-04T16:03:19Z", + "maxEndTime": "2024-06-04T16:03:19Z" + } + } + ], + "signalGroup": 2 + }, + { + "stateTimeSpeed": [ + { + "eventState": "STOP_AND_REMAIN", + "timing": { + "minEndTime": "2024-06-04T16:03:22.1Z", + "maxEndTime": "2024-06-04T16:03:22.1Z" + } + } + ], + "signalGroup": 4 + }, + { + "stateTimeSpeed": [ + { + "eventState": "PROTECTED_CLEARANCE", + "timing": { + "minEndTime": "2024-06-04T16:03:19Z", + "maxEndTime": "2024-06-04T16:03:19Z" + } + } + ], + "signalGroup": 6 + } + ], + "messageType": "SPAT", + "cti4501Conformant": false, + "intersectionId": 12109, + "odeReceivedAt": "2024-06-04T16:23:32.887Z", + "originIp": "10.11.81.12", + "status": { + "failureFlash": false, + "noValidSPATisAvailableAtThisTime": false, + "fixedTimeOperation": false, + "standbyOperation": false, + "trafficDependentOperation": false, + "manualControlIsEnabled": false, + "off": false, + "stopTimeIsActivated": false, + "recentChangeInMAPassignedLanesIDsUsed": false, + "recentMAPmessageUpdate": false, + "failureMode": false, + "noValidMAPisAvailableAtThisTime": false, + "signalPriorityIsActive": false, + "preemptIsActive": false + } +},{ + "_id": { + "$oid": "665f3fc18c25216ef4f5407f" + }, + "validationMessages": [ + { + "schemaPath": "#/$defs/J2735RoadRegulatorID/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].id.region", + "message": "$.payload.data.intersectionStateList.intersectionStatelist[0].id.region: null found, integer expected" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[0].state_time_speed.movementEventList[0].timing.startTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[0].state_time_speed.movementEventList[0].timing.nextTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[1].state_time_speed.movementEventList[0].timing.startTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[1].state_time_speed.movementEventList[0].timing.nextTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[2].state_time_speed.movementEventList[0].timing.startTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[2].state_time_speed.movementEventList[0].timing.nextTime", + "message": "DE_TimeMark is missing" + } + ], + "utcTimeStamp": "2024-06-04T16:23:32.995Z", + "schemaVersion": 1, + "recordGeneratedAt": { + "$date": { + "$numberLong": "1717518213000" + } + }, + "revision": 0, + "states": [ + { + "stateTimeSpeed": [ + { + "eventState": "PROTECTED_CLEARANCE", + "timing": { + "minEndTime": "2024-06-04T16:03:19Z", + "maxEndTime": "2024-06-04T16:03:19Z" + } + } + ], + "signalGroup": 2 + }, + { + "stateTimeSpeed": [ + { + "eventState": "STOP_AND_REMAIN", + "timing": { + "minEndTime": "2024-06-04T16:03:22.1Z", + "maxEndTime": "2024-06-04T16:03:22.1Z" + } + } + ], + "signalGroup": 4 + }, + { + "stateTimeSpeed": [ + { + "eventState": "PROTECTED_CLEARANCE", + "timing": { + "minEndTime": "2024-06-04T16:03:19Z", + "maxEndTime": "2024-06-04T16:03:19Z" + } + } + ], + "signalGroup": 6 + } + ], + "messageType": "SPAT", + "cti4501Conformant": false, + "intersectionId": 12109, + "odeReceivedAt": "2024-06-04T16:23:32.995Z", + "originIp": "10.11.81.12", + "status": { + "failureFlash": false, + "noValidSPATisAvailableAtThisTime": false, + "fixedTimeOperation": false, + "standbyOperation": false, + "trafficDependentOperation": false, + "manualControlIsEnabled": false, + "off": false, + "stopTimeIsActivated": false, + "recentChangeInMAPassignedLanesIDsUsed": false, + "recentMAPmessageUpdate": false, + "failureMode": false, + "noValidMAPisAvailableAtThisTime": false, + "signalPriorityIsActive": false, + "preemptIsActive": false + } +},{ + "_id": { + "$oid": "665f3fc18c25216ef4f54080" + }, + "validationMessages": [ + { + "schemaPath": "#/$defs/J2735RoadRegulatorID/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].id.region", + "message": "$.payload.data.intersectionStateList.intersectionStatelist[0].id.region: null found, integer expected" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[0].state_time_speed.movementEventList[0].timing.startTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[0].state_time_speed.movementEventList[0].timing.nextTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[1].state_time_speed.movementEventList[0].timing.startTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[1].state_time_speed.movementEventList[0].timing.nextTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[2].state_time_speed.movementEventList[0].timing.startTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[2].state_time_speed.movementEventList[0].timing.nextTime", + "message": "DE_TimeMark is missing" + } + ], + "utcTimeStamp": "2024-06-04T16:23:33.102Z", + "schemaVersion": 1, + "recordGeneratedAt": { + "$date": { + "$numberLong": "1717518213107" + } + }, + "revision": 0, + "states": [ + { + "stateTimeSpeed": [ + { + "eventState": "PROTECTED_CLEARANCE", + "timing": { + "minEndTime": "2024-06-04T16:03:19Z", + "maxEndTime": "2024-06-04T16:03:19Z" + } + } + ], + "signalGroup": 2 + }, + { + "stateTimeSpeed": [ + { + "eventState": "STOP_AND_REMAIN", + "timing": { + "minEndTime": "2024-06-04T16:03:22.1Z", + "maxEndTime": "2024-06-04T16:03:22.1Z" + } + } + ], + "signalGroup": 4 + }, + { + "stateTimeSpeed": [ + { + "eventState": "PROTECTED_CLEARANCE", + "timing": { + "minEndTime": "2024-06-04T16:03:19Z", + "maxEndTime": "2024-06-04T16:03:19Z" + } + } + ], + "signalGroup": 6 + } + ], + "messageType": "SPAT", + "cti4501Conformant": false, + "intersectionId": 12109, + "odeReceivedAt": "2024-06-04T16:23:33.102Z", + "originIp": "10.11.81.12", + "status": { + "failureFlash": false, + "noValidSPATisAvailableAtThisTime": false, + "fixedTimeOperation": false, + "standbyOperation": false, + "trafficDependentOperation": false, + "manualControlIsEnabled": false, + "off": false, + "stopTimeIsActivated": false, + "recentChangeInMAPassignedLanesIDsUsed": false, + "recentMAPmessageUpdate": false, + "failureMode": false, + "noValidMAPisAvailableAtThisTime": false, + "signalPriorityIsActive": false, + "preemptIsActive": false + } +},{ + "_id": { + "$oid": "665f3fc18c25216ef4f54081" + }, + "validationMessages": [ + { + "schemaPath": "#/$defs/J2735RoadRegulatorID/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].id.region", + "message": "$.payload.data.intersectionStateList.intersectionStatelist[0].id.region: null found, integer expected" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[0].state_time_speed.movementEventList[0].timing.startTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[0].state_time_speed.movementEventList[0].timing.nextTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[1].state_time_speed.movementEventList[0].timing.startTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[1].state_time_speed.movementEventList[0].timing.nextTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[2].state_time_speed.movementEventList[0].timing.startTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[2].state_time_speed.movementEventList[0].timing.nextTime", + "message": "DE_TimeMark is missing" + } + ], + "utcTimeStamp": "2024-06-04T16:23:33.21Z", + "schemaVersion": 1, + "recordGeneratedAt": { + "$date": { + "$numberLong": "1717518213215" + } + }, + "revision": 0, + "states": [ + { + "stateTimeSpeed": [ + { + "eventState": "PROTECTED_CLEARANCE", + "timing": { + "minEndTime": "2024-06-04T16:03:19Z", + "maxEndTime": "2024-06-04T16:03:19Z" + } + } + ], + "signalGroup": 2 + }, + { + "stateTimeSpeed": [ + { + "eventState": "STOP_AND_REMAIN", + "timing": { + "minEndTime": "2024-06-04T16:03:22.1Z", + "maxEndTime": "2024-06-04T16:03:22.1Z" + } + } + ], + "signalGroup": 4 + }, + { + "stateTimeSpeed": [ + { + "eventState": "PROTECTED_CLEARANCE", + "timing": { + "minEndTime": "2024-06-04T16:03:19Z", + "maxEndTime": "2024-06-04T16:03:19Z" + } + } + ], + "signalGroup": 6 + } + ], + "messageType": "SPAT", + "cti4501Conformant": false, + "intersectionId": 12109, + "odeReceivedAt": "2024-06-04T16:23:33.21Z", + "originIp": "10.11.81.12", + "status": { + "failureFlash": false, + "noValidSPATisAvailableAtThisTime": false, + "fixedTimeOperation": false, + "standbyOperation": false, + "trafficDependentOperation": false, + "manualControlIsEnabled": false, + "off": false, + "stopTimeIsActivated": false, + "recentChangeInMAPassignedLanesIDsUsed": false, + "recentMAPmessageUpdate": false, + "failureMode": false, + "noValidMAPisAvailableAtThisTime": false, + "signalPriorityIsActive": false, + "preemptIsActive": false + } +},{ + "_id": { + "$oid": "665f3fc18c25216ef4f54082" + }, + "validationMessages": [ + { + "schemaPath": "#/$defs/J2735RoadRegulatorID/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].id.region", + "message": "$.payload.data.intersectionStateList.intersectionStatelist[0].id.region: null found, integer expected" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[0].state_time_speed.movementEventList[0].timing.startTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[0].state_time_speed.movementEventList[0].timing.nextTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[1].state_time_speed.movementEventList[0].timing.startTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[1].state_time_speed.movementEventList[0].timing.nextTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[2].state_time_speed.movementEventList[0].timing.startTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[2].state_time_speed.movementEventList[0].timing.nextTime", + "message": "DE_TimeMark is missing" + } + ], + "utcTimeStamp": "2024-06-04T16:23:33.317Z", + "schemaVersion": 1, + "recordGeneratedAt": { + "$date": { + "$numberLong": "1717518213321" + } + }, + "revision": 0, + "states": [ + { + "stateTimeSpeed": [ + { + "eventState": "PROTECTED_CLEARANCE", + "timing": { + "minEndTime": "2024-06-04T16:03:19Z", + "maxEndTime": "2024-06-04T16:03:19Z" + } + } + ], + "signalGroup": 2 + }, + { + "stateTimeSpeed": [ + { + "eventState": "STOP_AND_REMAIN", + "timing": { + "minEndTime": "2024-06-04T16:03:22.1Z", + "maxEndTime": "2024-06-04T16:03:22.1Z" + } + } + ], + "signalGroup": 4 + }, + { + "stateTimeSpeed": [ + { + "eventState": "PROTECTED_CLEARANCE", + "timing": { + "minEndTime": "2024-06-04T16:03:19Z", + "maxEndTime": "2024-06-04T16:03:19Z" + } + } + ], + "signalGroup": 6 + } + ], + "messageType": "SPAT", + "cti4501Conformant": false, + "intersectionId": 12109, + "odeReceivedAt": "2024-06-04T16:23:33.317Z", + "originIp": "10.11.81.12", + "status": { + "failureFlash": false, + "noValidSPATisAvailableAtThisTime": false, + "fixedTimeOperation": false, + "standbyOperation": false, + "trafficDependentOperation": false, + "manualControlIsEnabled": false, + "off": false, + "stopTimeIsActivated": false, + "recentChangeInMAPassignedLanesIDsUsed": false, + "recentMAPmessageUpdate": false, + "failureMode": false, + "noValidMAPisAvailableAtThisTime": false, + "signalPriorityIsActive": false, + "preemptIsActive": false + } +},{ + "_id": { + "$oid": "665f3fc18c25216ef4f54083" + }, + "validationMessages": [ + { + "schemaPath": "#/$defs/J2735RoadRegulatorID/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].id.region", + "message": "$.payload.data.intersectionStateList.intersectionStatelist[0].id.region: null found, integer expected" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[0].state_time_speed.movementEventList[0].timing.startTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[0].state_time_speed.movementEventList[0].timing.nextTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[1].state_time_speed.movementEventList[0].timing.startTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[1].state_time_speed.movementEventList[0].timing.nextTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[2].state_time_speed.movementEventList[0].timing.startTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[2].state_time_speed.movementEventList[0].timing.nextTime", + "message": "DE_TimeMark is missing" + } + ], + "utcTimeStamp": "2024-06-04T16:23:33.41Z", + "schemaVersion": 1, + "recordGeneratedAt": { + "$date": { + "$numberLong": "1717518213412" + } + }, + "revision": 0, + "states": [ + { + "stateTimeSpeed": [ + { + "eventState": "PROTECTED_CLEARANCE", + "timing": { + "minEndTime": "2024-06-04T16:03:18Z", + "maxEndTime": "2024-06-04T16:03:18Z" + } + } + ], + "signalGroup": 2 + }, + { + "stateTimeSpeed": [ + { + "eventState": "STOP_AND_REMAIN", + "timing": { + "minEndTime": "2024-06-04T16:03:21.1Z", + "maxEndTime": "2024-06-04T16:03:21.1Z" + } + } + ], + "signalGroup": 4 + }, + { + "stateTimeSpeed": [ + { + "eventState": "PROTECTED_CLEARANCE", + "timing": { + "minEndTime": "2024-06-04T16:03:18Z", + "maxEndTime": "2024-06-04T16:03:18Z" + } + } + ], + "signalGroup": 6 + } + ], + "messageType": "SPAT", + "cti4501Conformant": false, + "intersectionId": 12109, + "odeReceivedAt": "2024-06-04T16:23:33.41Z", + "originIp": "10.11.81.12", + "status": { + "failureFlash": false, + "noValidSPATisAvailableAtThisTime": false, + "fixedTimeOperation": false, + "standbyOperation": false, + "trafficDependentOperation": false, + "manualControlIsEnabled": false, + "off": false, + "stopTimeIsActivated": false, + "recentChangeInMAPassignedLanesIDsUsed": false, + "recentMAPmessageUpdate": false, + "failureMode": false, + "noValidMAPisAvailableAtThisTime": false, + "signalPriorityIsActive": false, + "preemptIsActive": false + } +},{ + "_id": { + "$oid": "665f3fc18c25216ef4f54084" + }, + "validationMessages": [ + { + "schemaPath": "#/$defs/J2735RoadRegulatorID/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].id.region", + "message": "$.payload.data.intersectionStateList.intersectionStatelist[0].id.region: null found, integer expected" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[0].state_time_speed.movementEventList[0].timing.startTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[0].state_time_speed.movementEventList[0].timing.nextTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[1].state_time_speed.movementEventList[0].timing.startTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[1].state_time_speed.movementEventList[0].timing.nextTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[2].state_time_speed.movementEventList[0].timing.startTime", + "message": "DE_TimeMark is missing" + }, + { + "schemaPath": "#/$defs/J2735TimeMark/type", + "jsonPath": "$.payload.data.intersectionStateList.intersectionStatelist[0].states.movementList[2].state_time_speed.movementEventList[0].timing.nextTime", + "message": "DE_TimeMark is missing" + } + ], + "utcTimeStamp": "2024-06-04T16:23:33.519Z", + "schemaVersion": 1, + "recordGeneratedAt": { + "$date": { + "$numberLong": "1717518213520" + } + }, + "revision": 0, + "states": [ + { + "stateTimeSpeed": [ + { + "eventState": "PROTECTED_CLEARANCE", + "timing": { + "minEndTime": "2024-06-04T16:03:18Z", + "maxEndTime": "2024-06-04T16:03:18Z" + } + } + ], + "signalGroup": 2 + }, + { + "stateTimeSpeed": [ + { + "eventState": "STOP_AND_REMAIN", + "timing": { + "minEndTime": "2024-06-04T16:03:21.1Z", + "maxEndTime": "2024-06-04T16:03:21.1Z" + } + } + ], + "signalGroup": 4 + }, + { + "stateTimeSpeed": [ + { + "eventState": "PROTECTED_CLEARANCE", + "timing": { + "minEndTime": "2024-06-04T16:03:18Z", + "maxEndTime": "2024-06-04T16:03:18Z" + } + } + ], + "signalGroup": 6 + } + ], + "messageType": "SPAT", + "cti4501Conformant": false, + "intersectionId": 12109, + "odeReceivedAt": "2024-06-04T16:23:33.519Z", + "originIp": "10.11.81.12", + "status": { + "failureFlash": false, + "noValidSPATisAvailableAtThisTime": false, + "fixedTimeOperation": false, + "standbyOperation": false, + "trafficDependentOperation": false, + "manualControlIsEnabled": false, + "off": false, + "stopTimeIsActivated": false, + "recentChangeInMAPassignedLanesIDsUsed": false, + "recentMAPmessageUpdate": false, + "failureMode": false, + "noValidMAPisAvailableAtThisTime": false, + "signalPriorityIsActive": false, + "preemptIsActive": false + } +}] \ No newline at end of file diff --git a/docker-compose.yml b/docker-compose.yml index 0fcfc4bba..b6a757d65 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -171,7 +171,7 @@ services: depends_on: - zookeeper volumes: - - ${DOCKER_SHARED_VOLUME}/var/run/docker.sock:/var/run/docker.sock + - ode_vol:/var/run/docker.sock logging: options: max-size: '10m' @@ -195,14 +195,11 @@ services: environment: DOCKER_HOST_IP: ${DOCKER_HOST_IP} ZK: ${DOCKER_HOST_IP}:2181 - ODE_SECURITY_SVCS_SIGNATURE_URI: ${ODE_SECURITY_SVCS_SIGNATURE_URI} - ODE_RSU_USERNAME: ${ODE_RSU_USERNAME} - ODE_RSU_PASSWORD: ${ODE_RSU_PASSWORD} depends_on: - kafka volumes: - - ${DOCKER_SHARED_VOLUME}:/jpo-ode - - ${DOCKER_SHARED_VOLUME}/uploads:/home/uploads + - ode_vol:/jpo-ode + - ode_vol:/home/uploads logging: options: max-size: '10m' @@ -212,7 +209,6 @@ services: image: usdotjpoode/geojsonconverter:develop environment: DOCKER_HOST_IP: ${DOCKER_HOST_IP} - geometry.output.mode: ${GEOMETRY_OUTPUT_MODE} spring.kafka.bootstrap-servers: ${DOCKER_HOST_IP}:9092 logging: options: @@ -244,6 +240,9 @@ services: resources: limits: memory: 3G + depends_on: + - ode + - geojsonconverter conflictvisualizer-api: image: us-central1-docker.pkg.dev/cdot-oim-cv-dev/jpo-conflictvisualizer-api-cvmanager/jpo-conflictvisualizer-api-cvmanager:v1.0.0 @@ -289,6 +288,8 @@ services: resources: limits: memory: 3G + depends_on: + - conflictmonitor mongodb_container: image: mongo:6 @@ -305,12 +306,14 @@ services: - CM_MONGO_API_PASSWORD=${CM_MONGO_API_PASSWORD} - CM_MONGO_USER_USERNAME=${CM_MONGO_USER_USERNAME} - CM_MONGO_USER_PASSWORD=${CM_MONGO_USER_PASSWORD} + - CONFLICTMONITOR_MONGODB_INITIAL_DATASET=${CONFLICTMONITOR_MONGODB_INITIAL_DATASET} ports: - '27017:27017' volumes: - mongodb_data_container:/data/db - ./conflictmonitor/docker/mongo/a_init_replicas.js:/docker-entrypoint-initdb.d/a_init_replicas.js - ./conflictmonitor/docker/mongo/b_create_indexes.js:/docker-entrypoint-initdb.d/b_create_indexes.js + - ./conflictmonitor/datasets:/datasets healthcheck: test: | test $$(mongosh --username ${MONGO_INITDB_ROOT_USERNAME} --password ${MONGO_INITDB_ROOT_PASSWORD} --quiet --eval "try { rs.initiate({ _id: 'rs0', members: [{ _id: 0, host: '${DB_HOST_IP}' }] }).ok } catch (_) { rs.status().ok }") -eq 1 @@ -323,7 +326,14 @@ services: openssl rand -base64 741 > /data/keyfile.txt chmod 600 /data/keyfile.txt chown 999:999 /data/keyfile.txt - exec docker-entrypoint.sh $$@ + exec docker-entrypoint.sh $$@ & + sleep 30 # Wait for MongoDB to start + if [ -n "$${CONFLICTMONITOR_MONGODB_INITIAL_DATASET}" ]; then + echo "Adding MongoDB Data" + mongoimport --host localhost --db ConflictMonitor --collection ProcessedMap --type json --file /datasets/$$CONFLICTMONITOR_MONGODB_INITIAL_DATASET/ProcessedMap.json --jsonArray --authenticationDatabase admin --username $${MONGO_INITDB_ROOT_USERNAME} --password $${MONGO_INITDB_ROOT_PASSWORD} + mongoimport --host localhost --db ConflictMonitor --collection ProcessedSpat --type json --file /datasets/$$CONFLICTMONITOR_MONGODB_INITIAL_DATASET/ProcessedSpat.json --jsonArray --authenticationDatabase admin --username $${MONGO_INITDB_ROOT_USERNAME} --password $${MONGO_INITDB_ROOT_PASSWORD} + mongoimport --host localhost --db ConflictMonitor --collection OdeBsmJson --type json --file /datasets/$$CONFLICTMONITOR_MONGODB_INITIAL_DATASET/OdeBsmJson.json --jsonArray --authenticationDatabase admin --username $${MONGO_INITDB_ROOT_USERNAME} --password $${MONGO_INITDB_ROOT_PASSWORD} + fi command: ['mongod', '--replSet', 'rs0', '--bind_ip_all', '--keyFile', '/data/keyfile.txt'] logging: @@ -347,6 +357,10 @@ services: depends_on: mongodb_container: condition: service_healthy + kafka: + condition: service_started + conflictmonitor: + condition: service_started environment: DOCKER_HOST_IP: ${DOCKER_HOST_IP} DB_HOST_IP: ${DB_HOST_IP} @@ -382,9 +396,9 @@ services: - bash - -c - | - /etc/confluent/docker/run & + /etc/confluent/docker/run & echo "Waiting for Kafka Connect to start listening on kafka-connect ❳" - while [ $$(curl -s -o /dev/null -w %{http_code} http://${KAFKA_CONNECT_IP}:8083/connectors) -eq 000 ] ; do + while [ $$(curl -s -o /dev/null -w %{http_code} http://${KAFKA_CONNECT_IP}:8083/connectors) -eq 000 ] ; do echo -e $$(date) " Kafka Connect listener HTTP state: " $$(curl -s -o /dev/null -w %{http_code} http://${KAFKA_CONNECT_IP}:8083/connectors) " (waiting for 200)" sleep 5 done @@ -401,3 +415,4 @@ volumes: mongodb_data_container: pgdb: driver: local + ode_vol: From 7ce52a72bbe88576f4f774e084945881dd05673b Mon Sep 17 00:00:00 2001 From: jacob6838 Date: Tue, 4 Jun 2024 15:25:05 -0600 Subject: [PATCH 057/189] Cleaning up environment variables --- sample.env | 75 ++----------------- sample_no_cm.env | 184 +++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 189 insertions(+), 70 deletions(-) create mode 100644 sample_no_cm.env diff --git a/sample.env b/sample.env index 87ccb0dd4..e2519c73f 100644 --- a/sample.env +++ b/sample.env @@ -80,7 +80,7 @@ CSM_EMAIL_APP_USERNAME= CSM_EMAIL_APP_PASSWORD= CSM_EMAILS_TO_SEND_TO= CSM_TARGET_SMTP_SERVER_ADDRESS= -CSM_TARGET_SMTP_SERVER_PORT= +CSM_TARGET_SMTP_SERVER_PORT=587 # Python timezone for the CV Manager (You can list pytz timezones with the command 'pytz.all_timezones') TIMEZONE="US/Mountain" @@ -189,25 +189,20 @@ COUNTS_LOGGING_LEVEL="INFO" # Supported log levels are "ALL", "DEBUG", "ERROR", "FATAL", "INFO", "OFF", "TRACE" and "WARN" KC_LOGGING_LEVEL="INFO" +# Set the MongoDB dataset to load, specified in the conflictmonitor/datasets directory +CONFLICTMONITOR_MONGODB_INITIAL_DATASET="uturn_export" ########################################## # JPO ConflictMonitor Docker Environment # # Documentation: https://github.com/usdot-jpo-ode/jpo-conflictmonitor ########################################## -# General Properties # (Required) The IP address of Docker host machine which can be found by running "ifconfig" # Hint: look for "inet addr:" within "eth0" or "en0" for OSX # For Local deployments DB_HOST_IP and KAFKA_BROKER_IP should be set to DOCKER_HOST_IP, for remote deployments, these can be set independently. -DB_HOST_IP= -KAFKA_BROKER_IP= +DB_HOST_IP=${DOCKER_HOST_IP} +KAFKA_BROKER_IP=${DOCKER_HOST_IP} KAFKA_CONNECT_IP=localhost -# Set to "CONFLUENT" if broker is a Confluent Cloud broker -KAFKA_TYPE= - -# Set to actual Confluent Cloud access key and secret values for SASL authentication -CONFLUENT_KEY= -CONFLUENT_SECRET= # The Username and passwords to use for accessing mongoDB. MONGO_INITDB_ROOT_USERNAME=root @@ -220,66 +215,6 @@ CM_MONGO_USER_USERNAME=user CM_MONGO_USER_PASSWORD=user - -########################################### -# JPO GeoJsonConverter Docker Environment # -# Documentation: https://github.com/usdot-jpo-ode/jpo-geojsonconverter -########################################### -# General Properties - -#################### -# Confluent Cloud Properties (for Confluent Cloud support) - -# Set to "CONFLUENT" if broker is a Confluent Cloud broker -KAFKA_TYPE= - -# Set to actual Confluent Cloud access key and secret values for SASL authentication -CONFLUENT_KEY= -CONFLUENT_SECRET= - -# Feature geometry output mode -# Options are GEOJSON_ONLY or WKT, defaults to GEOJSON_ONLY if nothing is specified -# WKT mode will still generate geoJSON output -GEOMETRY_OUTPUT_MODE= - - -############################## -# JPO ODE Docker Environment # -# Documentation: https://github.com/usdot-jpo-ode/jpo-ode -############################## -# -# Variable usage guide: -# _ACCESS_KEY_ID : The Amazon Web Services (AWS) bucket access key ID. -# _SECRET_ACCESS_KEY : The AWS bucket secret access key. -# _BUCKET_NAME : The name of the destination AWS bucket. -# _DEPOSIT_KEY : The filename prefix appended to files deposited to the destination bucket (may be a file path). -# _TOPIC : The name of the Kafka topic to which this depositor will listen. -# _DESTINATION : (Optional) Depositors will default to Firehose depositing. Uncomment this to override to S3 deposit. -################################### - -#################### -# General Properties - -# (Required) The full path of a directory on the host machine to be shared with docker containers. -# Hint: usually the path to the `jpo-ode` directory. -DOCKER_SHARED_VOLUME= -DOCKER_SHARED_VOLUME_WINDOWS= - -# (Required if values are not sent in REST request JSON messages) -# RSU SNMP username and password -ODE_RSU_USERNAME= -ODE_RSU_PASSWORD= - -######################### -# Kafka and Confluent Cloud Properties - -# The IP address of Docker host machine which can be found by running "ifconfig" -KAFKA_TYPE= - -# Confluent Cloud API access credentials -CONFLUENT_KEY= -CONFLUENT_SECRET= - ############################################################## # jpo-conflictvisualizer cvmanager api environment variables # # Documentation: https://github.com/usdot-jpo-ode/jpo-conflictvisualizer/tree/cvmgr-cimms-integration/api diff --git a/sample_no_cm.env b/sample_no_cm.env new file mode 100644 index 000000000..681cd5542 --- /dev/null +++ b/sample_no_cm.env @@ -0,0 +1,184 @@ +# Environment variable file for the JPO CV Manager API deployment through docker-compose +DOCKER_HOST_IP= +# Note if debugging, this webapp host IP should be set to the IP of the host machine running the webapp (localhost) +WEBAPP_HOST_IP=${DOCKER_HOST_IP} +# Note if using WEBAPP_DOMAIN for the docker-compose-webapp-deployment.yml file you will need to include http:// or https:// +WEBAPP_DOMAIN=cvmanager.local.com +KC_HOST_IP=${DOCKER_HOST_IP} + +# Firmware Manager connectivity in the format 'http://endpoint:port' +FIRMWARE_MANAGER_ENDPOINT=http://${DOCKER_HOST_IP}:8089 + +# Allowed CORS domain for accessing the CV Manager API from (set to the web application hostname) +# Make sure to include http:// or https:// +# If using docker then this value should be set to: http://${WEBAPP_HOST_IP}:3000 +# If running the webapp using npm then set it to: http://localhost:3000 +# Leave as * to allow all domains access +CORS_DOMAIN=* + +# PostgreSQL Database connection information +# this value may need to follow with the webapp host if debugging the applications +PG_DB_HOST=${DOCKER_HOST_IP}:5432 +PG_DB_NAME=postgres +PG_DB_USER=postgres +# If the PG_DB_PASS variable has special characters, make sure to wrap it in single quotes +PG_DB_PASS= + +# If connecting to PGDB over websocket: +INSTANCE_CONNECTION_NAME= + +# Keycloak authentication credentials +KEYCLOAK_ADMIN=admin +KEYCLOAK_ADMIN_PASSWORD= + +# Keycloak Parameters - to generate secret key use a password generator such as: https://www.avast.com/en-us/random-password-generator#pc and set the length to 32 +KEYCLOAK_REALM=cvmanager +KEYCLOAK_API_CLIENT_ID=cvmanager-api +KEYCLOAK_API_CLIENT_SECRET_KEY= +KEYCLOAK_LOGIN_THEME_NAME=sample_theme +# Note if using KEYCLOAK_DOMAIN for the docker-compose-webapp-deployment.yml file you will need to include http:// or https:// +KEYCLOAK_DOMAIN=cvmanager.auth.com + + +# GCP OAuth2.0 client ID for SSO authentication in keycloak - if not specified the google SSO will not be functional +GOOGLE_CLIENT_ID= +GOOGLE_CLIENT_SECRET= + +# If "BIGQUERY", set the location of the GCP service account key attached as a volume +GOOGLE_APPLICATION_CREDENTIALS='./resources/google/sample_gcp_service_account.json' + +# If "MONGODB", set the MongoDB variables +MONGO_DB_URI= +MONGO_DB_NAME="ode" + +# Email configuration for sending firmware-manager failure emails +SMTP_EMAIL= +SMTP_USERNAME= +SMTP_PASSWORD= +FW_EMAIL_RECIPIENTS= +SMTP_SERVER_IP= + +# Set these variables if using either "MONGODB" or "BIGQUERY" +# COUNTS_MSG_TYPES: Comma separated list of message types. +# COUNTS_MSG_TYPES must be set for the counts menu to correctly populate when building an image for deployment +COUNTS_MSG_TYPES='BSM,SSM,SPAT,SRM,MAP' +VIEWER_MSG_TYPES='BSM' +GEO_DB_NAME='V2XGeoJson' +SSM_DB_NAME= +SRM_DB_NAME= + +# Specifies the maximum number of V2x messages returned from the geo_query_geo_data_mongo method before filtering occurs +MAX_GEO_QUERY_RECORDS= + +# WZDx API key and endpoint for pulling WZDx data into the CV Manager +WZDX_API_KEY= +WZDX_ENDPOINT= + +# Contact Support Menu Email Configuration +CSM_EMAIL_TO_SEND_FROM= +CSM_EMAIL_APP_USERNAME= +CSM_EMAIL_APP_PASSWORD= +CSM_EMAILS_TO_SEND_TO= +CSM_TARGET_SMTP_SERVER_ADDRESS= +CSM_TARGET_SMTP_SERVER_PORT= + +# Python timezone for the CV Manager (You can list pytz timezones with the command 'pytz.all_timezones') +TIMEZONE="US/Mountain" + +# Mapbox token for map rendering in the webapp +MAPBOX_TOKEN= +# DOT_NAME must be set for the DOT name to correctly populate when building an image for deployment +DOT_NAME="CDOT" +MAPBOX_INIT_LATITUDE="39.7392" +MAPBOX_INIT_LONGITUDE="-104.9903" +MAPBOX_INIT_ZOOM="10" + +GCP_PROJECT_ID = '' + +# --------------------------------------------------------------------- + +# Count Metric Addon: +ENABLE_EMAILER = 'True' + +# If ENABLE_EMAILER is 'True', set the following environment variables +DEPLOYMENT_TITLE = 'JPO-ODE' + +# SMTP REQUIRED VARIABLES +SMTP_SERVER_IP = '' +SMTP_USERNAME = '' +SMTP_PASSWORD = '' +SMTP_EMAIL = '' +# Multiple emails can be delimited by a ',' +SMTP_EMAIL_RECIPIENTS = 'test1@gmail.com,test2@gmail.com' + +# If ENABLE_EMAILER is 'False', set the following environment variables + +COUNT_MESSAGE_TYPES = 'bsm' +ODE_KAFKA_BROKERS = {DOCKER_HOST_IP}:9092 + +# EITHER "MONGODB" or "BIGQUERY" +COUNT_DESTINATION_DB = 'MONGODB' + +# MONGODB REQUIRED VARIABLES +INPUT_COUNTS_MONGO_COLLECTION_NAME = '' +OUTPUT_COUNTS_MONGO_COLLECTION_NAME = '' + +# BIGQUERY REQUIRED VARIABLES +KAFKA_BIGQUERY_TABLENAME = '' +# --------------------------------------------------------------------- + +# Firmware Manager Addon: +BLOB_STORAGE_PROVIDER=GCP +BLOB_STORAGE_BUCKET= +GCP_PROJECT= +# --------------------------------------------------------------------- + +# Geo-spatial message query Addon: +GEO_INPUT_COLLECTIONS='OdeBsmJson,OdePsmJson' +# TTL duration in days: +GEO_TTL_DURATION=90 +# --------------------------------------------------------------------- + +# ISS Health Check Addon +ISS_API_KEY= +ISS_API_KEY_NAME= +ISS_PROJECT_ID= +ISS_SCMS_TOKEN_REST_ENDPOINT= +ISS_SCMS_VEHICLE_REST_ENDPOINT= +# --------------------------------------------------------------------- + +# RSU Status Addon: + +# Services that can be toggled on or off +# 'True' or 'False' are the only legal values + +# Toggles monitoring of RSU online status +RSU_PING=True + +# Fetches ping data from Zabbix - alternatively the service will ping the RSUs on its own +# Only used when RSU_PING is 'True' +ZABBIX=False + +# Fetches SNMP configuration data for all RSUs +RSU_SNMP_FETCH=True + +# Zabbix endpoint and API authentication +# Only used when ZABBIX is 'True' +ZABBIX_ENDPOINT= +ZABBIX_USER= +ZABBIX_PASSWORD= + +# Customize the period at which the purger will determine a ping log is too old and will be deleted +# Number of hours +STALE_PERIOD=24 +# --------------------------------------------------------------------- + +# Levels are "DEBUG", "INFO", "WARNING", and "ERROR" +API_LOGGING_LEVEL="INFO" +FIRMWARE_MANAGER_LOGGING_LEVEL="INFO" +GEO_LOGGING_LEVEL="INFO" +ISS_LOGGING_LEVEL="INFO" +RSU_STATUS_LOGGING_LEVEL="INFO" +COUNTS_LOGGING_LEVEL="INFO" +# Supported log levels are "ALL", "DEBUG", "ERROR", "FATAL", "INFO", "OFF", "TRACE" and "WARN" +KC_LOGGING_LEVEL="INFO" \ No newline at end of file From 589a4cc582ea0d622bd76f3490c28d206566645a Mon Sep 17 00:00:00 2001 From: Marc Wodahl Date: Thu, 6 Jun 2024 07:59:42 -0600 Subject: [PATCH 058/189] Add check for orphan RSUs/Users before org delete --- services/api/src/admin_org.py | 24 +++++++++++++++++-- services/api/tests/src/test_admin_org.py | 10 ++++---- .../AdminOrganizationTab.tsx | 2 +- .../adminOrganizationTabSlice.tsx | 16 ++++++++++--- 4 files changed, 42 insertions(+), 10 deletions(-) diff --git a/services/api/src/admin_org.py b/services/api/src/admin_org.py index 28227c158..5647cc2ab 100644 --- a/services/api/src/admin_org.py +++ b/services/api/src/admin_org.py @@ -217,6 +217,25 @@ def modify_org(org_spec): def delete_org(org_name): + # Check to make sure there aren't any RSUs or users associated with just this organization + rsu_query = ( + "SELECT rsu_id, count(organization_id) FROM rsu_organization WHERE rsu_id IN (SELECT rsu_id FROM rsu_organization WHERE organization_id = " + f"(SELECT organization_id FROM organizations WHERE name = '{org_name}')) GROUP BY rsu_id" + ) + rsu_count = pgquery.query_db(rsu_query) + for rsu in rsu_count: + if rsu[1] == 1: + return {"message": "Cannot delete organization that has one or more RSUs only associated with this organization"}, 400 + + user_query = ( + "SELECT user_id, count(organization_id) FROM user_organization WHERE user_id IN (SELECT user_id FROM user_organization WHERE organization_id = " + f"(SELECT organization_id FROM organizations WHERE name = '{org_name}')) GROUP BY user_id" + ) + user_count = pgquery.query_db(user_query) + for user in user_count: + if user[1] == 1: + return {"message": "Cannot delete organization that has one or more users only associated with this organization"}, 400 + # Delete user-to-organization relationships user_org_remove_query = ( "DELETE FROM public.user_organization WHERE " @@ -235,7 +254,7 @@ def delete_org(org_name): org_remove_query = "DELETE FROM public.organizations WHERE " f"name = '{org_name}'" pgquery.write_db(org_remove_query) - return {"message": "Organization successfully deleted"} + return {"message": "Organization successfully deleted"}, 200 # REST endpoint resource class @@ -313,4 +332,5 @@ def delete(self): abort(400, errors) org_name = urllib.request.unquote(request.args["org_name"]) - return (delete_org(org_name), 200, self.headers) + message, code = delete_org(org_name) + return (message, code, self.headers) diff --git a/services/api/tests/src/test_admin_org.py b/services/api/tests/src/test_admin_org.py index 2f32f22eb..0e7e0e848 100644 --- a/services/api/tests/src/test_admin_org.py +++ b/services/api/tests/src/test_admin_org.py @@ -87,7 +87,7 @@ def test_entry_delete_user(mock_delete_org): req = MagicMock() req.environ = admin_org_data.request_environ req.args = admin_org_data.request_args_good - mock_delete_org.return_value = {} + mock_delete_org.return_value = {"message": "Organization successfully deleted"}, 200 with patch("api.src.admin_org.request", req): status = admin_org.AdminOrg() (body, code, headers) = status.delete() @@ -97,7 +97,7 @@ def test_entry_delete_user(mock_delete_org): ) assert code == 200 assert headers["Access-Control-Allow-Origin"] == "test.com" - assert body == {} + assert body == {"message": "Organization successfully deleted"} def test_entry_delete_schema(): @@ -268,8 +268,10 @@ def test_modify_org_sql_exception(mock_pgquery, mock_check_safe_input): @patch("api.src.admin_org.pgquery.write_db") -def test_delete_org(mock_write_db): - expected_result = {"message": "Organization successfully deleted"} +@patch("api.src.admin_org.pgquery.query_db") +def test_delete_org(mock_query_db, mock_write_db): + mock_query_db.return_value = [] + expected_result = {"message": "Organization successfully deleted"}, 200 actual_result = admin_org.delete_org("test org") calls = [ diff --git a/webapp/src/features/adminOrganizationTab/AdminOrganizationTab.tsx b/webapp/src/features/adminOrganizationTab/AdminOrganizationTab.tsx index fd0b85bfd..7fb7b83fb 100644 --- a/webapp/src/features/adminOrganizationTab/AdminOrganizationTab.tsx +++ b/webapp/src/features/adminOrganizationTab/AdminOrganizationTab.tsx @@ -116,7 +116,7 @@ const AdminOrganizationTab = () => { {errorState && (

- Failed to obtain data due to error: {errorMsg} + Failed to perform action due to error: {errorMsg}

)} diff --git a/webapp/src/features/adminOrganizationTab/adminOrganizationTabSlice.tsx b/webapp/src/features/adminOrganizationTab/adminOrganizationTabSlice.tsx index d5f992881..5410f8d04 100644 --- a/webapp/src/features/adminOrganizationTab/adminOrganizationTabSlice.tsx +++ b/webapp/src/features/adminOrganizationTab/adminOrganizationTabSlice.tsx @@ -105,10 +105,9 @@ export const deleteOrg = createAsyncThunk( case 200: console.debug('Successfully deleted Organization: ' + org) dispatch(getOrgData({ orgName: 'all', all: true })) - return + return { success: true, message: '' } default: - console.error(data) - return + return { success: false, message: data.message } } }, { condition: (_, { getState }) => selectToken(getState() as RootState) != undefined } @@ -234,6 +233,17 @@ export const adminOrganizationTabSlice = createSlice({ .addCase(editOrg.rejected, (state) => { state.loading = false }) + .addCase(deleteOrg.fulfilled, (state, action) => { + state.loading = false + if (action.payload.success) { + state.value.errorMsg = '' + state.value.errorState = false + } else { + console.log(action) + state.value.errorMsg = action.payload.message + state.value.errorState = true + } + }) }, }) From b03481ee4ec1525a5a528f41730e9ed3f40db1dd Mon Sep 17 00:00:00 2001 From: Marc Wodahl Date: Thu, 6 Jun 2024 12:58:47 -0600 Subject: [PATCH 059/189] Update orphan rsu/user check & testing update --- services/api/src/admin_org.py | 43 ++++++++++++++---------- services/api/tests/src/test_admin_org.py | 18 ++++++++++ 2 files changed, 44 insertions(+), 17 deletions(-) diff --git a/services/api/src/admin_org.py b/services/api/src/admin_org.py index 5647cc2ab..26ac1fb6e 100644 --- a/services/api/src/admin_org.py +++ b/services/api/src/admin_org.py @@ -217,24 +217,12 @@ def modify_org(org_spec): def delete_org(org_name): - # Check to make sure there aren't any RSUs or users associated with just this organization - rsu_query = ( - "SELECT rsu_id, count(organization_id) FROM rsu_organization WHERE rsu_id IN (SELECT rsu_id FROM rsu_organization WHERE organization_id = " - f"(SELECT organization_id FROM organizations WHERE name = '{org_name}')) GROUP BY rsu_id" - ) - rsu_count = pgquery.query_db(rsu_query) - for rsu in rsu_count: - if rsu[1] == 1: - return {"message": "Cannot delete organization that has one or more RSUs only associated with this organization"}, 400 + + if check_orphan_rsus(org_name): + return {"message": "Cannot delete organization that has one or more RSUs only associated with this organization"}, 400 - user_query = ( - "SELECT user_id, count(organization_id) FROM user_organization WHERE user_id IN (SELECT user_id FROM user_organization WHERE organization_id = " - f"(SELECT organization_id FROM organizations WHERE name = '{org_name}')) GROUP BY user_id" - ) - user_count = pgquery.query_db(user_query) - for user in user_count: - if user[1] == 1: - return {"message": "Cannot delete organization that has one or more users only associated with this organization"}, 400 + if check_orphan_users(org_name): + return {"message": "Cannot delete organization that has one or more users only associated with this organization"}, 400 # Delete user-to-organization relationships user_org_remove_query = ( @@ -256,6 +244,27 @@ def delete_org(org_name): return {"message": "Organization successfully deleted"}, 200 +def check_orphan_rsus(org): + rsu_query = ( + "SELECT rsu_id, count(organization_id) FROM rsu_organization WHERE rsu_id IN (SELECT rsu_id FROM rsu_organization WHERE organization_id = " + f"(SELECT organization_id FROM organizations WHERE name = '{org}')) GROUP BY rsu_id" + ) + rsu_count = pgquery.query_db(rsu_query) + for rsu in rsu_count: + if rsu[1] == 1: + return True + return False + +def check_orphan_users(org): + user_query = ( + "SELECT user_id, count(organization_id) FROM user_organization WHERE user_id IN (SELECT user_id FROM user_organization WHERE organization_id = " + f"(SELECT organization_id FROM organizations WHERE name = '{org}')) GROUP BY user_id" + ) + user_count = pgquery.query_db(user_query) + for user in user_count: + if user[1] == 1: + return True + return False # REST endpoint resource class from flask import request, abort diff --git a/services/api/tests/src/test_admin_org.py b/services/api/tests/src/test_admin_org.py index 0e7e0e848..f8524513b 100644 --- a/services/api/tests/src/test_admin_org.py +++ b/services/api/tests/src/test_admin_org.py @@ -281,3 +281,21 @@ def test_delete_org(mock_query_db, mock_write_db): ] mock_write_db.assert_has_calls(calls) assert actual_result == expected_result + +@patch("api.src.admin_org.pgquery.query_db") +def test_delete_org_failure_orphan_rsu(mock_query_db): + mock_query_db.return_value = [[1, 2], [2, 1]] + expected_result = {"message": "Cannot delete organization that has one or more RSUs only associated with this organization"}, 400 + actual_result = admin_org.delete_org("test org") + + assert actual_result == expected_result + +@patch("api.src.admin_org.pgquery.query_db") +@patch("api.src.admin_org.check_orphan_rsus") +def test_delete_org_failure_orphan_user(mock_orphan_rsus, mock_query_db): + mock_orphan_rsus.return_value = False + mock_query_db.return_value = [[1, 2], [2, 1]] + expected_result = {"message": "Cannot delete organization that has one or more users only associated with this organization"}, 400 + actual_result = admin_org.delete_org("test org") + + assert actual_result == expected_result \ No newline at end of file From 90aec8fde776c2e32926841627c3b5653b03687d Mon Sep 17 00:00:00 2001 From: Michael7371 <40476797+Michael7371@users.noreply.github.com> Date: Thu, 6 Jun 2024 13:17:44 -0600 Subject: [PATCH 060/189] download blob refactor --- .../images/firmware_manager/download_blob.py | 27 ----------- .../images/firmware_manager/upgrader.py | 7 ++- .../firmware_manager/test_download_blob.py | 45 ------------------- 3 files changed, 3 insertions(+), 76 deletions(-) diff --git a/services/addons/images/firmware_manager/download_blob.py b/services/addons/images/firmware_manager/download_blob.py index 36901273d..4e3190a11 100644 --- a/services/addons/images/firmware_manager/download_blob.py +++ b/services/addons/images/firmware_manager/download_blob.py @@ -1,34 +1,7 @@ -from google.cloud import storage import logging import os -def download_gcp_blob(blob_name, destination_file_name): - """Download a file from a GCP Bucket Storage bucket to a local file. - - Args: - blob_name (str): The name of the file in the bucket. - destination_file_name (str): The name of the local file to download the bucket file to. - """ - - if not validate_file_type(blob_name): - return False - - gcp_project = os.environ.get("GCP_PROJECT") - bucket_name = os.environ.get("BLOB_STORAGE_BUCKET") - storage_client = storage.Client(gcp_project) - bucket = storage_client.get_bucket(bucket_name) - blob = bucket.blob(blob_name) - - if blob.exists(): - blob.download_to_filename(destination_file_name) - logging.info( - f"Downloaded storage object {blob_name} from bucket {bucket_name} to local file {destination_file_name}." - ) - return True - return False - - def download_docker_blob(blob_name, destination_file_name): """Copy a file from a directory mounted as a volume in a Docker container to a local file. diff --git a/services/addons/images/firmware_manager/upgrader.py b/services/addons/images/firmware_manager/upgrader.py index ff0bd718d..b7fab0b2c 100644 --- a/services/addons/images/firmware_manager/upgrader.py +++ b/services/addons/images/firmware_manager/upgrader.py @@ -8,6 +8,7 @@ import requests import shutil from common.emailSender import EmailSender +import download_blob class UpgraderAbstractClass(abc.ABC): @@ -44,11 +45,9 @@ def download_blob(self, blob_name=None, local_file_name=None): "BLOB_STORAGE_PROVIDER", "DOCKER" ).casefold() if bspCaseInsensitive == "gcp": - return gcs_utils.download_blob.download_gcp_blob(blob_name, local_file_name) + return gcs_utils.download_gcp_blob(blob_name, local_file_name) elif bspCaseInsensitive == "docker": - return gcs_utils.download_blob.download_docker_blob( - blob_name, local_file_name - ) + return download_blob.download_docker_blob(blob_name, local_file_name) else: logging.error("Unsupported blob storage provider") raise StorageProviderNotSupportedException diff --git a/services/addons/tests/firmware_manager/test_download_blob.py b/services/addons/tests/firmware_manager/test_download_blob.py index 970ec8f97..e1be5adea 100644 --- a/services/addons/tests/firmware_manager/test_download_blob.py +++ b/services/addons/tests/firmware_manager/test_download_blob.py @@ -6,51 +6,6 @@ from addons.images.firmware_manager.download_blob import UnsupportedFileTypeException -@patch.dict( - os.environ, {"GCP_PROJECT": "test-project", "BLOB_STORAGE_BUCKET": "test-bucket"} -) -@patch("addons.images.firmware_manager.download_blob.logging") -@patch("addons.images.firmware_manager.download_blob.storage.Client") -def test_download_gcp_blob(mock_storage_client, mock_logging): - # mock - mock_client = mock_storage_client.return_value - mock_bucket = mock_client.get_bucket.return_value - mock_blob = mock_bucket.blob.return_value - - # run - download_blob.download_gcp_blob( - blob_name="test.tar", destination_file_name="/home/test/" - ) - - # validate - mock_storage_client.assert_called_with("test-project") - mock_client.get_bucket.assert_called_with("test-bucket") - mock_bucket.blob.assert_called_with("test.tar") - mock_blob.download_to_filename.assert_called_with("/home/test/") - mock_logging.info.assert_called_with( - "Downloaded storage object test.tar from bucket test-bucket to local file /home/test/." - ) - - -@patch.dict( - os.environ, {"GCP_PROJECT": "test-project", "BLOB_STORAGE_BUCKET": "test-bucket"} -) -@patch("addons.images.firmware_manager.download_blob.logging") -def test_download_gcp_blob_unsupported_file_type(mock_logging): - # prepare - blob_name = "test.blob" - destination_file_name = "/home/test/" - - # run - result = download_blob.download_gcp_blob(blob_name, destination_file_name) - - # validate - mock_logging.error.assert_called_with( - f"Unsupported file type for storage object {blob_name}. Only .tar files are supported." - ) - assert result == False - - @patch("addons.images.firmware_manager.download_blob.logging") def test_download_docker_blob(mock_logging): # prepare From d7cfc6dc5e0870e280ce45fcac48eecac120a3aa Mon Sep 17 00:00:00 2001 From: Michael7371 <40476797+Michael7371@users.noreply.github.com> Date: Thu, 6 Jun 2024 13:38:39 -0600 Subject: [PATCH 061/189] updates to download blob to move validate_file_type to util.py --- .../images/firmware_manager/download_blob.py | 21 +------------------ services/common/gcs_utils.py | 15 +++++++++++-- services/common/tests/test_gcs_utils.py | 7 +++---- services/common/tests/test_util.py | 20 ++++++++++++++++++ services/common/util.py | 16 ++++++++++++++ 5 files changed, 53 insertions(+), 26 deletions(-) diff --git a/services/addons/images/firmware_manager/download_blob.py b/services/addons/images/firmware_manager/download_blob.py index 4e3190a11..97894b5ad 100644 --- a/services/addons/images/firmware_manager/download_blob.py +++ b/services/addons/images/firmware_manager/download_blob.py @@ -1,5 +1,6 @@ import logging import os +from common.util import validate_file_type def download_docker_blob(blob_name, destination_file_name): @@ -20,23 +21,3 @@ def download_docker_blob(blob_name, destination_file_name): f"Copied storage object {blob_name} from directory {directory} to local file {destination_file_name}." ) return True - - -def validate_file_type(file_name): - """Validate the file type of the file to be downloaded. - - Args: - file_name (str): The name of the file to be downloaded. - """ - if not file_name.endswith(".tar"): - logging.error( - f"Unsupported file type for storage object {file_name}. Only .tar files are supported." - ) - return False - return True - - -class UnsupportedFileTypeException(Exception): - def __init__(self, message="Unsupported file type. Only .tar files are supported."): - self.message = message - super().__init__(self.message) diff --git a/services/common/gcs_utils.py b/services/common/gcs_utils.py index 7a9df0603..6af6653ca 100644 --- a/services/common/gcs_utils.py +++ b/services/common/gcs_utils.py @@ -1,15 +1,26 @@ from google.cloud import storage +from common.util import validate_file_type import logging import os -# Only supports GCP Bucket Storage for downloading blobs def download_gcp_blob(blob_name, destination_file_name): + """Download a file from a GCP Bucket Storage bucket to a local file. + + Args: + blob_name (str): The name of the file in the bucket. + destination_file_name (str): The name of the local file to download the bucket file to. + """ + + if not validate_file_type(blob_name): + return False + gcp_project = os.environ.get("GCP_PROJECT") bucket_name = os.environ.get("BLOB_STORAGE_BUCKET") storage_client = storage.Client(gcp_project) bucket = storage_client.get_bucket(bucket_name) blob = bucket.blob(blob_name) + if blob.exists(): blob.download_to_filename(destination_file_name) logging.info( @@ -30,7 +41,7 @@ def list_gcs_blobs(gcs_prefix, file_extension): download_count = 0 for blob in blobs: logging.debug(f"Blob: {blob.name}") - if blob.name.endswith(file_extension): + if validate_file_type(blob.name, file_extension): path = f"/firmwares/{blob.name.split(gcs_prefix)[-1]}" files.append(path) if not os.path.exists(path): diff --git a/services/common/tests/test_gcs_utils.py b/services/common/tests/test_gcs_utils.py index 15fcbf105..9d462634b 100644 --- a/services/common/tests/test_gcs_utils.py +++ b/services/common/tests/test_gcs_utils.py @@ -1,6 +1,5 @@ from unittest.mock import patch, MagicMock import os - from common import gcs_utils @@ -17,16 +16,16 @@ def test_download_gcp_blob(mock_storage_client, mock_logging): # run gcs_utils.download_gcp_blob( - blob_name="test.blob", destination_file_name="/home/test/" + blob_name="test.tar", destination_file_name="/home/test/" ) # validate mock_storage_client.assert_called_with("test-project") mock_client.get_bucket.assert_called_with("test-bucket") - mock_bucket.blob.assert_called_with("test.blob") + mock_bucket.blob.assert_called_with("test.tar") mock_blob.download_to_filename.assert_called_with("/home/test/") mock_logging.info.assert_called_with( - "Downloaded storage object test.blob from bucket test-bucket to local file /home/test/." + "Downloaded storage object test.tar from bucket test-bucket to local file /home/test/." ) diff --git a/services/common/tests/test_util.py b/services/common/tests/test_util.py index 2f1bf9523..b665d9300 100644 --- a/services/common/tests/test_util.py +++ b/services/common/tests/test_util.py @@ -47,3 +47,23 @@ def test_utc2tz_failure(): assert util.utc2tz(d) == None d = None assert util.utc2tz(d) == None + + +def test_validation_file_type(): + file_name = "test.tar" + file_extension = ".tar" + validation = util.validate_file_type(file_name, file_extension) + assert validation == True + + +def test_validation_file_type_no_extension(): + file_name = "test.tar" + validation = util.validate_file_type(file_name) + assert validation == True + + +def test_validation_file_type_failure(): + file_name = "test.blob" + file_extension = ".tar" + validation = util.validate_file_type(file_name, file_extension) + assert validation == False diff --git a/services/common/util.py b/services/common/util.py index b5dd4f545..92405210f 100644 --- a/services/common/util.py +++ b/services/common/util.py @@ -1,6 +1,7 @@ from dateutil.parser import parse import pytz import os +import logging # expects datetime string @@ -42,3 +43,18 @@ def utc2tz(d): return None tz_d = d.astimezone(pytz.timezone(os.getenv("TIMEZONE", "America/Denver"))) return tz_d + + +def validate_file_type(file_name, extension=".tar"): + """Validate the file type of the file to be downloaded. + + Args: + file_name (str): The name of the file to be downloaded. + extension (str): The file extension to validate against. + """ + if not file_name.endswith(extension): + logging.error( + f'Unsupported file type for storage object {file_name}. Only "{extension}" files are supported.' + ) + return False + return True From ce82c647c816ebf777c678bb1899b9136538f0a5 Mon Sep 17 00:00:00 2001 From: Michael7371 <40476797+Michael7371@users.noreply.github.com> Date: Thu, 6 Jun 2024 14:47:20 -0600 Subject: [PATCH 062/189] adding requirements for util.py to firmware manager and obu ota addons --- services/addons/images/firmware_manager/requirements.txt | 4 +++- services/addons/images/obu_ota_server/requirements.txt | 2 ++ 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/services/addons/images/firmware_manager/requirements.txt b/services/addons/images/firmware_manager/requirements.txt index 0484c7a7d..8411b9977 100644 --- a/services/addons/images/firmware_manager/requirements.txt +++ b/services/addons/images/firmware_manager/requirements.txt @@ -6,4 +6,6 @@ pg8000==1.30.2 requests==2.31.0 scp==0.14.5 sqlalchemy==2.0.21 -waitress==2.1.2 \ No newline at end of file +waitress==2.1.2 +python-dateutil==2.8.2 +pytz==2023.3.post1 diff --git a/services/addons/images/obu_ota_server/requirements.txt b/services/addons/images/obu_ota_server/requirements.txt index d4e91c8ff..8f8a14c7d 100644 --- a/services/addons/images/obu_ota_server/requirements.txt +++ b/services/addons/images/obu_ota_server/requirements.txt @@ -3,3 +3,5 @@ pydantic==2.6.2 uvicorn==0.27.1 aiofiles==23.2.1 google-cloud-storage==2.14.0 +python-dateutil==2.8.2 +pytz==2023.3.post1 From 04a81c3e29acc05e1f45922dced5145a1c4a01ba Mon Sep 17 00:00:00 2001 From: Michael7371 <40476797+Michael7371@users.noreply.github.com> Date: Fri, 7 Jun 2024 13:46:47 -0600 Subject: [PATCH 063/189] fixing typo in readme and error in test_download_blob --- services/addons/images/obu_ota_server/README.md | 2 +- services/addons/tests/firmware_manager/test_download_blob.py | 5 ++--- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/services/addons/images/obu_ota_server/README.md b/services/addons/images/obu_ota_server/README.md index 38e8af00c..776a719b4 100644 --- a/services/addons/images/obu_ota_server/README.md +++ b/services/addons/images/obu_ota_server/README.md @@ -26,7 +26,7 @@ Available REST endpoints: - Secured with basic authentication. - Firmware file name must comply with the following regex naming filter: `(?P(?Pob4|rs4)-generic(?:-.*)?)-(?:(?Prw)|ro)(?P-secureboot)?-(?P.*).tar.sig` - - Will either query GCS or the specified local path for a latest list of firmware files. Generates a manifest that includes details about each firmware file along with a `href` field that the OBU uses to get generate the download request. Here is a sample response: + - Will either query GCS or the specified local path for a latest list of firmware files. Generates a manifest that includes details about each firmware file along with a `href` field that the OBU uses to generate the download request. Here is a sample response: `{"content":[{"id":"","name":"ob4-generic-ro-secureboot-y20.48.2-b228647.tar.sig","variant":"ob4-generic","releaseVersion":"y20.48.2-b228647","type":"OBU","writableSystemPartition":false,"secure":true,"uploadedAt":"2024-05-29T22:12:33Z","size":135055616,"links":[{"rel":"local-file","href":"localhost/firmwares/commsignia/ob4-generic-ro-secureboot-y20.48.2-b228647.tar.sig","type":"application/octet-stream"}]}],"pageable":{"sort":{"empty":false,"unsorted":false,"sorted":true},"offset":0,"pageNumber":0,"pageSize":32,"paged":true,"unpaged":false},"last":true,"totalPages":1,"totalElements":1,"size":32,"number":0,"sort":{"empty":false,"unsorted":false,"sorted":true},"numberOfElements":1,"first":true,"empty":false}` - /firmwares/commsignia/{firmware_id} [ **GET** ] - Secured with basic authentication. diff --git a/services/addons/tests/firmware_manager/test_download_blob.py b/services/addons/tests/firmware_manager/test_download_blob.py index e1be5adea..a266c44d4 100644 --- a/services/addons/tests/firmware_manager/test_download_blob.py +++ b/services/addons/tests/firmware_manager/test_download_blob.py @@ -3,7 +3,6 @@ import pytest from addons.images.firmware_manager import download_blob -from addons.images.firmware_manager.download_blob import UnsupportedFileTypeException @patch("addons.images.firmware_manager.download_blob.logging") @@ -25,7 +24,7 @@ def test_download_docker_blob(mock_logging): ) -@patch("addons.images.firmware_manager.download_blob.logging") +@patch("common.util.logging") def test_download_docker_blob_unsupported_file_type(mock_logging): # prepare os.system = MagicMock() @@ -37,6 +36,6 @@ def test_download_docker_blob_unsupported_file_type(mock_logging): # validate mock_logging.error.assert_called_with( - f"Unsupported file type for storage object {blob_name}. Only .tar files are supported." + f'Unsupported file type for storage object {blob_name}. Only ".tar" files are supported.' ) assert result == False From 86a44e447b92a606301aa733b6b55ec80a698b4a Mon Sep 17 00:00:00 2001 From: Marc Wodahl Date: Mon, 10 Jun 2024 08:13:57 -0600 Subject: [PATCH 064/189] Update to use mui version 5 packages --- webapp/package-lock.json | 484 +++++++++++------- webapp/package.json | 11 +- .../AdminFormManager.test.tsx.snap | 56 +- .../AdminOrganizationDeleteMenu.test.tsx.snap | 2 +- .../__snapshots__/AdminTable.test.tsx.snap | 15 +- .../__snapshots__/SnmpwalkMenu.test.tsx.snap | 2 +- .../AdminOrganizationTab.test.tsx.snap | 4 +- .../__snapshots__/AdminUserTab.test.tsx.snap | 25 +- .../__snapshots__/DisplayCounts.test.tsx.snap | 4 +- .../pages/__snapshots__/Admin.test.tsx.snap | 2 +- .../src/pages/__snapshots__/Map.test.tsx.snap | 4 +- webapp/yarn.lock | 442 +++++++++------- 12 files changed, 601 insertions(+), 450 deletions(-) diff --git a/webapp/package-lock.json b/webapp/package-lock.json index 150013f5b..d353df7f4 100644 --- a/webapp/package-lock.json +++ b/webapp/package-lock.json @@ -8,14 +8,15 @@ "name": "cv-manager", "version": "1.2.0-SNAPSHOT", "dependencies": { - "@emotion/react": "^11.10.5", - "@emotion/styled": "^11.10.5", + "@emotion/react": "^11.11.4", + "@emotion/styled": "^11.11.5", "@hookform/error-message": "^2.0.1", "@material-table/core": "^6.1.6", "@material-ui/core": "^4.12.3", - "@mui/icons-material": "^5.11.9", - "@mui/material": "^5.11.9", + "@mui/icons-material": "^5.15.19", + "@mui/material": "^5.15.19", "@mui/styled-engine-sc": "^5.11.9", + "@mui/styles": "^5.15.19", "@mui/x-date-pickers": "^5.0.15", "@react-keycloak/web": "3.4.0", "@react-oauth/google": "^0.2.8", @@ -1946,9 +1947,9 @@ "integrity": "sha512-x/rqGMdzj+fWZvCOYForTghzbtqPDZ5gPwaoNGHdgDfF2QA/XZbCBp4Moo5scrkAMPhB7z26XM/AaHuIJdgauA==" }, "node_modules/@babel/runtime": { - "version": "7.23.8", - "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.23.8.tgz", - "integrity": "sha512-Y7KbAP984rn1VGMbGqKmBLio9V7y5Je9GvU4rQPCPinCyNfUcToxIXl06d59URp/F3LwinvODxab5N/G6qggkw==", + "version": "7.24.7", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.24.7.tgz", + "integrity": "sha512-UwgBRMjJP+xv857DCngvqXI3Iq6J4v0wXmwc6sapg+zyhbwmQX67LUEFrkK5tbyJ30jGuG3ZvWpBiB9LCy1kWw==", "dependencies": { "regenerator-runtime": "^0.14.0" }, @@ -2352,31 +2353,27 @@ } }, "node_modules/@emotion/babel-plugin": { - "version": "11.10.5", - "resolved": "https://registry.npmjs.org/@emotion/babel-plugin/-/babel-plugin-11.10.5.tgz", - "integrity": "sha512-xE7/hyLHJac7D2Ve9dKroBBZqBT7WuPQmWcq7HSGb84sUuP4mlOWoB8dvVfD9yk5DHkU1m6RW7xSoDtnQHNQeA==", + "version": "11.11.0", + "resolved": "https://registry.npmjs.org/@emotion/babel-plugin/-/babel-plugin-11.11.0.tgz", + "integrity": "sha512-m4HEDZleaaCH+XgDDsPF15Ht6wTLsgDTeR3WYj9Q/k76JtWhrJjcP4+/XlG8LGT/Rol9qUfOIztXeA84ATpqPQ==", "dependencies": { "@babel/helper-module-imports": "^7.16.7", - "@babel/plugin-syntax-jsx": "^7.17.12", "@babel/runtime": "^7.18.3", - "@emotion/hash": "^0.9.0", - "@emotion/memoize": "^0.8.0", - "@emotion/serialize": "^1.1.1", + "@emotion/hash": "^0.9.1", + "@emotion/memoize": "^0.8.1", + "@emotion/serialize": "^1.1.2", "babel-plugin-macros": "^3.1.0", "convert-source-map": "^1.5.0", "escape-string-regexp": "^4.0.0", "find-root": "^1.1.0", "source-map": "^0.5.7", - "stylis": "4.1.3" - }, - "peerDependencies": { - "@babel/core": "^7.0.0" + "stylis": "4.2.0" } }, "node_modules/@emotion/babel-plugin/node_modules/@emotion/hash": { - "version": "0.9.0", - "resolved": "https://registry.npmjs.org/@emotion/hash/-/hash-0.9.0.tgz", - "integrity": "sha512-14FtKiHhy2QoPIzdTcvh//8OyBlknNs2nXRwIhG904opCby3l+9Xaf/wuPvICBF0rc1ZCNBd3nKe9cd2mecVkQ==" + "version": "0.9.1", + "resolved": "https://registry.npmjs.org/@emotion/hash/-/hash-0.9.1.tgz", + "integrity": "sha512-gJB6HLm5rYwSLI6PQa+X1t5CFGrv1J1TWG+sOyMCeKz2ojaj6Fnl/rZEspogG+cvqbt4AE/2eIyD2QfLKTBNlQ==" }, "node_modules/@emotion/babel-plugin/node_modules/escape-string-regexp": { "version": "4.0.0", @@ -2390,15 +2387,15 @@ } }, "node_modules/@emotion/cache": { - "version": "11.10.5", - "resolved": "https://registry.npmjs.org/@emotion/cache/-/cache-11.10.5.tgz", - "integrity": "sha512-dGYHWyzTdmK+f2+EnIGBpkz1lKc4Zbj2KHd4cX3Wi8/OWr5pKslNjc3yABKH4adRGCvSX4VDC0i04mrrq0aiRA==", + "version": "11.11.0", + "resolved": "https://registry.npmjs.org/@emotion/cache/-/cache-11.11.0.tgz", + "integrity": "sha512-P34z9ssTCBi3e9EI1ZsWpNHcfY1r09ZO0rZbRO2ob3ZQMnFI35jB536qoXbkdesr5EUhYi22anuEJuyxifaqAQ==", "dependencies": { - "@emotion/memoize": "^0.8.0", - "@emotion/sheet": "^1.2.1", - "@emotion/utils": "^1.2.0", - "@emotion/weak-memoize": "^0.3.0", - "stylis": "4.1.3" + "@emotion/memoize": "^0.8.1", + "@emotion/sheet": "^1.2.2", + "@emotion/utils": "^1.2.1", + "@emotion/weak-memoize": "^0.3.1", + "stylis": "4.2.0" } }, "node_modules/@emotion/core": { @@ -2412,93 +2409,85 @@ "integrity": "sha512-kBJtf7PH6aWwZ6fka3zQ0p6SBYzx4fl1LoZXE2RrnYST9Xljm7WfKJrU4g/Xr3Beg72MLrp1AWNUmuYJTL7Cow==" }, "node_modules/@emotion/is-prop-valid": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/@emotion/is-prop-valid/-/is-prop-valid-1.2.0.tgz", - "integrity": "sha512-3aDpDprjM0AwaxGE09bOPkNxHpBd+kA6jty3RnaEXdweX1DF1U3VQpPYb0g1IStAuK7SVQ1cy+bNBBKp4W3Fjg==", + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/@emotion/is-prop-valid/-/is-prop-valid-1.2.2.tgz", + "integrity": "sha512-uNsoYd37AFmaCdXlg6EYD1KaPOaRWRByMCYzbKUX4+hhMfrxdVSelShywL4JVaAeM/eHUOSprYBQls+/neX3pw==", "dependencies": { - "@emotion/memoize": "^0.8.0" + "@emotion/memoize": "^0.8.1" } }, "node_modules/@emotion/memoize": { - "version": "0.8.0", - "resolved": "https://registry.npmjs.org/@emotion/memoize/-/memoize-0.8.0.tgz", - "integrity": "sha512-G/YwXTkv7Den9mXDO7AhLWkE3q+I92B+VqAE+dYG4NGPaHZGvt3G8Q0p9vmE+sq7rTGphUbAvmQ9YpbfMQGGlA==" + "version": "0.8.1", + "resolved": "https://registry.npmjs.org/@emotion/memoize/-/memoize-0.8.1.tgz", + "integrity": "sha512-W2P2c/VRW1/1tLox0mVUalvnWXxavmv/Oum2aPsRcoDJuob75FC3Y8FbpfLwUegRcxINtGUMPq0tFCvYNTBXNA==" }, "node_modules/@emotion/react": { - "version": "11.10.5", - "resolved": "https://registry.npmjs.org/@emotion/react/-/react-11.10.5.tgz", - "integrity": "sha512-TZs6235tCJ/7iF6/rvTaOH4oxQg2gMAcdHemjwLKIjKz4rRuYe1HJ2TQJKnAcRAfOUDdU8XoDadCe1rl72iv8A==", + "version": "11.11.4", + "resolved": "https://registry.npmjs.org/@emotion/react/-/react-11.11.4.tgz", + "integrity": "sha512-t8AjMlF0gHpvvxk5mAtCqR4vmxiGHCeJBaQO6gncUSdklELOgtwjerNY2yuJNfwnc6vi16U/+uMF+afIawJ9iw==", "dependencies": { "@babel/runtime": "^7.18.3", - "@emotion/babel-plugin": "^11.10.5", - "@emotion/cache": "^11.10.5", - "@emotion/serialize": "^1.1.1", - "@emotion/use-insertion-effect-with-fallbacks": "^1.0.0", - "@emotion/utils": "^1.2.0", - "@emotion/weak-memoize": "^0.3.0", + "@emotion/babel-plugin": "^11.11.0", + "@emotion/cache": "^11.11.0", + "@emotion/serialize": "^1.1.3", + "@emotion/use-insertion-effect-with-fallbacks": "^1.0.1", + "@emotion/utils": "^1.2.1", + "@emotion/weak-memoize": "^0.3.1", "hoist-non-react-statics": "^3.3.1" }, "peerDependencies": { - "@babel/core": "^7.0.0", "react": ">=16.8.0" }, "peerDependenciesMeta": { - "@babel/core": { - "optional": true - }, "@types/react": { "optional": true } } }, "node_modules/@emotion/serialize": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/@emotion/serialize/-/serialize-1.1.1.tgz", - "integrity": "sha512-Zl/0LFggN7+L1liljxXdsVSVlg6E/Z/olVWpfxUTxOAmi8NU7YoeWeLfi1RmnB2TATHoaWwIBRoL+FvAJiTUQA==", + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/@emotion/serialize/-/serialize-1.1.4.tgz", + "integrity": "sha512-RIN04MBT8g+FnDwgvIUi8czvr1LU1alUMI05LekWB5DGyTm8cCBMCRpq3GqaiyEDRptEXOyXnvZ58GZYu4kBxQ==", "dependencies": { - "@emotion/hash": "^0.9.0", - "@emotion/memoize": "^0.8.0", - "@emotion/unitless": "^0.8.0", - "@emotion/utils": "^1.2.0", + "@emotion/hash": "^0.9.1", + "@emotion/memoize": "^0.8.1", + "@emotion/unitless": "^0.8.1", + "@emotion/utils": "^1.2.1", "csstype": "^3.0.2" } }, "node_modules/@emotion/serialize/node_modules/@emotion/hash": { - "version": "0.9.0", - "resolved": "https://registry.npmjs.org/@emotion/hash/-/hash-0.9.0.tgz", - "integrity": "sha512-14FtKiHhy2QoPIzdTcvh//8OyBlknNs2nXRwIhG904opCby3l+9Xaf/wuPvICBF0rc1ZCNBd3nKe9cd2mecVkQ==" + "version": "0.9.1", + "resolved": "https://registry.npmjs.org/@emotion/hash/-/hash-0.9.1.tgz", + "integrity": "sha512-gJB6HLm5rYwSLI6PQa+X1t5CFGrv1J1TWG+sOyMCeKz2ojaj6Fnl/rZEspogG+cvqbt4AE/2eIyD2QfLKTBNlQ==" }, "node_modules/@emotion/serialize/node_modules/csstype": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.1.tgz", - "integrity": "sha512-DJR/VvkAvSZW9bTouZue2sSxDwdTN92uHjqeKVm+0dAqdfNykRzQ95tay8aXMBAAPpUiq4Qcug2L7neoRh2Egw==" + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.3.tgz", + "integrity": "sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==" }, "node_modules/@emotion/sheet": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/@emotion/sheet/-/sheet-1.2.1.tgz", - "integrity": "sha512-zxRBwl93sHMsOj4zs+OslQKg/uhF38MB+OMKoCrVuS0nyTkqnau+BM3WGEoOptg9Oz45T/aIGs1qbVAsEFo3nA==" + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/@emotion/sheet/-/sheet-1.2.2.tgz", + "integrity": "sha512-0QBtGvaqtWi+nx6doRwDdBIzhNdZrXUppvTM4dtZZWEGTXL/XE/yJxLMGlDT1Gt+UHH5IX1n+jkXyytE/av7OA==" }, "node_modules/@emotion/styled": { - "version": "11.10.5", - "resolved": "https://registry.npmjs.org/@emotion/styled/-/styled-11.10.5.tgz", - "integrity": "sha512-8EP6dD7dMkdku2foLoruPCNkRevzdcBaY6q0l0OsbyJK+x8D9HWjX27ARiSIKNF634hY9Zdoedh8bJCiva8yZw==", + "version": "11.11.5", + "resolved": "https://registry.npmjs.org/@emotion/styled/-/styled-11.11.5.tgz", + "integrity": "sha512-/ZjjnaNKvuMPxcIiUkf/9SHoG4Q196DRl1w82hQ3WCsjo1IUR8uaGWrC6a87CrYAW0Kb/pK7hk8BnLgLRi9KoQ==", "dependencies": { "@babel/runtime": "^7.18.3", - "@emotion/babel-plugin": "^11.10.5", - "@emotion/is-prop-valid": "^1.2.0", - "@emotion/serialize": "^1.1.1", - "@emotion/use-insertion-effect-with-fallbacks": "^1.0.0", - "@emotion/utils": "^1.2.0" + "@emotion/babel-plugin": "^11.11.0", + "@emotion/is-prop-valid": "^1.2.2", + "@emotion/serialize": "^1.1.4", + "@emotion/use-insertion-effect-with-fallbacks": "^1.0.1", + "@emotion/utils": "^1.2.1" }, "peerDependencies": { - "@babel/core": "^7.0.0", "@emotion/react": "^11.0.0-rc.0", "react": ">=16.8.0" }, "peerDependenciesMeta": { - "@babel/core": { - "optional": true - }, "@types/react": { "optional": true } @@ -2510,27 +2499,27 @@ "integrity": "sha512-h6KtPihKFn3T9fuIrwvXXUOwlx3rfUvfZIcP5a6rh8Y7zjE3O06hT5Ss4S/YI1AYhuZ1kjaE/5EaOOI2NqSylQ==" }, "node_modules/@emotion/unitless": { - "version": "0.8.0", - "resolved": "https://registry.npmjs.org/@emotion/unitless/-/unitless-0.8.0.tgz", - "integrity": "sha512-VINS5vEYAscRl2ZUDiT3uMPlrFQupiKgHz5AA4bCH1miKBg4qtwkim1qPmJj/4WG6TreYMY111rEFsjupcOKHw==" + "version": "0.8.1", + "resolved": "https://registry.npmjs.org/@emotion/unitless/-/unitless-0.8.1.tgz", + "integrity": "sha512-KOEGMu6dmJZtpadb476IsZBclKvILjopjUii3V+7MnXIQCYh8W3NgNcgwo21n9LXZX6EDIKvqfjYxXebDwxKmQ==" }, "node_modules/@emotion/use-insertion-effect-with-fallbacks": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/@emotion/use-insertion-effect-with-fallbacks/-/use-insertion-effect-with-fallbacks-1.0.0.tgz", - "integrity": "sha512-1eEgUGmkaljiBnRMTdksDV1W4kUnmwgp7X9G8B++9GYwl1lUdqSndSriIrTJ0N7LQaoauY9JJ2yhiOYK5+NI4A==", + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@emotion/use-insertion-effect-with-fallbacks/-/use-insertion-effect-with-fallbacks-1.0.1.tgz", + "integrity": "sha512-jT/qyKZ9rzLErtrjGgdkMBn2OP8wl0G3sQlBb3YPryvKHsjvINUhVaPFfP+fpBcOkmrVOVEEHQFJ7nbj2TH2gw==", "peerDependencies": { "react": ">=16.8.0" } }, "node_modules/@emotion/utils": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/@emotion/utils/-/utils-1.2.0.tgz", - "integrity": "sha512-sn3WH53Kzpw8oQ5mgMmIzzyAaH2ZqFEbozVVBSYp538E06OSE6ytOp7pRAjNQR+Q/orwqdQYJSe2m3hCOeznkw==" + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/@emotion/utils/-/utils-1.2.1.tgz", + "integrity": "sha512-Y2tGf3I+XVnajdItskUCn6LX+VUDmP6lTL4fcqsXAv43dnlbZiuW4MWQW38rW/BVWSE7Q/7+XQocmpnRYILUmg==" }, "node_modules/@emotion/weak-memoize": { - "version": "0.3.0", - "resolved": "https://registry.npmjs.org/@emotion/weak-memoize/-/weak-memoize-0.3.0.tgz", - "integrity": "sha512-AHPmaAx+RYfZz0eYu6Gviiagpmiyw98ySSlQvCUhVGDRtDFe4DBS0x1bSjdF3gqUDYOczB+yYvBTtEylYSdRhg==" + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/@emotion/weak-memoize/-/weak-memoize-0.3.1.tgz", + "integrity": "sha512-EsBwpc7hBUJWAsNPBmJy4hxWx12v6bshQsldrVmjxJoc3isbxhOrF2IcCpaXxfvq03NwkI7sbsOLXbYuqF/8Ww==" }, "node_modules/@eslint-community/eslint-utils": { "version": "4.4.0", @@ -2617,6 +2606,40 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/@floating-ui/core": { + "version": "1.6.2", + "resolved": "https://registry.npmjs.org/@floating-ui/core/-/core-1.6.2.tgz", + "integrity": "sha512-+2XpQV9LLZeanU4ZevzRnGFg2neDeKHgFLjP6YLW+tly0IvrhqT4u8enLGjLH3qeh85g19xY5rsAusfwTdn5lg==", + "dependencies": { + "@floating-ui/utils": "^0.2.0" + } + }, + "node_modules/@floating-ui/dom": { + "version": "1.6.5", + "resolved": "https://registry.npmjs.org/@floating-ui/dom/-/dom-1.6.5.tgz", + "integrity": "sha512-Nsdud2X65Dz+1RHjAIP0t8z5e2ff/IRbei6BqFrl1urT8sDVzM1HMQ+R0XcU5ceRfyO3I6ayeqIfh+6Wb8LGTw==", + "dependencies": { + "@floating-ui/core": "^1.0.0", + "@floating-ui/utils": "^0.2.0" + } + }, + "node_modules/@floating-ui/react-dom": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/@floating-ui/react-dom/-/react-dom-2.1.0.tgz", + "integrity": "sha512-lNzj5EQmEKn5FFKc04+zasr09h/uX8RtJRNj5gUXsSQIXHVWTVh+hVAg1vOMCexkX8EgvemMvIFpQfkosnVNyA==", + "dependencies": { + "@floating-ui/dom": "^1.0.0" + }, + "peerDependencies": { + "react": ">=16.8.0", + "react-dom": ">=16.8.0" + } + }, + "node_modules/@floating-ui/utils": { + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/@floating-ui/utils/-/utils-0.2.2.tgz", + "integrity": "sha512-J4yDIIthosAsRZ5CPYP/jQvUAQtlZTTD/4suA08/FEnlxqW3sKS9iAhgsa9VYLZ6vDHn/ixJgIqRQPotoBjxIw==" + }, "node_modules/@hello-pangea/dnd": { "version": "16.2.0", "resolved": "https://registry.npmjs.org/@hello-pangea/dnd/-/dnd-16.2.0.tgz", @@ -5159,25 +5182,24 @@ } }, "node_modules/@mui/base": { - "version": "5.0.0-alpha.119", - "resolved": "https://registry.npmjs.org/@mui/base/-/base-5.0.0-alpha.119.tgz", - "integrity": "sha512-XA5zhlYfXi67u613eIF0xRmktkatx6ERy3h+PwrMN5IcWFbgiL1guz8VpdXON+GWb8+G7B8t5oqTFIaCqaSAeA==", - "dependencies": { - "@babel/runtime": "^7.21.0", - "@emotion/is-prop-valid": "^1.2.0", - "@mui/types": "^7.2.3", - "@mui/utils": "^5.11.11", - "@popperjs/core": "^2.11.6", - "clsx": "^1.2.1", - "prop-types": "^15.8.1", - "react-is": "^18.2.0" + "version": "5.0.0-beta.40", + "resolved": "https://registry.npmjs.org/@mui/base/-/base-5.0.0-beta.40.tgz", + "integrity": "sha512-I/lGHztkCzvwlXpjD2+SNmvNQvB4227xBXhISPjEaJUXGImOQ9f3D2Yj/T3KasSI/h0MLWy74X0J6clhPmsRbQ==", + "dependencies": { + "@babel/runtime": "^7.23.9", + "@floating-ui/react-dom": "^2.0.8", + "@mui/types": "^7.2.14", + "@mui/utils": "^5.15.14", + "@popperjs/core": "^2.11.8", + "clsx": "^2.1.0", + "prop-types": "^15.8.1" }, "engines": { "node": ">=12.0.0" }, "funding": { "type": "opencollective", - "url": "https://opencollective.com/mui" + "url": "https://opencollective.com/mui-org" }, "peerDependencies": { "@types/react": "^17.0.0 || ^18.0.0", @@ -5190,33 +5212,36 @@ } } }, - "node_modules/@mui/base/node_modules/react-is": { - "version": "18.2.0", - "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.2.0.tgz", - "integrity": "sha512-xWGDIW6x921xtzPkhiULtthJHoJvBbF3q26fzloPCK0hsvxtPVelvftw3zjbHWSkR2km9Z+4uxbDDK/6Zw9B8w==" + "node_modules/@mui/base/node_modules/clsx": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/clsx/-/clsx-2.1.1.tgz", + "integrity": "sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA==", + "engines": { + "node": ">=6" + } }, "node_modules/@mui/core-downloads-tracker": { - "version": "5.11.12", - "resolved": "https://registry.npmjs.org/@mui/core-downloads-tracker/-/core-downloads-tracker-5.11.12.tgz", - "integrity": "sha512-LHh8HZQ5nPVcW5QnyLwkAZ40txc/S2bzKMQ3bTO+5mjuwAJ2AzQrjZINLVy1geY7ei1pHXVqO1hcWHg/QdT44w==", + "version": "5.15.19", + "resolved": "https://registry.npmjs.org/@mui/core-downloads-tracker/-/core-downloads-tracker-5.15.19.tgz", + "integrity": "sha512-tCHSi/Tomez9ERynFhZRvFO6n9ATyrPs+2N80DMDzp6xDVirbBjEwhPcE+x7Lj+nwYw0SqFkOxyvMP0irnm55w==", "funding": { "type": "opencollective", - "url": "https://opencollective.com/mui" + "url": "https://opencollective.com/mui-org" } }, "node_modules/@mui/icons-material": { - "version": "5.11.11", - "resolved": "https://registry.npmjs.org/@mui/icons-material/-/icons-material-5.11.11.tgz", - "integrity": "sha512-Eell3ADmQVE8HOpt/LZ3zIma8JSvPh3XgnhwZLT0k5HRqZcd6F/QDHc7xsWtgz09t+UEFvOYJXjtrwKmLdwwpw==", + "version": "5.15.19", + "resolved": "https://registry.npmjs.org/@mui/icons-material/-/icons-material-5.15.19.tgz", + "integrity": "sha512-RsEiRxA5azN9b8gI7JRqekkgvxQUlitoBOtZglflb8cUDyP12/cP4gRwhb44Ea1/zwwGGjAj66ZJpGHhKfibNA==", "dependencies": { - "@babel/runtime": "^7.21.0" + "@babel/runtime": "^7.23.9" }, "engines": { "node": ">=12.0.0" }, "funding": { "type": "opencollective", - "url": "https://opencollective.com/mui" + "url": "https://opencollective.com/mui-org" }, "peerDependencies": { "@mui/material": "^5.0.0", @@ -5230,19 +5255,19 @@ } }, "node_modules/@mui/material": { - "version": "5.11.12", - "resolved": "https://registry.npmjs.org/@mui/material/-/material-5.11.12.tgz", - "integrity": "sha512-M6BiIeJjySeEzWeiFJQ9pIjJy6mx5mHPWeMT99wjQdAmA2GxCQhE9A0fh6jQP4jMmYzxhOIhjsGcp0vSdpseXg==", - "dependencies": { - "@babel/runtime": "^7.21.0", - "@mui/base": "5.0.0-alpha.119", - "@mui/core-downloads-tracker": "^5.11.12", - "@mui/system": "^5.11.12", - "@mui/types": "^7.2.3", - "@mui/utils": "^5.11.12", - "@types/react-transition-group": "^4.4.5", - "clsx": "^1.2.1", - "csstype": "^3.1.1", + "version": "5.15.19", + "resolved": "https://registry.npmjs.org/@mui/material/-/material-5.15.19.tgz", + "integrity": "sha512-lp5xQBbcRuxNtjpWU0BWZgIrv2XLUz4RJ0RqFXBdESIsKoGCQZ6P3wwU5ZPuj5TjssNiKv9AlM+vHopRxZhvVQ==", + "dependencies": { + "@babel/runtime": "^7.23.9", + "@mui/base": "5.0.0-beta.40", + "@mui/core-downloads-tracker": "^5.15.19", + "@mui/system": "^5.15.15", + "@mui/types": "^7.2.14", + "@mui/utils": "^5.15.14", + "@types/react-transition-group": "^4.4.10", + "clsx": "^2.1.0", + "csstype": "^3.1.3", "prop-types": "^15.8.1", "react-is": "^18.2.0", "react-transition-group": "^4.4.5" @@ -5252,7 +5277,7 @@ }, "funding": { "type": "opencollective", - "url": "https://opencollective.com/mui" + "url": "https://opencollective.com/mui-org" }, "peerDependencies": { "@emotion/react": "^11.5.0", @@ -5273,10 +5298,18 @@ } } }, + "node_modules/@mui/material/node_modules/clsx": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/clsx/-/clsx-2.1.1.tgz", + "integrity": "sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA==", + "engines": { + "node": ">=6" + } + }, "node_modules/@mui/material/node_modules/csstype": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.1.tgz", - "integrity": "sha512-DJR/VvkAvSZW9bTouZue2sSxDwdTN92uHjqeKVm+0dAqdfNykRzQ95tay8aXMBAAPpUiq4Qcug2L7neoRh2Egw==" + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.3.tgz", + "integrity": "sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==" }, "node_modules/@mui/material/node_modules/react-is": { "version": "18.2.0", @@ -5284,12 +5317,12 @@ "integrity": "sha512-xWGDIW6x921xtzPkhiULtthJHoJvBbF3q26fzloPCK0hsvxtPVelvftw3zjbHWSkR2km9Z+4uxbDDK/6Zw9B8w==" }, "node_modules/@mui/private-theming": { - "version": "5.11.12", - "resolved": "https://registry.npmjs.org/@mui/private-theming/-/private-theming-5.11.12.tgz", - "integrity": "sha512-hnJ0svNI1TPeWZ18E6DvES8PB4NyMLwal6EyXf69rTrYqT6wZPLjB+HiCYfSOCqU/fwArhupSqIIkQpDs8CkAw==", + "version": "5.15.14", + "resolved": "https://registry.npmjs.org/@mui/private-theming/-/private-theming-5.15.14.tgz", + "integrity": "sha512-UH0EiZckOWcxiXLX3Jbb0K7rC8mxTr9L9l6QhOZxYc4r8FHUkefltV9VDGLrzCaWh30SQiJvAEd7djX3XXY6Xw==", "dependencies": { - "@babel/runtime": "^7.21.0", - "@mui/utils": "^5.11.12", + "@babel/runtime": "^7.23.9", + "@mui/utils": "^5.15.14", "prop-types": "^15.8.1" }, "engines": { @@ -5297,7 +5330,7 @@ }, "funding": { "type": "opencollective", - "url": "https://opencollective.com/mui" + "url": "https://opencollective.com/mui-org" }, "peerDependencies": { "@types/react": "^17.0.0 || ^18.0.0", @@ -5310,13 +5343,13 @@ } }, "node_modules/@mui/styled-engine": { - "version": "5.11.11", - "resolved": "https://registry.npmjs.org/@mui/styled-engine/-/styled-engine-5.11.11.tgz", - "integrity": "sha512-wV0UgW4lN5FkDBXefN8eTYeuE9sjyQdg5h94vtwZCUamGQEzmCOtir4AakgmbWMy0x8OLjdEUESn9wnf5J9MOg==", + "version": "5.15.14", + "resolved": "https://registry.npmjs.org/@mui/styled-engine/-/styled-engine-5.15.14.tgz", + "integrity": "sha512-RILkuVD8gY6PvjZjqnWhz8fu68dVkqhM5+jYWfB5yhlSQKg+2rHkmEwm75XIeAqI3qwOndK6zELK5H6Zxn4NHw==", "dependencies": { - "@babel/runtime": "^7.21.0", - "@emotion/cache": "^11.10.5", - "csstype": "^3.1.1", + "@babel/runtime": "^7.23.9", + "@emotion/cache": "^11.11.0", + "csstype": "^3.1.3", "prop-types": "^15.8.1" }, "engines": { @@ -5324,7 +5357,7 @@ }, "funding": { "type": "opencollective", - "url": "https://opencollective.com/mui" + "url": "https://opencollective.com/mui-org" }, "peerDependencies": { "@emotion/react": "^11.4.1", @@ -5366,22 +5399,80 @@ } }, "node_modules/@mui/styled-engine/node_modules/csstype": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.1.tgz", - "integrity": "sha512-DJR/VvkAvSZW9bTouZue2sSxDwdTN92uHjqeKVm+0dAqdfNykRzQ95tay8aXMBAAPpUiq4Qcug2L7neoRh2Egw==" + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.3.tgz", + "integrity": "sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==" + }, + "node_modules/@mui/styles": { + "version": "5.15.19", + "resolved": "https://registry.npmjs.org/@mui/styles/-/styles-5.15.19.tgz", + "integrity": "sha512-WOYjZT6DL/StJv/vFR/tJPJuHC98lwWza7Gzj2RWZk123eCcSJ+3ruc62/ZyXaCx4PKCbE849mVl+CWhA2ecmw==", + "dependencies": { + "@babel/runtime": "^7.23.9", + "@emotion/hash": "^0.9.1", + "@mui/private-theming": "^5.15.14", + "@mui/types": "^7.2.14", + "@mui/utils": "^5.15.14", + "clsx": "^2.1.0", + "csstype": "^3.1.3", + "hoist-non-react-statics": "^3.3.2", + "jss": "^10.10.0", + "jss-plugin-camel-case": "^10.10.0", + "jss-plugin-default-unit": "^10.10.0", + "jss-plugin-global": "^10.10.0", + "jss-plugin-nested": "^10.10.0", + "jss-plugin-props-sort": "^10.10.0", + "jss-plugin-rule-value-function": "^10.10.0", + "jss-plugin-vendor-prefixer": "^10.10.0", + "prop-types": "^15.8.1" + }, + "engines": { + "node": ">=12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/mui-org" + }, + "peerDependencies": { + "@types/react": "^17.0.0 || ^18.0.0", + "react": "^17.0.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@mui/styles/node_modules/@emotion/hash": { + "version": "0.9.1", + "resolved": "https://registry.npmjs.org/@emotion/hash/-/hash-0.9.1.tgz", + "integrity": "sha512-gJB6HLm5rYwSLI6PQa+X1t5CFGrv1J1TWG+sOyMCeKz2ojaj6Fnl/rZEspogG+cvqbt4AE/2eIyD2QfLKTBNlQ==" + }, + "node_modules/@mui/styles/node_modules/clsx": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/clsx/-/clsx-2.1.1.tgz", + "integrity": "sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA==", + "engines": { + "node": ">=6" + } + }, + "node_modules/@mui/styles/node_modules/csstype": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.3.tgz", + "integrity": "sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==" }, "node_modules/@mui/system": { - "version": "5.11.12", - "resolved": "https://registry.npmjs.org/@mui/system/-/system-5.11.12.tgz", - "integrity": "sha512-sYjsXkiwKpZDC3aS6O/6KTjji0jGINLQcrD5EJ5NTkIDiLf19I4HJhnufgKqlTWNfoDBlRohuTf3TzfM06c4ug==", - "dependencies": { - "@babel/runtime": "^7.21.0", - "@mui/private-theming": "^5.11.12", - "@mui/styled-engine": "^5.11.11", - "@mui/types": "^7.2.3", - "@mui/utils": "^5.11.12", - "clsx": "^1.2.1", - "csstype": "^3.1.1", + "version": "5.15.15", + "resolved": "https://registry.npmjs.org/@mui/system/-/system-5.15.15.tgz", + "integrity": "sha512-aulox6N1dnu5PABsfxVGOZffDVmlxPOVgj56HrUnJE8MCSh8lOvvkd47cebIVQQYAjpwieXQXiDPj5pwM40jTQ==", + "dependencies": { + "@babel/runtime": "^7.23.9", + "@mui/private-theming": "^5.15.14", + "@mui/styled-engine": "^5.15.14", + "@mui/types": "^7.2.14", + "@mui/utils": "^5.15.14", + "clsx": "^2.1.0", + "csstype": "^3.1.3", "prop-types": "^15.8.1" }, "engines": { @@ -5389,7 +5480,7 @@ }, "funding": { "type": "opencollective", - "url": "https://opencollective.com/mui" + "url": "https://opencollective.com/mui-org" }, "peerDependencies": { "@emotion/react": "^11.5.0", @@ -5409,17 +5500,25 @@ } } }, + "node_modules/@mui/system/node_modules/clsx": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/clsx/-/clsx-2.1.1.tgz", + "integrity": "sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA==", + "engines": { + "node": ">=6" + } + }, "node_modules/@mui/system/node_modules/csstype": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.1.tgz", - "integrity": "sha512-DJR/VvkAvSZW9bTouZue2sSxDwdTN92uHjqeKVm+0dAqdfNykRzQ95tay8aXMBAAPpUiq4Qcug2L7neoRh2Egw==" + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.3.tgz", + "integrity": "sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==" }, "node_modules/@mui/types": { - "version": "7.2.3", - "resolved": "https://registry.npmjs.org/@mui/types/-/types-7.2.3.tgz", - "integrity": "sha512-tZ+CQggbe9Ol7e/Fs5RcKwg/woU+o8DCtOnccX6KmbBc7YrfqMYEYuaIcXHuhpT880QwNkZZ3wQwvtlDFA2yOw==", + "version": "7.2.14", + "resolved": "https://registry.npmjs.org/@mui/types/-/types-7.2.14.tgz", + "integrity": "sha512-MZsBZ4q4HfzBsywtXgM1Ksj6HDThtiwmOKUXH1pKYISI9gAVXCNHNpo7TlGoGrBaYWZTdNoirIN7JsQcQUjmQQ==", "peerDependencies": { - "@types/react": "*" + "@types/react": "^17.0.0 || ^18.0.0" }, "peerDependenciesMeta": { "@types/react": { @@ -5428,13 +5527,12 @@ } }, "node_modules/@mui/utils": { - "version": "5.11.12", - "resolved": "https://registry.npmjs.org/@mui/utils/-/utils-5.11.12.tgz", - "integrity": "sha512-5vH9B/v8pzkpEPO2HvGM54ToXV6cFdAn8UrvdN8TMEEwpn/ycW0jLiyBcgUlPsQ+xha7hqXCPQYHaYFDIcwaiw==", + "version": "5.15.14", + "resolved": "https://registry.npmjs.org/@mui/utils/-/utils-5.15.14.tgz", + "integrity": "sha512-0lF/7Hh/ezDv5X7Pry6enMsbYyGKjADzvHyo3Qrc/SSlTsQ1VkbDMbH0m2t3OR5iIVLwMoxwM7yGd+6FCMtTFA==", "dependencies": { - "@babel/runtime": "^7.21.0", - "@types/prop-types": "^15.7.5", - "@types/react-is": "^16.7.1 || ^17.0.0", + "@babel/runtime": "^7.23.9", + "@types/prop-types": "^15.7.11", "prop-types": "^15.8.1", "react-is": "^18.2.0" }, @@ -5443,10 +5541,16 @@ }, "funding": { "type": "opencollective", - "url": "https://opencollective.com/mui" + "url": "https://opencollective.com/mui-org" }, "peerDependencies": { + "@types/react": "^17.0.0 || ^18.0.0", "react": "^17.0.0 || ^18.0.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } } }, "node_modules/@mui/utils/node_modules/react-is": { @@ -5655,9 +5759,9 @@ } }, "node_modules/@popperjs/core": { - "version": "2.11.6", - "resolved": "https://registry.npmjs.org/@popperjs/core/-/core-2.11.6.tgz", - "integrity": "sha512-50/17A98tWUfQ176raKiOGXuYpLyyVMkxxG6oylzL3BPOlA6ADGdK7EYunSa4I064xerltq9TGXs8HmOk5E+vw==", + "version": "2.11.8", + "resolved": "https://registry.npmjs.org/@popperjs/core/-/core-2.11.8.tgz", + "integrity": "sha512-P1st0aksCrn9sGZhp8GMYwBnQsbvAWsZAX44oXNNvLHGqAOcoVxmjZiohstwQ7SqKnbR47akdNi+uleWD8+g6A==", "funding": { "type": "opencollective", "url": "https://opencollective.com/popperjs" @@ -6689,9 +6793,9 @@ "integrity": "sha512-KufADq8uQqo1pYKVIYzfKbJfBAc0sOeXqGbFaSpv8MRmC/zXgowNZmFcbngndGk922QDmOASEXUZCaY48gs4cg==" }, "node_modules/@types/prop-types": { - "version": "15.7.5", - "resolved": "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.5.tgz", - "integrity": "sha512-JCB8C6SnDoQf0cNycqd/35A7MjcnK+ZTqE7judS6o7utxUCg6imJg3QK2qzHKszlTjcj2cn+NwMB2i96ubpj7w==" + "version": "15.7.12", + "resolved": "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.12.tgz", + "integrity": "sha512-5zvhXYtRNRluoE/jAp4GVsSduVUzNWKkOZrCDBWYtE7biZywwdC2AcEzg+cSMLFRfVgeAFqpfNabiPjxFddV1Q==" }, "node_modules/@types/q": { "version": "1.5.8", @@ -6727,14 +6831,6 @@ "@types/react": "*" } }, - "node_modules/@types/react-is": { - "version": "17.0.3", - "resolved": "https://registry.npmjs.org/@types/react-is/-/react-is-17.0.3.tgz", - "integrity": "sha512-aBTIWg1emtu95bLTLx0cpkxwGW3ueZv71nE2YFBpL8k/z5czEW8yYpOo8Dp+UUAFAtKwNaOsh/ioSeQnWlZcfw==", - "dependencies": { - "@types/react": "*" - } - }, "node_modules/@types/react-tabs": { "version": "5.0.5", "resolved": "https://registry.npmjs.org/@types/react-tabs/-/react-tabs-5.0.5.tgz", @@ -6745,9 +6841,9 @@ } }, "node_modules/@types/react-transition-group": { - "version": "4.4.5", - "resolved": "https://registry.npmjs.org/@types/react-transition-group/-/react-transition-group-4.4.5.tgz", - "integrity": "sha512-juKD/eiSM3/xZYzjuzH6ZwpP+/lejltmiS3QEzV/vmb/Q8+HfDmxu+Baga8UEMGBqV88Nbg4l2hY/K2DkyaLLA==", + "version": "4.4.10", + "resolved": "https://registry.npmjs.org/@types/react-transition-group/-/react-transition-group-4.4.10.tgz", + "integrity": "sha512-hT/+s0VQs2ojCX823m60m5f0sL5idt9SO6Tj6Dg+rdphGPIeJbJ6CxvBYkgkGKrYeDjvIpKTR38UzmtHJOGW3Q==", "dependencies": { "@types/react": "*" } @@ -25133,9 +25229,9 @@ } }, "node_modules/stylis": { - "version": "4.1.3", - "resolved": "https://registry.npmjs.org/stylis/-/stylis-4.1.3.tgz", - "integrity": "sha512-GP6WDNWf+o403jrEp9c5jibKavrtLW+/qYGhFxFrG8maXhwTBI7gLLhiBb0o7uFccWN+EOS9aMO6cGHWAO07OA==" + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/stylis/-/stylis-4.2.0.tgz", + "integrity": "sha512-Orov6g6BB1sDfYgzWfTHDOxamtX1bE/zo104Dh9e6fqJ3PooipYyfJ0pUmrZO2wAvO8YbEyeFrkV91XTsGMSrw==" }, "node_modules/sucrase": { "version": "3.35.0", diff --git a/webapp/package.json b/webapp/package.json index e7b6c8ba5..49b7ad756 100644 --- a/webapp/package.json +++ b/webapp/package.json @@ -3,14 +3,15 @@ "version": "1.2.0-SNAPSHOT", "private": true, "dependencies": { - "@emotion/react": "^11.10.5", - "@emotion/styled": "^11.10.5", + "@emotion/react": "^11.11.4", + "@emotion/styled": "^11.11.5", "@hookform/error-message": "^2.0.1", "@material-table/core": "^6.1.6", "@material-ui/core": "^4.12.3", - "@mui/icons-material": "^5.11.9", - "@mui/material": "^5.11.9", + "@mui/icons-material": "^5.15.19", + "@mui/material": "^5.15.19", "@mui/styled-engine-sc": "^5.11.9", + "@mui/styles": "^5.15.19", "@mui/x-date-pickers": "^5.0.15", "@react-keycloak/web": "3.4.0", "@react-oauth/google": "^0.2.8", @@ -101,4 +102,4 @@ "worker-loader!mapbox-gl/dist/mapbox-gl-csp-worker": "/mocks/workerLoader.js" } } -} \ No newline at end of file +} diff --git a/webapp/src/components/__snapshots__/AdminFormManager.test.tsx.snap b/webapp/src/components/__snapshots__/AdminFormManager.test.tsx.snap index 9e105adeb..47a00bfaf 100644 --- a/webapp/src/components/__snapshots__/AdminFormManager.test.tsx.snap +++ b/webapp/src/components/__snapshots__/AdminFormManager.test.tsx.snap @@ -147,7 +147,7 @@ exports[`snapshot organization 1`] = ` viewBox="0 0 24 24" > @@ -170,7 +170,7 @@ exports[`snapshot organization 1`] = ` viewBox="0 0 24 24" > @@ -417,7 +417,7 @@ exports[`snapshot rsu 1`] = ` viewBox="0 0 24 24" > @@ -495,7 +495,7 @@ exports[`snapshot rsu 1`] = ` style="width: 48px;" > @@ -592,7 +592,7 @@ exports[`snapshot rsu 1`] = ` viewBox="0 0 24 24" > @@ -634,7 +634,7 @@ exports[`snapshot rsu 1`] = ` viewBox="0 0 24 24" > @@ -676,7 +676,7 @@ exports[`snapshot rsu 1`] = ` viewBox="0 0 24 24" > @@ -718,7 +718,7 @@ exports[`snapshot rsu 1`] = ` viewBox="0 0 24 24" > @@ -788,20 +788,21 @@ exports[`snapshot rsu 1`] = ` />

Rows per page:

+ + + + 0 && selectedNotifications.length < notificationsCount + } + onChange={handleSelectAll} + /> + + Notification Type + Date + Message + Actions + + + + {customers.map((customer) => { + const isNotificationSelected = [...selectedNotifications].indexOf(customer.id) !== -1; + + return ( + + + handleSelectOne(event, customer.id)} + value="true" + /> + + + + + {customer.notificationType} + + + + {format(customer.notificationGeneratedAt, "MM/dd/yyyy")} + {customer.notificationText} + + + + + + + + + ); + })} + +
+ + + + + ); +}; + +AssessmentListResults.propTypes = { + customers: PropTypes.array.isRequired, + onSelectedItemsChanged: PropTypes.func, +}; diff --git a/webapp/src/components/intersection/assessments/connection-of-travel-assessment.tsx b/webapp/src/components/intersection/assessments/connection-of-travel-assessment.tsx new file mode 100644 index 000000000..0b5a4dcda --- /dev/null +++ b/webapp/src/components/intersection/assessments/connection-of-travel-assessment.tsx @@ -0,0 +1,148 @@ +import { Button, Card, CardContent, Grid, Typography } from "@mui/material"; +import { group } from "console"; +import React from "react"; +import { + ResponsiveContainer, + BarChart, + CartesianGrid, + XAxis, + YAxis, + Legend, + Bar, + Rectangle, + Tooltip, + TooltipProps, +} from "recharts"; +import { NameType, ValueType } from "recharts/types/component/DefaultTooltipContent"; + +export const ConnectionOfTravelAssessmentCard = (props: { assessment: ConnectionOfTravelAssessment | undefined }) => { + const { assessment } = props; + + function getWidthFactorFromData(data: any[] | undefined): number { + if (!data) return 0.1; + const maxFactor = 0.9; + const numRowsForMax = 40; + return 0.1 + Math.min(maxFactor, data.length / numRowsForMax); + } + const widthFactor = getWidthFactorFromData(assessment?.connectionOfTravelAssessmentGroups); + + const CustomTooltip = ({ active, payload }: TooltipProps) => { + if (active && payload) { + const obj = payload[0].payload; + return ( +
+ Connection ID: {obj.connectionID} +

Ingress Lane ID: {obj.ingressLaneID}

+

EgressLaneID: {obj.egressLaneID}

+

Event Count: {Math.max(obj.eventCountValid, obj.eventCountInvalid)}

+
+ ); + } + return null; + }; + + function sortByName(a, b) { + if (a.ingressLaneID < b.ingressLaneID) { + return -1; + } + if (a.ingressLaneID > b.ingressLaneID) { + return 1; + } + if (a.egressLaneId < b.egressLaneId) { + return -1; + } + if (a.egressLaneId > b.egressLaneId) { + return 1; + } + return 0; + } + +const data = assessment?.connectionOfTravelAssessmentGroups + .map((group) => { + return { + name: `${group.ingressLaneID}_${group.egressLaneID}`, + eventCountValid: group.connectionID == -1 ? 0 : group.eventCount, + eventCountInvalid: group.connectionID == -1 ? group.eventCount : 0, + connectionID: group.connectionID, + ingressLaneID: group.ingressLaneID, + egressLaneID: group.egressLaneID, + }; + }) + .sort(sortByName); + +const hasValidEvents = data?.some(item => item.eventCountValid > 0); +const hasInvalidEvents = data?.some(item => item.eventCountInvalid > 0); + + + return ( + + + + + + + Connection of Travel Assessment + + {assessment === undefined ? ( + + No Data + + ) : ( + + + + + + + + + + )} + + + + + + ); +}; diff --git a/webapp/src/components/intersection/assessments/lane-direction-of-travel-assessment.tsx b/webapp/src/components/intersection/assessments/lane-direction-of-travel-assessment.tsx new file mode 100644 index 000000000..83d09b17b --- /dev/null +++ b/webapp/src/components/intersection/assessments/lane-direction-of-travel-assessment.tsx @@ -0,0 +1,207 @@ +import { Card, CardContent, Grid, Typography } from "@mui/material"; +import React from "react"; +import { BarChart, CartesianGrid, XAxis, YAxis, Legend, Bar, Tooltip, TooltipProps } from "recharts"; +import { NameType, ValueType } from "recharts/types/component/DefaultTooltipContent"; + +export const LaneDirectionOfTravelAssessmentCard = (props: { + assessment: LaneDirectionOfTravelAssessment | undefined; +}) => { + const { assessment } = props; + + function getWidthFactorFromData(data: any[] | undefined): number { + if (!data) return 0.1; + const maxFactor = 0.9; + const numRowsForMax = 40; + return 0.1 + Math.min(maxFactor, data.length / numRowsForMax); + } + + const widthFactor = getWidthFactorFromData(assessment?.laneDirectionOfTravelAssessmentGroup); + const SegColors = [ + ["#d55d01"], + ["#d59201"], + ["#c5c900"], + ["#88ff00"], + ["#00bc0d"], + ["#00c295"], + ["#007add"], + ["#0014c7"], + ["#7006fc"], + ["#5800a0"], + ["#900bae"], + ["#850068"], + ["#a30336"], + ["#c00000"], + ["#750000"], + ]; + + function onlyUnique(value, index, array) { + return array.indexOf(value) === index; + } + + const segmentIds = + assessment?.laneDirectionOfTravelAssessmentGroup + .map((group) => group.segmentID) + .sort((a, b) => a - b) + .filter(onlyUnique) ?? []; + const maxSegmentId = Math.max(...segmentIds); + + const compressedGroups: { + [key: number]: { + laneId: number; + inTolerance: { + [key: string]: number; + }; + outOfTolerance: { + [key: string]: number; + }; + }; + } = {}; + for (let i = 0; i < (assessment?.laneDirectionOfTravelAssessmentGroup ?? []).length; i++) { + const group = assessment?.laneDirectionOfTravelAssessmentGroup[i]!; + if (!compressedGroups[group.laneID]) { + compressedGroups[group.laneID] = { + laneId: group.laneID, + inTolerance: {}, + outOfTolerance: {}, + }; + } + compressedGroups[group.laneID].inTolerance[group.segmentID] = group.inToleranceEvents; + compressedGroups[group.laneID].outOfTolerance[group.segmentID] = group.outOfToleranceEvents; + } + + const CustomTooltip = ({ active, payload }: TooltipProps) => { + if (active && payload) { + const obj = payload[0].payload; + return ( +
+ Lane {obj.laneId} +
+
+ + In Tolerance:{" "} + {segmentIds.map((segId) => obj.inTolerance[segId]).reduce((partialSum, a) => partialSum + (a ?? 0), 0)} + + {segmentIds.map((segmentId, index) => { + return ( +

+ {segmentId}: {obj.inTolerance[segmentId] ?? 0} +

+ ); + })} +
+
+ + Out of Tolerance:{" "} + {segmentIds + .map((segId) => obj.outOfTolerance[segId]) + .reduce((partialSum, a) => partialSum + (a ?? 0), 0)} + + {segmentIds.map((segmentId, index) => { + return ( +

+ {segmentId}: {obj.outOfTolerance[segmentId] ?? 0} +

+ ); + })} +
+
+
+ ); + } + return null; + }; + + return ( + + + + + + + Lane Direction of Travel Assessment + + {assessment === undefined ? ( + + No Data + + ) : ( + { + return { + name: `${group.laneId}`, + ...group, + }; + })} + margin={{ + top: 20, + right: 30, + left: 20, + bottom: 5, + }} + > + + + + + { + return { + value: `Segment ${segmentId}`, + id: `inTolerance.${segmentId}`, + color: + SegColors[ + (index * Math.max(Math.floor(SegColors.length / maxSegmentId), 1)) % SegColors.length + ][0], + }; + })} + /> + {segmentIds.map((segmentId, index) => { + return ( + + ); + })} + {segmentIds.map((segmentId, index) => { + return ( + + ); + })} + + )} + + + + + + ); +}; diff --git a/webapp/src/components/intersection/assessments/signal-state-event-assessment.tsx b/webapp/src/components/intersection/assessments/signal-state-event-assessment.tsx new file mode 100644 index 000000000..f27c4c64f --- /dev/null +++ b/webapp/src/components/intersection/assessments/signal-state-event-assessment.tsx @@ -0,0 +1,167 @@ +import { Card, CardContent, Grid, Typography } from "@mui/material"; +import React from "react"; +import { BarChart, CartesianGrid, XAxis, YAxis, Legend, Bar, Tooltip, TooltipProps } from "recharts"; +import { NameType, ValueType } from "recharts/types/component/DefaultTooltipContent"; + +export const SignalStateEventAssessmentCard = (props: { assessment: SignalStateEventAssessment | undefined }) => { + const { assessment } = props; + + function getWidthFactorFromData(data: any[] | undefined): number { + if (!data) return 0.1; + const maxFactor = 0.9; + const numRowsForMax = 40; + return 0.1 + Math.min(maxFactor, data.length / numRowsForMax); + } + 0; + const widthFactor = getWidthFactorFromData(assessment?.signalStateEventAssessmentGroup); + + const CustomTooltip = ({ active, payload }: TooltipProps) => { + if (active && payload) { + const obj = payload[0].payload; + return ( +
+ Signal Group: {obj.signalGroup} +

Total Events: {obj.total}

+
+
+

+ Red Events: {obj.redCount}, {Math.round(obj.red)}% +

+
+
+
+

+ Yellow Events: {obj.yellowCount}, {Math.round(obj.yellow)}% +

+
+
+
+

+ Green Events: {obj.greenCount}, {Math.round(obj.green)}% +

+
+
+
+

+ Dark Events: {obj.darkCount}, {Math.round(obj.dark)}% +

+
+
+ ); + } + return null; + }; + + function sortByName(a, b) { + if (a.name < b.name) { + return -1; + } + if (a.name > b.name) { + return 1; + } + return 0; + } + + const data = assessment?.signalStateEventAssessmentGroup + .map((group) => { + const total = + Math.max(group.redEvents + group.yellowEvents + group.greenEvents + group.darkEvents, 1) / 100; + return { + name: `${group.signalGroup}`, + signalGroup: `${group.signalGroup}`, + total: total * 100, + red: Math.floor((group.redEvents / total) * 100) / 100, + redCount: group.redEvents, + yellow: Math.floor((group.yellowEvents / total) * 100) / 100, + yellowCount: group.yellowEvents, + green: Math.floor((group.greenEvents / total) * 100) / 100, + greenCount: group.greenEvents, + dark: Math.floor((group.darkEvents / total) * 100) / 100, + darkCount: group.darkEvents, + }; + }) + .sort(sortByName); + +const hasDark = data?.some(item => item.darkCount > 0); + + return ( + + + + + + + Signal State Passage Assessment + + {assessment === undefined ? ( + + No Data + + ) : ( + + + + + + item !== null) as any[]} + /> + + + + + + )} + + + + + + ); +}; diff --git a/webapp/src/components/intersection/assessments/stop-line-stop-assessment.tsx b/webapp/src/components/intersection/assessments/stop-line-stop-assessment.tsx new file mode 100644 index 000000000..95a93951b --- /dev/null +++ b/webapp/src/components/intersection/assessments/stop-line-stop-assessment.tsx @@ -0,0 +1,173 @@ +import { Card, CardContent, Grid, Typography } from "@mui/material"; +import React from "react"; +import { BarChart, CartesianGrid, XAxis, YAxis, Legend, Bar, Tooltip, TooltipProps } from "recharts"; +import { NameType, ValueType } from "recharts/types/component/DefaultTooltipContent"; + +export const StopLineStopAssessmentCard = (props: { assessment: StopLineStopAssessment | undefined }) => { + const { assessment } = props; + + function getWidthFactorFromData(data?: any[] | undefined): number { + if (!data) return 0.1; + const maxFactor = 0.9; + const numRowsForMax = 40; + return 0.1 + Math.min(maxFactor, data.length / numRowsForMax); + } + 0; + const widthFactor = getWidthFactorFromData(assessment?.stopLineStopAssessmentGroup); + + const CustomTooltip = ({ active, payload }: TooltipProps) => { + if (active && payload) { + const obj = payload[0].payload; + return ( +
+ Signal Group: {obj.signalGroup} +

Total Avg Time Stopped: {obj.total}s

+
+
+

+ Red Time: {obj.redTime}s, {Math.round(obj.red)}% +

+
+
+
+

+ Yellow Time: {obj.yellowTime}s, {Math.round(obj.yellow)}% +

+
+
+
+

+ Green Time: {obj.greenTime}s, {Math.round(obj.green)}% +

+
+
+
+

+ Dark Time: {obj.darkTime}s, {Math.round(obj.dark)}% +

+
+
+ ); + } + return null; + }; + + function sortByName(a, b) { + if (a.name < b.name) { + return -1; + } + if (a.name > b.name) { + return 1; + } + return 0; + } + + const data = assessment?.stopLineStopAssessmentGroup + .map((group) => { + const total = + Math.max( + group.timeStoppedOnRed + + group.timeStoppedOnYellow + + group.timeStoppedOnGreen + + group.timeStoppedOnDark, + 1 + ) / 100; + return { + name: `${group.signalGroup}`, + signalGroup: `${group.signalGroup}`, + total: total * 100, + red: Math.floor((group.timeStoppedOnRed / total) * 100) / 100, + redTime: group.timeStoppedOnRed, + yellow: Math.floor((group.timeStoppedOnYellow / total) * 100) / 100, + yellowTime: group.timeStoppedOnYellow, + green: Math.floor((group.timeStoppedOnGreen / total) * 100) / 100, + greenTime: group.timeStoppedOnGreen, + dark: Math.floor((group.timeStoppedOnDark / total) * 100) / 100, + darkTime: group.timeStoppedOnDark, + }; + }) + .sort(sortByName); + +const hasDark = data?.some(item => item.dark > 0); + + return ( + + + + + + + Signal State Stop Assessment + + {assessment === undefined || assessment.stopLineStopAssessmentGroup === undefined ? ( + + No Data + + ) : ( + + + + + + item !== null) as any[]} + /> + + + + + + )} + + + + + + ); +}; diff --git a/webapp/src/components/intersection/configuration/configuration-create-form.tsx b/webapp/src/components/intersection/configuration/configuration-create-form.tsx new file mode 100644 index 000000000..504b380b2 --- /dev/null +++ b/webapp/src/components/intersection/configuration/configuration-create-form.tsx @@ -0,0 +1,158 @@ +import NextLink from "next/link"; +import { useRouter } from "next/router"; +import PropTypes from "prop-types"; +import toast from "react-hot-toast"; +import * as Yup from "yup"; +import { useFormik } from "formik"; +import { Button, Card, CardActions, CardContent, CardHeader, Divider, Grid, TextField } from "@mui/material"; +import { configParamApi } from "../../apis/configuration-param-api"; +import { useSession } from "next-auth/react"; +import { useDashboardContext } from "../../contexts/dashboard-context"; + +export const ConfigParamCreateForm = (props) => { + const { parameter }: { parameter: Config } = props; + const router = useRouter(); + const { data: session } = useSession(); + const { intersectionId, roadRegulatorId } = useDashboardContext(); + const formik = useFormik({ + initialValues: { + name: parameter.key, + unit: parameter.units, + value: parameter.value, + description: parameter.description, + submit: null, + }, + validationSchema: Yup.object({ + name: Yup.string(), + value: Yup.string().required("New value is required"), + }), + onSubmit: async (values, helpers) => { + if (!session?.accessToken || intersectionId == -1) { + console.error( + "Did not attempt to create configuration param. Access token:", + session?.accessToken, + "Intersection ID:", + intersectionId + ); + return; + } + try { + const updatedConfig: IntersectionConfig = { + ...parameter, + value: values.value, + intersectionID: intersectionId, + roadRegulatorID: roadRegulatorId, + rsuID: "", + }; + await configParamApi.updateIntersectionParameter(session?.accessToken, values.name, updatedConfig); + helpers.setStatus({ success: true }); + helpers.setSubmitting(false); + router + .push({ + pathname: "/configuration", + query: { returnUrl: router.asPath }, + }) + .catch(console.error); + } catch (err) { + console.error(err); + toast.error("Something went wrong!"); + helpers.setStatus({ success: false }); + helpers.setErrors({ submit: err.message }); + helpers.setSubmitting(false); + } + }, + }); + + return ( +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
+ ); +}; + +ConfigParamCreateForm.propTypes = { + parameter: PropTypes.object.isRequired, +}; diff --git a/webapp/src/components/intersection/configuration/configuration-edit-form.tsx b/webapp/src/components/intersection/configuration/configuration-edit-form.tsx new file mode 100644 index 000000000..5cf2d488d --- /dev/null +++ b/webapp/src/components/intersection/configuration/configuration-edit-form.tsx @@ -0,0 +1,200 @@ +import NextLink from "next/link"; +import { useRouter } from "next/router"; +import PropTypes from "prop-types"; +import toast from "react-hot-toast"; +import * as Yup from "yup"; +import { useFormik } from "formik"; +import { Button, Card, CardActions, CardContent, CardHeader, Divider, Grid, TextField } from "@mui/material"; +import { configParamApi } from "../../apis/configuration-param-api"; +import { useSession } from "next-auth/react"; + +export const ConfigParamEditForm = (props) => { + const { parameter }: { parameter: DefaultConfig | IntersectionConfig } = props; + const router = useRouter(); + const { data: session } = useSession(); + const formik = useFormik({ + initialValues: { + name: parameter.key, + unit: parameter.units, + value: parameter.value, + description: parameter.description, + submit: null, + }, + validationSchema: Yup.object({ + name: Yup.string(), + value: Yup.string().required("New value is required"), + }), + onSubmit: async (values, helpers) => { + if (!session?.accessToken) { + console.error("Did not attempt to edit configuration parameter. Access token:", Boolean(session?.accessToken)); + return; + } + try { + const valueType = parameter.type; + let typedValue: number | string | boolean | null = values.value; + switch (valueType) { + case "java.lang.Integer": + try { + parseInt(values.value); + typedValue = values.value.toString(); + break; + } catch (e) { + toast.error("Invalid integer value"); + helpers.setStatus({ success: false }); + helpers.setSubmitting(false); + return; + } + case "java.lang.Boolean": + typedValue = values.value == "true"; + break; + case "java.lang.Long": + try { + parseInt(values.value); + typedValue = values.value.toString(); + break; + } catch (e) { + toast.error("Invalid long value"); + helpers.setStatus({ success: false }); + helpers.setSubmitting(false); + return; + } + case "java.lang.Double": + try { + Number(values.value); + typedValue = values.value.toString(); + break; + } catch (e) { + toast.error("Invalid double value"); + helpers.setStatus({ success: false }); + helpers.setSubmitting(false); + return; + } + case "java.lang.String": + break; + default: + break; + } + if ("intersectionID" in parameter) { + const updatedConfig: IntersectionConfig = { + ...(parameter as IntersectionConfig), + value: typedValue, + }; + await configParamApi.updateIntersectionParameter(session?.accessToken, values.name, updatedConfig); + } else { + const updatedConfig = { + ...parameter, + value: typedValue, + }; + await configParamApi.updateDefaultParameter(session?.accessToken, values.name, updatedConfig); + } + helpers.setStatus({ success: true }); + helpers.setSubmitting(false); + router + .push({ + pathname: "/configuration", + query: { returnUrl: router.asPath }, + }) + .catch(console.error); + } catch (err) { + console.error(err); + toast.error("Something went wrong!"); + helpers.setStatus({ success: false }); + helpers.setErrors({ submit: err.message }); + helpers.setSubmitting(false); + } + }, + }); + + return ( +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
+ ); +}; + +ConfigParamEditForm.propTypes = { + parameter: PropTypes.object.isRequired, +}; diff --git a/webapp/src/components/intersection/configuration/configuration-list-table.tsx b/webapp/src/components/intersection/configuration/configuration-list-table.tsx new file mode 100644 index 000000000..3ab675e24 --- /dev/null +++ b/webapp/src/components/intersection/configuration/configuration-list-table.tsx @@ -0,0 +1,176 @@ +import PerfectScrollbar from "react-perfect-scrollbar"; +import PropTypes from "prop-types"; +import NextLink from "next/link"; +import { + Box, + Card, + IconButton, + Table, + TableBody, + TableCell, + TableHead, + TablePagination, + TableRow, + Chip, + Typography, +} from "@mui/material"; +import React from "react"; +import { PencilAlt as PencilAltIcon } from "../../icons/pencil-alt"; +import CancelIcon from "@mui/icons-material/Cancel"; +import AddIcon from "@mui/icons-material/Add"; + +export const ConfigParamListTable = (props) => { + const { intersectionId, parameters, parametersCount, onPageChange, onRowsPerPageChange, page, rowsPerPage } = props; + + const readOnlyRow = (param) => { + return ( + + {param.key} + {param.value.toString()} + {param.units?.toString()} + {param.description} + + + ); + }; + + const generalDefaultRow = (param) => { + return ( + + {param.key} + {param.value?.toString()} + {param.units?.toString()} + {param.description} + + + + + + + + + ); + }; + + const generalIntersectionRow = (param) => { + return ( + + {param.key} + {param.value.toString()} + {param.units?.toString()} + {param.description} + + {intersectionId != -1 ? ( + + + + + + ) : null} + + + + + + + + ); + }; + + const intersectionRow = (param) => { + return ( + + {param.key} + + {param.value.toString()} + { + + Overrriden + + } + size="small" + /> + } + + {param.unit} + {param.description} + + + + + + + {intersectionId != -1 ? ( + + + + + + ) : null} + + + ); + }; + + return ( + + + + + + + Name + Value + Unit + Description + Actions + + + + {(parameters as Config[]).map((param) => { + switch (param.updateType) { + case "READ_ONLY": + return readOnlyRow(param); + case "DEFAULT": + return generalDefaultRow(param); + case "INTERSECTION": + return "intersectionID" in param ? intersectionRow(param) : generalIntersectionRow(param); + default: + return readOnlyRow(param); + } + })} + +
+
+
+ +
+ ); +}; + +ConfigParamListTable.propTypes = { + intersectionId: PropTypes.number.isRequired, + parameters: PropTypes.array.isRequired, + parametersCount: PropTypes.number.isRequired, + onPageChange: PropTypes.func.isRequired, + onRowsPerPageChange: PropTypes.func, + page: PropTypes.number.isRequired, + rowsPerPage: PropTypes.number.isRequired, +}; diff --git a/webapp/src/components/intersection/configuration/configuration-remove-form.tsx b/webapp/src/components/intersection/configuration/configuration-remove-form.tsx new file mode 100644 index 000000000..6775bc959 --- /dev/null +++ b/webapp/src/components/intersection/configuration/configuration-remove-form.tsx @@ -0,0 +1,163 @@ +import NextLink from "next/link"; +import { useRouter } from "next/router"; +import PropTypes from "prop-types"; +import toast from "react-hot-toast"; +import * as Yup from "yup"; +import { useFormik } from "formik"; +import { Button, Card, CardActions, CardContent, CardHeader, Divider, Grid, TextField } from "@mui/material"; +import { configParamApi } from "../../apis/configuration-param-api"; +import { useDashboardContext } from "../../contexts/dashboard-context"; +import { useSession } from "next-auth/react"; + +export const ConfigParamRemoveForm = (props) => { + const { parameter, defaultParameter, ...other } = props; + const router = useRouter(); + const { data: session } = useSession(); + const { intersectionId } = useDashboardContext(); + const formik = useFormik({ + initialValues: { + name: parameter.key, + unit: parameter.unit, + value: parameter.value, + defaultValue: defaultParameter.value, + description: parameter.description, + submit: null, + }, + validationSchema: Yup.object({}), + onSubmit: async (values, helpers) => { + if (!session?.accessToken || intersectionId == -1) { + console.error( + "Did not attempt to remove configuration parameter. Access token:", + session?.accessToken, + "Intersection ID:", + intersectionId + ); + return; + } + try { + await configParamApi.removeOverriddenParameter(session?.accessToken, values.name, parameter); + helpers.setStatus({ success: true }); + helpers.setSubmitting(false); + router + .push({ + pathname: "/configuration", + query: { returnUrl: router.asPath }, + }) + .catch(console.error); + } catch (err) { + console.error(err); + toast.error("Something went wrong!"); + helpers.setStatus({ success: false }); + helpers.setErrors({ submit: err.message }); + helpers.setSubmitting(false); + } + }, + }); + + return ( +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ ); +}; + +ConfigParamRemoveForm.propTypes = { + parameter: PropTypes.object.isRequired, + defaultParameter: PropTypes.object.isRequired, +}; diff --git a/webapp/src/components/intersection/dashboard-layout.tsx b/webapp/src/components/intersection/dashboard-layout.tsx new file mode 100644 index 000000000..f1a225237 --- /dev/null +++ b/webapp/src/components/intersection/dashboard-layout.tsx @@ -0,0 +1,65 @@ +import { useEffect } from 'react' +import React from 'react' +import MessageMonitorApi from '../../apis/intersections/mm-api' +import VerticalTabs from '../VerticalTabs' +import { NotFound } from '../../pages/404' +import DashboardPage from './dashboard' +import NotificationsPage from './notifications' +import PerformanceReportsPage from './performance-reports' +import DataSelectorPage from './data-selector' +import { useSelector } from 'react-redux' +import { selectToken } from '../../generalSlices/userSlice' +import { selectSelectedIntersectionId, setSelectedIntersection } from '../../generalSlices/intersectionSlice' + +export const DashboardLayout = (props) => { + const { children } = props + + const authToken = useSelector(selectToken) + + const intersectionId = useSelector(selectSelectedIntersectionId) + + useEffect(() => { + if (authToken) { + MessageMonitorApi.getIntersections({ token: authToken }).then((intersections: IntersectionReferenceData[]) => { + setSelectedIntersection(intersections?.[0]?.intersectionID) + }) + } else { + console.error('Did not attempt to update user automatically. Access token:', Boolean(authToken)) + } + }, [authToken]) + + return ( + + } + defaultTabIndex={0} + tabs={[ + { + path: '', + title: 'Dashboard', + child: , + }, + { + path: 'notifications', + title: 'Notifications', + child: , + }, + { + path: 'reports', + title: 'Performance Reports', + child: , + }, + { + path: 'data', + title: 'Data Selector', + child: , + }, + ]} + /> + ) +} diff --git a/webapp/src/components/intersection/dashboard-sidebar-item.tsx b/webapp/src/components/intersection/dashboard-sidebar-item.tsx new file mode 100644 index 000000000..a0724620d --- /dev/null +++ b/webapp/src/components/intersection/dashboard-sidebar-item.tsx @@ -0,0 +1,144 @@ +import { useState } from "react"; +import NextLink from "next/link"; +import PropTypes from "prop-types"; +import { Box, Button, Collapse, ListItem } from "@mui/material"; +import { ChevronDown as ChevronDownIcon } from "../icons/chevron-down"; +import { ChevronRight as ChevronRightIcon } from "../icons/chevron-right"; + +export const DashboardSidebarItem = (props) => { + const { + active, + children, + chip, + depth, + icon, + info, + open: openProp, + path, + title, + ...other + } = props; + const [open, setOpen] = useState(!!openProp); + + const handleToggle = () => { + setOpen((prevOpen) => !prevOpen); + }; + + let paddingLeft = 24; + + if (depth > 0) { + paddingLeft = 32 + 8 * depth; + } + + // Branch + if (children) { + return ( + + + + {children} + + + ); + } + + // Leaf + return ( + + + + + + ); +}; + +DashboardSidebarItem.propTypes = { + active: PropTypes.bool, + children: PropTypes.node, + depth: PropTypes.number.isRequired, + icon: PropTypes.node, + chip: PropTypes.node, + info: PropTypes.node, + open: PropTypes.bool, + path: PropTypes.string, + title: PropTypes.string.isRequired, +}; + +DashboardSidebarItem.defaultProps = { + active: false, + open: false, +}; diff --git a/webapp/src/components/intersection/dashboard-sidebar-section.tsx b/webapp/src/components/intersection/dashboard-sidebar-section.tsx new file mode 100644 index 000000000..2135bf1c5 --- /dev/null +++ b/webapp/src/components/intersection/dashboard-sidebar-section.tsx @@ -0,0 +1,97 @@ +import PropTypes from "prop-types"; +import { List, ListSubheader } from "@mui/material"; +import { DashboardSidebarItem } from "./dashboard-sidebar-item"; + +const renderNavItems = ({ depth = 0, items, path }) => ( + + {items.reduce((acc, item) => reduceChildRoutes({ acc, depth, item, path }), [])} + +); + +const getPartialMatch = (path, itemPath) => { + if (path === "/" && itemPath === path) return true; + else if (itemPath && itemPath !== "/") { + return path.includes(itemPath); + } + return false; +}; + +const reduceChildRoutes = ({ acc, depth, item, path }) => { + const key = `${item.title}-${depth}`; + const partialMatch = getPartialMatch(path, item.path); + const exactMatch = path.split("?")[0] === item.path; // We don't compare query params + + if (item.children) { + acc.push( + + {renderNavItems({ + depth: depth + 1, + items: item.children, + path, + })} + + ); + } else { + acc.push( + + ); + } + + return acc; +}; + +export const DashboardSidebarSection = (props) => { + const { items, path, title, ...other } = props; + + return ( + + {title} + + } + {...other} + > + {renderNavItems({ + items, + path, + })} + + ); +}; + +DashboardSidebarSection.propTypes = { + items: PropTypes.array.isRequired, + path: PropTypes.string.isRequired, + title: PropTypes.string.isRequired, +}; diff --git a/webapp/src/components/intersection/dashboard-sidebar.tsx b/webapp/src/components/intersection/dashboard-sidebar.tsx new file mode 100644 index 000000000..67fd968d8 --- /dev/null +++ b/webapp/src/components/intersection/dashboard-sidebar.tsx @@ -0,0 +1,210 @@ +import { useEffect } from "react"; +import NextLink from "next/link"; +import { useRouter } from "next/router"; +import PropTypes from "prop-types"; +import { Box, Button, Divider, Drawer, Typography, useMediaQuery, Theme, Chip } from "@mui/material"; +import OpenInNewIcon from "@mui/icons-material/OpenInNew"; +import { ChartBar as ChartBarIcon } from "../icons/chart-bar"; +import { Cog as CogIcon } from "../icons/cog"; +import { Lock as LockIcon } from "../icons/lock"; +import { Selector as SelectorIcon } from "../icons/selector"; +import { ShoppingBag as ShoppingBagIcon } from "../icons/shopping-bag"; +import { User as UserIcon } from "../icons/user"; +import { UserAdd as UserAddIcon } from "../icons/user-add"; +import { Users as UsersIcon } from "../icons/users"; +import MapIcon from "@mui/icons-material/Map"; +import TimelineIcon from "@mui/icons-material/Timeline"; +import NotificationsIcon from "@mui/icons-material/Notifications"; +import ArticleIcon from "@mui/icons-material/Article"; +import SubtitlesIcon from "@mui/icons-material/Subtitles"; +import { Logo } from "./logo"; +import { NavItem } from "./nav-item"; +import React from "react"; +import { DashboardSidebarSection } from "./dashboard-sidebar-section"; +import { useDashboardContext } from "../contexts/dashboard-context"; +import { useSession } from "next-auth/react"; + +const generalItems = [ + { + path: "/", + icon: , + title: "Dashboard", + }, + { + path: "/notifications", + icon: , + title: "Notifications", + }, + { + path: "/reports", + icon: , + title: "Performance Reports", + }, + { + path: "/map", + icon: , + title: "Map", + }, + { + path: "/data-selector", + icon: , + title: "Data Selector", + }, + { + path: "/decoder", + icon: , + title: "ASN.1 Decoder", + }, + { + path: "/configuration", + icon: , + title: "Configuration", + }, + { + path: "/settings", + icon: , + title: "Settings", + }, +]; + +const adminItems = [ + { + path: "/users", + icon: , + title: "Users", + }, +]; + +const getSections = (role) => + role == "ADMIN" + ? [ + { + title: "General", + items: generalItems, + }, + { + title: "Admin", + items: adminItems, + }, + ] + : [ + { + title: "General", + items: generalItems, + }, + ]; + +export const DashboardSidebar = (props) => { + const { open, onClose } = props; + const router = useRouter(); + const lgUp = useMediaQuery((theme: Theme) => theme.breakpoints.up("lg"), { + defaultMatches: true, + noSsr: false, + }); + const { data: session } = useSession(); + + useEffect( + () => { + if (!router.isReady) { + return; + } + + if (open) { + onClose?.(); + } + }, + // eslint-disable-next-line react-hooks/exhaustive-deps + [router.asPath] + ); + + const content = ( + <> + +
+ + + + + + + +
+ + + {getSections(session?.role).map((section) => ( + + ))} + +
+ + ); + + if (lgUp) { + return ( + + {content} + + ); + } + + return ( + theme.zIndex.appBar + 100 }} + variant="temporary" + > + {content} + + ); +}; + +DashboardSidebar.propTypes = { + onClose: PropTypes.func, + open: PropTypes.bool, +}; diff --git a/webapp/src/components/intersection/dashboard.tsx b/webapp/src/components/intersection/dashboard.tsx new file mode 100644 index 000000000..f02556206 --- /dev/null +++ b/webapp/src/components/intersection/dashboard.tsx @@ -0,0 +1,105 @@ +import { Box, Container, Grid } from '@mui/material' +import { NotificationsTable } from './notifications/notifications-table' +import { ConnectionOfTravelAssessmentCard } from './assessments/connection-of-travel-assessment' +import { LaneDirectionOfTravelAssessmentCard } from './assessments/lane-direction-of-travel-assessment' +import { StopLineStopAssessmentCard } from './assessments/stop-line-stop-assessment' +import { SignalStateEventAssessmentCard } from './assessments/signal-state-event-assessment' +import React, { useEffect, useState, useRef } from 'react' +import AssessmentsApi from '../../apis/intersections/assessments-api' +import { selectToken } from '../../generalSlices/userSlice' +import { selectSelectedIntersectionId, selectSelectedRoadRegulatorId } from '../../generalSlices/intersectionSlice' +import { useSelector } from 'react-redux' + +const DashboardPage = () => { + const authToken = useSelector(selectToken) + + const intersectionId = useSelector(selectSelectedIntersectionId) + const roadRegulatorId = useSelector(selectSelectedRoadRegulatorId) + + // create hooks, and methods for each assessment type: + const [stopLineStopAssessment, setStopLineStopAssessment] = useState(undefined) + // create hooks, and methods for each assessment type: + const [signalStateEventAssessment, setSignalStateEventAssessment] = useState( + undefined + ) + const [connectionOfTravelAssessment, setConnectionOfTravelAssessment] = useState< + ConnectionOfTravelAssessment | undefined + >(undefined) + const [laneDirectionOfTravelAssessment, setLaneDirectionOfTravelAssessment] = useState< + LaneDirectionOfTravelAssessment | undefined + >(undefined) + + const getAssessments = async () => { + if (intersectionId && authToken) { + setStopLineStopAssessment( + (await AssessmentsApi.getLatestAssessment( + authToken, + 'signal_state_assessment', + intersectionId, + roadRegulatorId + )) as StopLineStopAssessment + ) + setSignalStateEventAssessment( + (await AssessmentsApi.getLatestAssessment( + authToken, + 'signal_state_event_assessment', + intersectionId, + roadRegulatorId + )) as SignalStateEventAssessment + ) + setConnectionOfTravelAssessment( + (await AssessmentsApi.getLatestAssessment( + authToken, + 'connection_of_travel', + intersectionId, + roadRegulatorId + )) as ConnectionOfTravelAssessment + ) + setLaneDirectionOfTravelAssessment( + (await AssessmentsApi.getLatestAssessment( + authToken, + 'lane_direction_of_travel', + intersectionId, + roadRegulatorId + )) as LaneDirectionOfTravelAssessment + ) + } else { + console.error( + 'Did not attempt to get assessment data. Access token:', + authToken, + 'Intersection ID:', + intersectionId + ) + } + } + + useEffect(() => { + getAssessments() + }, [intersectionId]) + + return ( + <> + + + + + + + + + + + + + + + ) +} + +export default DashboardPage diff --git a/webapp/src/components/intersection/data-selector.tsx b/webapp/src/components/intersection/data-selector.tsx new file mode 100644 index 000000000..75e5df65b --- /dev/null +++ b/webapp/src/components/intersection/data-selector.tsx @@ -0,0 +1,365 @@ +import { useEffect } from 'react' +import { Box, Container, Typography } from '@mui/material' +import EventsApi from '../../apis/intersections/events-api' +import AssessmentsApi from '../../apis/intersections/assessments-api' +import MessageMonitorApi from '../../apis/intersections/mm-api' +import GraphsApi from '../../apis/intersections/graphs-api' +import { DataSelectorEditForm } from './data-selector/data-selector-edit-form' +import { EventDataTable } from './data-selector/event-data-table' +import { AssessmentDataTable } from './data-selector/assessment-data-table' +import { DataVisualizer } from './data-selector/data-visualizer' +import toast from 'react-hot-toast' +import MapDialog from './intersection-selector/intersection-selector-dialog' +import { AnyAction, ThunkDispatch } from '@reduxjs/toolkit' +import JSZip from 'jszip' +import FileSaver from 'file-saver' +import { useDispatch, useSelector } from 'react-redux' +import { selectToken } from '../../generalSlices/userSlice' +import { selectSelectedIntersectionId, selectSelectedRoadRegulatorId } from '../../generalSlices/intersectionSlice' +import { + selectAssessments, + selectDataSelectorForm, + selectEvents, + selectGraphData, + selectOpenMapDialog, + selectRoadRegulatorIntersectionIds, + selectType, + setAssessments, + setEvents, + setGraphData, + setOpenMapDialog, + setRoadRegulatorIntersectionIds, + setType, +} from './data-selector/data-selector-slice' +import { RootState } from '../../store' +import React from 'react' + +const DataSelectorPage = () => { + const dispatch: ThunkDispatch = useDispatch() + + const authToken = useSelector(selectToken) + + const intersectionId = useSelector(selectSelectedIntersectionId) + const roadRegulatorId = useSelector(selectSelectedRoadRegulatorId) + + const type = useSelector(selectType) + const events = useSelector(selectEvents) + const assessments = useSelector(selectAssessments) + const graphData = useSelector(selectGraphData) + const openMapDialog = useSelector(selectOpenMapDialog) + const roadRegulatorIntersectionIds = useSelector(selectRoadRegulatorIntersectionIds) + const dataSelectorForm = useSelector(selectDataSelectorForm) + + const getPaddedTimestamp = () => { + const date = new Date() + // create padded timestamp like YYMMdd_HHMMSS + return `${date.getFullYear().toString().slice(-2)}${(date.getMonth() + 1).toString().padStart(2, '0')}${date + .getDate() + .toString() + .padStart(2, '0')}_${date.getHours().toString().padStart(2, '0')}${date + .getMinutes() + .toString() + .padStart(2, '0')}${date.getSeconds().toString().padStart(2, '0')}` + } + + const downloadFile = (contents: string, name: string, extension: string = 'txt') => { + const element = document.createElement('a') + const file = new Blob([contents], { type: 'text/plain' }) + element.href = URL.createObjectURL(file) + element.download = `${name}_${getPaddedTimestamp()}.${extension}` + document.body.appendChild(element) // Required for this to work in FireFox + element.click() + } + + useEffect(() => { + if (authToken) { + MessageMonitorApi.getIntersections({ token: authToken }).then((intersections) => { + const roadRegulatorIntersectionIds: { [roadRegulatorId: number]: number[] } = {} + for (const intersection of intersections) { + if (!roadRegulatorIntersectionIds[intersection.roadRegulatorID]) { + roadRegulatorIntersectionIds[intersection.roadRegulatorID] = [] + } + roadRegulatorIntersectionIds[intersection.roadRegulatorID].push(intersection.intersectionID) + } + dispatch(setRoadRegulatorIntersectionIds(roadRegulatorIntersectionIds)) + }) + } else { + console.error('Did not attempt to update user automatically. Access token:', Boolean(authToken)) + } + }, [authToken]) + + const query = async ({ + type, + intersectionId, + roadRegulatorId, + startDate, + endTime, + eventTypes, + assessmentTypes, + bsmVehicleId, + }) => { + if (!authToken) { + console.error('Did not attempt to query for data. Access token:', Boolean(authToken)) + return + } + dispatch(setType(type)) + switch (type) { + case 'events': + const events: MessageMonitor.Event[] = [] + // iterate through each event type in a for loop and add the events to events array + const eventPromises: Promise[] = [] + for (let i = 0; i < eventTypes.length; i++) { + const eventType = eventTypes[i] + const promise = EventsApi.getEvents(authToken, eventType, intersectionId, roadRegulatorId, startDate, endTime) + eventPromises.push(promise) + } + const allEventsPromise = Promise.all(eventPromises) + toast.promise(allEventsPromise, { + loading: `Loading event data`, + success: `Successfully got event data`, + error: `Failed to get event data. Please see console`, + }) + + try { + const allEvents = await allEventsPromise + allEvents.forEach((event) => { + events.push(...event) + }) + } catch (e) { + console.error(`Failed to load event data because ${e}`) + } + + events.sort((a, b) => a.eventGeneratedAt - b.eventGeneratedAt) + dispatch(setEvents(events)) + // setAssessments([]); + return events + case 'assessments': + const assessments: Assessment[] = [] + const assessmentPromises: Promise[] = [] + // iterate through each event type in a for loop and add the events to events array + for (let i = 0; i < assessmentTypes.length; i++) { + const assessmentType = assessmentTypes[i] + const promise = AssessmentsApi.getAssessments( + authToken, + assessmentType, + intersectionId, + roadRegulatorId, + startDate, + endTime + ) + assessmentPromises.push(promise) + } + + const allAssessmentsPromise = Promise.all(assessmentPromises) + toast.promise(allAssessmentsPromise, { + loading: `Loading assessment data`, + success: `Successfully got assessment data`, + error: `Failed to get assessment data`, + }) + + try { + const allAssessments = await allAssessmentsPromise + allAssessments.forEach((assessment) => { + assessments.push(...assessment) + }) + } catch (e) { + console.error(`Failed to load assessment data because ${e}`) + } + assessments.sort((a, b) => a.assessmentGeneratedAt - b.assessmentGeneratedAt) + dispatch(setAssessments(assessments)) + // setEvents([]); + return assessments + } + return + } + + const onVisualize = async ({ + intersectionId, + roadRegulatorId, + startDate, + endTime, + eventTypes, + }: { + intersectionId: number + roadRegulatorId: number + startDate: Date + endTime: Date + eventTypes: string[] + }) => { + if (!authToken) { + console.error('Did not attempt to visualize data counts. Access token:', Boolean(authToken)) + return + } + dispatch( + setGraphData( + await GraphsApi.getGraphData({ + token: authToken, + intersectionId: intersectionId, + roadRegulatorId: roadRegulatorId, + startTime: startDate, + endTime: endTime, + event_types: eventTypes, + }) + ) + ) + // setGraphData( + // await GraphsApi.getGraphData({ + // token: authToken, + // intersection_id: intersectionId, + // data_type: type, + // startTime: startDate, + // endTime: new Date(startDate.getTime() + timeRange * 60 * 1000), + // }) + // ); + } + + function sanitizeCsvString(term) { + try { + if (term instanceof Object || term instanceof Array) { + return `"${JSON.stringify(term).replaceAll('"', '""')}"` + } + if (term.match && term.match(/,|"/)) { + return `"${term.replaceAll('"', '""')}"` + } else { + return term + } + } catch (e) { + console.error(e) + return term + } + } + + const convertToCsv = (data: any[]) => { + const csvRows: string[] = [] + const headers = Object.keys(data[0]) + csvRows.push(headers.join(',')) + + for (const row of data) { + const values = headers.map((header) => row[header]?.toString() ?? 'undefined') + csvRows.push(values.join(',')) + } + return csvRows.join('\n') + } + + const downloadEventCsvFiles = (data: MessageMonitor.Event[]) => { + const csvRows: { [id: string]: string[] } = {} + for (const event of data) { + if (!csvRows[event.eventType]) { + csvRows[event.eventType] = [Object.keys(event).join(',')] + } + csvRows[event.eventType].push(Object.values(event).map(sanitizeCsvString).join(',')) + } + + var zip = new JSZip() + for (const eventType in csvRows) { + zip.file(`cimms_events_${eventType}_export.csv`, csvRows[eventType].join('\n')) + } + zip.generateAsync({ type: 'blob' }).then(function (content) { + FileSaver.saveAs(content, `cimms_events_export.zip`) + }) + } + + const downloadAssessmentCsvFiles = (data: Assessment[]) => { + const csvRows: { [id: string]: string[] } = {} + for (const event of data) { + if (!csvRows[event.assessmentType]) { + csvRows[event.assessmentType] = [Object.keys(event).join(',')] + } + // switch(event.assessmentType) { + // case "SignalStateAssessment": + // const signalStateAssessment = event as SignalStateAssessment; + // signalStateAssessment + // break; + // case "LaneDirectionOfTravelAssessment": + // break; + // case "ConnectionOfTravelAssessment": + // break; + // case "VehicleStopAssessment": + // break; + // } + csvRows[event.assessmentType].push(Object.values(event).map(sanitizeCsvString).join(',')) + } + + var zip = new JSZip() + for (const assessmentType in csvRows) { + zip.file(`cimms_assessments_${assessmentType}_export.csv`, csvRows[assessmentType].join('\n')) + } + zip.generateAsync({ type: 'blob' }).then(function (content) { + FileSaver.saveAs(content, `cimms_assessments_export.zip`) + }) + } + + return ( + <> + + + +
+ + Query + +
+
+ + + +
+ + {type == 'events' && ( + downloadEventCsvFiles(events)} + onDownloadJson={() => + downloadFile(events.map((e) => JSON.stringify(e)).join('\n'), 'cimms_events_export') + } + /> + )} + {type == 'assessments' && ( + downloadAssessmentCsvFiles(assessments)} + onDownloadJson={() => + downloadFile(assessments.map((e) => JSON.stringify(e)).join('\n'), 'cimms_assessments_export') + } + /> + )} + {graphData.length > 0 && ( + { + return downloadFile(convertToCsv(graphData), 'cimms_graph_data_export', 'csv') + }} + /> + )} + +
+ { + dispatch(setOpenMapDialog(false)) + }} + intersections={[]} + /> + + ) +} + +export default DataSelectorPage diff --git a/webapp/src/components/intersection/data-selector/assessment-data-table.tsx b/webapp/src/components/intersection/data-selector/assessment-data-table.tsx new file mode 100644 index 000000000..cde2ff5b9 --- /dev/null +++ b/webapp/src/components/intersection/data-selector/assessment-data-table.tsx @@ -0,0 +1,104 @@ +import Head from "next/head"; +import { + Box, + Button, + Card, + Container, + Divider, + Grid, + InputAdornment, + Stack, + Tab, + Tabs, + TextField, + TextFieldProps, + Typography, + CardHeader, +} from "@mui/material"; +import { AssessmentListResults } from "./assessment-list-results"; +import { DashboardLayout } from "../dashboard-layout"; +import { Refresh as RefreshIcon } from "../../icons/refresh"; +import { Search as SearchIcon } from "../../icons/search"; +import NotificationApi from "../../apis/notification-api"; +import React, { useEffect, useState, useRef } from "react"; + +const applyPagination = (parameters, page, rowsPerPage) => + parameters.slice(page * rowsPerPage, page * rowsPerPage + rowsPerPage); + +export const AssessmentDataTable = (props: { + assessments: Assessment[]; + onDownload: () => void; + onDownloadJson: () => void; +}) => { + const { assessments, onDownload, onDownloadJson } = props; + const queryRef = useRef(null); + const [page, setPage] = useState(0); + const [rowsPerPage, setRowsPerPage] = useState(10); + const [currentDescription, setCurrentDescription] = useState(""); + + useEffect(() => { + setPage(0); + }, [assessments]); + + const handlePageChange = (event, newPage) => { + setPage(newPage); + }; + + const handleRowsPerPageChange = (event) => { + setRowsPerPage(parseInt(event.target.value, 10)); + }; + + // Usually query is done on backend with indexing solutions + const paginatedNotifications = applyPagination(assessments, page, rowsPerPage); + + return ( + <> + + + <> + + + + + + + + + + + + + + + + + + + ); +}; diff --git a/webapp/src/components/intersection/data-selector/assessment-list-results.tsx b/webapp/src/components/intersection/data-selector/assessment-list-results.tsx new file mode 100644 index 000000000..05faae108 --- /dev/null +++ b/webapp/src/components/intersection/data-selector/assessment-list-results.tsx @@ -0,0 +1,93 @@ +import PerfectScrollbar from "react-perfect-scrollbar"; +import PropTypes from "prop-types"; +import { format } from "date-fns"; +import NextLink from "next/link"; +import { + Box, + Card, + IconButton, + Table, + TableBody, + TableCell, + TableHead, + TablePagination, + TableRow, + Typography, +} from "@mui/material"; +import React from "react"; +import MapRoundedIcon from "@mui/icons-material/MapRounded"; + +export const AssessmentListResults = ({ + assessments, + assessmentsCount, + onPageChange, + onRowsPerPageChange, + page, + rowsPerPage, +}) => { + const getAssessmentDescription = (assessment: Assessment) => { + return JSON.stringify(assessment).replace(/,/g, ", "); + }; + + return ( + + + + + + + Assessment Type + Date + Open Map + Message + + + + {assessments.map((assessment: Assessment, i: number) => { + return ( + + + + + {assessment.assessmentType} + + + + {format(assessment.assessmentGeneratedAt, "MM/dd/yyyy HH:mm:ss")} + + + + + + + + {getAssessmentDescription(assessment)} + + ); + })} + +
+
+
+ +
+ ); +}; + +AssessmentListResults.propTypes = { + assessments: PropTypes.array.isRequired, + onSelectedItemsChanged: PropTypes.func, +}; diff --git a/webapp/src/components/intersection/data-selector/data-selector-edit-form.tsx b/webapp/src/components/intersection/data-selector/data-selector-edit-form.tsx new file mode 100644 index 000000000..e15170862 --- /dev/null +++ b/webapp/src/components/intersection/data-selector/data-selector-edit-form.tsx @@ -0,0 +1,357 @@ +import React, { useState } from "react"; +import NextLink from "next/link"; +import { useRouter } from "next/router"; +import PropTypes from "prop-types"; +import toast from "react-hot-toast"; +import * as Yup from "yup"; +import { useFormik } from "formik"; +import dayjs, { Dayjs } from "dayjs"; +import { AdapterDayjs } from "@mui/x-date-pickers/AdapterDayjs"; +import { LocalizationProvider } from "@mui/x-date-pickers/LocalizationProvider"; +import { DateTimePicker } from "@mui/x-date-pickers/DateTimePicker"; +import { Theme, useTheme } from "@mui/material/styles"; +import { + Autocomplete, + Button, + Box, + Card, + CardActions, + CardContent, + CardHeader, + Chip, + Divider, + Grid, + TextField, + InputLabel, + MenuItem, + Select, + InputAdornment, + OutlinedInput, + Checkbox, +} from "@mui/material"; +import CheckBoxOutlineBlankIcon from "@mui/icons-material/CheckBoxOutlineBlank"; +import CheckBoxIcon from "@mui/icons-material/CheckBox"; +import { FormikCheckboxList } from "./formik-checkbox-list"; +import { useQueryContext } from "../../contexts/query-context"; + +interface Item { + label: string; + value: string; +} + +const EVENT_TYPES: Item[] = [ + { label: "All", value: "All" }, + { label: "ConnectionOfTravelEvent", value: "connection_of_travel" }, + { label: "IntersectionReferenceAlignmentEvent", value: "intersection_reference_alignment" }, + { label: "LaneDirectionOfTravelEvent", value: "lane_direction_of_travel" }, + // { label: "ProcessingTimePeriod", value: "processing_time_period" }, + { label: "SignalGroupAlignmentEvent", value: "signal_group_alignment" }, + { label: "SignalStateConflictEvent", value: "signal_state_conflict" }, + { label: "SignalStateEvent", value: "signal_state" }, + { label: "SignalStateStopEvent", value: "signal_state_stop" }, + { label: "TimeChangeDetailsEvent", value: "time_change_details" }, + { label: "MapMinimumDataEvent", value: "map_minimum_data" }, + { label: "SpatMinimumDataEvent", value: "spat_minimum_data" }, + { label: "MapBroadcastRateEvent", value: "map_broadcast_rate" }, + { label: "SpatBroadcastRateEvent", value: "spat_broadcast_rate" }, +]; + +const ASSESSMENT_TYPES: Item[] = [ + { label: "All", value: "All" }, + { label: "SignalStateEventAssessment", value: "signal_state_event_assessment" }, + { label: "StopLineStopAssessment", value: "signal_state_assessment" }, + { label: "LaneDirectionOfTravelAssessment", value: "lane_direction_of_travel" }, + { label: "ConnectionOfTravelAssessment", value: "connection_of_travel" }, +]; + +export const DataSelectorEditForm = (props: { + onQuery: (query: any) => void; + onVisualize: (query: any) => void; + dbIntersectionId: number | undefined; + roadRegulatorIntersectionIds: { [roadRegulatorId: number]: number[] }; +}) => { + const { onQuery, onVisualize, dbIntersectionId, roadRegulatorIntersectionIds, ...other } = props; + const [visualize, setVisualize] = useState(false); + + const { dataSelectorForm, setDataSelectorForm } = useQueryContext(); + + const router = useRouter(); + const formik = useFormik({ + initialValues: { + ...dataSelectorForm, + intersectionId: dataSelectorForm.intersectionId ?? dbIntersectionId, + }, + validationSchema: Yup.object({ + // type: Yup.string().required("Type is required"), + // startDate: Yup.date().required("Start date is required"), + // timeRange: Yup.number().required("Time interval is required"), + // intersectionId: Yup.string().required("Intersection ID is required"), + // roadRegulatorId: Yup.string().required("Road Regulator ID is required"), + // bsmVehicleId: Yup.string(), + }), + onSubmit: async (values, helpers) => { + // const endTime = new Date(startDate.getTime() + timeRange * 60 * 1000); + // convert timeRange to endTime using the timeUnit field + const endTime = dayjs(values.startDate).add(values.timeRange, values.timeUnit).toDate(); + try { + if (visualize) { + onVisualize({ + intersectionId: values.intersectionId, + roadRegulatorId: values.roadRegulatorId, + startDate: values.startDate, + endTime: endTime, + eventTypes: values.eventTypes.map((e) => e.value).filter((e) => e !== "All"), + }); + } else { + setDataSelectorForm(values); + helpers.setStatus({ success: true }); + helpers.setSubmitting(false); + onQuery({ + type: values.type, + intersectionId: values.intersectionId, + roadRegulatorId: values.roadRegulatorId, + startDate: values.startDate, + endTime: endTime, + eventTypes: values.eventTypes.map((e) => e.value).filter((e) => e !== "All"), + assessmentTypes: values.assessmentTypes.map((e) => e.value).filter((e) => e !== "All"), + bsmVehicleId: values.bsmVehicleId, + }); + } + } catch (err) { + console.error(err); + toast.error("Something went wrong!"); + helpers.setStatus({ success: false }); + helpers.setErrors({ submit: err.message }); + helpers.setSubmitting(false); + } + }, + }); + + const onTypeChange = (newType) => { + formik.setFieldValue("eventTypes", [] as Item[]); + formik.setFieldValue("assessmentTypes", [] as Item[]); + }; + + const getTypeSpecificFilters = (type) => { + switch (type) { + case "bsm": + return ( + <> + + + + + ); + case "events": + return ( + <> + + + Event Type + + formik.setFieldValue("eventTypes", val)} + /> + + + ); + case "assessments": + return ( + <> + + + Assessment Type + + formik.setFieldValue("assessmentTypes", val)} + /> + + + ); + default: + return <>; + } + }; + + return ( +
+ + {/* */} + + + + + + {/* */} + + + + {/* */} + + + + + + + ( + + )} + value={formik.values.startDate} + onChange={(e) => formik.setFieldValue("startDate", (e as Dayjs | null)?.toDate(), true)} + /> + + + + + + + ), + }} + value={formik.values.timeRange} + /> + + {getTypeSpecificFilters(formik.values.type)} + + + + + + + +
+ ); +}; diff --git a/webapp/src/components/intersection/data-selector/data-selector-slice.tsx b/webapp/src/components/intersection/data-selector/data-selector-slice.tsx new file mode 100644 index 000000000..6ddd0ad41 --- /dev/null +++ b/webapp/src/components/intersection/data-selector/data-selector-slice.tsx @@ -0,0 +1,92 @@ +import { createSlice, PayloadAction } from '@reduxjs/toolkit' +import { RootState } from '../../../store' +import dayjs from 'dayjs' + +interface Item { + label: string + value: string +} + +interface DataSelectorFormType { + type: string + startDate: Date + timeRange: number + timeUnit: dayjs.ManipulateType + intersectionId: number | undefined + roadRegulatorId: number + submit: boolean | null + + // type specific filters + bsmVehicleId: number | null + eventTypes: Item[] + assessmentTypes: Item[] +} + +export const initialState = { + type: '', + events: [], + assessments: [], + graphData: [], + openMapDialog: false, + roadRegulatorIntersectionIds: {}, + dataSelectorForm: { + type: 'events', + startDate: new Date(), + timeRange: 0, + timeUnit: 'minutes' as dayjs.ManipulateType, + intersectionId: undefined, + roadRegulatorId: -1, + submit: null, + + // type specific filters + bsmVehicleId: null, + eventTypes: [] as Item[], + assessmentTypes: [] as Item[], + } as DataSelectorFormType, +} + +export const dataSelectorSlice = createSlice({ + name: 'dataSelector', + initialState: { + loading: false, + value: initialState, + }, + reducers: { + setType: (state, action: PayloadAction) => { + state.value.type = action.payload + }, + setEvents: (state, action: PayloadAction) => { + state.value.events = action.payload + state.value.assessments = [] + state.value.graphData = [] + }, + setAssessments: (state, action: PayloadAction) => { + state.value.assessments = action.payload + }, + setGraphData: (state, action: PayloadAction) => { + state.value.graphData = action.payload + state.value.events = [] + state.value.assessments = [] + }, + setOpenMapDialog: (state, action: PayloadAction) => { + state.value.openMapDialog = action.payload + }, + setRoadRegulatorIntersectionIds: (state, action: PayloadAction<{ [roadRegulatorId: number]: number[] }>) => { + state.value.roadRegulatorIntersectionIds = action.payload + }, + }, +}) + +export const selectType = (state: RootState) => state.dataSelector.value.type +export const selectEvents = (state: RootState) => state.dataSelector.value.events +export const selectAssessments = (state: RootState) => state.dataSelector.value.assessments +export const selectGraphData = (state: RootState) => state.dataSelector.value.graphData +export const selectOpenMapDialog = (state: RootState) => state.dataSelector.value.openMapDialog +export const selectRoadRegulatorIntersectionIds = (state: RootState) => + state.dataSelector.value.roadRegulatorIntersectionIds +export const selectDataSelectorForm = (state: RootState) => state.dataSelector.value.dataSelectorForm + +export const { setType, setEvents, setAssessments, setGraphData, setOpenMapDialog, setRoadRegulatorIntersectionIds } = + dataSelectorSlice.actions + +export default dataSelectorSlice.reducer diff --git a/webapp/src/components/intersection/data-selector/data-visualizer.tsx b/webapp/src/components/intersection/data-selector/data-visualizer.tsx new file mode 100644 index 000000000..17ea6c6a3 --- /dev/null +++ b/webapp/src/components/intersection/data-selector/data-visualizer.tsx @@ -0,0 +1,69 @@ +import { Box, Button, Card, Container, Divider, Grid, CardHeader } from "@mui/material"; +import React from "react"; +import { LineChart, Line, CartesianGrid, XAxis, YAxis } from "recharts"; + +export const DataVisualizer = (props: { data: any[]; onDownload: () => void }) => { + const { data, onDownload } = props; + + const dateFormatter = (unix_timestamp) => { + const date = new Date(unix_timestamp); + + // return date in YY/MM/DD + return `${date.getFullYear().toString().slice(-2)}${(date.getMonth() + 1).toString().padStart(2, "0")}/${date + .getDate() + .toString() + .padStart(2, "0")}/`; + }; + + return ( + <> + + + <> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + ); +}; diff --git a/webapp/src/components/intersection/data-selector/event-data-table.tsx b/webapp/src/components/intersection/data-selector/event-data-table.tsx new file mode 100644 index 000000000..adb4d084e --- /dev/null +++ b/webapp/src/components/intersection/data-selector/event-data-table.tsx @@ -0,0 +1,103 @@ +import Head from "next/head"; +import { + Box, + Button, + Card, + Container, + Divider, + Grid, + InputAdornment, + Stack, + Tab, + Tabs, + TextField, + TextFieldProps, + Typography, + CardHeader, +} from "@mui/material"; +import { EventListResults } from "./event-list-results"; +import { DashboardLayout } from "../dashboard-layout"; +import { Refresh as RefreshIcon } from "../../icons/refresh"; +import { Search as SearchIcon } from "../../icons/search"; +import NotificationApi from "../../apis/notification-api"; +import React, { useEffect, useState, useRef } from "react"; + +const applyPagination = (parameters, page, rowsPerPage) => + parameters.slice(page * rowsPerPage, page * rowsPerPage + rowsPerPage); + +export const EventDataTable = (props: { + events: MessageMonitor.Event[]; + onDownload: () => void; + onDownloadJson: () => void; +}) => { + const { events, onDownload, onDownloadJson } = props; + const queryRef = useRef(null); + const [page, setPage] = useState(0); + const [rowsPerPage, setRowsPerPage] = useState(10); + const [currentDescription, setCurrentDescription] = useState(""); + + useEffect(() => { + setPage(0); + }, events); + + const handlePageChange = (event, newPage) => { + setPage(newPage); + }; + + const handleRowsPerPageChange = (event) => { + setRowsPerPage(parseInt(event.target.value, 10)); + }; + + // Usually query is done on backend with indexing solutions + const paginatedNotifications = [...applyPagination(events, page, rowsPerPage)]; + + return ( + <> + + + <> + + + + + + + + + + + + + + + + + + ); +}; diff --git a/webapp/src/components/intersection/data-selector/event-list-results.tsx b/webapp/src/components/intersection/data-selector/event-list-results.tsx new file mode 100644 index 000000000..1b9a8e854 --- /dev/null +++ b/webapp/src/components/intersection/data-selector/event-list-results.tsx @@ -0,0 +1,86 @@ +import PerfectScrollbar from "react-perfect-scrollbar"; +import PropTypes from "prop-types"; +import { format } from "date-fns"; +import NextLink from "next/link"; +import { + Box, + Card, + IconButton, + Table, + TableBody, + TableCell, + TableHead, + TablePagination, + TableRow, + Typography, +} from "@mui/material"; +import React from "react"; +import MapRoundedIcon from "@mui/icons-material/MapRounded"; + +export const EventListResults = ({ events, eventsCount, onPageChange, onRowsPerPageChange, page, rowsPerPage }) => { + const getEventDescription = (event: MessageMonitor.Event) => { + return JSON.stringify(event).replace(/,/g, ", "); + }; + + return ( + + + + + + + Event Type + Date + Open Map + Message + + + + {events.map((event: MessageMonitor.Event, i: number) => { + return ( + + + + + {event.eventType} + + + + {format(event.eventGeneratedAt, "MM/dd/yyyy HH:mm:ss")} + + + + + + + + {getEventDescription(event)} + + ); + })} + +
+
+
+ +
+ ); +}; + +EventListResults.propTypes = { + events: PropTypes.array.isRequired, + onSelectedItemsChanged: PropTypes.func, +}; diff --git a/webapp/src/components/intersection/data-selector/formik-checkbox-list.tsx b/webapp/src/components/intersection/data-selector/formik-checkbox-list.tsx new file mode 100644 index 000000000..c122125de --- /dev/null +++ b/webapp/src/components/intersection/data-selector/formik-checkbox-list.tsx @@ -0,0 +1,46 @@ +import PropTypes from "prop-types"; +import { Checkbox } from "@mui/material"; + +export const FormikCheckboxList = (props) => { + const { values, selectedValues, setValues } = props; + + return values.map((eventType) => ( +
+ -1} + onChange={(e) => { + const newEventTypes = [...selectedValues]; + // if value is All, check or uncheck all + if (eventType.label === "All") { + if (e.target.checked) { + newEventTypes.push(...values); + } else { + newEventTypes.splice(0, newEventTypes.length); + } + } else { + // if value is not All, uncheck All + const index = newEventTypes.findIndex((val) => val.label === "All"); + if (index > -1) { + newEventTypes.splice(index, 1); + } + + if (e.target.checked) { + newEventTypes.push(eventType); + } else { + newEventTypes.splice(newEventTypes.indexOf(eventType), 1); + } + } + setValues(newEventTypes); + }} + /> + {eventType.label} +
+ )); +}; + +FormikCheckboxList.propTypes = { + values: PropTypes.array.isRequired, + selectedValues: PropTypes.array.isRequired, + setValues: PropTypes.func.isRequired, +}; diff --git a/webapp/src/components/intersection/graphs/graph-query-edit-form.tsx b/webapp/src/components/intersection/graphs/graph-query-edit-form.tsx new file mode 100644 index 000000000..07e4bd44f --- /dev/null +++ b/webapp/src/components/intersection/graphs/graph-query-edit-form.tsx @@ -0,0 +1,293 @@ +import NextLink from "next/link"; +import { useRouter } from "next/router"; +import PropTypes from "prop-types"; +import toast from "react-hot-toast"; +import * as Yup from "yup"; +import { useFormik } from "formik"; +import dayjs, { Dayjs } from "dayjs"; +import { AdapterDayjs } from "@mui/x-date-pickers/AdapterDayjs"; +import { LocalizationProvider } from "@mui/x-date-pickers/LocalizationProvider"; +import { DateTimePicker } from "@mui/x-date-pickers/DateTimePicker"; +import { Theme, useTheme } from "@mui/material/styles"; +import { + Autocomplete, + Button, + Box, + Card, + CardActions, + CardContent, + CardHeader, + Chip, + Divider, + Grid, + TextField, + InputLabel, + MenuItem, + Select, + InputAdornment, + OutlinedInput, + Checkbox, +} from "@mui/material"; +import CheckBoxOutlineBlankIcon from "@mui/icons-material/CheckBoxOutlineBlank"; +import CheckBoxIcon from "@mui/icons-material/CheckBox"; +import { FormikCheckboxList } from "../data-selector/formik-checkbox-list"; + +interface Item { + label: string; + value: string; +} + +const EVENT_TYPES: Item[] = [ + { label: "All", value: "All" }, + { label: "ConnectionOfTravelEvent", value: "connection_of_travel" }, + { label: "IntersectionReferenceAlignmentEvent", value: "intersection_reference_alignment" }, + { label: "LaneDirectionOfTravelEvent", value: "lane_direction_of_travel" }, + // { label: "ProcessingTimePeriod", value: "processing_time_period" }, + { label: "SignalGroupAlignmentEvent", value: "signal_group_alignment" }, + { label: "SignalStateConflictEvent", value: "signal_state_conflict" }, + { label: "SignalStateEvent", value: "signal_state" }, + { label: "SignalStateStopEvent", value: "signal_state_stop" }, + { label: "TimeChangeDetailsEvent", value: "time_change_details" }, + // { label: "MapMinimumDataEvent", value: "All" }, + // { label: "SpatMinimumDataEvent", value: "All" }, + // { label: "MapBroadcastRateEvent", value: "All" }, + // { label: "SpatBroadcastRateEvent", value: "All" }, +]; + +const ASSESSMENT_TYPES: Item[] = [ + { label: "All", value: "All" }, + { label: "SignalStateEventAssessment", value: "signal_state_event_assessment" }, + { label: "SignalStateAssessment", value: "signal_state_assessment" }, + { label: "LaneDirectionOfTravelAssessment", value: "lane_direction_of_travel" }, + { label: "ConnectionOfTravelAssessment", value: "connection_of_travel" }, +]; + +export const GraphQueryEditForm = (props) => { + const { onQuery, dbIntersectionId, ...other } = props; + const router = useRouter(); + const formik = useFormik({ + initialValues: { + type: "EVENT", + startDate: new Date(), + timeRange: 0, + intersectionId: dbIntersectionId, + roadRegulatorId: -1, + submit: null, + + // type specific filters + bsmVehicleId: null, + eventTypes: [] as Item[], + assessmentTypes: [] as Item[], + }, + validationSchema: Yup.object({ + // type: Yup.string().required("Type is required"), + // startDate: Yup.date().required("Start date is required"), + // timeRange: Yup.number().required("Time interval is required"), + // intersectionId: Yup.string().required("Intersection ID is required"), + // roadRegulatorId: Yup.string().required("Road Regulator ID is required"), + // bsmVehicleId: Yup.string(), + }), + onSubmit: async (values, helpers) => { + try { + helpers.setStatus({ success: true }); + helpers.setSubmitting(false); + onQuery({ + type: values.type, + intersectionId: values.intersectionId, + roadRegulatorId: values.roadRegulatorId, + startDate: values.startDate, + timeRange: values.timeRange, + eventTypes: values.eventTypes.map((e) => e.value).filter((e) => e !== "All"), + assessmentTypes: values.assessmentTypes.map((e) => e.value).filter((e) => e !== "All"), + bsmVehicleId: values.bsmVehicleId, + }); + } catch (err) { + console.error(err); + toast.error("Something went wrong!"); + helpers.setStatus({ success: false }); + helpers.setErrors({ submit: err.message }); + helpers.setSubmitting(false); + } + }, + }); + + const onTypeChange = (newType) => { + formik.setFieldValue("eventTypes", [] as Item[]); + formik.setFieldValue("assessmentTypes", [] as Item[]); + }; + + const getTypeSpecificFilters = (type) => { + switch (type) { + case "bsm": + return ( + <> + + + + + ); + case "events": + return ( + <> + + + Event Type + + formik.setFieldValue("eventTypes", val)} + /> + + + ); + case "assessments": + return ( + <> + + + Assessment Type + + formik.setFieldValue("assessmentTypes", val)} + /> + + + ); + default: + return <>; + } + }; + + return ( +
+ + {/* */} + + + + + + + + + + + {/* + Query Type + */} + + + + + ( + + )} + value={formik.values.startDate} + onChange={(e) => formik.setFieldValue("startDate", (e as Dayjs | null)?.toDate(), true)} + /> + + + + minutes, + }} + value={formik.values.timeRange} + /> + + {getTypeSpecificFilters(formik.values.type)} + + + + + + + + + +
+ ); +}; + +GraphQueryEditForm.propTypes = { + onQuery: PropTypes.func.isRequired, + dbIntersectionId: PropTypes.number, +}; diff --git a/webapp/src/components/intersection/intersection-selector/intersection-selection-map.tsx b/webapp/src/components/intersection/intersection-selector/intersection-selection-map.tsx new file mode 100644 index 000000000..3c9b953cf --- /dev/null +++ b/webapp/src/components/intersection/intersection-selector/intersection-selection-map.tsx @@ -0,0 +1,194 @@ +import React, { useState, useEffect } from 'react' +import Map, { Layer, MapRef, Marker, Popup, Source, SymbolLayer } from 'react-map-gl' +import mbStyle from '../../../styles/intersectionMapStyle.json' + +import { Container, Col } from 'reactstrap' +import EnvironmentVars from '../../../EnvironmentVars' + +const getBoundsForIntersections = ( + selectedIntersection: IntersectionReferenceData | undefined, + intersections: IntersectionReferenceData[] +) => { + let bounds = { + xMin: -105.0907089, + xMax: -105.0907089, + yMin: 39.587905, + yMax: 39.587905, + } + if (selectedIntersection != undefined) { + bounds = { + xMin: selectedIntersection.longitude, + xMax: selectedIntersection.longitude, + yMin: selectedIntersection.latitude, + yMax: selectedIntersection.latitude, + } + } else if (intersections.length >= 1) { + bounds = { + xMin: intersections[0].longitude, + xMax: intersections[0].longitude, + yMin: intersections[0].latitude, + yMax: intersections[0].latitude, + } + } + + var latitude: number, longitude: number + + for (var i = 0; i < intersections.length; i++) { + longitude = intersections[i].longitude + latitude = intersections[i].latitude + if (longitude >= bounds.xMin && longitude <= bounds.xMax && latitude <= bounds.yMin && latitude >= bounds.yMax) { + if (bounds.xMin === undefined) { + bounds = { + xMin: longitude, + xMax: longitude, + yMin: latitude, + yMax: latitude, + } + } else { + bounds.xMin = longitude < bounds.xMin ? longitude : bounds.xMin + bounds.xMax = longitude > bounds.xMax ? longitude : bounds.xMax + bounds.yMin = latitude < bounds.yMin ? latitude : bounds.yMin + bounds.yMax = latitude > bounds.yMax ? latitude : bounds.yMax + } + } + } + + return [bounds.xMin, bounds.yMin, bounds.xMax, bounds.yMax] +} + +const zoomToBounds = (mapRef: React.RefObject, bounds: number[], padding: number = 50) => { + if (bounds) { + const [long1, lat1, long2, lat2] = bounds + mapRef?.current?.fitBounds( + [ + [long1, lat1], + [long2, lat2], + ], + { + padding: { + top: padding, + bottom: padding, + left: padding, + right: padding + 300, + }, + animate: true, + duration: 1000, + maxZoom: 15, + } + ) + } +} + +const intersectionLabelsLayer: SymbolLayer = { + id: 'intersection-labels', + type: 'symbol', + layout: { + 'text-field': ['to-string', ['get', 'intersectionName']], + 'text-size': 20, + 'text-offset': [0, 2], + 'text-variable-anchor': ['top', 'left', 'right', 'bottom'], + 'text-allow-overlap': true, + 'icon-text-fit': 'both', + }, + paint: { + 'text-color': '#000000', + 'text-halo-color': '#ffffff', + 'text-halo-width': 5, + }, +} + +type Props = { + intersections: IntersectionReferenceData[] + selectedIntersection: IntersectionReferenceData | undefined + onSelectIntersection: (id: number, roadRegulatorId?: number) => void +} + +const IntersectionMap = (props: Props) => { + const [selectedIntersection, setSelectedIntersection] = useState( + props.selectedIntersection + ) + const [viewState, setViewState] = useState({ + latitude: props.selectedIntersection?.latitude ?? 39.587905, + longitude: props.selectedIntersection?.longitude ?? -105.0907089, + zoom: 11, + }) + const myRef = React.createRef() + + const viewBounds = getBoundsForIntersections(selectedIntersection, props.intersections) + useEffect(() => { + zoomToBounds(myRef, viewBounds) + }, []) + + const markers = props.intersections + .filter((intersection) => intersection.latitude != 0) + .map((intersection) => { + return ( + { + e.originalEvent.preventDefault() + props.onSelectIntersection(intersection.intersectionID, intersection.roadRegulatorID) + setSelectedIntersection(intersection) + }} + > + + + ) + }) + + return ( + + + © CDOT']} + styleDiffing + style={{ width: '100%', height: '100%' }} + onMove={(evt) => setViewState(evt.viewState)} + onLoad={() => { + zoomToBounds(myRef, viewBounds) + }} + > + {markers} + {selectedIntersection && ( + +
SELECTED {selectedIntersection.intersectionID}
+
+ )} + ({ + type: 'Feature', + properties: { + intersectionId: intersection.intersectionID, + intersectionName: intersection.intersectionID, + }, + geometry: { + type: 'Point', + coordinates: [intersection.longitude, intersection.latitude], + }, + })), + }} + > + + +
+ +
+ ) +} + +export default IntersectionMap diff --git a/webapp/src/components/intersection/intersection-selector/intersection-selector-dialog.tsx b/webapp/src/components/intersection/intersection-selector/intersection-selector-dialog.tsx new file mode 100644 index 000000000..f2ab6a6f0 --- /dev/null +++ b/webapp/src/components/intersection/intersection-selector/intersection-selector-dialog.tsx @@ -0,0 +1,47 @@ +import { Dialog, DialogTitle, Container, DialogActions, Button } from '@mui/material' +import IntersectionMap from './intersection-selection-map' +import { useSelector } from 'react-redux' +import { + selectSelectedIntersectionId, + selectSelectedRoadRegulatorId, + setSelectedIntersectionId, +} from '../../../generalSlices/intersectionSlice' +import React from 'react' + +type Props = { + onClose: () => void + open: boolean + intersections: IntersectionReferenceData[] +} + +const MapDialog = (props: Props) => { + const { onClose, intersections, open } = props + + const intersectionId = useSelector(selectSelectedIntersectionId) + + const handleClose = () => { + onClose() + } + + return ( + <> + + Select Intersection + + e.intersectionID == intersectionId)} + onSelectIntersection={(intersectionId, _) => setSelectedIntersectionId(intersectionId)} + /> + + + + + + + ) +} + +export default MapDialog diff --git a/webapp/src/components/intersectionMap/control-panel.tsx b/webapp/src/components/intersection/map/control-panel.tsx similarity index 100% rename from webapp/src/components/intersectionMap/control-panel.tsx rename to webapp/src/components/intersection/map/control-panel.tsx diff --git a/webapp/src/components/intersectionMap/custom-table.css b/webapp/src/components/intersection/map/custom-table.css similarity index 100% rename from webapp/src/components/intersectionMap/custom-table.css rename to webapp/src/components/intersection/map/custom-table.css diff --git a/webapp/src/components/intersectionMap/custom-table.tsx b/webapp/src/components/intersection/map/custom-table.tsx similarity index 100% rename from webapp/src/components/intersectionMap/custom-table.tsx rename to webapp/src/components/intersection/map/custom-table.tsx diff --git a/webapp/src/components/intersectionMap/expandable-table.tsx b/webapp/src/components/intersection/map/expandable-table.tsx similarity index 100% rename from webapp/src/components/intersectionMap/expandable-table.tsx rename to webapp/src/components/intersection/map/expandable-table.tsx diff --git a/webapp/src/components/intersectionMap/map-component.tsx b/webapp/src/components/intersection/map/map-component.tsx similarity index 98% rename from webapp/src/components/intersectionMap/map-component.tsx rename to webapp/src/components/intersection/map/map-component.tsx index e4c802e51..5b8fbb691 100644 --- a/webapp/src/components/intersectionMap/map-component.tsx +++ b/webapp/src/components/intersection/map/map-component.tsx @@ -9,7 +9,7 @@ import ControlPanel from './control-panel' import { SidePanel } from './side-panel' import { CustomPopup } from './popup' import { useDispatch, useSelector } from 'react-redux' -import { selectToken } from '../../generalSlices/userSlice' +import { selectToken } from '../../../generalSlices/userSlice' import { selectBsmLayerStyle, selectConnectingLanesLabelsLayerStyle, @@ -68,13 +68,13 @@ import { updateRenderTimeInterval, updateRenderedMapState, } from './map-slice' -import EnvironmentVars from '../../EnvironmentVars' +import EnvironmentVars from '../../../EnvironmentVars' import { addConnections, createMarkerForNotification } from './utilities/message-utils' import { AnyAction, ThunkDispatch } from '@reduxjs/toolkit' -import { RootState } from '../../store' +import { RootState } from '../../../store' import { MapLegend } from './map-legend' -import { selectSelectedSrm } from '../../generalSlices/rsuSlice' -import mbStyle from '../../styles/intersectionMapStyle.json' +import { selectSelectedSrm } from '../../../generalSlices/rsuSlice' +import mbStyle from '../../../styles/intersectionMapStyle.json' const generateQueryParams = (source: MAP_PROPS['sourceData'], sourceDataType: MAP_PROPS['sourceDataType']) => { const startOffset = 1000 * 60 * 1 diff --git a/webapp/src/components/intersectionMap/map-layer-style-slice.ts b/webapp/src/components/intersection/map/map-layer-style-slice.ts similarity index 100% rename from webapp/src/components/intersectionMap/map-layer-style-slice.ts rename to webapp/src/components/intersection/map/map-layer-style-slice.ts diff --git a/webapp/src/components/intersectionMap/map-legend.tsx b/webapp/src/components/intersection/map/map-legend.tsx similarity index 100% rename from webapp/src/components/intersectionMap/map-legend.tsx rename to webapp/src/components/intersection/map/map-legend.tsx diff --git a/webapp/src/components/intersectionMap/map-slice.tsx b/webapp/src/components/intersection/map/map-slice.tsx similarity index 100% rename from webapp/src/components/intersectionMap/map-slice.tsx rename to webapp/src/components/intersection/map/map-slice.tsx diff --git a/webapp/src/components/intersectionMap/popup.tsx b/webapp/src/components/intersection/map/popup.tsx similarity index 100% rename from webapp/src/components/intersectionMap/popup.tsx rename to webapp/src/components/intersection/map/popup.tsx diff --git a/webapp/src/components/intersectionMap/side-panel.tsx b/webapp/src/components/intersection/map/side-panel.tsx similarity index 100% rename from webapp/src/components/intersectionMap/side-panel.tsx rename to webapp/src/components/intersection/map/side-panel.tsx diff --git a/webapp/src/components/intersectionMap/utilities/colors.ts b/webapp/src/components/intersection/map/utilities/colors.ts similarity index 100% rename from webapp/src/components/intersectionMap/utilities/colors.ts rename to webapp/src/components/intersection/map/utilities/colors.ts diff --git a/webapp/src/components/intersectionMap/utilities/file-utilities.ts b/webapp/src/components/intersection/map/utilities/file-utilities.ts similarity index 100% rename from webapp/src/components/intersectionMap/utilities/file-utilities.ts rename to webapp/src/components/intersection/map/utilities/file-utilities.ts diff --git a/webapp/src/components/intersectionMap/utilities/map-utils.ts b/webapp/src/components/intersection/map/utilities/map-utils.ts similarity index 100% rename from webapp/src/components/intersectionMap/utilities/map-utils.ts rename to webapp/src/components/intersection/map/utilities/map-utils.ts diff --git a/webapp/src/components/intersectionMap/utilities/message-utils.ts b/webapp/src/components/intersection/map/utilities/message-utils.ts similarity index 100% rename from webapp/src/components/intersectionMap/utilities/message-utils.ts rename to webapp/src/components/intersection/map/utilities/message-utils.ts diff --git a/webapp/src/components/intersection/message-counts/message-count-week-chart.tsx b/webapp/src/components/intersection/message-counts/message-count-week-chart.tsx new file mode 100644 index 000000000..e2e54b5b4 --- /dev/null +++ b/webapp/src/components/intersection/message-counts/message-count-week-chart.tsx @@ -0,0 +1,171 @@ +import { + Card, + Typography, + CardHeader, + CardContent, + Box, +} from "@mui/material"; +import React, { useEffect, useState} from "react"; +import MessageMonitorApi from '../../apis/mm-api'; +import {LocalizationProvider } from '@mui/x-date-pickers'; +import AdapterDateFns from '@date-io/date-fns'; +import { LineChart, Line, XAxis, YAxis, ResponsiveContainer, Tooltip, TooltipProps } from 'recharts'; +import { NameType, ValueType } from "recharts/types/component/DefaultTooltipContent"; +import CircularProgress from '@mui/material/CircularProgress'; + +export const MessageCountWeekChart = (props: { + accessToken: string | undefined; + intersectionId: number; + messageType: string; + messageLabel: string; + barColor: string; + disclaimer: string | undefined; +}) => { + const { accessToken, intersectionId, messageType, messageLabel, barColor, disclaimer } = props; + + type ChartData = { date: string; count: number; dayOfWeek: string }; + + const [messageCounts, setMessageCounts] = useState([]); + const [loading, setLoading] = useState(true); + let promises: Promise<{ date: string; count: number; dayOfWeek: string; }>[] = []; + +useEffect(() => { + if (accessToken) { + setLoading(true); + const weekCounts: ChartData[] = []; + + for (let i = 0; i < 7; i++) { + const dayStart = new Date(); + dayStart.setDate(dayStart.getDate() - (6-i)); + dayStart.setHours(0, 0, 0, 0); + + const dayEnd = new Date(); + dayEnd.setDate(dayEnd.getDate() - (6-i)); + dayEnd.setHours(23, 59, 59, 0); + + const messageCountPromise = MessageMonitorApi.getMessageCount( + accessToken, + messageType, + intersectionId, + dayStart, + dayEnd + ); + promises.push( + messageCountPromise.then((count) => { + return { + date: dayStart.toLocaleDateString(undefined, { month: '2-digit', day: '2-digit' }), + count: count, + dayOfWeek: dayStart.toLocaleDateString(undefined, { weekday: 'long' }) + }; + }) + ); + } + + Promise.all(promises) + .then((weekCounts) => { + setMessageCounts(weekCounts); + setLoading(false); + }) + .catch((error) => console.error(error)); + + } +}, [intersectionId]); + +const CustomTooltip = ({ active, payload }: TooltipProps) => { + if (active && payload) { + const obj = payload[0].payload; + return ( +
+ {obj.dayOfWeek} {obj.date} +

{obj.count.toLocaleString()} messages

+
+
+
+ ); + } + return null; +}; + +//Reduce y axis for small graphs +const maxCount = Math.max(...messageCounts.map(data => data.count)); +const tickCount = maxCount < 5 ? maxCount + 1 : undefined; + +const hasData = messageCounts.length > 0 && !messageCounts.every(({ count }) => count === 0); + +return ( + + + + {`Seven-day ${messageLabel} trend`} + + } + sx={{ pb: 0 }} + /> + + + {loading ? ( + + + + ) : hasData ? ( + + + { + if (value >= 1000000) { + return `${value / 1000000}M`; + } else if (value >= 1000) { + return `${value / 1000}K`; + } else { + return value; + } + }} + /> + + + + ) : ( + + No Data + + )} + + + + {disclaimer && hasData && ( + + *{disclaimer} + + )} + + + +); +}; diff --git a/webapp/src/components/intersection/message-counts/message-count-widget.tsx b/webapp/src/components/intersection/message-counts/message-count-widget.tsx new file mode 100644 index 000000000..0c842b73e --- /dev/null +++ b/webapp/src/components/intersection/message-counts/message-count-widget.tsx @@ -0,0 +1,166 @@ +import { + Box, + Card, + Container, + Grid, + TextField, + Typography, + CardHeader, + CardContent, +} from "@mui/material"; +import React, { useEffect, useState } from "react"; +import MessageMonitorApi from '../../apis/mm-api'; +import { DateTimePicker, LocalizationProvider } from '@mui/x-date-pickers'; +import AdapterDateFns from '@date-io/date-fns'; + +export const MessageCountWidget = (props: { + accessToken: string | undefined; + intersectionId: number; +}) => { + const { accessToken, intersectionId } = props; + + const [startDate, setStartDate] = useState(new Date()); + const [endDate, setEndDate] = useState(new Date()); + const [bsmCount, setBsmCount] = useState(0); + const [spatCount, setSpatCount] = useState(0); + const [mapCount, setMapCount] = useState(0); + + useEffect(() => { + const dayStart = new Date(); + dayStart.setHours(0, 0, 0, 0); + setStartDate(dayStart); + const dayEnd = new Date(); + dayEnd.setHours(23, 59, 59, 0); + setEndDate(dayEnd); + }, []); + + useEffect(() => { + if (accessToken) { + const bsmCountPromise = MessageMonitorApi.getMessageCount( + accessToken, + "bsm", + intersectionId, + startDate, + endDate + ); + bsmCountPromise.then((count) => setBsmCount(count)) + .catch(error => console.error(error)); + + const spatCountPromise = MessageMonitorApi.getMessageCount( + accessToken, + "spat", + intersectionId, + startDate, + endDate + ); + spatCountPromise.then((count) => setSpatCount(count)) + .catch(error => console.error(error)); + + const mapCountPromise = MessageMonitorApi.getMessageCount( + accessToken, + "map", + intersectionId, + startDate, + endDate + ); + mapCountPromise.then((count) => setMapCount(count)) + .catch(error => console.error(error)); + } + + }, [startDate, endDate, intersectionId]); + + return ( + + + + + + + + {`Select time range`} + + } + sx={{ pb: 0 }} + /> + + setStartDate(newStartDate || new Date())} + renderInput={(params) => } + /> + setEndDate(newEndDate || new Date())} + renderInput={(params) => } + /> + + + + + + + + + + {`BSM Count`} + + } + sx={{ pb: 0 }} + /> + + + {bsmCount !== 0 ? bsmCount : "-"} + + + + + + + + + {`SPAT Count`} + + } + sx={{ pb: 0 }} + /> + + + {spatCount !== 0 ? spatCount : "-"} + + + + + + + + + {`MAP Count`} + + } + sx={{ pb: 0 }} + /> + + + {mapCount !== 0 ? mapCount : "-"} + + + + + + + + + + + ); +}; \ No newline at end of file diff --git a/webapp/src/components/intersection/notifications.tsx b/webapp/src/components/intersection/notifications.tsx new file mode 100644 index 000000000..893607228 --- /dev/null +++ b/webapp/src/components/intersection/notifications.tsx @@ -0,0 +1,22 @@ +import { Box } from '@mui/material' +import { NotificationsTable } from './notifications/notifications-table' +import { DashboardLayout } from './dashboard-layout' +import React from 'react' + +const Page = () => { + return ( + + + + ) +} + +Page.getLayout = (page) => {page} + +export default Page diff --git a/webapp/src/components/intersection/notifications/customer-list-toolbar.tsx b/webapp/src/components/intersection/notifications/customer-list-toolbar.tsx new file mode 100644 index 000000000..65daf5ba6 --- /dev/null +++ b/webapp/src/components/intersection/notifications/customer-list-toolbar.tsx @@ -0,0 +1,54 @@ +import { + Box, + Button, + Card, + CardContent, + TextField, + InputAdornment, + SvgIcon, + Typography, +} from "@mui/material"; +import { Search as SearchIcon } from "../../icons/search"; +import { Upload as UploadIcon } from "../../icons/upload"; +import { Download as DownloadIcon } from "../../icons/download"; +import React from "react"; + +export const CustomerListToolbar = (props) => ( + + + + Notifications + + + + + + + + + + + + ), + }} + placeholder="Search customer" + variant="outlined" + /> + + + + + +); diff --git a/webapp/src/components/intersection/notifications/notifications-table-results.tsx b/webapp/src/components/intersection/notifications/notifications-table-results.tsx new file mode 100644 index 000000000..2622d856c --- /dev/null +++ b/webapp/src/components/intersection/notifications/notifications-table-results.tsx @@ -0,0 +1,280 @@ +import PerfectScrollbar from "react-perfect-scrollbar"; +import PropTypes from "prop-types"; +import { format } from "date-fns"; +import NextLink from "next/link"; +import { + Box, + Card, + Checkbox, + Table, + TableBody, + TableCell, + TableHead, + TablePagination, + TableRow, + Typography, + IconButton, + TableContainer, + Collapse, +} from "@mui/material"; +import React, { ReactElement } from "react"; +import MapRoundedIcon from "@mui/icons-material/MapRounded"; +import KeyboardArrowDownIcon from "@mui/icons-material/KeyboardArrowDown"; +import KeyboardArrowUpIcon from "@mui/icons-material/KeyboardArrowUp"; + +export const NotificationsTableResults = ({ + customers, + allTabNotifications, + notificationsCount, + selectedNotifications, + onSelectedItemsChanged, + expandedNotifications, + onExpandedItemsChanged, + onPageChange, + onRowsPerPageChange, + page, + rowsPerPage, +}) => { + const handleSelectAll = (event) => { + let newSelectedCustomerIds: string[] = []; + if (notificationsCount === 0) return; + if (event.target.checked) { + newSelectedCustomerIds = allTabNotifications.map((notification) => notification.key); + } else { + newSelectedCustomerIds = []; + } + + onSelectedItemsChanged(newSelectedCustomerIds); + }; + + const handleSelectOne = (event, notificationId: string) => { + if (!selectedNotifications.includes(notificationId)) { + onSelectedItemsChanged((prevSelected: string[]) => [...prevSelected, notificationId]); + } else { + onSelectedItemsChanged((prevSelected: string[]) => prevSelected.filter((key: string) => key !== notificationId)); + } + }; + + const handleExpandOne = (notificationId: string) => { + if (!expandedNotifications.includes(notificationId)) { + onExpandedItemsChanged((prevExpanded: string[]) => [...prevExpanded, notificationId]); + } else { + onExpandedItemsChanged((prevExpanded: string[]) => prevExpanded.filter((key: string) => key !== notificationId)); + } + }; + + const getDescriptionTextForNotification = (notification: MessageMonitor.Notification): ReactElement => { + switch (notification.notificationType) { + case "ConnectionOfTravelNotification": + const connectionOfTravelNotification = notification as ConnectionOfTravelNotification; + return ( + + {"Associated Assessment Data"} +
+ {connectionOfTravelNotification.assessment.connectionOfTravelAssessmentGroups.map((assessmentGroup) => ( + <> + {`- Ingress lane: ${assessmentGroup.ingressLaneID}, egress lane: ${assessmentGroup.egressLaneID}, connectionId: ${assessmentGroup.connectionID}, eventCount: ${assessmentGroup.eventCount}`} +
+ + ))} +
+ ); + case "IntersectionReferenceAlignmentNotification": + const intersectionReferenceAlignmentNotification = notification as IntersectionReferenceAlignmentNotification; + const intersectionReferenceAlignmentEvent = intersectionReferenceAlignmentNotification.event; + const mapArr = Array.from(intersectionReferenceAlignmentEvent.mapRegulatorIntersectionIds) ?? []; + const spatArr = Array.from(intersectionReferenceAlignmentEvent.spatRegulatorIntersectionIds) ?? []; + return ( + + {`- Intersection IDs, MAP: ${mapArr.map((v) => v.intersectionId)}, SPAT: ${spatArr.map( + (v) => v.intersectionId + )}`} +
+ {`- Road Regulator IDs, MAP: ${mapArr.map((v) => v.roadRegulatorId)}, SPAT: ${spatArr.map( + (v) => v.roadRegulatorId + )}`} +
+ ); + case "LaneDirectionOfTravelAssessmentNotification": + const laneDirTravelNotification = notification as LaneDirectionOfTravelNotification; + const laneDirTravelAssessmentGroups = laneDirTravelNotification.assessment.laneDirectionOfTravelAssessmentGroup; + return ( + + {"Associated Assessment Data"} +
+ {laneDirTravelAssessmentGroups.map((assessmentGroup) => { + const numEvents = assessmentGroup.inToleranceEvents + assessmentGroup.outOfToleranceEvents; + const eventsRatio = assessmentGroup.inToleranceEvents / numEvents; + assessmentGroup.distanceFromCenterlineTolerance; + return ( + <> + {`- lane ID ${assessmentGroup.laneID}, in tolerance events ${eventsRatio} (${assessmentGroup.inToleranceEvents}/${numEvents})`} +
+ {` - Centerline Distance. Expected: ${assessmentGroup.distanceFromCenterlineTolerance}, Median: ${assessmentGroup.medianCenterlineDistance}, Median in tolerance: ${assessmentGroup.medianInToleranceCenterlineDistance}`} +
+ {` - Heading. Expected: ${assessmentGroup.expectedHeading}, Median: ${assessmentGroup.medianHeading}, Median in tolerance: ${assessmentGroup.medianInToleranceHeading}`} +
+ + ); + })} +
+ ); + case "SignalGroupAlignmentNotification": + const sigGroupAlignmentNotification = notification as SignalGroupAlignmentNotification; + const sigGroupAlignmentEvent = sigGroupAlignmentNotification.event as SignalGroupAlignmentEvent & { + sourceID: string; + spatSignalGroupIds: number[]; + mapSignalGroupIds: number[]; + }; + return ( + + {`Source ID: ${sigGroupAlignmentEvent.sourceID}`} +
+ {`- SPAT Signal Group IDs: ${sigGroupAlignmentEvent.spatSignalGroupIds}`} +
+ {`- MAP Signal Group IDs: ${sigGroupAlignmentEvent.mapSignalGroupIds}`} +
+ ); + case "SignalStateConflictNotification": + const sigStateConflictNotification = notification as SignalStateConflictNotification; + const sigStateConflictEvent = sigStateConflictNotification.event; + return ( + + {`Conflict type: ${sigStateConflictEvent.conflictType}`} +
+ {`- First conflicting signal state: ${sigStateConflictEvent.firstConflictingSignalState} of group: ${sigStateConflictEvent.firstConflictingSignalGroup}`} +
+ {`- Second conflicting signal state: ${sigStateConflictEvent.secondConflictingSignalState} of group: ${sigStateConflictEvent.secondConflictingSignalGroup}`} +
+ ); + case "TimeChangeDetailsNotification": + const timeChangeDetailsNotification = notification as TimeChangeDetailsNotification; + const timeChangeDetailsEvent = timeChangeDetailsNotification.event; + return ( + + {`Signal group: ${timeChangeDetailsEvent.signalGroup}`} +
+ {`- First conflicting timemark: ${timeChangeDetailsEvent.firstConflictingTimemark}, spat timestamp: ${timeChangeDetailsEvent.firstSpatTimestamp}, type: ${timeChangeDetailsEvent.firstTimeMarkType}`} +
+ {`- Second conflicting timemark: ${timeChangeDetailsEvent.secondConflictingTimemark} spat timestamp: ${timeChangeDetailsEvent.secondSpatTimestamp}, type: ${timeChangeDetailsEvent.secondTimeMarkType}`} +
+ ); + case "KafkaStreamsAnomalyNotification": + // No markers for this notification + return No Data; + case "BroadcastRateNotification": + // No markers for this notification + return No Data; + default: + return No Data; + } + }; + + return ( + + + + + + + + + 0 && selectedNotifications.length < notificationsCount + } + onChange={handleSelectAll} + /> + + Notification Type + Date + Message + Actions + + + + {customers.map((notification: MessageMonitor.Notification) => { + const isNotificationSelected = [...selectedNotifications].indexOf(notification.key) !== -1; + const isNotificationExpanded = [...expandedNotifications].indexOf(notification.key) !== -1; + return ( + <> + + + handleExpandOne(notification.key)} + > + {isNotificationExpanded ? : } + + + + handleSelectOne(event, notification.key)} + value="true" + /> + + + + + {notification.notificationType} + + + + {format(notification.notificationGeneratedAt, "MM/dd/yyyy HH:mm:ss")} + {notification.notificationText} + + + + + + + + + + + + + + {getDescriptionTextForNotification(notification)} + + + + + + + ); + })} + +
+
+
+ +
+ ); +}; + +NotificationsTableResults.propTypes = { + customers: PropTypes.array.isRequired, + onSelectedItemsChanged: PropTypes.func, +}; diff --git a/webapp/src/components/intersection/notifications/notifications-table.tsx b/webapp/src/components/intersection/notifications/notifications-table.tsx new file mode 100644 index 000000000..52e086b72 --- /dev/null +++ b/webapp/src/components/intersection/notifications/notifications-table.tsx @@ -0,0 +1,301 @@ +import Head from "next/head"; +import { + Box, + Button, + Card, + Container, + Divider, + Grid, + InputAdornment, + Stack, + Tab, + Tabs, + TextField, + TextFieldProps, + Typography, + CardHeader, +} from "@mui/material"; +import { NotificationsTableResults } from "./notifications-table-results"; +import { DashboardLayout } from "../dashboard-layout"; +import { Refresh as RefreshIcon } from "../../icons/refresh"; +import { Search as SearchIcon } from "../../icons/search"; +import NotificationApi from "../../apis/notification-api"; +import React, { useEffect, useState, useRef } from "react"; +import { useSession } from "next-auth/react"; +import { useDashboardContext } from "../../contexts/dashboard-context"; + +const tabs = [ + { + label: "All", + value: "all", + description: "All Notifications", + }, + { + label: "Cease Broadcast", + value: "CeaseBaroadcast", + description: "Notification Requests to Cease Broadcast of associated messages", + }, +]; + +const applyFilters = (parameters, filter) => + parameters.filter((parameter) => { + if (filter.query) { + let queryMatched = false; + const properties = ["notificationType", "notificationText"]; + properties.forEach((property) => { + if (parameter[property].toLowerCase().includes(filter.query.toLowerCase())) { + queryMatched = true; + } + }); + + if (!queryMatched) { + return false; + } + } + + if (filter.tab === "all") { + return true; + } + + return parameter["notificationType"] == filter.tab; + }); + +const applyPagination = (parameters, page, rowsPerPage) => + parameters.slice(page * rowsPerPage, page * rowsPerPage + rowsPerPage); + +export const NotificationsTable = (props: { simple: Boolean }) => { + const { simple } = props; + const queryRef = useRef(null); + const [notifications, setNotifications] = useState([]); + const [acceptedNotifications, setAcceptedNotifications] = useState([]); + const [expandedNotifications, setExpandedNotifications] = useState([]); + const [currentTab, setCurrentTab] = useState("all"); + const [page, setPage] = useState(0); + const [rowsPerPage, setRowsPerPage] = useState(10); + const [currentDescription, setCurrentDescription] = useState(""); + const [filter, setFilter] = useState({ + query: "", + tab: currentTab, + }); + const { data: session } = useSession(); + const { intersectionId: dbIntersectionId, roadRegulatorId } = useDashboardContext(); + + const updateNotifications = () => { + if (session?.accessToken && dbIntersectionId) { + NotificationApi.getActiveNotifications({ + token: session?.accessToken, + intersectionId: dbIntersectionId, + roadRegulatorId: roadRegulatorId, + }).then((notifs) => setNotifications(notifs)); + } else { + console.error( + "Did not attempt to update notifications. Access token:", + session?.accessToken, + "Intersection ID:", + dbIntersectionId + ); + } + }; + + const dismissNotifications = (ids: string[]) => { + if (session?.accessToken && dbIntersectionId) { + NotificationApi.dismissNotifications({ token: session?.accessToken, ids }); + } else { + console.error( + "Did not attempt to dismiss notifications. Access token:", + session?.accessToken, + "Intersection ID:", + dbIntersectionId + ); + } + }; + + useEffect(() => { + updateNotifications(); + setAcceptedNotifications([]); + }, [dbIntersectionId]); + + useEffect(() => { + updateDescription(); + }, [currentTab]); + + const handleTabsChange = (event, value) => { + const updatedFilter = { ...filter, tab: value }; + setCurrentTab(value); + setFilter(updatedFilter); + setPage(0); + setCurrentTab(value); + }; + + const handleQueryChange = (event) => { + event.preventDefault(); + setFilter((prevState) => ({ + ...prevState, + query: queryRef.current?.value as string, + })); + }; + + const handlePageChange = (event, newPage) => { + setPage(newPage); + }; + + const handleRowsPerPageChange = (event) => { + setRowsPerPage(parseInt(event.target.value, 10)); + }; + + const updateDescription = () => { + for (let i = 0; i < tabs.length; i++) { + if (tabs[i].value === currentTab) { + setCurrentDescription(tabs[i].description); + } + } + }; + + // Usually query is done on backend with indexing solutions + const filteredNotifications = applyFilters(notifications, filter); + const paginatedNotifications = applyPagination(filteredNotifications, page, rowsPerPage); + + return ( + <> + + {!simple && ( + <> + + + + + Notifications + + + + + + + + + + )} + + {!simple && ( + <> + + + {tabs.map((tab) => ( + + ))} + + + + + + + + + ), + }} + placeholder="Search parameters" + /> + + {currentDescription} + + + + )} + + + + + + + + + + + + + + + ); +}; diff --git a/webapp/src/components/intersection/performance-reports.tsx b/webapp/src/components/intersection/performance-reports.tsx new file mode 100644 index 000000000..d6fe2de2d --- /dev/null +++ b/webapp/src/components/intersection/performance-reports.tsx @@ -0,0 +1,213 @@ +import { useState, useEffect, useRef } from 'react' +import { endOfDay, startOfDay } from 'date-fns' +import { Box, Button, FormControlLabel, Stack, Typography } from '@mui/material' +import { styled } from '@mui/material/styles' +import { FilterAlt } from '@mui/icons-material' +import { ReportListFilters } from './reports/report-list-filters' +import { ReportListTable } from './reports/report-list-table' +import ReportsApi, { ReportMetadata } from '../../apis/intersections/reports-api' +import { ReportGenerationDialog } from './reports/report-generation-dialog' +import { selectToken } from '../../generalSlices/userSlice' +import { selectSelectedIntersectionId, selectSelectedRoadRegulatorId } from '../../generalSlices/intersectionSlice' +import { useSelector } from 'react-redux' +import React from 'react' + +const applyPagination = (logs, page, rowsPerPage) => logs.slice(page * rowsPerPage, page * rowsPerPage + rowsPerPage) + +type LogsListInnerProps = { + theme: any + open: boolean +} + +const LogsListInner = styled('div', { shouldForwardProp: (prop) => prop !== 'open' })( + ({ theme, open }: LogsListInnerProps) => ({ + flexGrow: 1, + overflow: 'hidden', + paddingLeft: theme.spacing(3), + paddingRight: theme.spacing(3), + paddingTop: theme.spacing(8), + paddingBottom: theme.spacing(8), + zIndex: 1, + marginLeft: -380, + transition: theme.transitions.create('margin', { + easing: theme.transitions.easing.sharp, + duration: theme.transitions.duration.leavingScreen, + }), + ...(open && { + marginLeft: 0, + transition: theme.transitions.create('margin', { + easing: theme.transitions.easing.easeOut, + duration: theme.transitions.duration.enteringScreen, + }), + }), + }) +) + +const PerformanceReportPage = () => { + const rootRef = useRef(null) + + const authToken = useSelector(selectToken) + + const intersectionId = useSelector(selectSelectedIntersectionId) + const roadRegulatorId = useSelector(selectSelectedRoadRegulatorId) + + const [group, setGroup] = useState(true) + const [logs, setLogs] = useState([]) + const [page, setPage] = useState(0) + const [rowsPerPage, setRowsPerPage] = useState(5) + const [openFilters, setOpenFilters] = useState(true) + const [loading, setLoading] = useState(false) + const [filters, setFilters] = useState({ + query: '', + endDate: new Date(), + startDate: new Date(new Date().getTime() - 7 * 24 * 60 * 60 * 1000), + logLevel: 'ERROR', + customer: [], + }) + const [openReportGenerationDialog, setOpenReportGenerationDialog] = useState(false) + + function sortReportByAge(a: ReportMetadata, b: ReportMetadata) { + if (a.reportGeneratedAt < b.reportGeneratedAt) { + return -1 + } + if (a.reportGeneratedAt > b.reportGeneratedAt) { + return 1 + } + return 0 + } + + const listReports = async ( + start_timestamp: Date, + end_timestamp: Date, + intersectionId: number, + roadRegulatorId: number + ) => { + if (!authToken) { + console.error('Did not attempt to list reports. Access token:', authToken) + setLoading(false) + return + } + try { + setLoading(true) + let data = + (await ReportsApi.listReports({ + token: authToken, + intersectionId, + roadRegulatorId, + startTime: start_timestamp, + endTime: end_timestamp, + })) ?? [] + data = data.sort(sortReportByAge) + setLogs(data) + setLoading(false) + } catch (err) { + console.error(err) + setLoading(false) + } + } + + useEffect( + () => { + setLoading(true) + setTimeout(() => listReports(filters.startDate, filters.endDate, intersectionId, roadRegulatorId), 300) + }, + // eslint-disable-next-line react-hooks/exhaustive-deps + [filters, intersectionId] + ) + + const handleChangeGroup = (event) => { + setGroup(event.target.checked) + } + + const handleToggleFilters = () => { + setOpenFilters((prevState) => !prevState) + } + + const handleChangeFilters = (newFilters) => { + setFilters(newFilters) + setPage(0) + } + + const handleCloseFilters = () => { + setOpenFilters(false) + } + + const handlePageChange = (event, newPage) => { + setPage(newPage) + } + + const handleRowsPerPageChange = (event) => { + setRowsPerPage(parseInt(event.target.value, 10)) + } + + // Usually query is done on backend with indexing solutions + const paginatedLogs = applyPagination(logs, page, rowsPerPage) + + return ( + <> + + + + + + Reports + + + + + + + + + + { + setOpenReportGenerationDialog(false) + }} + /> + + ) +} + +export default PerformanceReportPage diff --git a/webapp/src/components/intersection/reports/report-generation-dialog.tsx b/webapp/src/components/intersection/reports/report-generation-dialog.tsx new file mode 100644 index 000000000..22ca3bc73 --- /dev/null +++ b/webapp/src/components/intersection/reports/report-generation-dialog.tsx @@ -0,0 +1,90 @@ +import React, { useState, useEffect } from "react"; +import { Dialog, DialogTitle, Container, DialogActions, Button, Paper, Box, Typography } from "@mui/material"; + +import { useDashboardContext } from "../../contexts/dashboard-context"; +import { useSession } from "next-auth/react"; +import { ReportRequestEditForm } from "./report-request-edit-form"; +import ReportsApi from "../../apis/reports-api"; +import toast from "react-hot-toast"; + +type ReportGenerationDialogProps = { + onClose: () => void; + open: boolean; +}; + +export const ReportGenerationDialog = (props: ReportGenerationDialogProps) => { + const { intersectionId } = useDashboardContext(); + const { data: session } = useSession(); + + const { onClose, open } = props; + + const handleClose = () => { + onClose(); + }; + + const getReport = async ({ + intersectionId, + roadRegulatorId, + startTime, + endTime, + }: { + intersectionId?: number; + roadRegulatorId?: number; + startTime: Date; + endTime: Date; + }) => { + if (!session?.accessToken || !intersectionId || !roadRegulatorId) { + console.error( + "Did not attempt to generate report. Access token:", + session?.accessToken, + "Intersection ID:", + intersectionId, + "Road Regulator ID:", + roadRegulatorId + ); + return; + } + const promise = ReportsApi.generateReport({ + token: session?.accessToken, + intersectionId, + roadRegulatorId, + startTime, + endTime, + }); + toast.promise(promise, { + loading: "Generating Performance Report", + success: "Successfully Generated Performance Report", + error: "Error Generating Performance Report", + }); + const report = await promise; + const name = `Performance Report ${new Date().toISOString()}.pdf`; + if (report !== undefined) { + downloadPdf(report, name); + } + }; + + const downloadPdf = (contents: Blob, name: string) => { + const url = window.URL.createObjectURL(contents); + const link = document.createElement("a"); + link.href = url; + link.setAttribute("download", name); //or any other extension + document.body.appendChild(link); + link.click(); + }; + + return ( + <> + + Generate Performance Report + + + + + + + + + ); +}; diff --git a/webapp/src/components/intersection/reports/report-list-filters.tsx b/webapp/src/components/intersection/reports/report-list-filters.tsx new file mode 100644 index 000000000..ae3ae0249 --- /dev/null +++ b/webapp/src/components/intersection/reports/report-list-filters.tsx @@ -0,0 +1,192 @@ +import { useRef, useState, useEffect } from "react"; +import PropTypes from "prop-types"; +import { + Box, + Button, + Checkbox, + CircularProgress, + Drawer, + FormControlLabel, + FormGroup, + IconButton, + InputAdornment, + MenuItem, + Select, + Stack, + Switch, + TextField, + Typography, + useMediaQuery, +} from "@mui/material"; +import { styled } from "@mui/material/styles"; +import { DateTimePicker } from "@mui/x-date-pickers/DateTimePicker"; +import { Close } from "@mui/icons-material"; +import NextLink from "next/link"; + +const FiltersDrawerDesktop = styled(Drawer)({ + flexShrink: 0, + width: 380, + "& .MuiDrawer-paper": { + position: "relative", + width: 380, + }, +}); + +const FiltersDrawerMobile = styled(Drawer)({ + maxWidth: "100%", + width: 380, + "& .MuiDrawer-paper": { + height: "calc(100% - 64px)", + maxWidth: "100%", + top: 64, + width: 380, + }, +}); + +export type ReportListFilter = { + startDate: Date | null; + endDate: Date | null; +}; + +interface ReportListFiltersProps { + filters: ReportListFilter; + onChange: (filters: ReportListFilter) => void; + onClose: () => void; + open: boolean; + loading: boolean; + containerRef: any; + setOpenReportGenerationDialog: (open: boolean) => void; +} + +export const ReportListFilters = (props: ReportListFiltersProps) => { + const { containerRef, filters, onChange, onClose, open, loading, setOpenReportGenerationDialog, ...other } = props; + const [currentFilters, setCurrentFilters] = useState(filters); + const [filtersValid, setFiltersValid] = useState([true, ""]); + + useEffect(() => { + updateFiltersValid(); + }, [currentFilters]); + + const startDateChange = (date: Date | null) => { + setCurrentFilters({ ...currentFilters, startDate: date }); + }; + + const endDateChange = (date: Date | null) => { + setCurrentFilters({ ...currentFilters, endDate: date }); + }; + + const updateFilters = () => { + onChange?.(currentFilters); + }; + + const updateFiltersValid = () => { + let filtersValidLocal = true; + const reasons: string[] = []; + if (currentFilters.startDate === null || currentFilters.endDate === null) { + filtersValidLocal = false; + reasons.push("Start Date and End Date must be set"); + } else if (currentFilters.startDate >= currentFilters.endDate) { + filtersValidLocal = false; + reasons.push("Start Date must be before End Date"); + } + // TODO: validate dates + setFiltersValid([filtersValidLocal, reasons.join("\n")]); + if (filtersValidLocal) { + onChange?.(currentFilters); + } + }; + + const content = ( + + + + + + + + Issue date + + + } + value={currentFilters.startDate} + disabled={loading} + /> + } + value={currentFilters.endDate} + disabled={loading} + /> + + {!filtersValid[0] && ( + + Invalid filters: {filtersValid[1]} + + )} + + {loading && ( + + )} + + + + ); + + return ( + + {content} + + ); +}; + +ReportListFilters.propTypes = { + containerRef: PropTypes.any, + filters: PropTypes.object, + onChange: PropTypes.func, + onClose: PropTypes.func, + open: PropTypes.bool, + loading: PropTypes.bool, +}; diff --git a/webapp/src/components/intersection/reports/report-list-table.tsx b/webapp/src/components/intersection/reports/report-list-table.tsx new file mode 100644 index 000000000..8454e3dd8 --- /dev/null +++ b/webapp/src/components/intersection/reports/report-list-table.tsx @@ -0,0 +1,181 @@ +import { Fragment } from "react"; +import NextLink from "next/link"; +import { format } from "date-fns"; +import PropTypes from "prop-types"; +import { + Avatar, + Box, + Button, + IconButton, + Table, + TableBody, + TableCell, + TablePagination, + TableRow, + Typography, +} from "@mui/material"; +import PerfectScrollbar from "react-perfect-scrollbar"; +import { ArrowDownward } from "@mui/icons-material"; +import { useSession } from "next-auth/react"; +import toast from "react-hot-toast"; +import ReportsApi, { ReportMetadata } from "../../apis/reports-api"; + +interface ReportRowProps { + report: ReportMetadata; +} + +const ReportRow = (props: ReportRowProps) => { + const { data: session } = useSession(); + const { report } = props; + + const downloadReport = async (reportName: string) => { + if (!session?.accessToken) { + console.error("Did not attempt to download report. Access token:", session?.accessToken); + return; + } + const promise = ReportsApi.downloadReport({ token: session?.accessToken, reportName }); + toast.promise(promise, { + loading: `Downloading Performance Report ${reportName}`, + success: `Successfully Downloaded Performance Report ${reportName}`, + error: `Error Downloading Performance Report ${reportName}`, + }); + const report = await promise; + const name = `Performance Report ${reportName}.pdf`; + if (report !== undefined) { + downloadPdf(report, name); + } + }; + + const downloadPdf = (contents: Blob, name: string) => { + const url = window.URL.createObjectURL(contents); + const link = document.createElement("a"); + link.href = url; + link.setAttribute("download", name); //or any other extension + document.body.appendChild(link); + link.click(); + }; + + return ( + + theme.transitions.create("box-shadow", { + easing: theme.transitions.easing.easeOut, + }), + "&:hover": { + boxShadow: 8, + }, + "& > td": { + backgroundColor: "background.paper", + borderBottom: 0, + }, + }} + > + + + + + + {report.reportName} + + + + + + + + Report Duration + + {report.reportStartTime && format(new Date(report.reportStartTime), "MMM dd, h:mm:ss a")} -{" "} + {report.reportStopTime && format(new Date(report.reportStopTime), "MMM dd, h:mm:ss a")} + + + + + + Generated At + + {report.reportGeneratedAt && format(new Date(report.reportGeneratedAt), "MMM dd, h:mm:ss a")} + + + + + + + + ); +}; + +interface ReportListTableProps { + group: boolean; + reports: ReportMetadata[]; + reportsCount: number; + onPageChange: (event: React.MouseEvent | null, newPage: number) => void; + onRowsPerPageChange: (event: React.ChangeEvent) => void; + page: number; + rowsPerPage: number; +} + +export const ReportListTable = (props: ReportListTableProps) => { + const { group, reports, reportsCount, onPageChange, onRowsPerPageChange, page, rowsPerPage, ...other } = props; + + return ( +
+ + `0 ${theme.spacing(3)}`, + minWidth: 600, + marginTop: (theme) => `-${theme.spacing(3)}`, + p: "1px", + }} + > + { + + {reports.map((report: ReportMetadata) => ( + + ))} + + } +
+
+ +
+ ); +}; diff --git a/webapp/src/components/intersection/reports/report-request-edit-form.tsx b/webapp/src/components/intersection/reports/report-request-edit-form.tsx new file mode 100644 index 000000000..b3ba3f36f --- /dev/null +++ b/webapp/src/components/intersection/reports/report-request-edit-form.tsx @@ -0,0 +1,139 @@ +import toast from "react-hot-toast"; +import * as Yup from "yup"; +import { useFormik } from "formik"; +import { Dayjs } from "dayjs"; +import { AdapterDayjs } from "@mui/x-date-pickers/AdapterDayjs"; +import { LocalizationProvider } from "@mui/x-date-pickers/LocalizationProvider"; +import { DateTimePicker } from "@mui/x-date-pickers/DateTimePicker"; +import { Button, Card, CardActions, CardContent, Divider, Grid, TextField } from "@mui/material"; + +type Props = { + onGenerateReport: ({ + intersectionId, + roadRegulatorId, + startTime, + endTime, + }: { + intersectionId?: number; + roadRegulatorId?: number; + startTime: Date; + endTime: Date; + }) => void; + dbIntersectionId?: number; +}; + +export const ReportRequestEditForm = (props: Props) => { + const { onGenerateReport, dbIntersectionId } = props; + const formik = useFormik({ + initialValues: { + startDate: new Date(Date.now() - 86400000), //yesterday + endDate: new Date(), + intersectionId: dbIntersectionId, + roadRegulatorId: -1, + submit: null, + }, + validationSchema: Yup.object({ + startDate: Yup.date().required("Start date is required"), + endDate: Yup.date() + .required("End date is required") + .min(Yup.ref("startDate"), "end date must be after start date"), + intersectionId: Yup.string().required("Intersection ID is required"), + // roadRegulatorId: Yup.string().required("Road Regulator ID is required"), + }), + onSubmit: async (values, helpers) => { + try { + helpers.setStatus({ success: true }); + helpers.setSubmitting(false); + onGenerateReport({ + intersectionId: values.intersectionId, + roadRegulatorId: values.roadRegulatorId, + startTime: values.startDate, + endTime: values.endDate, + }); + } catch (err) { + console.error(err); + toast.error("Something went wrong!"); + helpers.setStatus({ success: false }); + helpers.setErrors({ submit: err.message }); + helpers.setSubmitting(false); + } + }, + }); + + return ( +
+ + + + + + + + {/* + + */} + + + ( + + )} + value={formik.values.startDate} + onChange={(e) => formik.setFieldValue("startDate", (e as Dayjs | null)?.toDate(), true)} + disableFuture + /> + + + + + ( + + )} + value={formik.values.endDate} + onChange={(e) => formik.setFieldValue("endDate", (e as Dayjs | null)?.toDate(), true)} + disableFuture + /> + + + + + + + + +
+ ); +}; diff --git a/webapp/src/components/intersection/settings/settings-notifications.tsx b/webapp/src/components/intersection/settings/settings-notifications.tsx new file mode 100644 index 000000000..e05b8000c --- /dev/null +++ b/webapp/src/components/intersection/settings/settings-notifications.tsx @@ -0,0 +1,185 @@ +import { + Box, + Button, + Card, + CardContent, + CardHeader, + Checkbox, + Divider, + FormControlLabel, + Grid, + Radio, + RadioGroup, + Typography, + Switch, +} from "@mui/material"; +import React, { useEffect } from "react"; + +export const SettingsNotifications = (props: { + value?: EmailPreferences; + onSave: (v: EmailPreferences) => void; + userRole: UserRole; +}) => { + const [value, setValue] = React.useState( + props.value ?? { + receiveAnnouncements: true, + notificationFrequency: "ONCE_PER_DAY", + receiveCeaseBroadcastRecommendations: true, + receiveCriticalErrorMessages: true, + receiveNewUserRequests: false, + } + ); + + useEffect(() => { + setValue( + props.value ?? { + receiveAnnouncements: true, + notificationFrequency: "ONCE_PER_DAY", + receiveCeaseBroadcastRecommendations: true, + receiveCriticalErrorMessages: true, + receiveNewUserRequests: false, + } + ); + }, [props.value]); + + return ( +
+ + + + + + + + Notifications Email Rate + + { + setValue((prevValue) => ({ ...prevValue, notificationFrequency: v as EmailFrequency })); + }} + > + } label="Always" /> + } label="Once Per Hour" /> + } label="Once Per Day" /> + } label="Once Per Week" /> + } label="Once Per Month" /> + } label="Never" /> + + + + + Receive Cease Broadcast Recommendation Emails + + + setValue((prevValue) => ({ ...prevValue, receiveCeaseBroadcastRecommendations: v })) + } + inputProps={{ "aria-label": "controlled" }} + /> + + + + Receive Announcement Emails + + setValue((prevValue) => ({ ...prevValue, receiveAnnouncements: v }))} + inputProps={{ "aria-label": "controlled" }} + /> + + + + Receive Critical Error Message Emails + + setValue((prevValue) => ({ ...prevValue, receiveCriticalErrorMessages: v }))} + inputProps={{ "aria-label": "controlled" }} + /> + + {props.userRole === "ADMIN" && ( + + + Receive New User Request Emails (Admins Only) + + setValue((prevValue) => ({ ...prevValue, receiveNewUserRequests: v }))} + inputProps={{ "aria-label": "controlled" }} + /> + + )} + + + + + + + +
+ ); +}; diff --git a/webapp/src/components/intersection/users/user-create-form.tsx b/webapp/src/components/intersection/users/user-create-form.tsx new file mode 100644 index 000000000..bc2f0adea --- /dev/null +++ b/webapp/src/components/intersection/users/user-create-form.tsx @@ -0,0 +1,168 @@ +import NextLink from "next/link"; +import { useRouter } from "next/router"; +import PropTypes from "prop-types"; +import toast from "react-hot-toast"; +import * as Yup from "yup"; +import { useFormik } from "formik"; +import { + Button, + Card, + CardActions, + CardContent, + CardHeader, + Divider, + Grid, + TextField, + Typography, + Select, + MenuItem, +} from "@mui/material"; +import userManagementApi from "../../apis/user-management-api"; +import { useSession } from "next-auth/react"; + +export const UserCreateForm = (props) => { + const { data: session } = useSession(); + const { user }: { user: User } = props; + const router = useRouter(); + const formik = useFormik({ + initialValues: { + email: user.email, + first_name: user.first_name, + last_name: user.last_name, + role: user.role, + submit: null, + }, + validationSchema: Yup.object({ + email: Yup.string().email("Must be a valid email").max(255).required("Username is required"), + first_name: Yup.string(), + last_name: Yup.string(), + }), + onSubmit: async (values, helpers) => { + if (!session?.accessToken) { + toast.error("Not authenticated"); + helpers.setStatus({ success: false }); + helpers.setErrors({ submit: "Not Authenticated" }); + helpers.setSubmitting(false); + console.error("Did not attempt to create user. Access token:", Boolean(session?.accessToken)); + return; + } + try { + await userManagementApi.createUser({ + token: session?.accessToken, + email: values.email, + first_name: values.first_name, + last_name: values.last_name, + role: values.role, + }); + await router + .push({ + pathname: "/users", + query: { returnUrl: router.asPath }, + }) + .catch(console.error); + } catch (err) { + console.error(err); + helpers.setStatus({ success: false }); + helpers.setErrors({ submit: err.message }); + helpers.setSubmitting(false); + } + }, + }); + + return ( +
+ + + + + + + + + + + + + + + + Role + + + + + + {" "} + After creating the user, an email will be send to their email address with instructions to set up a + password. + + + + + + + + + + +
+ ); +}; + +UserCreateForm.propTypes = { + user: PropTypes.object, +}; diff --git a/webapp/src/components/intersection/users/user-creation-request-list-table.tsx b/webapp/src/components/intersection/users/user-creation-request-list-table.tsx new file mode 100644 index 000000000..4a1e9ca24 --- /dev/null +++ b/webapp/src/components/intersection/users/user-creation-request-list-table.tsx @@ -0,0 +1,81 @@ +import PerfectScrollbar from "react-perfect-scrollbar"; +import PropTypes from "prop-types"; +import NextLink from "next/link"; +import { + Box, + Card, + IconButton, + Table, + TableBody, + TableCell, + TableHead, + TablePagination, + TableRow, +} from "@mui/material"; +import React from "react"; +import AddIcon from "@mui/icons-material/Add"; + +export const UserCreationRequestListTable = (props) => { + const { users, parametersCount, onPageChange, onRowsPerPageChange, page, rowsPerPage } = props; + + const generalDefaultRow = (user: User) => { + return ( + + {user.email} + {user.first_name} + {user.last_name} + {user.role} + + + + + + + + + ); + }; + + return ( + + + + + + + Email + First Name + Last Name + Role + Actions + + + + {(users as User[]).map((user) => { + return generalDefaultRow(user); + })} + +
+
+
+ +
+ ); +}; + +UserCreationRequestListTable.propTypes = { + users: PropTypes.array.isRequired, + parametersCount: PropTypes.number.isRequired, + onPageChange: PropTypes.func.isRequired, + onRowsPerPageChange: PropTypes.func, + page: PropTypes.number.isRequired, + rowsPerPage: PropTypes.number.isRequired, +}; diff --git a/webapp/src/components/intersection/users/user-edit-form.tsx b/webapp/src/components/intersection/users/user-edit-form.tsx new file mode 100644 index 000000000..33183d88e --- /dev/null +++ b/webapp/src/components/intersection/users/user-edit-form.tsx @@ -0,0 +1,190 @@ +import NextLink from "next/link"; +import { useRouter } from "next/router"; +import PropTypes from "prop-types"; +import toast from "react-hot-toast"; +import * as Yup from "yup"; +import { useFormik } from "formik"; +import { + Box, + Button, + Card, + CardActions, + CardContent, + CardHeader, + Divider, + Grid, + MenuItem, + Select, + TextField, + Typography, +} from "@mui/material"; +import keycloakApi from "../../apis/keycloak-api"; +import { useSession } from "next-auth/react"; + +export const UserEditForm = (props: { user: User }) => { + const { user, ...other } = props; + const router = useRouter(); + const { data: session } = useSession(); + + const initialValues = { + email: user.email || "", + firstName: user.first_name || "", + lastName: user.last_name || "", + role: user.role || "USER", + submit: null, + }; + + const removeUser = (userId: string) => { + keycloakApi.removeUser({ token: session?.accessToken!, id: userId }); + + router.push("/users").catch(console.error); + }; + + const formik = useFormik({ + initialValues: initialValues, + validationSchema: Yup.object({ + email: Yup.string().email("Must be a valid email").max(255).required("Email is required"), + firstName: Yup.string().max(50), + lastName: Yup.string().max(50), + role: Yup.string().max(50), + }), + onSubmit: async (values, helpers) => { + if (!session?.accessToken) { + toast.error("Not authenticated"); + helpers.setStatus({ success: false }); + helpers.setErrors({ submit: "Not Authenticated" }); + helpers.setSubmitting(false); + console.error("Did not attempt to edit user. Access token:", Boolean(session?.accessToken)); + return; + } + try { + if (values.role !== initialValues.role) { + keycloakApi.removeUserFromGroup({ token: session?.accessToken, id: user.id, role: initialValues.role }); + keycloakApi.addUserToGroup({ token: session?.accessToken, id: user.id, role: values.role }); + } + keycloakApi.updateUserInfo({ + token: session?.accessToken, + id: user.id, + email: values.email == initialValues.email ? undefined : values.email, + first_name: values.firstName == initialValues.firstName ? undefined : values.firstName, + last_name: values.lastName == initialValues.lastName ? undefined : values.lastName, + }); + helpers.setStatus({ success: true }); + helpers.setSubmitting(false); + toast.success("User Profile Updated!"); + router + .push({ + pathname: "/users", + query: { returnUrl: router.asPath }, + }) + .catch(console.error); + } catch (err) { + console.error(err); + toast.error("Something went wrong!"); + helpers.setStatus({ success: false }); + helpers.setErrors({ submit: err.message }); + helpers.setSubmitting(false); + } + }, + }); + + return ( +
+ + + + + + + + + + + + + + + + Role + + + + + + + + + + + + + +
+ ); +}; + +UserEditForm.propTypes = { + user: PropTypes.object.isRequired, +}; diff --git a/webapp/src/components/intersection/users/user-list-table.tsx b/webapp/src/components/intersection/users/user-list-table.tsx new file mode 100644 index 000000000..d2c188eb2 --- /dev/null +++ b/webapp/src/components/intersection/users/user-list-table.tsx @@ -0,0 +1,81 @@ +import PerfectScrollbar from "react-perfect-scrollbar"; +import PropTypes from "prop-types"; +import NextLink from "next/link"; +import { + Box, + Card, + IconButton, + Table, + TableBody, + TableCell, + TableHead, + TablePagination, + TableRow, +} from "@mui/material"; +import React from "react"; +import { PencilAlt as PencilAltIcon } from "../../icons/pencil-alt"; + +export const UserListTable = (props) => { + const { users, parametersCount, onPageChange, onRowsPerPageChange, page, rowsPerPage } = props; + + const generalDefaultRow = (user: User) => { + return ( + + {user.email} + {user.first_name} + {user.last_name} + {user.role} + + + + + + + + + ); + }; + + return ( + + + + + + + Email + First Name + Last Name + Role + Actions + + + + {(users as User[]).map((user) => { + return generalDefaultRow(user); + })} + +
+
+
+ +
+ ); +}; + +UserListTable.propTypes = { + users: PropTypes.array.isRequired, + parametersCount: PropTypes.number.isRequired, + onPageChange: PropTypes.func.isRequired, + onRowsPerPageChange: PropTypes.func, + page: PropTypes.number.isRequired, + rowsPerPage: PropTypes.number.isRequired, +}; diff --git a/webapp/src/features/adminUserTab/AdminUserTab.tsx b/webapp/src/features/adminUserTab/AdminUserTab.tsx index 872409f5a..f9a0e3962 100644 --- a/webapp/src/features/adminUserTab/AdminUserTab.tsx +++ b/webapp/src/features/adminUserTab/AdminUserTab.tsx @@ -1,5 +1,5 @@ import React, { useState, useEffect } from 'react' -import AdminAddUser from '../adminAdduser/AdminAddUser' +import AdminAddUser from '../adminAddUser/AdminAddUser' import AdminEditUser from '../adminEditUser/AdminEditUser' import AdminTable from '../../components/AdminTable' import { IoChevronBackCircleOutline, IoRefresh } from 'react-icons/io5' diff --git a/webapp/src/generalSlices/intersectionSlice.tsx b/webapp/src/generalSlices/intersectionSlice.tsx index 642db777a..5c8909de1 100644 --- a/webapp/src/generalSlices/intersectionSlice.tsx +++ b/webapp/src/generalSlices/intersectionSlice.tsx @@ -1,6 +1,6 @@ import { createAsyncThunk, createSlice, PayloadAction, ThunkDispatch } from '@reduxjs/toolkit' import { RootState } from '../store' -import MessageMonitorApi from '../apis/mm-api' +import MessageMonitorApi from '../apis/intersections/mm-api' import { selectToken } from './userSlice' import { SymbolLayer } from 'react-map-gl' diff --git a/webapp/src/pages/Admin.tsx b/webapp/src/pages/Admin.tsx index ca625b60f..ba35cb523 100644 --- a/webapp/src/pages/Admin.tsx +++ b/webapp/src/pages/Admin.tsx @@ -13,6 +13,7 @@ import AdminRsuTab from '../features/adminRsuTab/AdminRsuTab' import AdminUserTab from '../features/adminUserTab/AdminUserTab' import { NotFound } from './404' import { SecureStorageManager } from '../managers' +import VerticalTabs from '../components/VerticalTabs' interface TabPanelProps { children?: React.ReactNode @@ -57,101 +58,33 @@ function Admin() { ) : (

CV Manager Admin Interface

- - - - - - - - - - - } /> - } /> - } /> - } /> - - } - /> - - - + + } + defaultTabIndex={0} + tabs={[ + { + path: 'rsus', + title: 'RSUs', + child: , + }, + { + path: 'users', + title: 'Users', + child: , + }, + { + path: 'organizations', + title: 'Organizations', + child: , + }, + ]} + />
)} diff --git a/webapp/src/pages/IntersectionMapView.tsx b/webapp/src/pages/IntersectionMapView.tsx index e6c13b386..69f48c087 100644 --- a/webapp/src/pages/IntersectionMapView.tsx +++ b/webapp/src/pages/IntersectionMapView.tsx @@ -1,7 +1,7 @@ import React, { useEffect } from 'react' import { useDispatch, useSelector } from 'react-redux' import { Box, Container } from '@mui/material' -import IntersectionMap from '../components/intersectionMap/map-component' +import IntersectionMap from '../components/intersection/map/map-component' import { getIntersections, selectSelectedIntersectionId, diff --git a/webapp/src/store.tsx b/webapp/src/store.tsx index 0609a7b7e..f162922fa 100644 --- a/webapp/src/store.tsx +++ b/webapp/src/store.tsx @@ -6,7 +6,7 @@ import configReducer from './generalSlices/configSlice' import intersectionReducer from './generalSlices/intersectionSlice' import adminAddOrganizationReducer from './features/adminAddOrganization/adminAddOrganizationSlice' import adminAddRsuReducer from './features/adminAddRsu/adminAddRsuSlice' -import adminAddUserReducer from './features/adminAdduser/adminAddUserSlice' +import adminAddUserReducer from './features/adminAddUser/adminAddUserSlice' import adminEditOrganizationReducer from './features/adminEditOrganization/adminEditOrganizationSlice' import adminEditRsuReducer from './features/adminEditRsu/adminEditRsuSlice' import adminEditUserReducer from './features/adminEditUser/adminEditUserSlice' @@ -16,8 +16,9 @@ import adminOrganizationTabRsuReducer from './features/adminOrganizationTabRsu/a import adminRsuTabReducer from './features/adminRsuTab/adminRsuTabSlice' import adminUserTabReducer from './features/adminUserTab/adminUserTabSlice' import menuReducer from './features/menu/menuSlice' -import intersectionMapReducer from './components/intersectionMap/map-slice' -import intersectionMapLayerStyleReducer from './components/intersectionMap/map-layer-style-slice' +import intersectionMapReducer from './components/intersection/map/map-slice' +import intersectionMapLayerStyleReducer from './components/intersection/map/map-layer-style-slice' +import dataSelectorReducer from './components/intersection/data-selector/data-selector-slice' export const setupStore = (preloadedState: any) => { return configureStore({ @@ -41,6 +42,7 @@ export const setupStore = (preloadedState: any) => { menu: menuReducer, intersectionMap: intersectionMapReducer, intersectionMapLayerStyle: intersectionMapLayerStyleReducer, + dataSelector: dataSelectorReducer, }, preloadedState, middleware: (getDefaultMiddleware) => From 0f1db9f3fdf406a050cad679355b6daea6a51908 Mon Sep 17 00:00:00 2001 From: Marc Wodahl Date: Tue, 11 Jun 2024 08:22:31 -0600 Subject: [PATCH 067/189] Address style/theme changes --- webapp/src/components/AdminTable.tsx | 2 +- .../features/adminEditOrganization/AdminEditOrganization.tsx | 2 +- webapp/src/features/adminEditRsu/AdminEditRsu.tsx | 1 - webapp/src/features/adminEditUser/AdminEditUser.tsx | 2 +- webapp/src/styles/index.ts | 2 +- 5 files changed, 4 insertions(+), 5 deletions(-) diff --git a/webapp/src/components/AdminTable.tsx b/webapp/src/components/AdminTable.tsx index eed297bac..b860f11a0 100644 --- a/webapp/src/components/AdminTable.tsx +++ b/webapp/src/components/AdminTable.tsx @@ -1,6 +1,6 @@ import React from 'react' import MaterialTable, { Action, Column } from '@material-table/core' -import { ThemeProvider } from '@mui/material' +import { ThemeProvider } from '@mui/material/styles' import { tableTheme } from '../styles' import '../features/adminRsuTab/Admin.css' diff --git a/webapp/src/features/adminEditOrganization/AdminEditOrganization.tsx b/webapp/src/features/adminEditOrganization/AdminEditOrganization.tsx index 5959a306d..fdc8bf587 100644 --- a/webapp/src/features/adminEditOrganization/AdminEditOrganization.tsx +++ b/webapp/src/features/adminEditOrganization/AdminEditOrganization.tsx @@ -26,7 +26,7 @@ import { setSelectedOrg, } from '../adminOrganizationTab/adminOrganizationTabSlice' import { Link, useParams, useNavigate } from 'react-router-dom' -import { ThemeProvider, Typography } from '@mui/material' +import { Typography } from '@mui/material' import { theme } from '../../styles' const AdminEditOrganization = () => { diff --git a/webapp/src/features/adminEditRsu/AdminEditRsu.tsx b/webapp/src/features/adminEditRsu/AdminEditRsu.tsx index aa70d7cb6..dd0319eae 100644 --- a/webapp/src/features/adminEditRsu/AdminEditRsu.tsx +++ b/webapp/src/features/adminEditRsu/AdminEditRsu.tsx @@ -43,7 +43,6 @@ import { AdminRsu } from '../../types/Rsu' import { Link, useParams } from 'react-router-dom' import { selectTableData, updateTableData } from '../adminRsuTab/adminRsuTabSlice' import { Typography } from '@mui/material' -import { ThemeProvider } from '@mui/material' import { theme } from '../../styles' export type AdminEditRsuFormType = { diff --git a/webapp/src/features/adminEditUser/AdminEditUser.tsx b/webapp/src/features/adminEditUser/AdminEditUser.tsx index eb22d12c0..00b60dc07 100644 --- a/webapp/src/features/adminEditUser/AdminEditUser.tsx +++ b/webapp/src/features/adminEditUser/AdminEditUser.tsx @@ -28,7 +28,7 @@ import { ThunkDispatch, AnyAction } from '@reduxjs/toolkit' import { RootState } from '../../store' import { Link, useParams } from 'react-router-dom' import { getAvailableUsers, selectTableData } from '../adminUserTab/adminUserTabSlice' -import { ThemeProvider, Typography } from '@mui/material' +import { Typography } from '@mui/material' import { theme } from '../../styles' const AdminEditUser = () => { diff --git a/webapp/src/styles/index.ts b/webapp/src/styles/index.ts index 70486febc..ffa4be008 100644 --- a/webapp/src/styles/index.ts +++ b/webapp/src/styles/index.ts @@ -110,7 +110,7 @@ export const tableTheme = createTheme({ color: '#333333', backgroundColor: '#dadde5', borderRadius: '50%', - padding: '2px', + padding: 2, }, }, }, From 0594d4b905894daf861743d7d0bbc4f33be1467a Mon Sep 17 00:00:00 2001 From: Marc Wodahl Date: Tue, 11 Jun 2024 10:14:03 -0600 Subject: [PATCH 068/189] address component changes --- webapp/src/components/css/Header.css | 2 +- webapp/src/pages/Map.tsx | 9 +++++---- 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/webapp/src/components/css/Header.css b/webapp/src/components/css/Header.css index 3fed288a6..54eb9e264 100644 --- a/webapp/src/components/css/Header.css +++ b/webapp/src/components/css/Header.css @@ -87,7 +87,7 @@ } #userInfoGrid { - padding: 30px; + padding-right: 30px; height: 100px; } diff --git a/webapp/src/pages/Map.tsx b/webapp/src/pages/Map.tsx index 7f52908f0..eed3b78e4 100644 --- a/webapp/src/pages/Map.tsx +++ b/webapp/src/pages/Map.tsx @@ -624,10 +624,11 @@ function MapPage(props: MapPageProps) { setDisplayType('') } - const handleRsuDisplayTypeChange = (event: React.ChangeEvent) => { - if (event.target.value === 'online') handleOnlineStatus() - else if (event.target.value === 'scms') handleScmsStatus() - else if (event.target.value === 'none') handleNoneStatus() + const handleRsuDisplayTypeChange = (event: React.SyntheticEvent) => { + const target = event.target as HTMLInputElement + if (target.value === 'online') handleOnlineStatus() + else if (target.value === 'scms') handleScmsStatus() + else if (target.value === 'none') handleNoneStatus() } const handleButtonToggle = (event: React.SyntheticEvent, origin: 'config' | 'msgViewer') => { From 03bc7f137cdd952c84d24a6db52cb1b0dccaa6ef Mon Sep 17 00:00:00 2001 From: Marc Wodahl Date: Tue, 11 Jun 2024 10:30:30 -0600 Subject: [PATCH 069/189] Add StyledEngineProvider --- webapp/src/App.tsx | 30 +- webapp/src/components/AdminTable.tsx | 38 +-- webapp/src/components/SnmpwalkMenu.tsx | 177 ++++++------ .../AdminOrganizationTabRsu.tsx | 124 ++++---- .../AdminOrganizationTabUser.tsx | 168 +++++------ webapp/src/features/menu/ConfigureRSU.tsx | 273 ++++++++++-------- webapp/src/pages/Map.tsx | 70 ++--- 7 files changed, 459 insertions(+), 421 deletions(-) diff --git a/webapp/src/App.tsx b/webapp/src/App.tsx index 290ac2b0c..af93876c0 100644 --- a/webapp/src/App.tsx +++ b/webapp/src/App.tsx @@ -15,7 +15,7 @@ import { BrowserRouter, Routes, Route, Navigate } from 'react-router-dom' import Dashboard from './Dashboard' import { NotFound } from './pages/404' import { theme } from './styles' -import { ThemeProvider } from '@mui/material' +import { ThemeProvider, StyledEngineProvider } from '@mui/material' const App = () => { const dispatch: ThunkDispatch = useDispatch() @@ -44,19 +44,21 @@ const App = () => { }, [authLoginData, dispatch]) return ( - - - {routeNotFound ? ( - - ) : ( - - } /> - } /> - } /> - - )} - - + + + + {routeNotFound ? ( + + ) : ( + + } /> + } /> + } /> + + )} + + + ) } diff --git a/webapp/src/components/AdminTable.tsx b/webapp/src/components/AdminTable.tsx index b860f11a0..36e09829b 100644 --- a/webapp/src/components/AdminTable.tsx +++ b/webapp/src/components/AdminTable.tsx @@ -1,6 +1,6 @@ import React from 'react' import MaterialTable, { Action, Column } from '@material-table/core' -import { ThemeProvider } from '@mui/material/styles' +import { ThemeProvider, StyledEngineProvider } from '@mui/material/styles' import { tableTheme } from '../styles' import '../features/adminRsuTab/Admin.css' @@ -16,23 +16,25 @@ interface AdminTableProps { const AdminTable = (props: AdminTableProps) => { return (
- - - + + + + +
) } diff --git a/webapp/src/components/SnmpwalkMenu.tsx b/webapp/src/components/SnmpwalkMenu.tsx index 88c76b1a2..c4bcd99a9 100644 --- a/webapp/src/components/SnmpwalkMenu.tsx +++ b/webapp/src/components/SnmpwalkMenu.tsx @@ -19,7 +19,7 @@ import { // Actions deleteSnmpSet, } from '../generalSlices/configSlice' -import { IconButton, ThemeProvider, Tooltip, createTheme } from '@mui/material' +import { IconButton, ThemeProvider, StyledEngineProvider, Tooltip, createTheme } from '@mui/material' import { AnyAction, ThunkDispatch } from '@reduxjs/toolkit' import { RootState } from '../store' @@ -67,96 +67,99 @@ const SnmpwalkMenu = () => { return (
- -
-

Message Forwarding

- - { - dispatch(refreshSnmpFwdConfig(rsuIp)) - }} - > - - - -
+ + +
+

Message Forwarding

+ + { + dispatch(refreshSnmpFwdConfig(rsuIp)) + }} + size="medium" + > + + + +
-
- {Object.hasOwn(msgFwdConfig, 'rsuXmitMsgFwdingTable') && - Object.hasOwn(msgFwdConfig, 'rsuReceivedMsgTable') ? ( -
-

TX Forward Table

- {Object.keys(msgFwdConfig.rsuXmitMsgFwdingTable).map((index) => ( -
- - -
- ))} +
+ {Object.hasOwn(msgFwdConfig, 'rsuXmitMsgFwdingTable') && + Object.hasOwn(msgFwdConfig, 'rsuReceivedMsgTable') ? ( +
+

TX Forward Table

+ {Object.keys(msgFwdConfig.rsuXmitMsgFwdingTable).map((index) => ( +
+ + +
+ ))} -

RX Forward Table

- {Object.keys(msgFwdConfig.rsuReceivedMsgTable).map((index) => ( -
- - -
- ))} -
+

RX Forward Table

+ {Object.keys(msgFwdConfig.rsuReceivedMsgTable).map((index) => ( +
+ + +
+ ))} +
+ ) : ( +
+ {Object.keys(msgFwdConfig).map((index) => ( +
+ + +
+ ))} +
+ )} +
+ + {errorState !== '' ? ( + ) : ( -
- {Object.keys(msgFwdConfig).map((index) => ( -
- - -
- ))} -
+
)} -
- - {errorState !== '' ? ( - - ) : ( -
- )} - + +
) } diff --git a/webapp/src/features/adminOrganizationTabRsu/AdminOrganizationTabRsu.tsx b/webapp/src/features/adminOrganizationTabRsu/AdminOrganizationTabRsu.tsx index e04a124e8..50dc5e21b 100644 --- a/webapp/src/features/adminOrganizationTabRsu/AdminOrganizationTabRsu.tsx +++ b/webapp/src/features/adminOrganizationTabRsu/AdminOrganizationTabRsu.tsx @@ -1,7 +1,7 @@ import React, { useState, useEffect } from 'react' import AdminTable from '../../components/AdminTable' import { AiOutlinePlusCircle } from 'react-icons/ai' -import { ThemeProvider, createTheme } from '@mui/material' +import { ThemeProvider, StyledEngineProvider, createTheme } from '@mui/material' import Accordion from '@mui/material/Accordion' import AccordionSummary from '@mui/material/AccordionSummary' import AccordionDetails from '@mui/material/AccordionDetails' @@ -134,66 +134,70 @@ const AdminOrganizationTabRsu = (props: AdminOrganizationTabRsuProps) => { return (
- - - } - aria-controls="panel1a-content" - id="panel1a-header" - > - {selectedOrg} RSUs - - - {loadingGlobal === false && [ -
- - - } - aria-controls="panel1a-content" - id="panel1a-header" - > - Add RSUs to {selectedOrg} - - -
- { - dispatch(setSelectedRsuList(value)) - }} - /> - - -
-
-
-
-
, -
- -
, - ]} -
-
-
+ Add RSUs to {selectedOrg} + + +
+ { + dispatch(setSelectedRsuList(value)) + }} + /> + + +
+
+ + + +
, +
+ +
, + ]} + + + +
) } diff --git a/webapp/src/features/adminOrganizationTabUser/AdminOrganizationTabUser.tsx b/webapp/src/features/adminOrganizationTabUser/AdminOrganizationTabUser.tsx index cf9660e95..d592f671c 100644 --- a/webapp/src/features/adminOrganizationTabUser/AdminOrganizationTabUser.tsx +++ b/webapp/src/features/adminOrganizationTabUser/AdminOrganizationTabUser.tsx @@ -1,7 +1,7 @@ import React, { useState, useEffect } from 'react' import AdminTable from '../../components/AdminTable' import { AiOutlinePlusCircle } from 'react-icons/ai' -import { ThemeProvider, createTheme } from '@mui/material' +import { ThemeProvider, StyledEngineProvider, createTheme } from '@mui/material' import Accordion from '@mui/material/Accordion' import AccordionSummary from '@mui/material/AccordionSummary' import AccordionDetails from '@mui/material/AccordionDetails' @@ -227,88 +227,92 @@ const AdminOrganizationTabUser = (props: AdminOrganizationTabUserProps) => { return (
- - - } - aria-controls="panel1a-content" - id="panel1a-header" - > - {props.selectedOrg} Users - - - {loadingGlobal === false && [ -
- - - } - aria-controls="panel1a-content" - id="panel1a-header" - > - Add Users to {props.selectedOrg} - - -
- dispatch(setSelectedUserList(value))} - /> - -
- {selectedUserList.length > 0 && ( -

- Please select a role for: -

- )} - {selectedUserList.length > 0 && [ - selectedUserList.map((user) => { - return ( -
-

{user.email}

- { - dispatch(setSelectedUserRole({ email: user.email, role: value.role })) - }} - /> -
- ) - }), - ]} -
-
-
-
, -
- -
, - ]} -
-
-
+ Add Users to {props.selectedOrg} + + +
+ dispatch(setSelectedUserList(value))} + /> + +
+ {selectedUserList.length > 0 && ( +

+ Please select a role for: +

+ )} + {selectedUserList.length > 0 && [ + selectedUserList.map((user) => { + return ( +
+

{user.email}

+ { + dispatch(setSelectedUserRole({ email: user.email, role: value.role })) + }} + /> +
+ ) + }), + ]} +
+ + + +
, +
+ +
, + ]} + + +
+
) } diff --git a/webapp/src/features/menu/ConfigureRSU.tsx b/webapp/src/features/menu/ConfigureRSU.tsx index 126a6c33f..7ee9131b0 100644 --- a/webapp/src/features/menu/ConfigureRSU.tsx +++ b/webapp/src/features/menu/ConfigureRSU.tsx @@ -7,7 +7,7 @@ import Accordion from '@mui/material/Accordion' import AccordionDetails from '@mui/material/AccordionDetails' import AccordionSummary from '@mui/material/AccordionSummary' import { useDispatch, useSelector } from 'react-redux' -import { ThemeProvider, createTheme } from '@mui/material' +import { ThemeProvider, StyledEngineProvider, createTheme } from '@mui/material' import ExpandMoreIcon from '@mui/icons-material/ExpandMore' import Typography from '@mui/material/Typography' import { selectSelectedRsu, selectRsu } from '../../generalSlices/rsuSlice' @@ -52,91 +52,101 @@ const ConfigureRSU = () => { {selectedRsu && (
- - - } - aria-controls="panel1bh-content" - id="panel1bh-header" + + + - Current Configuration - - - - - - - - - - - } - aria-controls="panel2bh-content" - id="panel2bh-header" - className="expand" + } + aria-controls="panel1bh-content" + id="panel1bh-header" + > + Current Configuration + + + + + + + + + + + + - Message Forwarding - - - - - - - - - - - } - aria-controls="panel3bh-content" - id="panel3bh-header" + } + aria-controls="panel2bh-content" + id="panel2bh-header" + className="expand" + > + Message Forwarding + + + + + + + + + + + + - Firmware - - - - - - - - - - - } - aria-controls="panel3bh-content" - id="panel3bh-header" + } + aria-controls="panel3bh-content" + id="panel3bh-header" + > + Firmware + + + + + + + + + + + + - Reboot - - - - - - - - - - + } + aria-controls="panel3bh-content" + id="panel3bh-header" + > + Reboot + + + + + + + + + + + + +
)} {selectedConfigList.length > 0 && !selectedRsu && ( @@ -157,53 +167,62 @@ const ConfigureRSU = () => { )} {selectedConfigList.length > 0 && !selectedRsu && (
- - - } - aria-controls="panel2bh-content" - id="panel2bh-header" - className="expand" + + + - Message Forwarding - - - - - val.toString())} /> - - - - - - } - aria-controls="panel3bh-content" - id="panel3bh-header" + } + aria-controls="panel2bh-content" + id="panel2bh-header" + className="expand" + > + Message Forwarding + + + + + + val.toString())} + /> + + + + + + - Firmware - - - - - val.toString())} - /> - - - - - + } + aria-controls="panel3bh-content" + id="panel3bh-header" + > + Firmware + + + + + + val.toString())} + /> + + + + + + +
)}
diff --git a/webapp/src/pages/Map.tsx b/webapp/src/pages/Map.tsx index eed3b78e4..beded1535 100644 --- a/webapp/src/pages/Map.tsx +++ b/webapp/src/pages/Map.tsx @@ -72,6 +72,7 @@ import { Switch, TextField, ThemeProvider, + StyledEngineProvider, Tooltip, createTheme, } from '@mui/material' @@ -691,39 +692,42 @@ function MapPage(props: MapPageProps) { {SecureStorageManager.getUserRole() === 'admin' && ( <>

RSU Configuration

- - - } - label={'Add Points'} - onChange={(e) => handleButtonToggle(e, 'config')} - /> - {configCoordinates.length > 0 && ( - - { - dispatch(clearConfig()) - }} - > - - - - )} - - - - - + + + + } + label={'Add Points'} + onChange={(e) => handleButtonToggle(e, 'config')} + /> + {configCoordinates.length > 0 && ( + + { + dispatch(clearConfig()) + }} + size="large" + > + + + + )} + + + + + + )}
From de02514df7ceeb3370da2d455fbc91d1db3f0300 Mon Sep 17 00:00:00 2001 From: Marc Wodahl Date: Thu, 13 Jun 2024 09:20:04 -0600 Subject: [PATCH 070/189] Update queries to return json rows --- services/api/src/admin_org.py | 20 ++++++++++++-------- services/api/tests/src/test_admin_org.py | 4 ++-- 2 files changed, 14 insertions(+), 10 deletions(-) diff --git a/services/api/src/admin_org.py b/services/api/src/admin_org.py index 26ac1fb6e..fe0765722 100644 --- a/services/api/src/admin_org.py +++ b/services/api/src/admin_org.py @@ -246,23 +246,27 @@ def delete_org(org_name): def check_orphan_rsus(org): rsu_query = ( - "SELECT rsu_id, count(organization_id) FROM rsu_organization WHERE rsu_id IN (SELECT rsu_id FROM rsu_organization WHERE organization_id = " - f"(SELECT organization_id FROM organizations WHERE name = '{org}')) GROUP BY rsu_id" + "SELECT to_jsonb(row) " + "FROM (SELECT rsu_id, count(organization_id) count FROM rsu_organization WHERE rsu_id IN (SELECT rsu_id FROM rsu_organization WHERE organization_id = " + f"(SELECT organization_id FROM organizations WHERE name = '{org}')) GROUP BY rsu_id) as row" ) rsu_count = pgquery.query_db(rsu_query) - for rsu in rsu_count: - if rsu[1] == 1: + for row in rsu_count: + rsu = dict(row[0]) + if rsu["count"] == 1: return True return False def check_orphan_users(org): user_query = ( - "SELECT user_id, count(organization_id) FROM user_organization WHERE user_id IN (SELECT user_id FROM user_organization WHERE organization_id = " - f"(SELECT organization_id FROM organizations WHERE name = '{org}')) GROUP BY user_id" + "SELECT to_jsonb(row) " + "FROM (SELECT user_id, count(organization_id) count FROM user_organization WHERE user_id IN (SELECT user_id FROM user_organization WHERE organization_id = " + f"(SELECT organization_id FROM organizations WHERE name = '{org}')) GROUP BY user_id) as row" ) user_count = pgquery.query_db(user_query) - for user in user_count: - if user[1] == 1: + for row in user_count: + user = dict(row[0]) + if user["count"] == 1: return True return False diff --git a/services/api/tests/src/test_admin_org.py b/services/api/tests/src/test_admin_org.py index f8524513b..ae9af46c7 100644 --- a/services/api/tests/src/test_admin_org.py +++ b/services/api/tests/src/test_admin_org.py @@ -284,7 +284,7 @@ def test_delete_org(mock_query_db, mock_write_db): @patch("api.src.admin_org.pgquery.query_db") def test_delete_org_failure_orphan_rsu(mock_query_db): - mock_query_db.return_value = [[1, 2], [2, 1]] + mock_query_db.return_value = [[{"user_id": 1, "count": 2}], [{"user_id": 2, "count": 1}]] expected_result = {"message": "Cannot delete organization that has one or more RSUs only associated with this organization"}, 400 actual_result = admin_org.delete_org("test org") @@ -294,7 +294,7 @@ def test_delete_org_failure_orphan_rsu(mock_query_db): @patch("api.src.admin_org.check_orphan_rsus") def test_delete_org_failure_orphan_user(mock_orphan_rsus, mock_query_db): mock_orphan_rsus.return_value = False - mock_query_db.return_value = [[1, 2], [2, 1]] + mock_query_db.return_value = [[{"user_id": 1, "count": 2}], [{"user_id": 2, "count": 1}]] expected_result = {"message": "Cannot delete organization that has one or more users only associated with this organization"}, 400 actual_result = admin_org.delete_org("test org") From 67564b8765fb3b7d6cd485dbc36730e0c72d8cbb Mon Sep 17 00:00:00 2001 From: Marc Wodahl Date: Wed, 19 Jun 2024 11:16:04 -0600 Subject: [PATCH 071/189] SQL script update --- .../sql_scripts/CVManager_CreateTables.sql | 38 +++++++++++++++++++ .../sql_scripts/CVManager_SampleData.sql | 4 ++ 2 files changed, 42 insertions(+) diff --git a/resources/sql_scripts/CVManager_CreateTables.sql b/resources/sql_scripts/CVManager_CreateTables.sql index da02cf1d4..4d0ceccab 100644 --- a/resources/sql_scripts/CVManager_CreateTables.sql +++ b/resources/sql_scripts/CVManager_CreateTables.sql @@ -401,4 +401,42 @@ CREATE TABLE IF NOT EXISTS public.snmp_msgfwd_config ON DELETE NO ACTION ); +CREATE SEQUENCE public.email_type_email_type_id_seq + INCREMENT 1 + START 1 + MINVALUE 1 + MAXVALUE 2147483647 + CACHE 1; + +CREATE TABLE IF NOT EXISTS public.email_type +( + email_type_id integer NOT NULL DEFAULT nextval('email_type_email_type_id_seq'::regclass), + CONSTRAINT email_type_pkey PRIMARY KEY (email_type_id), + email_type character varying(128) COLLATE pg_catalog.default NOT NULL, + CONSTRAINT email_type_unique UNIQUE (email_type) +); + +CREATE SEQUENCE public.user_email_notification_user_email_notification_id_seq + INCREMENT 1 + START 1 + MINVALUE 1 + MAXVALUE 2147483647 + CACHE 1; + +CREATE TABLE IF NOT EXISTS public.user_email_notification +( + user_email_notification_id integer NOT NULL DEFAULT nextval('user_email_notification_user_email_notification_id_seq'::regclass), + user_id integer NOT NULL, + email_type_id integer NOT NULL, + CONSTRAINT user_email_notification_pkey PRIMARY KEY (user_email_notification_id), + CONSTRAINT fk_user_id FOREIGN KEY (user_id) + REFERENCES public.users (user_id) MATCH SIMPLE + ON UPDATE NO ACTION + ON DELETE NO ACTION, + CONSTRAINT fk_email_type_id FOREIGN KEY (email_type_id) + REFERENCES public.email_type (email_type_id) MATCH SIMPLE + ON UPDATE NO ACTION + ON DELETE NO ACTION +); + CREATE SCHEMA IF NOT EXISTS keycloak; \ No newline at end of file diff --git a/resources/sql_scripts/CVManager_SampleData.sql b/resources/sql_scripts/CVManager_SampleData.sql index 3d8af39bf..1262cafac 100644 --- a/resources/sql_scripts/CVManager_SampleData.sql +++ b/resources/sql_scripts/CVManager_SampleData.sql @@ -69,3 +69,7 @@ INSERT INTO public.snmp_msgfwd_config( (2, 2, 2, 'BSM', '10.0.0.81', 46800, '2024/04/01T00:00:00', '2034/04/01T00:00:00', '1'), (2, 3, 1, 'MAP', '10.0.0.80', 44920, '2024/04/01T00:00:00', '2034/04/01T00:00:00', '1'), (2, 3, 2, 'SPAT', '10.0.0.80', 44910, '2024/04/01T00:00:00', '2034/04/01T00:00:00', '1'); + +INSERT INTO public.email_type( + email_type) + VALUES ('Contact Support'), ('Firmware Upgrade Failure'), ('Daily Message Counts'); \ No newline at end of file From cd3c4afcc73fd5af81120a806b410edd1dc50def Mon Sep 17 00:00:00 2001 From: Marc Wodahl Date: Wed, 19 Jun 2024 11:23:58 -0600 Subject: [PATCH 072/189] Add Notification Tab --- webapp/src/EnvironmentVars.tsx | 2 + .../AdminNotificationTab.test.tsx | 19 + .../AdminNotificationTab.tsx | 202 ++++++ .../AdminNotificationTab.test.tsx.snap | 596 ++++++++++++++++++ .../adminNotificationTabSlice.test.ts | 274 ++++++++ .../adminNotificationTabSlice.tsx | 127 ++++ webapp/src/pages/Admin.tsx | 18 + 7 files changed, 1238 insertions(+) create mode 100644 webapp/src/features/adminNotificationTab/AdminNotificationTab.test.tsx create mode 100644 webapp/src/features/adminNotificationTab/AdminNotificationTab.tsx create mode 100644 webapp/src/features/adminNotificationTab/__snapshots__/AdminNotificationTab.test.tsx.snap create mode 100644 webapp/src/features/adminNotificationTab/adminNotificationTabSlice.test.ts create mode 100644 webapp/src/features/adminNotificationTab/adminNotificationTabSlice.tsx diff --git a/webapp/src/EnvironmentVars.tsx b/webapp/src/EnvironmentVars.tsx index f4eb63468..b074c2a6e 100644 --- a/webapp/src/EnvironmentVars.tsx +++ b/webapp/src/EnvironmentVars.tsx @@ -55,6 +55,8 @@ class EnvironmentVars { static adminRsu = `${this.getBaseApiUrl()}/admin-rsu` static adminAddUser = `${this.getBaseApiUrl()}/admin-new-user` static adminUser = `${this.getBaseApiUrl()}/admin-user` + static adminNotification = `${this.getBaseApiUrl()}/admin-notification` + static adminAddNotification = `${this.getBaseApiUrl()}/admin-new-notification` static adminAddOrg = `${this.getBaseApiUrl()}/admin-new-org` static adminOrg = `${this.getBaseApiUrl()}/admin-org` static contactSupport = `${this.getBaseApiUrl()}/contact-support` diff --git a/webapp/src/features/adminNotificationTab/AdminNotificationTab.test.tsx b/webapp/src/features/adminNotificationTab/AdminNotificationTab.test.tsx new file mode 100644 index 000000000..62a000b70 --- /dev/null +++ b/webapp/src/features/adminNotificationTab/AdminNotificationTab.test.tsx @@ -0,0 +1,19 @@ +import React from 'react' +import { render } from '@testing-library/react' +import AdminNotificationTab from './AdminNotificationTab' +import { Provider } from 'react-redux' +import { setupStore } from '../../store' +import { replaceChaoticIds } from '../../utils/test-utils' +import { BrowserRouter } from 'react-router-dom' + +it('should take a snapshot', () => { + const { container } = render( + + + + + + ) + + expect(replaceChaoticIds(container)).toMatchSnapshot() +}) diff --git a/webapp/src/features/adminNotificationTab/AdminNotificationTab.tsx b/webapp/src/features/adminNotificationTab/AdminNotificationTab.tsx new file mode 100644 index 000000000..22d92589a --- /dev/null +++ b/webapp/src/features/adminNotificationTab/AdminNotificationTab.tsx @@ -0,0 +1,202 @@ +import React, { useState, useEffect } from 'react' +import AdminTable from '../../components/AdminTable' +import { IoChevronBackCircleOutline, IoRefresh } from 'react-icons/io5' +import { AiOutlinePlusCircle } from 'react-icons/ai' +import { confirmAlert } from 'react-confirm-alert' +import { Options } from '../../components/AdminDeletionOptions' +import { selectLoading } from '../../generalSlices/rsuSlice' +// Need to import the authData to get current user email +import { + selectTableData, + getUserNotifications, + deleteNotifications, + updateTitle, + setActiveDiv, + setEditNotificationRowData, +} from './adminNotificationTabSlice' +import { useSelector, useDispatch } from 'react-redux' + +import '../adminRsuTab/Admin.css' +import { AnyAction, ThunkDispatch } from '@reduxjs/toolkit' +import { RootState } from '../../store' +import { Action } from '@material-table/core' +import { Route, Routes, useLocation, useNavigate } from 'react-router-dom' +import { NotFound } from '../../pages/404' +import AdminEditNotification from '../adminEditNotification/AdminEditNotification' +import AdminAddNotification from '../adminAddNotification/AdminAddNotification' +import { AdminEmailNotification } from '../../types/Notifications' + +const getTitle = (activeTab: string) => { + if (activeTab === undefined) { + return 'CV Manager Email Notifications' + } else if (activeTab === 'editNotification') { + return 'Edit Email Notifications' + } else if (activeTab === 'addNotification') { + return 'Add Email Notifications' + } + return 'Unknown' +} + +const AdminNotificationTab = () => { + const dispatch: ThunkDispatch = useDispatch() + const navigate = useNavigate() + const location = useLocation() + + const activeTab = location.pathname.split('/')[4] + const title = getTitle(activeTab) + + const tableData = useSelector(selectTableData) + const [columns] = useState([ + { title: 'First Name', field: 'first_name', id: 0 }, + { title: 'Last Name', field: 'last_name', id: 1 }, + { title: 'Email', field: 'email', id: 2 }, + { title: 'Email Type', field: 'email_type', id: 3 }, + ]) + const loading = useSelector(selectLoading) + + let tableActions: Action[] = [ + { + icon: 'delete', + tooltip: 'Delete Email Notification', + position: 'row', + onClick: (event, rowData: AdminEmailNotification) => { + const buttons = [ + { + label: 'Yes', + onClick: () => dispatch(deleteNotifications([rowData])), + }, + { + label: 'No', + onClick: () => {}, + }, + ] + const alertOptions = Options( + 'Delete Email Notification', + 'Are you sure you want to delete ' + rowData.email_type + ' for "' + rowData.email + '"?', + buttons + ) + confirmAlert(alertOptions) + }, + }, + { + icon: 'edit', + tooltip: 'Edit Notification', + position: 'row', + onClick: (event, rowData: AdminEmailNotification) => onEdit(rowData), + }, + { + tooltip: 'Remove All Selected Notifications', + icon: 'delete', + onClick: (event, rowData: AdminEmailNotification[]) => { + const buttons = [ + { + label: 'Yes', + onClick: () => dispatch(deleteNotifications(rowData)), + }, + { + label: 'No', + onClick: () => {}, + }, + ] + const alertOptions = Options( + 'Delete Selected Email Notifications', + 'Are you sure you want to delete ' + rowData.length + ' email notifications?', + buttons + ) + confirmAlert(alertOptions) + }, + }, + ] + + // dispatch + const updateTableData = async () => { + dispatch(getUserNotifications()) + } + + useEffect(() => { + dispatch(setActiveDiv('notification_table')) + }, [dispatch]) + + useEffect(() => { + dispatch(updateTitle()) + }, [activeTab, dispatch]) + + const onEdit = (row: AdminEmailNotification) => { + dispatch(setEditNotificationRowData(row)) + navigate('editNotification/' + row.email_type) + } + + return ( +
+
+

+ {activeTab !== undefined && ( + + )} + {title} + {activeTab === undefined && [ + , + , + ]} +

+
+ + + +
+ ) + } + /> + + +
+ } + /> + + +
+ } + /> + + } + /> + +
+ ) +} + +export default AdminNotificationTab diff --git a/webapp/src/features/adminNotificationTab/__snapshots__/AdminNotificationTab.test.tsx.snap b/webapp/src/features/adminNotificationTab/__snapshots__/AdminNotificationTab.test.tsx.snap new file mode 100644 index 000000000..fb20371b9 --- /dev/null +++ b/webapp/src/features/adminNotificationTab/__snapshots__/AdminNotificationTab.test.tsx.snap @@ -0,0 +1,596 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`should take a snapshot 1`] = ` +
+
+
+

+ CV Manager Email Notifications + + +

+
+
+
+
+
+ +
+
+
+
+ + ​ + + +
+ +
+ +
+
+
+
+
+
+ +
+ +
+
+
+
+
+
+
+
+ + + + + + + + + + + + + + + + +
+ + + + + + +
+ + First Name + + + +
+
+
+ + Last Name + + + +
+
+
+ + Email + + + +
+
+
+ + Email Type + + + +
+
+ + Actions + +
+ No records to display +
+
+
+
+
+ + + + + + +
+
+
+
+
+
+`; diff --git a/webapp/src/features/adminNotificationTab/adminNotificationTabSlice.test.ts b/webapp/src/features/adminNotificationTab/adminNotificationTabSlice.test.ts new file mode 100644 index 000000000..11425ccd7 --- /dev/null +++ b/webapp/src/features/adminNotificationTab/adminNotificationTabSlice.test.ts @@ -0,0 +1,274 @@ +import reducer from './adminNotificationTabSlice' +import { + // async thunks + getNotificationData, + deleteNotifications, + + // functions + getUserNotifications, + deleteNotification, + + // reducers + updateTitle, + setActiveDiv, + setEditNotificationRowData, + + // selectors + selectLoading, + selectActiveDiv, + selectTableData, + selectTitle, + selectEditNotificationRowData, +} from './adminNotificationTabSlice' +import apiHelper from '../../apis/api-helper' +import EnvironmentVars from '../../EnvironmentVars' +import { RootState } from '../../store' + +describe('admin Notification tab reducer', () => { + it('should handle initial state', () => { + expect(reducer(undefined, { type: 'unknown' })).toEqual({ + loading: false, + value: { + activeDiv: 'notification_table', + tableData: [], + title: 'Email Notifications', + editNotificationRowData: {}, + }, + }) + }) +}) + +describe('async thunks', () => { + const initialState: RootState['adminNotificationTab'] = { + loading: null, + value: { + activeDiv: null, + tableData: null, + title: null, + editNotificationRowData: null, + }, + } + + beforeAll(() => { + jest.mock('../../apis/api-helper') + }) + + afterAll(() => { + jest.unmock('../../apis/api-helper') + }) + + describe('getUserNotifications', () => { + it('returns and calls the api correctly', async () => { + let dispatch = jest.fn() + const getState = jest.fn().mockReturnValue({ + user: { + value: { + authLoginData: { token: 'token' }, + }, + }, + }) + const action = getUserNotifications() + + apiHelper._getDataWithCodes = jest.fn().mockReturnValue({ status: 200, message: 'message', body: 'data' }) + let resp = await action(dispatch, getState, undefined) + expect(resp.payload).toEqual('data') + }) + + it('Updates the state correctly pending', async () => { + const loading = true + const state = reducer(initialState, { + type: 'adminNotificationTab/getUserNotifications/pending', + }) + expect(state).toEqual({ + ...initialState, + loading, + value: { ...initialState.value }, + }) + }) + + it('Updates the state correctly fulfilled', async () => { + const loading = false + let success = true + let notification_data = [ + { first_name: 'first-name', last_name: 'last-name', email: 'test@gmail.com', email_type: 'some-type' }, + { first_name: 'first-name', last_name: 'last-name', email: 'test2@gmail.com', email_type: 'some-type-2' }, + ] + let tableData = [ + { + email: 'test@gmail.com', + email_type: 'some-type', + first_name: 'first-name', + last_name: 'last-name', + }, + { + email: 'test2@gmail.com', + email_type: 'some-type-2', + first_name: 'first-name', + last_name: 'last-name', + }, + ] + let state = reducer(initialState, { + type: 'adminNotificationTab/getUserNotifications/fulfilled', + payload: { notification_data }, + }) + expect(state).toEqual({ + ...initialState, + loading, + value: { ...initialState.value, tableData }, + }) + }) + + it('Updates the state correctly rejected', async () => { + const loading = false + const state = reducer(initialState, { + type: 'adminNotificationTab/getUserNotifications/rejected', + }) + expect(state).toEqual({ ...initialState, loading, value: { ...initialState.value } }) + }) + }) + + describe('deleteNotifications', () => { + it('returns and calls the api correctly', async () => { + let dispatch = jest.fn() + const getState = jest.fn().mockReturnValue({ + user: { + value: { + authLoginData: { token: 'token' }, + }, + }, + }) + const data = [ + { + email: 'test1@gmail.com', + email_type: 'some_type', + first_name: 'first_name', + last_name: 'last_name', + }, + { + email: 'test2@gmail.com', + email_type: 'some-type-2', + first_name: 'first_name', + last_name: 'last_name', + }, + { + email: 'test3@gmail.com', + email_type: 'some-type-3', + first_name: 'first_name', + last_name: 'last_name', + }, + ] + + let action = deleteNotifications(data) + + apiHelper._deleteData = jest + .fn() + .mockReturnValueOnce({ status: 200 }) + .mockReturnValueOnce({ status: 200 }) + .mockReturnValueOnce({ status: 500 }) + await action(dispatch, getState, undefined) + expect(apiHelper._deleteData).toHaveBeenCalledTimes(3) + expect(dispatch).toHaveBeenCalledTimes(1 + 2) + }) + }) +}) + +describe('reducers', () => { + const initialState: RootState['adminNotificationTab'] = { + loading: null, + value: { + activeDiv: null, + tableData: null, + title: null, + editNotificationRowData: null, + }, + } + + it('updateTitle reducer updates state correctly', async () => { + let activeDiv = 'notification_table' + let title = 'CV Manager Email Notifications' + expect(reducer({ ...initialState, value: { ...initialState.value, activeDiv } }, updateTitle())).toEqual({ + ...initialState, + value: { ...initialState.value, activeDiv, title }, + }) + + activeDiv = 'edit_notification' + title = 'Edit Email Notification' + expect(reducer({ ...initialState, value: { ...initialState.value, activeDiv } }, updateTitle())).toEqual({ + ...initialState, + value: { ...initialState.value, activeDiv, title }, + }) + + activeDiv = 'add_notification' + title = 'Add Email Notification' + expect(reducer({ ...initialState, value: { ...initialState.value, activeDiv } }, updateTitle())).toEqual({ + ...initialState, + value: { ...initialState.value, activeDiv, title }, + }) + }) + + it('setActiveDiv reducer updates state correctly', async () => { + const activeDiv = 'activeDiv' + expect(reducer(initialState, setActiveDiv(activeDiv))).toEqual({ + ...initialState, + value: { ...initialState.value, activeDiv }, + }) + }) + + it('setEditUserRowData reducer updates state correctly', async () => { + const editNotificationRowData = 'editNotificationRowData' + expect(reducer(initialState, setEditNotificationRowData(editNotificationRowData))).toEqual({ + ...initialState, + value: { ...initialState.value, editNotificationRowData }, + }) + }) +}) + +describe('functions', () => { + it('getUserNotifications', async () => { + const user_email = 'test@gmail.com' + const token = 'token' + apiHelper._getDataWithCodes = jest.fn().mockReturnValue({ data: 'data' }) + const resp = await getNotificationData(user_email, token) + expect(resp).toEqual({ data: 'data' }) + expect(apiHelper._getDataWithCodes).toHaveBeenCalledWith({ + url: EnvironmentVars.adminNotification, + token, + query_params: { user_email }, + additional_headers: { 'Content-Type': 'application/json' }, + }) + }) + + it('deleteUser', async () => { + const email = 'test@gmail.com' + const token = 'token' + const email_type = 'email_type' + apiHelper._deleteData = jest.fn().mockReturnValue({ status: 200, data: 'data' }) + await deleteNotification(email, email_type, token) + expect(apiHelper._deleteData).toHaveBeenCalledWith({ + url: EnvironmentVars.adminNotification, + token, + query_params: { email, email_type }, + }) + }) +}) + +describe('selectors', () => { + const initialState = { + loading: 'loading', + value: { + activeDiv: 'activeDiv', + tableData: 'tableData', + title: 'title', + editNotificationRowData: 'editNotificationRowData', + }, + } + const state = { adminNotificationTab: initialState } as any + + it('selectors return the correct value', async () => { + expect(selectLoading(state)).toEqual('loading') + expect(selectActiveDiv(state)).toEqual('activeDiv') + expect(selectTableData(state)).toEqual('tableData') + expect(selectTitle(state)).toEqual('title') + expect(selectEditNotificationRowData(state)).toEqual('editNotificationRowData') + }) +}) diff --git a/webapp/src/features/adminNotificationTab/adminNotificationTabSlice.tsx b/webapp/src/features/adminNotificationTab/adminNotificationTabSlice.tsx new file mode 100644 index 000000000..ad6b884b2 --- /dev/null +++ b/webapp/src/features/adminNotificationTab/adminNotificationTabSlice.tsx @@ -0,0 +1,127 @@ +import { createAsyncThunk, createSlice } from '@reduxjs/toolkit' +import { selectEmail, selectToken } from '../../generalSlices/userSlice' +import EnvironmentVars from '../../EnvironmentVars' +import apiHelper from '../../apis/api-helper' +import { RootState } from '../../store' +import { AdminEmailNotification } from '../../types/Notifications' + +const initialState = { + activeDiv: 'notification_table', + tableData: [] as AdminEmailNotification[], + title: 'Email Notifications', + editNotificationRowData: {} as AdminEmailNotification, +} + +export const getNotificationData = async (user_email: string, token: string) => { + return await apiHelper._getDataWithCodes({ + url: EnvironmentVars.adminNotification, + token, + query_params: { user_email }, + additional_headers: { 'Content-Type': 'application/json' }, + }) +} + +export const deleteNotification = async (email: string, email_type: string, token: string) => { + const data = await apiHelper._deleteData({ + url: EnvironmentVars.adminNotification, + token, + query_params: { email, email_type }, + }) + + switch (data.status) { + case 200: + console.debug(`Successfully deleted Notification: ${email_type} for ${email}`) + break + default: + console.error(data.message) + break + } +} + +export const getUserNotifications = createAsyncThunk( + 'adminNotificationTab/getUserNotifications', + async (_, { getState }) => { + const currentState = getState() as RootState + const token = selectToken(currentState) + const email = selectEmail(currentState) + + const data = await getNotificationData(email, token) + + switch (data.status) { + case 200: + return data.body + default: + console.error(data.message) + return + } + }, + { condition: (_, { getState }) => selectToken(getState() as RootState) != undefined } +) + +export const deleteNotifications = createAsyncThunk( + 'adminUserTab/deleteNotification', + async ( + data: Array<{ email: string; email_type: string; first_name: string; last_name: string }>, + { getState, dispatch } + ) => { + const currentState = getState() as RootState + const token = selectToken(currentState) + + let promises = [] + for (const user of data) { + promises.push(deleteNotification(user.email, user.email_type, token)) + } + await Promise.all(promises) + dispatch(getUserNotifications()) + }, + { condition: (_, { getState }) => selectToken(getState() as RootState) != undefined } +) + +export const adminNotificationTabSlice = createSlice({ + name: 'adminNotificationTab', + initialState: { + loading: false, + value: initialState, + }, + reducers: { + updateTitle: (state) => { + if (state.value.activeDiv === 'notification_table') { + state.value.title = 'CV Manager Email Notifications' + } else if (state.value.activeDiv === 'edit_notification') { + state.value.title = 'Edit Email Notification' + } else if (state.value.activeDiv === 'add_notification') { + state.value.title = 'Add Email Notification' + } + }, + setActiveDiv: (state, action) => { + state.value.activeDiv = action.payload + }, + setEditNotificationRowData: (state, action) => { + state.value.editNotificationRowData = action.payload + }, + }, + extraReducers: (builder) => { + builder + .addCase(getUserNotifications.pending, (state) => { + state.loading = true + }) + .addCase(getUserNotifications.fulfilled, (state, action) => { + state.loading = false + state.value.tableData = action.payload?.notification_data + }) + .addCase(getUserNotifications.rejected, (state) => { + state.loading = false + }) + }, +}) + +export const { updateTitle, setActiveDiv, setEditNotificationRowData } = adminNotificationTabSlice.actions + +export const selectLoading = (state: RootState) => state.adminNotificationTab.loading +export const selectActiveDiv = (state: RootState) => state.adminNotificationTab.value.activeDiv +export const selectTableData = (state: RootState) => state.adminNotificationTab.value.tableData +export const selectTitle = (state: RootState) => state.adminNotificationTab.value.title +export const selectEditNotificationRowData = (state: RootState) => + state.adminNotificationTab.value.editNotificationRowData + +export default adminNotificationTabSlice.reducer diff --git a/webapp/src/pages/Admin.tsx b/webapp/src/pages/Admin.tsx index ca625b60f..596c701e6 100644 --- a/webapp/src/pages/Admin.tsx +++ b/webapp/src/pages/Admin.tsx @@ -13,6 +13,8 @@ import AdminRsuTab from '../features/adminRsuTab/AdminRsuTab' import AdminUserTab from '../features/adminUserTab/AdminUserTab' import { NotFound } from './404' import { SecureStorageManager } from '../managers' +import AdminNotificationTab from '../features/adminNotificationTab/AdminNotificationTab' +import { getUserNotifications } from '../features/adminNotificationTab/adminNotificationTabSlice' interface TabPanelProps { children?: React.ReactNode @@ -46,6 +48,7 @@ function Admin() { useEffect(() => { dispatch(updateRsuTableData()) dispatch(getAvailableUsers()) + dispatch(getUserNotifications()) }, [dispatch]) return ( @@ -131,6 +134,20 @@ function Admin() { '&&': { color: value === 'organizations' || value === 2 ? '#fff' : '#d4d4d4' }, // set color when deselected }} /> + @@ -139,6 +156,7 @@ function Admin() { } /> } /> } /> + } /> Date: Wed, 19 Jun 2024 11:24:48 -0600 Subject: [PATCH 073/189] Admin Edit Notification --- .../AdminEditNotification.test.tsx | 19 + .../AdminEditNotification.tsx | 112 +++++ .../AdminEditNotification.test.tsx.snap | 108 +++++ .../adminEditNotificationSlice.test.ts | 419 ++++++++++++++++++ .../adminEditNotificationSlice.tsx | 175 ++++++++ 5 files changed, 833 insertions(+) create mode 100644 webapp/src/features/adminEditNotification/AdminEditNotification.test.tsx create mode 100644 webapp/src/features/adminEditNotification/AdminEditNotification.tsx create mode 100644 webapp/src/features/adminEditNotification/__snapshots__/AdminEditNotification.test.tsx.snap create mode 100644 webapp/src/features/adminEditNotification/adminEditNotificationSlice.test.ts create mode 100644 webapp/src/features/adminEditNotification/adminEditNotificationSlice.tsx diff --git a/webapp/src/features/adminEditNotification/AdminEditNotification.test.tsx b/webapp/src/features/adminEditNotification/AdminEditNotification.test.tsx new file mode 100644 index 000000000..c55f235eb --- /dev/null +++ b/webapp/src/features/adminEditNotification/AdminEditNotification.test.tsx @@ -0,0 +1,19 @@ +import React from 'react' +import { render } from '@testing-library/react' +import AdminEditNotification from './AdminEditNotification' +import { Provider } from 'react-redux' +import { setupStore } from '../../store' +import { replaceChaoticIds } from '../../utils/test-utils' +import { BrowserRouter } from 'react-router-dom' + +it('should take a snapshot', () => { + const { container } = render( + + + + + + ) + + expect(replaceChaoticIds(container)).toMatchSnapshot() +}) diff --git a/webapp/src/features/adminEditNotification/AdminEditNotification.tsx b/webapp/src/features/adminEditNotification/AdminEditNotification.tsx new file mode 100644 index 000000000..96f510dba --- /dev/null +++ b/webapp/src/features/adminEditNotification/AdminEditNotification.tsx @@ -0,0 +1,112 @@ +import React, { useEffect } from 'react' +import { Form } from 'react-bootstrap' +import { useForm } from 'react-hook-form' +import { DropdownList } from 'react-widgets' +import { + selectSuccessMsg, + selectErrorState, + selectErrorMsg, + selectSubmitAttempt, + selectApiData, + setSelectedType, + + // actions + submitForm, + updateEmailTypesApiData, + getNotificationData, + selectSelectedType, + selectAvailableTypes, +} from './adminEditNotificationSlice' +import { useSelector, useDispatch } from 'react-redux' + +import '../adminRsuTab/Admin.css' +import 'react-widgets/styles.css' +import { ThunkDispatch, AnyAction } from '@reduxjs/toolkit' +import { RootState } from '../../store' +import { Link, useParams } from 'react-router-dom' +import { selectEditNotificationRowData, selectTableData } from '../adminNotificationTab/adminNotificationTabSlice' +import { AdminNotificationForm } from '../adminAddNotification/adminAddNotificationSlice' +import { selectEmail } from '../../generalSlices/userSlice' + +const AdminEditNotification = () => { + const dispatch: ThunkDispatch = useDispatch() + const successMsg = useSelector(selectSuccessMsg) + const apiData = useSelector(selectApiData) + const errorState = useSelector(selectErrorState) + const errorMsg = useSelector(selectErrorMsg) + const submitAttempt = useSelector(selectSubmitAttempt) + const selectedType = useSelector(selectSelectedType) + const availableTypes = useSelector(selectAvailableTypes) + const notificationEditTableData = useSelector(selectEditNotificationRowData) + const userEmail = useSelector(selectEmail) + const { + register, + handleSubmit, + formState: { errors }, + } = useForm() + + const { email } = useParams<{ email: string }>() + + useEffect(() => { + dispatch(getNotificationData()) + dispatch(setSelectedType({ type: '' })) + }, [dispatch]) + + useEffect(() => { + dispatch(updateEmailTypesApiData()) + }, [apiData, dispatch]) + + const onSubmit = (data: AdminNotificationForm) => { + dispatch(submitForm({ data })) + } + + return ( +
+
+ + Email + + + + + Email Notification + { + dispatch(setSelectedType(value)) + }} + /> + + + {selectedType.type === '' && submitAttempt && ( +

+ Must select a new email notification type +

+ )} + + {successMsg && ( +

+ {successMsg} +

+ )} + {errorState && ( +

+ Failed to update email notification due to error: {errorMsg} +

+ )} +
+ + +
+
+
+ ) +} + +export default AdminEditNotification diff --git a/webapp/src/features/adminEditNotification/__snapshots__/AdminEditNotification.test.tsx.snap b/webapp/src/features/adminEditNotification/__snapshots__/AdminEditNotification.test.tsx.snap new file mode 100644 index 000000000..5d59233d2 --- /dev/null +++ b/webapp/src/features/adminEditNotification/__snapshots__/AdminEditNotification.test.tsx.snap @@ -0,0 +1,108 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`should take a snapshot 1`] = ` +
+
+
+
+ + +
+
+ + +
+ +
+
+
+
+
+`; diff --git a/webapp/src/features/adminEditNotification/adminEditNotificationSlice.test.ts b/webapp/src/features/adminEditNotification/adminEditNotificationSlice.test.ts new file mode 100644 index 000000000..66e9eeca3 --- /dev/null +++ b/webapp/src/features/adminEditNotification/adminEditNotificationSlice.test.ts @@ -0,0 +1,419 @@ +import reducer, { + editNotification, + getNotificationData, + selectSelectedType, + setSelectedType, + updateEmailTypesApiData, +} from './adminEditNotificationSlice' +import { + // async thunks + submitForm, + + // reducers + setSuccessMsg, + + // selectors + selectLoading, + selectSuccessMsg, + selectApiData, + selectErrorState, + selectErrorMsg, + selectSubmitAttempt, +} from './adminEditNotificationSlice' +import apiHelper from '../../apis/api-helper' +import EnvironmentVars from '../../EnvironmentVars' +import { RootState } from '../../store' + +describe('admin edit Notification reducer', () => { + it('should handle initial state', () => { + expect(reducer(undefined, { type: 'unknown' })).toEqual({ + loading: false, + value: { + successMsg: '', + availableTypes: [] as { type: string }[], + apiData: {}, + errorState: false, + errorMsg: '', + submitAttempt: false, + selectedType: { type: '' }, + }, + }) + }) +}) + +describe('async thunks', () => { + const initialState: RootState['adminEditNotification'] = { + loading: null, + value: { + successMsg: null, + availableTypes: null, + apiData: null, + errorState: null, + errorMsg: null, + submitAttempt: null, + selectedType: null, + }, + } + + beforeAll(() => { + jest.mock('../../apis/api-helper') + }) + + afterAll(() => { + jest.unmock('../../apis/api-helper') + }) + + describe('getNotificationData', () => { + it('returns and calls the api correctly', async () => { + let dispatch = jest.fn() + const getState = jest.fn().mockReturnValue({ + user: { + value: { + authLoginData: { token: 'token', data: { email: 'test@gmail.com' } }, + }, + }, + }) + const user_email = 'test@gmail.com' + const action = getNotificationData() + + apiHelper._getDataWithCodes = jest.fn().mockReturnValue({ status: 200, message: 'message', body: 'body' }) + let resp = await action(dispatch, getState, undefined) + expect(resp.payload).toEqual({ success: true, message: '', data: 'body' }) + expect(apiHelper._getDataWithCodes).toHaveBeenCalledWith({ + url: EnvironmentVars.adminAddNotification, + token: 'token', + query_params: { user_email }, + additional_headers: { 'Content-Type': 'application/json' }, + }) + expect(dispatch).toHaveBeenCalledTimes(0 + 2) + + dispatch = jest.fn() + apiHelper._getDataWithCodes = jest.fn().mockReturnValue({ status: 500, message: 'message' }) + resp = await action(dispatch, getState, undefined) + expect(resp.payload).toEqual({ success: false, message: 'message' }) + expect(apiHelper._getDataWithCodes).toHaveBeenCalledWith({ + url: EnvironmentVars.adminAddNotification, + token: 'token', + query_params: { user_email }, + additional_headers: { 'Content-Type': 'application/json' }, + }) + expect(dispatch).toHaveBeenCalledTimes(0 + 2) + }) + + it('Updates the state correctly pending', async () => { + const loading = true + const state = reducer(initialState, { + type: 'adminEditNotification/getNotificationData/pending', + }) + expect(state).toEqual({ + ...initialState, + loading, + value: { ...initialState.value }, + }) + }) + + it('Updates the state correctly fulfilled', async () => { + const loading = false + const apiData = 'apiData' as any + let successMsg = '' + let errorMsg = '' + let errorState = false + + let state = reducer( + { ...initialState, value: { ...initialState.value, apiData, successMsg: '' } }, + { + type: 'adminEditNotification/getNotificationData/fulfilled', + payload: { message: 'message', success: true, data: apiData }, + } + ) + + expect(state).toEqual({ + ...initialState, + loading, + value: { ...initialState.value, errorMsg, errorState, apiData, successMsg }, + }) + + // Error Case + successMsg = '' + errorMsg = 'message' + errorState = true + + state = reducer( + { + ...initialState, + value: { ...initialState.value, successMsg: '' }, + }, + { + type: 'adminEditNotification/getNotificationData/fulfilled', + payload: { message: 'message', success: false }, + } + ) + + expect(state).toEqual({ + ...initialState, + loading, + value: { ...initialState.value, successMsg, errorMsg, errorState }, + }) + }) + + it('Updates the state correctly rejected', async () => { + const loading = false + const state = reducer(initialState, { + type: 'adminEditNotification/getNotificationData/rejected', + }) + expect(state).toEqual({ ...initialState, loading, value: { ...initialState.value } }) + }) + }) + + describe('editNotification', () => { + it('returns and calls the api correctly', async () => { + let dispatch = jest.fn() + const getState = jest.fn().mockReturnValue({ + user: { + value: { + authLoginData: { token: 'token' }, + }, + }, + }) + const json = { data: 'data' } + let action = editNotification({ json }) + + global.setTimeout = jest.fn((cb) => cb()) as any + try { + apiHelper._patchData = jest.fn().mockReturnValue({ status: 200, message: 'message' }) + let resp = await action(dispatch, getState, undefined) + expect(resp.payload).toEqual({ success: true, message: 'Changes were successfully applied!' }) + expect(apiHelper._patchData).toHaveBeenCalledWith({ + url: EnvironmentVars.adminNotification, + token: 'token', + body: JSON.stringify(json), + }) + expect(setTimeout).toHaveBeenCalledTimes(1) + expect(dispatch).toHaveBeenCalledTimes(2 + 2) + } catch (e) { + ;(global.setTimeout as any).mockClear() + throw e + } + + dispatch = jest.fn() + action = editNotification({ json }) + global.setTimeout = jest.fn((cb) => cb()) as any + try { + apiHelper._patchData = jest.fn().mockReturnValue({ status: 500, message: 'message' }) + let resp = await action(dispatch, getState, undefined) + expect(resp.payload).toEqual({ success: false, message: 'message' }) + expect(apiHelper._patchData).toHaveBeenCalledWith({ + url: EnvironmentVars.adminNotification, + token: 'token', + body: JSON.stringify(json), + }) + expect(setTimeout).not.toHaveBeenCalled() + expect(dispatch).toHaveBeenCalledTimes(0 + 2) + } catch (e) { + ;(global.setTimeout as any).mockClear() + throw e + } + }) + + it('Updates the state correctly pending', async () => { + const loading = true + const state = reducer(initialState, { + type: 'adminEditNotification/editNotification/pending', + }) + expect(state).toEqual({ + ...initialState, + loading, + value: { ...initialState.value }, + }) + }) + + it('Updates the state correctly fulfilled', async () => { + const loading = false + let successMsg = 'message' + let errorMsg = '' + let errorState = false + + let state = reducer( + { ...initialState, value: { ...initialState.value } }, + { + type: 'adminEditNotification/editNotification/fulfilled', + payload: { message: 'message', success: true }, + } + ) + + expect(state).toEqual({ + ...initialState, + loading, + value: { ...initialState.value, successMsg, errorMsg, errorState }, + }) + + // Error Case + successMsg = '' + errorMsg = 'message' + errorState = true + + state = reducer(initialState, { + type: 'adminEditNotification/editNotification/fulfilled', + payload: { message: 'message', success: false }, + }) + + expect(state).toEqual({ + ...initialState, + loading, + value: { ...initialState.value, successMsg, errorMsg, errorState }, + }) + }) + + it('Updates the state correctly rejected', async () => { + const loading = false + const state = reducer(initialState, { + type: 'adminEditNotification/editNotification/rejected', + }) + expect(state).toEqual({ ...initialState, loading, value: { ...initialState.value } }) + }) + }) + + describe('submitForm', () => { + it('returns and calls the api correctly', async () => { + let dispatch = jest.fn() + let getState = jest.fn().mockReturnValue({ + user: { + value: { + authLoginData: { token: 'token' }, + }, + }, + adminEditNotification: { + value: { + selectedType: { type: 'type1' }, + }, + }, + adminNotificationTab: { + value: { + editNotificationRowData: { email: 'email' }, + }, + }, + }) + const data = { data: 'data' } as any + let updateEmailTypesApiData = jest.fn() + + let action = submitForm({ data }) + let resp = await action(dispatch, getState, undefined) + expect(resp.payload).toEqual(false) + expect(dispatch).toHaveBeenCalledTimes(1 + 2) + + // empty selectedOrganizations + dispatch = jest.fn() + getState = jest.fn().mockReturnValue({ + user: { + value: { + authLoginData: { token: 'token' }, + }, + }, + adminEditNotification: { + value: { + selectedType: { type: '' }, + }, + }, + adminNotificationTab: { + value: { + editNotificationRowData: { email: 'email' }, + }, + }, + }) + action = submitForm({ data }) + resp = await action(dispatch, getState, undefined) + expect(resp.payload).toEqual(true) + expect(dispatch).toHaveBeenCalledTimes(0 + 2) + }) + + it('Updates the state correctly fulfilled', async () => { + const submitAttempt = 'submitAttempt' + + const state = reducer(initialState, { + type: 'adminEditNotification/submitForm/fulfilled', + payload: submitAttempt, + }) + + expect(state).toEqual({ + ...initialState, + value: { ...initialState.value, submitAttempt }, + }) + }) + }) +}) + +describe('reducers', () => { + const initialState: RootState['adminEditNotification'] = { + loading: null, + value: { + successMsg: null, + availableTypes: null, + apiData: null, + errorState: null, + errorMsg: null, + submitAttempt: null, + selectedType: null, + }, + } + + it('setSuccessMsg reducer updates state correctly', async () => { + const successMsg = 'successMsg' + expect(reducer(initialState, setSuccessMsg(successMsg))).toEqual({ + ...initialState, + value: { ...initialState.value, successMsg }, + }) + }) + + it('setSelectedType reducer updates state correctly', async () => { + const selectedType = { type: 'type1' } + const payload = { type: 'type1' } + expect( + reducer({ ...initialState, value: { ...initialState.value, selectedType } }, setSelectedType(payload)) + ).toEqual({ + ...initialState, + value: { + ...initialState.value, + selectedType: { type: 'type1' }, + }, + }) + }) + + it('updateEmailTypesApiData', async () => { + const apiData = { + email_types: ['type1', 'type2'], + } + + expect( + reducer({ ...initialState, value: { ...initialState.value, apiData: apiData } }, updateEmailTypesApiData()) + ).toEqual({ + ...initialState, + value: { ...initialState.value, apiData: apiData, availableTypes: [{ type: 'type1' }, { type: 'type2' }] }, + }) + }) + + describe('selectors', () => { + const initialState = { + loading: 'loading', + value: { + successMsg: 'successMsg', + apiData: 'apiData', + errorState: 'errorState', + errorMsg: 'errorMsg', + submitAttempt: 'submitAttempt', + selectedType: 'selectedType', + user_email: 'user_email', + }, + } + const state = { adminEditNotification: initialState } as any + + it('selectors return the correct value', async () => { + expect(selectLoading(state)).toEqual('loading') + expect(selectSuccessMsg(state)).toEqual('successMsg') + expect(selectApiData(state)).toEqual('apiData') + expect(selectErrorState(state)).toEqual('errorState') + expect(selectErrorMsg(state)).toEqual('errorMsg') + expect(selectSubmitAttempt(state)).toEqual('submitAttempt') + expect(selectSelectedType(state)).toEqual('selectedType') + }) + }) +}) diff --git a/webapp/src/features/adminEditNotification/adminEditNotificationSlice.tsx b/webapp/src/features/adminEditNotification/adminEditNotificationSlice.tsx new file mode 100644 index 000000000..c3e8aad2e --- /dev/null +++ b/webapp/src/features/adminEditNotification/adminEditNotificationSlice.tsx @@ -0,0 +1,175 @@ +import { createAsyncThunk, createSlice } from '@reduxjs/toolkit' +import { selectEmail, selectToken } from '../../generalSlices/userSlice' +import EnvironmentVars from '../../EnvironmentVars' +import apiHelper from '../../apis/api-helper' +import { RootState } from '../../store' +import { getUserNotifications } from '../adminNotificationTab/adminNotificationTabSlice' +import { AdminNotificationForm } from '../adminAddNotification/adminAddNotificationSlice' + +export type AdminNotificationApiData = { + email_types: string[] +} + +const initialState = { + successMsg: '', + availableTypes: [] as { type: string }[], + apiData: {} as AdminNotificationApiData, + errorState: false, + errorMsg: '', + submitAttempt: false, + selectedType: { type: '' }, +} + +export const getNotificationData = createAsyncThunk( + 'adminEditNotification/getNotificationData', + async (_, { getState }) => { + const currentState = getState() as RootState + const token = selectToken(currentState) + const user_email = selectEmail(currentState) + + const data = await apiHelper._getDataWithCodes({ + url: EnvironmentVars.adminAddNotification, + token, + query_params: { user_email }, + additional_headers: { 'Content-Type': 'application/json' }, + }) + + switch (data.status) { + case 200: + return { success: true, message: '', data: data.body } + default: + return { success: false, message: data.message } + } + }, + { condition: (_, { getState }) => selectToken(getState() as RootState) != undefined } +) + +export const editNotification = createAsyncThunk( + 'adminEditNotification/editNotification', + async (payload: { json: Object }, { getState, dispatch }) => { + const { json } = payload + const currentState = getState() as RootState + const token = selectToken(currentState) + + const data = await apiHelper._patchData({ + url: EnvironmentVars.adminNotification, + token, + body: JSON.stringify(json), + }) + + switch (data.status) { + case 200: + dispatch(getUserNotifications()) + setTimeout(() => dispatch(adminEditNotificationSlice.actions.setSuccessMsg('')), 5000) + return { success: true, message: 'Changes were successfully applied!' } + default: + return { success: false, message: data.message } + } + }, + { condition: (_, { getState }) => selectToken(getState() as RootState) != undefined } +) + +export const submitForm = createAsyncThunk( + 'adminEditNotification/submitForm', + async (payload: { data: AdminNotificationForm }, { getState, dispatch }) => { + const { data } = payload + const currentState = getState() as RootState + + var tmpData = { + email: data.email, + old_email_type: currentState.adminNotificationTab.value.editNotificationRowData.email_type, + new_email_type: currentState.adminEditNotification.value.selectedType.type, + } + + if (currentState.adminEditNotification.value.selectedType.type !== '') { + dispatch(editNotification({ json: tmpData })) + return false + } else { + return true + } + } +) + +export const adminEditNotificationSlice = createSlice({ + name: 'adminEditNotification', + initialState: { + loading: false, + value: initialState, + }, + reducers: { + updateEmailTypesApiData: (state) => { + if (Object.keys(state.value.apiData).length !== 0) { + let typeData = [] as { type: string }[] + state.value.apiData.email_types.forEach((type) => typeData.push({ type })) + state.value.availableTypes = [...typeData] + } + }, + setSuccessMsg: (state, action) => { + state.value.successMsg = action.payload + }, + setSelectedType: ( + state, + action: { + payload: { + type: string + } + } + ) => { + state.value.selectedType = { type: action.payload.type } + }, + }, + extraReducers: (builder) => { + builder + .addCase(getNotificationData.pending, (state) => { + state.loading = true + }) + .addCase(getNotificationData.fulfilled, (state, action) => { + state.loading = false + if (action.payload.success) { + state.value.apiData = action.payload.data + state.value.errorMsg = '' + state.value.errorState = false + } else { + state.value.errorMsg = action.payload.message + state.value.errorState = true + } + }) + .addCase(getNotificationData.rejected, (state) => { + state.loading = false + }) + .addCase(editNotification.pending, (state) => { + state.loading = true + }) + .addCase(editNotification.fulfilled, (state, action) => { + state.loading = false + if (action.payload.success) { + state.value.successMsg = action.payload.message + state.value.errorMsg = '' + state.value.errorState = false + } else { + state.value.successMsg = '' + state.value.errorMsg = action.payload.message + state.value.errorState = true + } + }) + .addCase(editNotification.rejected, (state) => { + state.loading = false + }) + .addCase(submitForm.fulfilled, (state, action) => { + state.value.submitAttempt = action.payload + }) + }, +}) + +export const { setSuccessMsg, setSelectedType, updateEmailTypesApiData } = adminEditNotificationSlice.actions + +export const selectLoading = (state: RootState) => state.adminEditNotification.loading +export const selectSuccessMsg = (state: RootState) => state.adminEditNotification.value.successMsg +export const selectAvailableTypes = (state: RootState) => state.adminEditNotification.value.availableTypes +export const selectApiData = (state: RootState) => state.adminEditNotification.value.apiData +export const selectErrorState = (state: RootState) => state.adminEditNotification.value.errorState +export const selectErrorMsg = (state: RootState) => state.adminEditNotification.value.errorMsg +export const selectSubmitAttempt = (state: RootState) => state.adminEditNotification.value.submitAttempt +export const selectSelectedType = (state: RootState) => state.adminEditNotification.value.selectedType + +export default adminEditNotificationSlice.reducer From f974f52e2ffd1762a9a59149dfbfbafbcff6ed72 Mon Sep 17 00:00:00 2001 From: Marc Wodahl Date: Wed, 19 Jun 2024 11:25:43 -0600 Subject: [PATCH 074/189] Admin Add Notification --- .../AdminAddNotification.test.tsx | 16 + .../AdminAddNotification.tsx | 106 +++++ .../AdminAddNotification.test.tsx.snap | 108 +++++ .../adminAddNotificationSlice.test.ts | 425 ++++++++++++++++++ .../adminAddNotificationSlice.tsx | 192 ++++++++ 5 files changed, 847 insertions(+) create mode 100644 webapp/src/features/adminAddNotification/AdminAddNotification.test.tsx create mode 100644 webapp/src/features/adminAddNotification/AdminAddNotification.tsx create mode 100644 webapp/src/features/adminAddNotification/__snapshots__/AdminAddNotification.test.tsx.snap create mode 100644 webapp/src/features/adminAddNotification/adminAddNotificationSlice.test.ts create mode 100644 webapp/src/features/adminAddNotification/adminAddNotificationSlice.tsx diff --git a/webapp/src/features/adminAddNotification/AdminAddNotification.test.tsx b/webapp/src/features/adminAddNotification/AdminAddNotification.test.tsx new file mode 100644 index 000000000..c129f0f21 --- /dev/null +++ b/webapp/src/features/adminAddNotification/AdminAddNotification.test.tsx @@ -0,0 +1,16 @@ +import React from 'react' +import { render } from '@testing-library/react' +import AdminAddNotification from '../adminAddNotification/AdminAddNotification' +import { Provider } from 'react-redux' +import { setupStore } from '../../store' +import { replaceChaoticIds } from '../../utils/test-utils' + +it('should take a snapshot', () => { + const { container } = render( + + + + ) + + expect(replaceChaoticIds(container)).toMatchSnapshot() +}) diff --git a/webapp/src/features/adminAddNotification/AdminAddNotification.tsx b/webapp/src/features/adminAddNotification/AdminAddNotification.tsx new file mode 100644 index 000000000..ec6ad3b24 --- /dev/null +++ b/webapp/src/features/adminAddNotification/AdminAddNotification.tsx @@ -0,0 +1,106 @@ +import React, { useEffect } from 'react' +import { Form } from 'react-bootstrap' +import { useForm } from 'react-hook-form' +import { DropdownList } from 'react-widgets' +import { + selectSuccessMsg, + selectSelectedType, + selectAvailableTypes, + selectApiData, + selectErrorState, + selectErrorMsg, + selectSubmitAttempt, + + // actions + submitForm, + AdminNotificationForm, + setSelectedType, + updateEmailTypesApiData, + getNotificationData, +} from './adminAddNotificationSlice' +import { selectEmail } from '../../generalSlices/userSlice' +import { useSelector, useDispatch } from 'react-redux' + +import '../adminRsuTab/Admin.css' +import 'react-widgets/styles.css' +import { AnyAction, ThunkDispatch } from '@reduxjs/toolkit' +import { RootState } from '../../store' + +const AdminAddNotification = () => { + const dispatch: ThunkDispatch = useDispatch() + const successMsg = useSelector(selectSuccessMsg) + const apiData = useSelector(selectApiData) + const errorState = useSelector(selectErrorState) + const errorMsg = useSelector(selectErrorMsg) + const submitAttempt = useSelector(selectSubmitAttempt) + const selectedType = useSelector(selectSelectedType) + const availableTypes = useSelector(selectAvailableTypes) + const userEmail = useSelector(selectEmail) + const { + register, + handleSubmit, + reset, + formState: { errors }, + } = useForm() + + useEffect(() => { + dispatch(getNotificationData()) + }, [dispatch]) + + useEffect(() => { + dispatch(updateEmailTypesApiData()) + }, [apiData, dispatch]) + + const onSubmit = (data: AdminNotificationForm) => dispatch(submitForm({ data, reset })) + + return ( +
+
+ + Email + + + + + Email Notification + { + dispatch(setSelectedType(value)) + }} + /> + + + {selectedType.type === '' && submitAttempt && ( +

+ Must select at least one email notification type +

+ )} + + {successMsg && ( +

+ {successMsg} +

+ )} + {errorState && ( +

+ Failed to add email notification due to error: {errorMsg} +

+ )} +
+ + +
+
+
+ ) +} + +export default AdminAddNotification diff --git a/webapp/src/features/adminAddNotification/__snapshots__/AdminAddNotification.test.tsx.snap b/webapp/src/features/adminAddNotification/__snapshots__/AdminAddNotification.test.tsx.snap new file mode 100644 index 000000000..e77dae26b --- /dev/null +++ b/webapp/src/features/adminAddNotification/__snapshots__/AdminAddNotification.test.tsx.snap @@ -0,0 +1,108 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`should take a snapshot 1`] = ` +
+
+
+
+ + +
+
+ + +
+ +
+
+
+
+
+`; diff --git a/webapp/src/features/adminAddNotification/adminAddNotificationSlice.test.ts b/webapp/src/features/adminAddNotification/adminAddNotificationSlice.test.ts new file mode 100644 index 000000000..76fe7bd29 --- /dev/null +++ b/webapp/src/features/adminAddNotification/adminAddNotificationSlice.test.ts @@ -0,0 +1,425 @@ +import reducer, { + // async thunks + resetForm, + submitForm, + + // reducers + setSuccessMsg, + + // selectors + selectLoading, + selectSuccessMsg, + selectApiData, + selectErrorState, + selectErrorMsg, + selectSubmitAttempt, + selectUserEmail, + selectSelectedType, + selectAvailableTypes, + updateEmailTypesApiData, + setSelectedType, + createNotification, + getNotificationData, +} from '../adminAddNotification/adminAddNotificationSlice' +import apiHelper from '../../apis/api-helper' +import EnvironmentVars from '../../EnvironmentVars' +import { RootState } from '../../store' +describe('admin add User reducer', () => { + it('should handle initial state', () => { + expect(reducer(undefined, { type: 'unknown' })).toEqual({ + loading: false, + value: { + successMsg: '', + availableTypes: [], + apiData: {}, + errorState: false, + errorMsg: '', + submitAttempt: false, + selectedType: { type: '' }, + user_email: '', + }, + }) + }) +}) + +describe('async thunks', () => { + var initialState: RootState['adminAddNotification'] = { + loading: null, + value: { + successMsg: null, + availableTypes: null, + apiData: null, + errorState: null, + errorMsg: null, + submitAttempt: null, + selectedType: null, + user_email: null, + }, + } + + beforeAll(() => { + jest.mock('../../apis/api-helper') + }) + + afterAll(() => { + jest.unmock('../../apis/api-helper') + }) + + describe('getNotificationData', () => { + it('returns and calls the api correctly', async () => { + let dispatch = jest.fn() + const getState = jest.fn().mockReturnValue({ + user: { + value: { + authLoginData: { token: 'token' }, + }, + }, + }) + const action = getNotificationData() + + apiHelper._getDataWithCodes = jest.fn().mockReturnValue({ status: 200, message: 'message', body: 'body' }) + let resp = await action(dispatch, getState, undefined) + expect(resp.payload).toEqual({ success: true, message: '', data: 'body' }) + expect(apiHelper._getDataWithCodes).toHaveBeenCalledWith({ + url: EnvironmentVars.adminAddNotification, + token: 'token', + query_params: { user_email: undefined }, + additional_headers: { 'Content-Type': 'application/json' }, + }) + + apiHelper._getDataWithCodes = jest.fn().mockReturnValue({ status: 500, message: 'message', body: 'body' }) + resp = await action(dispatch, getState, undefined) + expect(resp.payload).toEqual({ success: false, message: 'message' }) + expect(apiHelper._getDataWithCodes).toHaveBeenCalledWith({ + url: EnvironmentVars.adminAddNotification, + token: 'token', + query_params: { user_email: undefined }, + additional_headers: { 'Content-Type': 'application/json' }, + }) + }) + + it('Updates the state correctly pending', async () => { + const loading = true + const state = reducer(initialState, { + type: 'adminAddNotification/getNotificationData/pending', + }) + expect(state).toEqual({ + ...initialState, + loading, + value: { ...initialState.value }, + }) + }) + + it('Updates the state correctly fulfilled', async () => { + const loading = false + const apiData = 'data' + let errorMsg = '' + let errorState = false + + let state = reducer(initialState, { + type: 'adminAddNotification/getNotificationData/fulfilled', + payload: { data: apiData, success: true }, + }) + + expect(state).toEqual({ + ...initialState, + loading, + value: { ...initialState.value, errorMsg, errorState, apiData }, + }) + + // Error Case + errorMsg = 'message' + errorState = true + + state = reducer(initialState, { + type: 'adminAddNotification/getNotificationData/fulfilled', + payload: { message: 'message', success: false }, + }) + + expect(state).toEqual({ + ...initialState, + loading, + value: { ...initialState.value, errorMsg, errorState }, + }) + }) + + it('Updates the state correctly rejected', async () => { + const loading = false + const state = reducer(initialState, { + type: 'adminAddNotification/getNotificationData/rejected', + }) + expect(state).toEqual({ ...initialState, loading, value: { ...initialState.value } }) + }) + }) + + describe('createNotification', () => { + it('returns and calls the api correctly', async () => { + let dispatch = jest.fn() + const getState = jest.fn().mockReturnValue({ + user: { + value: { + authLoginData: { token: 'token' }, + }, + }, + }) + const json = { data: 'data' } as any + + let reset = jest.fn() + let action = createNotification({ json, reset }) + apiHelper._postData = jest + .fn() + .mockReturnValue({ status: 200, message: 'Email Notification Creation is successful.' }) + let resp = await action(dispatch, getState, undefined) + expect(resp.payload).toEqual({ success: true, message: 'Email Notification Creation is successful.' }) + expect(apiHelper._postData).toHaveBeenCalledWith({ + url: EnvironmentVars.adminAddNotification, + token: 'token', + body: JSON.stringify(json), + }) + expect(dispatch).toHaveBeenCalledTimes(2 + 2) + + // Error Code Other + dispatch = jest.fn() + action = createNotification({ json, reset }) + apiHelper._postData = jest.fn().mockReturnValue({ status: 500, message: 'message' }) + resp = await action(dispatch, getState, undefined) + expect(resp.payload).toEqual({ success: false, message: 'message' }) + expect(apiHelper._postData).toHaveBeenCalledWith({ + url: EnvironmentVars.adminAddNotification, + token: 'token', + body: JSON.stringify(json), + }) + expect(dispatch).toHaveBeenCalledTimes(0 + 2) + }) + + it('Updates the state correctly pending', async () => { + const loading = true + const state = reducer(initialState, { + type: 'adminAddNotification/createNotification/pending', + }) + expect(state).toEqual({ + ...initialState, + loading, + value: { ...initialState.value }, + }) + }) + + it('Updates the state correctly fulfilled', async () => { + const loading = false + let successMsg = 'message' + let errorMsg = '' + let errorState = false + + let state = reducer(initialState, { + type: 'adminAddNotification/createNotification/fulfilled', + payload: { message: 'message', success: true }, + }) + + expect(state).toEqual({ + ...initialState, + loading, + value: { ...initialState.value, successMsg, errorMsg, errorState }, + }) + + // Error Case + successMsg = '' + errorMsg = 'message' + errorState = true + + state = reducer(initialState, { + type: 'adminAddNotification/createNotification/fulfilled', + payload: { message: 'message', success: false }, + }) + + expect(state).toEqual({ + ...initialState, + loading, + value: { ...initialState.value, successMsg, errorMsg, errorState }, + }) + }) + + it('Updates the state correctly rejected', async () => { + const loading = false + const state = reducer(initialState, { + type: 'adminAddNotification/createNotification/rejected', + }) + expect(state).toEqual({ ...initialState, loading, value: { ...initialState.value } }) + }) + }) + + describe('resetForm', () => { + it('returns and calls the api correctly', async () => { + let dispatch = jest.fn() + let getState = jest.fn().mockReturnValue({ + user: { + value: { + authLoginData: { token: 'token' }, + }, + }, + }) + + let reset = jest.fn() + global.setTimeout = jest.fn((cb) => cb()) as any + try { + let action = resetForm(reset) + await action(dispatch, getState, undefined) + expect(reset).toHaveBeenCalledTimes(1) + expect(global.setTimeout).toHaveBeenCalledTimes(1) + expect(dispatch).toHaveBeenCalledTimes(2 + 2) + } catch (e) { + ;(global.setTimeout as any).mockClear() + throw e + } + }) + + it('Updates the state correctly fulfilled', async () => { + const selectedType = { type: '' } + + const state = reducer(initialState, { + type: 'adminAddNotification/resetForm/fulfilled', + }) + + expect(state).toEqual({ + ...initialState, + value: { ...initialState.value, selectedType }, + }) + }) + }) + + describe('submitForm', () => { + it('returns and calls the api correctly', async () => { + let dispatch = jest.fn() + let getState = jest.fn().mockReturnValue({ + user: { + value: { + authLoginData: { token: 'token' }, + }, + }, + adminAddNotification: { + value: { + selectedType: { type: 'type1' }, + }, + }, + }) + const data = { data: 'data' } as any + + let reset = jest.fn() + let action = submitForm({ data, reset }) + let resp = await action(dispatch, getState, undefined) + expect(resp.payload).toEqual(false) + expect(dispatch).toHaveBeenCalledTimes(1 + 2) + + // invalid checkForm + dispatch = jest.fn() + getState = jest.fn().mockReturnValue({ + user: { + value: { + authLoginData: { token: 'token' }, + }, + }, + adminAddNotification: { + value: { + selectedType: { type: '' }, + }, + }, + }) + action = submitForm({ data, reset }) + resp = await action(dispatch, getState, undefined) + expect(resp.payload).toEqual(true) + expect(dispatch).toHaveBeenCalledTimes(0 + 2) + }) + + it('Updates the state correctly fulfilled', async () => { + const submitAttempt = 'submitAttempt' + + const state = reducer(initialState, { + type: 'adminAddNotification/submitForm/fulfilled', + payload: submitAttempt, + }) + + expect(state).toEqual({ + ...initialState, + value: { ...initialState.value, submitAttempt }, + }) + }) + }) +}) + +describe('reducers', () => { + const initialState: RootState['adminAddNotification'] = { + loading: null, + value: { + successMsg: null, + availableTypes: null, + apiData: null, + errorState: null, + errorMsg: null, + submitAttempt: null, + selectedType: null, + user_email: null, + }, + } + + it('updateEmailTypesApiData reducer updates state correctly', async () => { + const apiData = { + email_types: ['type1', 'type2'], + } as any + expect(reducer({ ...initialState, value: { ...initialState.value, apiData } }, updateEmailTypesApiData())).toEqual({ + ...initialState, + value: { + ...initialState.value, + availableTypes: [{ type: 'type1' }, { type: 'type2' }], + apiData, + }, + }) + }) + + it('setSuccessMsg reducer updates state correctly', async () => { + const successMsg = 'successMsg' + expect(reducer(initialState, setSuccessMsg(successMsg))).toEqual({ + ...initialState, + value: { ...initialState.value, successMsg }, + }) + }) + + it('setSelectedType reducer updates state correctly', async () => { + const payload = { type: 'type1' } as any + expect(reducer({ ...initialState, value: { ...initialState.value } }, setSelectedType(payload))).toEqual({ + ...initialState, + value: { + ...initialState.value, + selectedType: { type: 'type1' }, + }, + }) + }) +}) + +describe('selectors', () => { + const initialState = { + loading: 'loading', + value: { + successMsg: 'successMsg', + availableTypes: 'availableTypes', + apiData: 'apiData', + errorState: 'errorState', + errorMsg: 'errorMsg', + submitAttempt: 'submitAttempt', + selectedType: 'selectedType', + user_email: 'user_email', + }, + } + const state = { adminAddNotification: initialState } as any + + it('selectors return the correct value', async () => { + expect(selectLoading(state)).toEqual('loading') + + expect(selectSuccessMsg(state)).toEqual('successMsg') + expect(selectApiData(state)).toEqual('apiData') + expect(selectErrorState(state)).toEqual('errorState') + expect(selectErrorMsg(state)).toEqual('errorMsg') + expect(selectSubmitAttempt(state)).toEqual('submitAttempt') + expect(selectAvailableTypes(state)).toEqual('availableTypes') + expect(selectSelectedType(state)).toEqual('selectedType') + expect(selectUserEmail(state)).toEqual('user_email') + }) +}) diff --git a/webapp/src/features/adminAddNotification/adminAddNotificationSlice.tsx b/webapp/src/features/adminAddNotification/adminAddNotificationSlice.tsx new file mode 100644 index 000000000..fb4c90fbe --- /dev/null +++ b/webapp/src/features/adminAddNotification/adminAddNotificationSlice.tsx @@ -0,0 +1,192 @@ +import { createAsyncThunk, createSlice } from '@reduxjs/toolkit' +import { selectEmail, selectToken } from '../../generalSlices/userSlice' +import EnvironmentVars from '../../EnvironmentVars' +import apiHelper from '../../apis/api-helper' +import { getUserNotifications } from '../adminNotificationTab/adminNotificationTabSlice' +import { RootState } from '../../store' + +export type AdminNotificationForm = { + email: string + email_type: string +} + +export type AdminNotificationApiData = { + email_types: string[] +} + +export type AdminNotificationRow = { + first_name: string + last_name: string + email: string + email_type: string +} + +const initialState = { + successMsg: '', + availableTypes: [] as { type: string }[], + apiData: {} as AdminNotificationApiData, + errorState: false, + errorMsg: '', + submitAttempt: false, + selectedType: { type: '' }, + user_email: '', +} + +export const getNotificationData = createAsyncThunk( + 'adminAddNotification/getNotificationData', + async (_, { getState }) => { + const currentState = getState() as RootState + const token = selectToken(currentState) + const user_email = selectEmail(currentState) + + const data = await apiHelper._getDataWithCodes({ + url: EnvironmentVars.adminAddNotification, + token, + query_params: { user_email }, + additional_headers: { 'Content-Type': 'application/json' }, + }) + + switch (data.status) { + case 200: + return { success: true, message: '', data: data.body } + default: + return { success: false, message: data.message } + } + }, + { condition: (_, { getState }) => selectToken(getState() as RootState) != undefined } +) + +export const createNotification = createAsyncThunk( + 'adminAddNotification/createNotification', + async (payload: { json: AdminNotificationForm; reset: () => void }, { getState, dispatch }) => { + const { json, reset } = payload + const currentState = getState() as RootState + const token = selectToken(currentState) + + const data = await apiHelper._postData({ + url: EnvironmentVars.adminAddNotification, + token, + body: JSON.stringify(json), + }) + + switch (data.status) { + case 200: + dispatch(getUserNotifications()) + dispatch(resetForm(reset)) + return { success: true, message: 'Email Notification Creation is successful.' } + default: + return { success: false, message: data.message } + } + }, + { condition: (_, { getState }) => selectToken(getState() as RootState) != undefined } +) + +export const resetForm = createAsyncThunk('adminAddNotification/resetForm', async (reset: () => void, { dispatch }) => { + reset() + dispatch(adminAddNotificationSlice.actions.setSelectedType({ type: '' })) + setTimeout(() => dispatch(adminAddNotificationSlice.actions.setSuccessMsg('')), 5000) +}) + +export const submitForm = createAsyncThunk( + 'adminAddNotification/submitForm', + async (payload: { data: AdminNotificationForm; reset: () => void }, { getState, dispatch }) => { + const { data, reset } = payload + data.email_type = (getState() as RootState).adminAddNotification.value.selectedType.type + const currentState = getState() as RootState + if (currentState.adminAddNotification.value.selectedType.type !== '') { + dispatch(createNotification({ json: data, reset })) + return false + } else { + return true + } + }, + { condition: (_, { getState }) => selectToken(getState() as RootState) != undefined } +) + +export const adminAddNotificationSlice = createSlice({ + name: 'adminAddNotification', + initialState: { + loading: false, + value: initialState, + }, + reducers: { + updateEmailTypesApiData: (state) => { + if (Object.keys(state.value.apiData).length !== 0) { + let typeData = [] as { type: string }[] + state.value.apiData.email_types.forEach((type) => typeData.push({ type })) + state.value.availableTypes = [...typeData] + } + }, + setSuccessMsg: (state, action) => { + state.value.successMsg = action.payload + }, + setSelectedType: ( + state, + action: { + payload: { + type: string + } + } + ) => { + state.value.selectedType = { type: action.payload.type } + }, + }, + extraReducers: (builder) => { + builder + .addCase(getNotificationData.pending, (state) => { + state.loading = true + }) + .addCase(getNotificationData.fulfilled, (state, action) => { + state.loading = false + if (action.payload.success) { + state.value.apiData = action.payload.data + state.value.errorMsg = '' + state.value.errorState = false + } else { + state.value.errorMsg = action.payload.message + state.value.errorState = true + } + }) + .addCase(getNotificationData.rejected, (state) => { + state.loading = false + }) + .addCase(createNotification.pending, (state) => { + state.loading = true + }) + .addCase(createNotification.fulfilled, (state, action) => { + state.loading = false + if (action.payload.success) { + state.value.errorMsg = '' + state.value.errorState = false + state.value.successMsg = action.payload.message + } else { + state.value.errorMsg = action.payload.message + state.value.errorState = true + state.value.successMsg = '' + } + }) + .addCase(createNotification.rejected, (state) => { + state.loading = false + }) + .addCase(resetForm.fulfilled, (state) => { + state.value.selectedType = { type: '' } + }) + .addCase(submitForm.fulfilled, (state, action) => { + state.value.submitAttempt = action.payload + }) + }, +}) + +export const { setSuccessMsg, setSelectedType, updateEmailTypesApiData } = adminAddNotificationSlice.actions + +export const selectLoading = (state: RootState) => state.adminAddNotification.loading +export const selectSuccessMsg = (state: RootState) => state.adminAddNotification.value.successMsg +export const selectApiData = (state: RootState) => state.adminAddNotification.value.apiData +export const selectErrorState = (state: RootState) => state.adminAddNotification.value.errorState +export const selectErrorMsg = (state: RootState) => state.adminAddNotification.value.errorMsg +export const selectSubmitAttempt = (state: RootState) => state.adminAddNotification.value.submitAttempt +export const selectSelectedType = (state: RootState) => state.adminAddNotification.value.selectedType +export const selectAvailableTypes = (state: RootState) => state.adminAddNotification.value.availableTypes +export const selectUserEmail = (state: RootState) => state.adminAddNotification.value.user_email + +export default adminAddNotificationSlice.reducer From 2b590d6c2d8464ad850ff2782be64b8ae0a16dde Mon Sep 17 00:00:00 2001 From: Marc Wodahl Date: Wed, 19 Jun 2024 11:26:25 -0600 Subject: [PATCH 075/189] yarn, store update --- webapp/src/store.tsx | 6 ++++++ webapp/yarn.lock | 5 +++++ 2 files changed, 11 insertions(+) diff --git a/webapp/src/store.tsx b/webapp/src/store.tsx index eb1c076b8..20078fc31 100644 --- a/webapp/src/store.tsx +++ b/webapp/src/store.tsx @@ -14,6 +14,9 @@ import adminOrganizationTabUserReducer from './features/adminOrganizationTabUser import adminOrganizationTabRsuReducer from './features/adminOrganizationTabRsu/adminOrganizationTabRsuSlice' import adminRsuTabReducer from './features/adminRsuTab/adminRsuTabSlice' import adminUserTabReducer from './features/adminUserTab/adminUserTabSlice' +import adminNotificationTabReducer from './features/adminNotificationTab/adminNotificationTabSlice' +import adminAddNotificationReducer from './features/adminAddNotification/adminAddNotificationSlice' +import adminEditNotificationReducer from './features/adminEditNotification/adminEditNotificationSlice' import menuReducer from './features/menu/menuSlice' export const setupStore = (preloadedState: any) => { @@ -34,6 +37,9 @@ export const setupStore = (preloadedState: any) => { adminOrganizationTabRsu: adminOrganizationTabRsuReducer, adminRsuTab: adminRsuTabReducer, adminUserTab: adminUserTabReducer, + adminNotificationTab: adminNotificationTabReducer, + adminAddNotification: adminAddNotificationReducer, + adminEditNotification: adminEditNotificationReducer, menu: menuReducer, }, preloadedState, diff --git a/webapp/yarn.lock b/webapp/yarn.lock index 4581822ab..edd3c8da6 100644 --- a/webapp/yarn.lock +++ b/webapp/yarn.lock @@ -6338,6 +6338,11 @@ fs.realpath@^1.0.0: resolved "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz" integrity sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw== +fsevents@^2.1.2, fsevents@^2.3.2, fsevents@~2.3.2: + version "2.3.2" + resolved "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz" + integrity sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA== + function-bind@^1.1.1, function-bind@^1.1.2: version "1.1.2" resolved "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz" From 330d92aa6132f616ce83a6fd88ed2325cd8c28cf Mon Sep 17 00:00:00 2001 From: Marc Wodahl Date: Wed, 19 Jun 2024 11:27:08 -0600 Subject: [PATCH 076/189] Add new API endpoints --- services/api/src/admin_email_notification.py | 184 ++++++++++++++++++ .../api/src/admin_new_email_notification.py | 124 ++++++++++++ services/api/src/main.py | 4 + services/api/src/middleware.py | 2 + 4 files changed, 314 insertions(+) create mode 100644 services/api/src/admin_email_notification.py create mode 100644 services/api/src/admin_new_email_notification.py diff --git a/services/api/src/admin_email_notification.py b/services/api/src/admin_email_notification.py new file mode 100644 index 000000000..df51f6552 --- /dev/null +++ b/services/api/src/admin_email_notification.py @@ -0,0 +1,184 @@ +import logging +import common.pgquery as pgquery +import sqlalchemy +import os + + +def get_notification_data(user_email): + query = ( + "SELECT to_jsonb(row) " + "FROM (" + "SELECT u.email, u.first_name, u.last_name, e.email_type " + "FROM public.user_email_notification " + "JOIN public.users AS u ON u.user_id = user_email_notification.user_id " + "JOIN public.email_type AS e ON e.email_type_id = user_email_notification.email_type_id " + f"WHERE user_email_notification.user_id IN (SELECT user_id FROM public.users WHERE email = '{user_email}')" + ") as row" + ) + + data = pgquery.query_db(query) + + notification_dict = {} + for row in data: + row = dict(row[0]) + notification_dict[row["email_type"]] = { + "email": row["email"], + "first_name": row["first_name"], + "last_name": row["last_name"], + "email_type": row["email_type"] + } + + print(notification_dict) + notification_list = list(notification_dict.values()) + print(notification_list) + # If list is empty and a single user was requested, return empty object + if len(notification_list) == 0: + return [] + else: + return notification_list + + +def get_modify_notification_data(user_email): + modify_notification_obj = {} + modify_notification_obj["notification_data"] = get_notification_data(user_email) + return modify_notification_obj + + +def check_safe_input(notification_spec): + special_characters = "!\"#$%&'()*+,./:;<=>?@[\]^`{|}~" + special_characters_email = "!\"#$%&'()*+,/:;<=>?[\]^`{|}~" + # Check all string based fields for special characters + if ( + any(c in special_characters_email for c in notification_spec["email"]) + or "--" in notification_spec["email"] + ): + return False + if ( + any(c in special_characters for c in notification_spec["old_email_type"]) + or "--" in notification_spec["old_email_type"] + ): + return False + if ( + any(c in special_characters for c in notification_spec["new_email_type"]) + or "--" in notification_spec["new_email_type"] + ): + return False + return True + + +def modify_notification(notification_spec): + # Check for special characters for potential SQL injection + if not check_safe_input(notification_spec): + return { + "message": "No special characters are allowed: !\"#$%&'()*+,./:;<=>?@[\]^`{|}~. No sequences of '-' characters are allowed" + }, 500 + + try: + # Modify the existing user data + query = ( + "UPDATE public.user_email_notification SET " + f"email_type_id = (SELECT email_type_id FROM public.email_type WHERE email_type = '{notification_spec['new_email_type']}') " + f"WHERE user_id = (SELECT user_id FROM public.users WHERE email = '{notification_spec['email']}') " + f"AND email_type_id = (SELECT email_type_id FROM public.email_type WHERE email_type = '{notification_spec['old_email_type']}')" + ) + pgquery.write_db(query) + + except sqlalchemy.exc.IntegrityError as e: + failed_value = e.orig.args[0]["D"] + failed_value = failed_value.replace("(", '"') + failed_value = failed_value.replace(")", '"') + failed_value = failed_value.replace("=", " = ") + logging.error(f"Exception encountered: {failed_value}") + return {"message": failed_value}, 500 + except Exception as e: + logging.error(f"Exception encountered: {e}") + return {"message": "Encountered unknown issue"}, 500 + + return {"message": "Email notification successfully modified"}, 200 + + +def delete_notification(user_email, email_type): + # Delete user-to-organization relationships + notification_remove_query = ( + "DELETE FROM public.user_email_notification WHERE " + f"user_id IN (SELECT user_id FROM public.users WHERE email = '{user_email}') " + f"AND email_type_id IN (SELECT email_type_id FROM public.email_type WHERE email_type = '{email_type}')" + ) + pgquery.write_db(notification_remove_query) + + return {"message": "Email notification successfully deleted"} + + +# REST endpoint resource class +from flask import request, abort +from flask_restful import Resource +from marshmallow import Schema, fields +import urllib.request + + +class AdminNotificationGetSchema(Schema): + user_email = fields.Str(required=True) + + +class AdminNotificationDeleteSchema(Schema): + email = fields.Str(required=True) + email_type = fields.Str(required=True) + + +class AdminNotificationPatchSchema(Schema): + email = fields.Str(required=True) + old_email_type = fields.Str(required=True) + new_email_type = fields.Str(required=True) + + +class AdminNotification(Resource): + options_headers = { + "Access-Control-Allow-Origin": os.environ["CORS_DOMAIN"], + "Access-Control-Allow-Headers": "Content-Type,Authorization", + "Access-Control-Allow-Methods": "GET,PATCH,DELETE", + "Access-Control-Max-Age": "3600", + } + + headers = { + "Access-Control-Allow-Origin": os.environ["CORS_DOMAIN"], + "Content-Type": "application/json", + } + + def options(self): + # CORS support + return ("", 204, self.options_headers) + + def get(self): + logging.debug("AdminNotification GET requested") + schema = AdminNotificationGetSchema() + errors = schema.validate(request.args) + if errors: + logging.error(errors) + abort(400, errors) + + user_email = urllib.request.unquote(request.args["user_email"]) + return (get_modify_notification_data(user_email), 200, self.headers) + + def patch(self): + logging.debug("AdminUser PATCH requested") + # Check for main body values + schema = AdminNotificationPatchSchema() + errors = schema.validate(request.json) + if errors: + logging.error(str(errors)) + abort(400, str(errors)) + + data, code = modify_notification(request.json) + return (data, code, self.headers) + + def delete(self): + logging.debug("AdminNotification DELETE requested") + schema = AdminNotificationDeleteSchema() + errors = schema.validate(request.args) + if errors: + logging.error(errors) + abort(400, errors) + + user_email = urllib.request.unquote(request.args["email"]) + email_type = urllib.request.unquote(request.args["email_type"]) + return (delete_notification(user_email, email_type), 200, self.headers) diff --git a/services/api/src/admin_new_email_notification.py b/services/api/src/admin_new_email_notification.py new file mode 100644 index 000000000..526e35ccd --- /dev/null +++ b/services/api/src/admin_new_email_notification.py @@ -0,0 +1,124 @@ +import logging +import common.pgquery as pgquery +import sqlalchemy +import os + +def query_and_return_list(query): + data = pgquery.query_db(query) + return_list = [] + for row in data: + return_list.append(" ".join(row)) + return return_list + +def get_allowed_types(user_email): + allowed = {} + + email_types_query = ( + "SELECT email_type FROM public.email_type WHERE email_type_id NOT IN (" + "SELECT email_type_id FROM public.user_email_notification WHERE user_id =" + f"(SELECT user_id FROM public.users WHERE email = '{user_email}'))" +) + + allowed["email_types"] = query_and_return_list(email_types_query) + + return allowed + + +def check_safe_input(notification_spec): + special_characters = "!\"#$%&'()*+,./:;<=>?@[\]^`{|}~" + special_characters_email = "!\"#$%&'()*+,/:;<=>?[\]^`{|}~" + # Check all string based fields for special characters + if ( + any(c in special_characters_email for c in notification_spec["email"]) + or "--" in notification_spec["email"] + ): + return False + if ( + any(c in special_characters for c in notification_spec["email_type"]) + or "--" in notification_spec["email_type"] + ): + return False + return True + +def add_notification(notification_spec): + # Check for special characters for potential SQL injection + if not check_safe_input(notification_spec): + return { + "message": "No special characters are allowed: !\"#$%&'()*+,./:;<=>?@[\]^`{|}~. No sequences of '-' characters are allowed" + }, 500 + try: + notification_insert_query = ( + "INSERT into public.user_email_notification(user_id, email_type_id) VALUES (" + f"(SELECT user_id FROM public.users WHERE email='{notification_spec['email']}'), " + f"(SELECT email_type_id FROM public.email_type WHERE email_type='{notification_spec['email_type']}'))" + ) + pgquery.write_db(notification_insert_query) + + except sqlalchemy.exc.IntegrityError as e: + failed_value = e.orig.args[0]["D"] + failed_value = failed_value.replace("(", '"') + failed_value = failed_value.replace(")", '"') + failed_value = failed_value.replace("=", " = ") + logging.error(f"Exception encountered: {failed_value}") + return {"message": failed_value}, 500 + except Exception as e: + logging.error(f"Exception encountered: {e}") + return {"message": "Encountered unknown issue"}, 500 + + return {"message": "New email notification successfully added"}, 200 + + +# REST endpoint resource class +from flask import request, abort +from flask_restful import Resource +from marshmallow import Schema, fields +import urllib.request + + +class AdminNewNotificationSchema(Schema): + email = fields.Str(required=True) + email_type = fields.Str(required=True) + +class AdminGetNotificationSchema(Schema): + user_email = fields.Str(required=True) + + +class AdminNewNotification(Resource): + options_headers = { + "Access-Control-Allow-Origin": os.environ["CORS_DOMAIN"], + "Access-Control-Allow-Headers": "Content-Type,Authorization", + "Access-Control-Allow-Methods": "GET,POST", + "Access-Control-Max-Age": "3600", + } + + headers = { + "Access-Control-Allow-Origin": os.environ["CORS_DOMAIN"], + "Content-Type": "application/json", + } + + def options(self): + # CORS support + return ("", 204, self.options_headers) + + def get(self): + logging.debug("AdminNewNotification GET requested") + # Check for main body values + schema = AdminGetNotificationSchema() + errors = schema.validate(request.args) + if errors: + logging.error(str(errors)) + abort(400, str(errors)) + user_email = urllib.request.unquote(request.args["user_email"]) + return (get_allowed_types(user_email), 200, self.headers) + + def post(self): + logging.debug("AdminNewNotification POST requested") + # Check for main body values + schema = AdminNewNotificationSchema() + errors = schema.validate(request.json) + if errors: + logging.error(str(errors)) + abort(400, str(errors)) + + data, code = add_notification(request.json) + return (data, code, self.headers) diff --git a/services/api/src/main.py b/services/api/src/main.py index ad3b6e6ea..6d4040e7d 100644 --- a/services/api/src/main.py +++ b/services/api/src/main.py @@ -5,6 +5,8 @@ # Custom script imports from middleware import Middleware +from admin_email_notification import AdminNotification +from admin_new_email_notification import AdminNewNotification from userauth import UserAuth from healthcheck import HealthCheck from rsuinfo import RsuInfo @@ -57,6 +59,8 @@ api.add_resource(AdminUser, "/admin-user") api.add_resource(AdminNewOrg, "/admin-new-org") api.add_resource(AdminOrg, "/admin-org") +api.add_resource(AdminNotification, "/admin-notification") +api.add_resource(AdminNewNotification, "/admin-new-notification") api.add_resource(ContactSupportResource, "/contact-support") api.add_resource(UnsubErrorEmails, "/unsubscribe-error-emails/") diff --git a/services/api/src/middleware.py b/services/api/src/middleware.py index 9c8d0b0b3..97858c4c1 100644 --- a/services/api/src/middleware.py +++ b/services/api/src/middleware.py @@ -59,6 +59,8 @@ def get_user_role(token): "/admin-new-org": False, "/admin-org": False, "/rsu-geo-query": True, + "/admin-new-notification": False, + "/admin-notification": False, } From 5a51ad659bf6f3c4be3bcb4e62e04085ac05f2a4 Mon Sep 17 00:00:00 2001 From: Marc Wodahl Date: Wed, 19 Jun 2024 11:29:22 -0600 Subject: [PATCH 077/189] Remove previously used .env email variables --- README.md | 1 - docker-compose-addons.yml | 4 +--- docker-compose.yml | 1 - resources/kubernetes/cv-manager-api.yaml | 2 -- resources/kubernetes/firmware-manager.yaml | 2 -- sample.env | 3 --- services/addons/images/count_metric/README.md | 2 -- .../addons/images/count_metric/daily_emailer.py | 3 ++- services/addons/images/count_metric/sample.env | 1 - .../addons/images/firmware_manager/README.md | 2 +- .../addons/images/firmware_manager/sample.env | 1 - .../addons/images/firmware_manager/upgrader.py | 3 ++- services/api/README.md | 1 - services/api/sample.env | 1 - services/api/src/contact_support.py | 8 +++----- services/common/email_util.py | 16 ++++++++++++++++ 16 files changed, 25 insertions(+), 26 deletions(-) create mode 100644 services/common/email_util.py diff --git a/README.md b/README.md index 56425ad5f..8275d2c85 100644 --- a/README.md +++ b/README.md @@ -192,7 +192,6 @@ For the "Debug Solution" to run properly on Windows 10/11 using WSL, the followi - CSM_EMAIL_TO_SEND_FROM: Origin email address for the API. - CSM_EMAIL_APP_USERNAME: Username for the SMTP server. - CSM_EMAIL_APP_PASSWORD: Password for the SMTP server. -- CSM_EMAILS_TO_SEND_TO: Destination email list. - CSM_TARGET_SMTP_SERVER_ADDRESS: Destination SMTP server address. - CSM_TARGET_SMTP_SERVER_PORT: Destination SMTP server port. - API_LOGGING_LEVEL: The level of which the CV Manager API will log. (DEBUG, INFO, WARNING, ERROR) diff --git a/docker-compose-addons.yml b/docker-compose-addons.yml index 166b9ff1b..3a17be6c3 100644 --- a/docker-compose-addons.yml +++ b/docker-compose-addons.yml @@ -37,7 +37,6 @@ services: SMTP_USERNAME: ${SMTP_USERNAME} SMTP_PASSWORD: ${SMTP_PASSWORD} SMTP_EMAIL: ${SMTP_EMAIL} - SMTP_EMAIL_RECIPIENTS: ${SMTP_EMAIL_RECIPIENTS} MESSAGE_TYPES: ${COUNT_MESSAGE_TYPES} PROJECT_ID: ${GCP_PROJECT_ID} @@ -143,7 +142,6 @@ services: GCP_PROJECT: ${GCP_PROJECT_ID} GOOGLE_APPLICATION_CREDENTIALS: '/google/gcp_credentials.json' - FW_EMAIL_RECIPIENTS: ${FW_EMAIL_RECIPIENTS} SMTP_SERVER_IP: ${SMTP_SERVER_IP} SMTP_EMAIL: ${SMTP_EMAIL} SMTP_USERNAME: ${SMTP_USERNAME} @@ -155,4 +153,4 @@ services: logging: options: max-size: '10m' - max-file: '5' \ No newline at end of file + max-file: '5' diff --git a/docker-compose.yml b/docker-compose.yml index c2a5117e2..3ef21293b 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -44,7 +44,6 @@ services: CSM_EMAIL_TO_SEND_FROM: ${CSM_EMAIL_TO_SEND_FROM} CSM_EMAIL_APP_USERNAME: ${CSM_EMAIL_APP_USERNAME} CSM_EMAIL_APP_PASSWORD: ${CSM_EMAIL_APP_PASSWORD} - CSM_EMAILS_TO_SEND_TO: ${CSM_EMAILS_TO_SEND_TO} CSM_TARGET_SMTP_SERVER_ADDRESS: ${CSM_TARGET_SMTP_SERVER_ADDRESS} CSM_TARGET_SMTP_SERVER_PORT: ${CSM_TARGET_SMTP_SERVER_PORT} diff --git a/resources/kubernetes/cv-manager-api.yaml b/resources/kubernetes/cv-manager-api.yaml index e0c99eb02..d3b851ec5 100644 --- a/resources/kubernetes/cv-manager-api.yaml +++ b/resources/kubernetes/cv-manager-api.yaml @@ -156,8 +156,6 @@ spec: secretKeyRef: name: some_email_secret_password key: some_email_secret_key - - name: CSM_EMAILS_TO_SEND_TO - value: "" - name: CSM_TARGET_SMTP_SERVER_ADDRESS value: "" - name: CSM_TARGET_SMTP_SERVER_PORT diff --git a/resources/kubernetes/firmware-manager.yaml b/resources/kubernetes/firmware-manager.yaml index d72462387..9aaec97b2 100644 --- a/resources/kubernetes/firmware-manager.yaml +++ b/resources/kubernetes/firmware-manager.yaml @@ -84,8 +84,6 @@ spec: value: "" - name: LOGGING_LEVEL value: "INFO" - - name: FW_EMAIL_RECIPIENTS - value: "" - name: SMTP_SERVER_IP value: "" - name: SMTP_EMAIL diff --git a/sample.env b/sample.env index 2ee06914c..a93d0d614 100644 --- a/sample.env +++ b/sample.env @@ -73,7 +73,6 @@ WZDX_ENDPOINT= CSM_EMAIL_TO_SEND_FROM= CSM_EMAIL_APP_USERNAME= CSM_EMAIL_APP_PASSWORD= -CSM_EMAILS_TO_SEND_TO= CSM_TARGET_SMTP_SERVER_ADDRESS= CSM_TARGET_SMTP_SERVER_PORT= @@ -81,7 +80,6 @@ CSM_TARGET_SMTP_SERVER_PORT= SMTP_EMAIL= SMTP_USERNAME= SMTP_PASSWORD= -FW_EMAIL_RECIPIENTS= SMTP_SERVER_IP= # Python timezone for the CV Manager (You can list pytz timezones with the command 'pytz.all_timezones') @@ -111,7 +109,6 @@ SMTP_USERNAME = '' SMTP_PASSWORD = '' SMTP_EMAIL = '' # Multiple emails can be delimited by a ',' -SMTP_EMAIL_RECIPIENTS = 'test1@gmail.com,test2@gmail.com' # If ENABLE_EMAILER is 'False', set the following environment variables diff --git a/services/addons/images/count_metric/README.md b/services/addons/images/count_metric/README.md index 5e2375f7e..2e2b15328 100644 --- a/services/addons/images/count_metric/README.md +++ b/services/addons/images/count_metric/README.md @@ -36,8 +36,6 @@ To run this service, the following environment variables must be set: SMTP_EMAIL: The origin email that the count_metric will send the email from. This is usually associated with the SMTP server authentication. -SMTP_EMAIL_RECIPIENTS: Recipient emails, delimited by ','. - ## Daily Counter (MongoDB) The daily counter is a feature that aggregates JPO-ODE mongoDB message type counts for BSM, PSM, TIM, Map, SPaT, SRM and SSM and inserts them into a new collection in mongoDB. This new collection is named "CVCounts". This new collection is useful for the CV Manager to query the message counts in a performant manner. This script runs on a cron every 24 hours. diff --git a/services/addons/images/count_metric/daily_emailer.py b/services/addons/images/count_metric/daily_emailer.py index 5f3dcb8ec..734f3bb15 100644 --- a/services/addons/images/count_metric/daily_emailer.py +++ b/services/addons/images/count_metric/daily_emailer.py @@ -3,6 +3,7 @@ import gen_email from common.emailSender import EmailSender import common.pgquery as pgquery +from common.email_util import get_email_list from datetime import datetime, timedelta from pymongo import MongoClient @@ -127,7 +128,7 @@ def prepare_rsu_dict(): def email_daily_counts(email_body): logging.info("Attempting to send the count emails...") try: - email_addresses = os.environ["SMTP_EMAIL_RECIPIENTS"].split(",") + email_addresses = get_email_list("Daily Message Counts") for email_address in email_addresses: emailSender = EmailSender( diff --git a/services/addons/images/count_metric/sample.env b/services/addons/images/count_metric/sample.env index f266b3fd4..b796d3861 100644 --- a/services/addons/images/count_metric/sample.env +++ b/services/addons/images/count_metric/sample.env @@ -22,7 +22,6 @@ SMTP_USERNAME = '' SMTP_PASSWORD = '' SMTP_EMAIL = '' # Multiple emails can be delimited by a ',' -SMTP_EMAIL_RECIPIENTS = 'test1@gmail.com,test2@gmail.com' # --------------------------------------------------------------------- # If ENABLE_EMAILER is 'False', set the following environment variables diff --git a/services/addons/images/firmware_manager/README.md b/services/addons/images/firmware_manager/README.md index 8842cfd08..be1be2acc 100644 --- a/services/addons/images/firmware_manager/README.md +++ b/services/addons/images/firmware_manager/README.md @@ -62,7 +62,6 @@ To do so the following environment variables must be set: - SMTP_EMAIL - Email to send from. - SMTP_USERNAME - SMTP username for SMTP_EMAIL. - SMTP_PASSWORD - SMTP password for SMTP_EMAIL. -- FW_EMAIL_RECIPIENTS - Comma-separated list of emails to send failure notifications to. - SMTP_SERVER_IP - Address of the SMTP server. GCP Required environment variables: @@ -71,6 +70,7 @@ GCP Required environment variables: - GOOGLE_APPLICATION_CREDENTIALS - Service account location. Recommended to attach as a volume. Docker volume required environment variables: + - HOST_BLOB_STORAGE_DIRECTORY - Directory mounted as a docker volume for firmware storage. A relative path can be specified here. ## Vendor Specific Requirements diff --git a/services/addons/images/firmware_manager/sample.env b/services/addons/images/firmware_manager/sample.env index 8f045a07a..2d8d8069e 100644 --- a/services/addons/images/firmware_manager/sample.env +++ b/services/addons/images/firmware_manager/sample.env @@ -18,7 +18,6 @@ GCP_PROJECT="" GOOGLE_APPLICATION_CREDENTIALS="" # For sending failure emails -FW_EMAIL_RECIPIENTS= SMTP_SERVER_IP= SMTP_EMAIL= SMTP_USERNAME= diff --git a/services/addons/images/firmware_manager/upgrader.py b/services/addons/images/firmware_manager/upgrader.py index b7fab0b2c..94ddcf8cd 100644 --- a/services/addons/images/firmware_manager/upgrader.py +++ b/services/addons/images/firmware_manager/upgrader.py @@ -8,6 +8,7 @@ import requests import shutil from common.emailSender import EmailSender +from common.email_util import get_email_list import download_blob @@ -97,7 +98,7 @@ def check_online(self): def send_error_email(self, type="Firmware Upgrader", err=""): try: - email_addresses = os.environ.get("FW_EMAIL_RECIPIENTS").split(",") + email_addresses = get_email_list("Firmware Upgrade Failure") subject = ( f"{self.rsu_ip} Firmware Upgrader Failure" diff --git a/services/api/README.md b/services/api/README.md index ba1ad8c68..d8abe5bf1 100644 --- a/services/api/README.md +++ b/services/api/README.md @@ -312,7 +312,6 @@ HTTP URL Arguments: - CSM_EMAIL_TO_SEND_FROM: Origin email address for the API. - CSM_EMAIL_APP_USERNAME: Username for the SMTP server. - CSM_EMAIL_APP_PASSWORD: Password for the SMTP server. -- CSM_EMAILS_TO_SEND_TO: Destination email list. - CSM_TARGET_SMTP_SERVER_ADDRESS: Destination SMTP server address. - CSM_TARGET_SMTP_SERVER_PORT: Destination SMTP server port. - WZDX_ENDPOINT: WZDX datafeed enpoint. diff --git a/services/api/sample.env b/services/api/sample.env index c42114959..136d0429f 100644 --- a/services/api/sample.env +++ b/services/api/sample.env @@ -50,7 +50,6 @@ WZDX_ENDPOINT = CSM_EMAIL_TO_SEND_FROM= CSM_EMAIL_APP_USERNAME= CSM_EMAIL_APP_PASSWORD= -CSM_EMAILS_TO_SEND_TO= CSM_TARGET_SMTP_SERVER_ADDRESS= CSM_TARGET_SMTP_SERVER_PORT= diff --git a/services/api/src/contact_support.py b/services/api/src/contact_support.py index 2db2d4fcc..4e5e5b4e4 100644 --- a/services/api/src/contact_support.py +++ b/services/api/src/contact_support.py @@ -6,6 +6,7 @@ from marshmallow import fields from common.emailSender import EmailSender +from common.email_util import get_email_list class ContactSupportSchema(Schema): @@ -33,7 +34,6 @@ def __init__(self): self.CSM_EMAIL_TO_SEND_FROM = os.environ.get("CSM_EMAIL_TO_SEND_FROM") self.CSM_EMAIL_APP_USERNAME = os.environ.get("CSM_EMAIL_APP_USERNAME") self.CSM_EMAIL_APP_PASSWORD = os.environ.get("CSM_EMAIL_APP_PASSWORD") - self.CSM_EMAILS_TO_SEND_TO = os.environ.get("CSM_EMAILS_TO_SEND_TO") self.CSM_TARGET_SMTP_SERVER_ADDRESS = os.environ.get( "CSM_TARGET_SMTP_SERVER_ADDRESS" ) @@ -50,9 +50,6 @@ def __init__(self): if not self.CSM_EMAIL_APP_PASSWORD: logging.error("CSM_EMAIL_APP_PASSWORD environment variable not set") abort(500) - if not self.CSM_EMAILS_TO_SEND_TO: - logging.error("CSM_EMAILS_TO_SEND_TO environment variable not set") - abort(500) if not self.CSM_TARGET_SMTP_SERVER_ADDRESS: logging.error("CSM_TARGET_SMTP_SERVER_ADDRESS environment variable not set") abort(500) @@ -78,7 +75,8 @@ def post(self): subject = request.json["subject"] message = request.json["message"] - email_addresses = self.CSM_EMAILS_TO_SEND_TO.split(",") + email_addresses = get_email_list("Contact Support") + logging.info(f"Email addresses: {email_addresses}") for email_address in email_addresses: emailSender = EmailSender( self.CSM_TARGET_SMTP_SERVER_ADDRESS, diff --git a/services/common/email_util.py b/services/common/email_util.py new file mode 100644 index 000000000..04d926863 --- /dev/null +++ b/services/common/email_util.py @@ -0,0 +1,16 @@ +from common.pgquery import query_db + +def get_email_list(msg_type): + email_list = [] + email_query = ( + "SELECT to_jsonb(row) FROM (SELECT email FROM public.users WHERE user_id IN " + "(SELECT user_id FROM public.user_email_notification WHERE email_type_id = " + f"(SELECT email_type_id FROM email_type WHERE email_type = '{msg_type}'))) as row" + ) + + data = query_db(email_query) + for row in data: + row = dict(row[0]) + email_list.append(row["email"]) + + return email_list \ No newline at end of file From 85db75fb83a2006664a4a83357593c815945bb91 Mon Sep 17 00:00:00 2001 From: Marc Wodahl Date: Wed, 19 Jun 2024 11:29:46 -0600 Subject: [PATCH 078/189] Unit testing updates --- .../tests/count_metric/test_daily_emailer.py | 5 +- .../tests/data/admin_new_notification_data.py | 17 ++ .../api/tests/data/admin_notification_data.py | 60 +++++ .../api/tests/data/contact_support_data.py | 1 - .../src/test_admin_email_notification.py | 228 ++++++++++++++++++ .../src/test_admin_new_email_notification.py | 121 ++++++++++ .../api/tests/src/test_contact_support.py | 46 ---- services/common/tests/test_email_util.py | 8 + 8 files changed, 437 insertions(+), 49 deletions(-) create mode 100644 services/api/tests/data/admin_new_notification_data.py create mode 100644 services/api/tests/data/admin_notification_data.py create mode 100644 services/api/tests/src/test_admin_email_notification.py create mode 100644 services/api/tests/src/test_admin_new_email_notification.py create mode 100644 services/common/tests/test_email_util.py diff --git a/services/addons/tests/count_metric/test_daily_emailer.py b/services/addons/tests/count_metric/test_daily_emailer.py index 5de38639d..6d16f58ab 100644 --- a/services/addons/tests/count_metric/test_daily_emailer.py +++ b/services/addons/tests/count_metric/test_daily_emailer.py @@ -197,11 +197,12 @@ def test_prepare_rsu_dict(mock_query_db): "SMTP_USERNAME": "username", "SMTP_PASSWORD": "password", "SMTP_EMAIL": "test@gmail.com", - "SMTP_EMAIL_RECIPIENTS": "bob@gmail.com", }, ) @patch("addons.images.count_metric.daily_emailer.EmailSender") -def test_email_daily_counts(mock_emailsender): +@patch("addons.images.count_metric.daily_emailer.get_email_list") +def test_email_daily_counts(mock_email_list, mock_emailsender): + mock_email_list.return_value = ["bob@gmail.com"] emailsender_obj = mock_emailsender.return_value daily_emailer.email_daily_counts("test") diff --git a/services/api/tests/data/admin_new_notification_data.py b/services/api/tests/data/admin_new_notification_data.py new file mode 100644 index 000000000..3a7ad4b3f --- /dev/null +++ b/services/api/tests/data/admin_new_notification_data.py @@ -0,0 +1,17 @@ +import multidict + +##################################### request_data ########################################### + +request_params_good = multidict.MultiDict([]) + +request_json_good = {"email": "email@email.com", "email_type": "some type"} + +request_json_bad = {} + +##################################### test_data ########################################### + +good_input = {"email": "email@email.com", "email_type": "some type"} + +bad_input = {"email": "email@email.com", "email_type": "some type^@^&!"} + +notification_insert_query = "INSERT into public.user_email_notification(user_id, email_type_id) VALUES ((SELECT user_id FROM public.users WHERE email='email@email.com'), (SELECT email_type_id FROM public.email_type WHERE email_type='some type'))" diff --git a/services/api/tests/data/admin_notification_data.py b/services/api/tests/data/admin_notification_data.py new file mode 100644 index 000000000..ac86df95a --- /dev/null +++ b/services/api/tests/data/admin_notification_data.py @@ -0,0 +1,60 @@ +import multidict + +##################################### request data ########################################### + +request_environ = multidict.MultiDict([]) + +request_args_good = {"user_email": "email@email.com"} + +request_args_delete_good = {"email": "email@email.com", "email_type": "old"} + +request_args_bad = {"user_email": 14} + +request_json_good = { + "email": "email@email.com", + "old_email_type": "old", + "new_email_type": "new", +} + +request_json_bad = { + "email": "email@email.com", + "new_email_type": "new", +} + +request_json_unsafe_input = { + "email": "email@email.com!", + "email_type": "type", +} + +##################################### function data ########################################### + +# get_notification_data + +get_notification_data_pgdb_return = [ + ({"email": "email@email.com", "first_name": "first", "last_name": "last", "email_type": "test type"},), +] + +get_notification_data_result = [{"email": "email@email.com", "first_name": "first", "last_name": "last", "email_type": "test type"}] + +get_notification_data_sql = ( +"SELECT to_jsonb(row) FROM (SELECT u.email, u.first_name, u.last_name, " +"e.email_type FROM public.user_email_notification JOIN public.users AS u " +"ON u.user_id = user_email_notification.user_id JOIN public.email_type " +"AS e ON e.email_type_id = user_email_notification.email_type_id WHERE" +" user_email_notification.user_id IN (SELECT user_id FROM public.users" +" WHERE email = 'email@email.com')) as row" +) + +# modify_notification + +modify_notification_sql = ( + "UPDATE public.user_email_notification SET email_type_id = " + "(SELECT email_type_id FROM public.email_type WHERE email_type = 'new') " + "WHERE user_id = (SELECT user_id FROM public.users WHERE email = " + "'email@email.com') AND email_type_id = (SELECT email_type_id " + "FROM public.email_type WHERE email_type = 'old')" +) + +# delete_notification + +delete_notification_call = "DELETE FROM public.user_email_notification WHERE user_id IN (SELECT user_id FROM public.users WHERE email = 'email@email.com') AND email_type_id IN (SELECT email_type_id FROM public.email_type WHERE email_type = 'test type')" diff --git a/services/api/tests/data/contact_support_data.py b/services/api/tests/data/contact_support_data.py index ff66ca72d..d21948da1 100644 --- a/services/api/tests/data/contact_support_data.py +++ b/services/api/tests/data/contact_support_data.py @@ -1,5 +1,4 @@ CSM_EMAIL_TO_SEND_FROM = "test@test.com" -CSM_EMAILS_TO_SEND_TO = "bob@test.com" CSM_EMAIL_APP_USERNAME = "testusername" CSM_EMAIL_APP_PASSWORD = "testpassword" diff --git a/services/api/tests/src/test_admin_email_notification.py b/services/api/tests/src/test_admin_email_notification.py new file mode 100644 index 000000000..35d63b56c --- /dev/null +++ b/services/api/tests/src/test_admin_email_notification.py @@ -0,0 +1,228 @@ +from unittest.mock import patch, MagicMock, call +import pytest +import api.src.admin_email_notification as admin_notification +import api.tests.data.admin_notification_data as admin_notification_data +import sqlalchemy +from werkzeug.exceptions import HTTPException + +###################################### Testing Requests ########################################## + +# OPTIONS endpoint test + + +def test_request_options(): + info = admin_notification.AdminNotification() + (body, code, headers) = info.options() + assert body == "" + assert code == 204 + assert headers["Access-Control-Allow-Methods"] == "GET,PATCH,DELETE" + + +# GET endpoint tests + + +@patch("api.src.admin_email_notification.get_modify_notification_data") +def test_entry_get(mock_get_modify_notification_data): + req = MagicMock() + req.environ = admin_notification_data.request_environ + req.args = admin_notification_data.request_args_good + mock_get_modify_notification_data.return_value = {} + with patch("api.src.admin_email_notification.request", req): + status = admin_notification.AdminNotification() + (body, code, headers) = status.get() + + mock_get_modify_notification_data.assert_called_once_with( + admin_notification_data.request_args_good["user_email"] + ) + assert code == 200 + assert headers["Access-Control-Allow-Origin"] == "test.com" + assert body == {} + + +# Test schema for string value +def test_entry_get_schema_str(): + req = MagicMock() + req.environ = admin_notification_data.request_environ + req.args = admin_notification_data.request_args_bad + with patch("api.src.admin_email_notification.request", req): + status = admin_notification.AdminNotification() + with pytest.raises(HTTPException): + status.get() + + +# PATCH endpoint tests + + +@patch("api.src.admin_email_notification.modify_notification") +def test_entry_patch(mock_modify_notification): + req = MagicMock() + req.environ = admin_notification_data.request_environ + req.json = admin_notification_data.request_json_good + mock_modify_notification.return_value = {}, 200 + with patch("api.src.admin_email_notification.request", req): + status = admin_notification.AdminNotification() + (body, code, headers) = status.patch() + + mock_modify_notification.assert_called_once() + assert code == 200 + assert headers["Access-Control-Allow-Origin"] == "test.com" + assert body == {} + + +def test_entry_patch_schema(): + req = MagicMock() + req.environ = admin_notification_data.request_environ + req.json = admin_notification_data.request_json_bad + with patch("api.src.admin_email_notification.request", req): + status = admin_notification.AdminNotification() + with pytest.raises(HTTPException): + status.patch() + + +# DELETE endpoint tests + + +@patch("api.src.admin_email_notification.delete_notification") +def test_entry_delete_user(mock_delete_notification): + req = MagicMock() + req.environ = admin_notification_data.request_environ + req.args = admin_notification_data.request_args_delete_good + mock_delete_notification.return_value = {} + with patch("api.src.admin_email_notification.request", req): + status = admin_notification.AdminNotification() + (body, code, headers) = status.delete() + + + + mock_delete_notification.assert_called_once_with( + admin_notification_data.request_args_delete_good["email"], + admin_notification_data.request_args_delete_good["email_type"], + ) + assert code == 200 + assert headers["Access-Control-Allow-Origin"] == "test.com" + assert body == {} + + +def test_entry_delete_schema(): + req = MagicMock() + req.environ = admin_notification_data.request_environ + req.args = admin_notification_data.request_args_bad + with patch("api.src.admin_email_notification.request", req): + status = admin_notification.AdminNotification() + with pytest.raises(HTTPException): + status.delete() + + +###################################### Testing Functions ########################################## + +# get_notification_data + + +@patch("api.src.admin_email_notification.pgquery.query_db") +def test_get_all_notifications(mock_query_db): + mock_query_db.return_value = admin_notification_data.get_notification_data_pgdb_return + expected_result = admin_notification_data.get_notification_data_result + expected_query = admin_notification_data.get_notification_data_sql + actual_result = admin_notification.get_notification_data("email@email.com") + + mock_query_db.assert_called_with(expected_query) + assert actual_result == expected_result + + +# get_modify_notification_data + + +@patch("api.src.admin_email_notification.get_notification_data") +def test_get_modify_notification_data_all(mock_get_notification_data): + mock_get_notification_data.return_value = [ + {"email": "email@email.com", "first_name": "first", "last_name": "last", "email_type": "test type"} + ] + expected_notification_data = {"notification_data": [{"email": "email@email.com", "first_name": "first", "last_name": "last", "email_type": "test type"}]} + actual_result = admin_notification.get_modify_notification_data("email@email.com") + + assert actual_result == expected_notification_data + + +# check_safe_input + + +def test_check_safe_input(): + expected_result = True + actual_result = admin_notification.check_safe_input(admin_notification_data.request_json_good) + assert actual_result == expected_result + + +def test_check_safe_input_bad(): + expected_result = False + actual_result = admin_notification.check_safe_input(admin_notification_data.request_json_unsafe_input) + assert actual_result == expected_result + + +# modify_notification + + +@patch("api.src.admin_email_notification.check_safe_input") +@patch("api.src.admin_email_notification.pgquery.write_db") +def test_modify_notification_success(mock_pgquery, mock_check_safe_input): + mock_check_safe_input.return_value = True + expected_msg, expected_code = {"message": "Email notification successfully modified"}, 200 + actual_msg, actual_code = admin_notification.modify_notification(admin_notification_data.request_json_good) + + calls = [ + call(admin_notification_data.modify_notification_sql) + ] + mock_pgquery.assert_has_calls(calls) + assert actual_msg == expected_msg + assert actual_code == expected_code + + +@patch("api.src.admin_email_notification.check_safe_input") +@patch("api.src.admin_email_notification.pgquery.write_db") +def test_modify_notification_check_fail(mock_pgquery, mock_check_safe_input): + mock_check_safe_input.return_value = False + expected_msg, expected_code = { + "message": "No special characters are allowed: !\"#$%&'()*+,./:;<=>?@[\\]^`{|}~. No sequences of '-' characters are allowed" + }, 500 + actual_msg, actual_code = admin_notification.modify_notification(admin_notification_data.request_json_good) + + calls = [] + mock_pgquery.assert_has_calls(calls) + assert actual_msg == expected_msg + assert actual_code == expected_code + + +@patch("api.src.admin_email_notification.check_safe_input") +@patch("api.src.admin_email_notification.pgquery.write_db") +def test_modify_notification_generic_exception(mock_pgquery, mock_check_safe_input): + mock_check_safe_input.return_value = True + mock_pgquery.side_effect = Exception("Test") + expected_msg, expected_code = {"message": "Encountered unknown issue"}, 500 + actual_msg, actual_code = admin_notification.modify_notification(admin_notification_data.request_json_good) + + assert actual_msg == expected_msg + assert actual_code == expected_code + + +@patch("api.src.admin_email_notification.check_safe_input") +@patch("api.src.admin_email_notification.pgquery.write_db") +def test_modify_notification_sql_exception(mock_pgquery, mock_check_safe_input): + mock_check_safe_input.return_value = True + orig = MagicMock() + orig.args = ({"D": "SQL issue encountered"},) + mock_pgquery.side_effect = sqlalchemy.exc.IntegrityError("", {}, orig) + expected_msg, expected_code = {"message": "SQL issue encountered"}, 500 + actual_msg, actual_code = admin_notification.modify_notification(admin_notification_data.request_json_good) + + assert actual_msg == expected_msg + assert actual_code == expected_code + + +# delete_notification + + +@patch("api.src.admin_email_notification.pgquery.write_db") +def test_delete_notification(mock_write_db): + expected_result = {"message": "Email notification successfully deleted"} + actual_result = admin_notification.delete_notification("email@email.com", "test type") + mock_write_db.assert_called_with(admin_notification_data.delete_notification_call) + assert actual_result == expected_result diff --git a/services/api/tests/src/test_admin_new_email_notification.py b/services/api/tests/src/test_admin_new_email_notification.py new file mode 100644 index 000000000..33614643f --- /dev/null +++ b/services/api/tests/src/test_admin_new_email_notification.py @@ -0,0 +1,121 @@ +from unittest.mock import patch, MagicMock, call +import pytest +import api.src.admin_new_email_notification as admin_new_notification +import api.tests.data.admin_new_notification_data as admin_new_notification_data +import sqlalchemy +from werkzeug.exceptions import HTTPException + +###################################### Testing Requests ########################################## + + +def test_request_options(): + info = admin_new_notification.AdminNewNotification() + (body, code, headers) = info.options() + assert body == "" + assert code == 204 + assert headers["Access-Control-Allow-Methods"] == "GET,POST" + + +@patch("api.src.admin_new_email_notification.add_notification") +def test_entry_post(mock_add_notification): + req = MagicMock() + req.environ = admin_new_notification_data.request_params_good + req.json = admin_new_notification_data.request_json_good + mock_add_notification.return_value = {}, 200 + with patch("api.src.admin_new_email_notification.request", req): + status = admin_new_notification.AdminNewNotification() + (body, code, headers) = status.post() + + mock_add_notification.assert_called_once() + assert code == 200 + assert headers["Access-Control-Allow-Origin"] == "test.com" + assert body == {} + + +def test_entry_post_schema(): + req = MagicMock() + req.environ = admin_new_notification_data.request_params_good + req.json = admin_new_notification_data.request_json_bad + with patch("api.src.admin_new_email_notification.request", req): + status = admin_new_notification.AdminNewNotification() + with pytest.raises(HTTPException): + status.post() + + +###################################### Testing Functions ########################################## + + +def test_check_safe_input(): + expected_result = True + actual_result = admin_new_notification.check_safe_input(admin_new_notification_data.good_input) + assert actual_result == expected_result + + +def test_check_safe_input_bad(): + expected_result = False + actual_result = admin_new_notification.check_safe_input(admin_new_notification_data.bad_input) + assert actual_result == expected_result + + +@patch("api.src.admin_new_email_notification.check_safe_input") +@patch("api.src.admin_new_email_notification.pgquery.write_db") +def test_add_org_success_commsignia(mock_pgquery, mock_check_safe_input): + mock_check_safe_input.return_value = True + expected_msg, expected_code = { + "message": "New email notification successfully added" + }, 200 + actual_msg, actual_code = admin_new_notification.add_notification( + admin_new_notification_data.request_json_good + ) + + calls = [call(admin_new_notification_data.notification_insert_query)] + mock_pgquery.assert_has_calls(calls) + assert actual_msg == expected_msg + assert actual_code == expected_code + + +@patch("api.src.admin_new_email_notification.check_safe_input") +@patch("api.src.admin_new_email_notification.pgquery.write_db") +def test_add_org_safety_fail(mock_pgquery, mock_check_safe_input): + mock_check_safe_input.return_value = False + expected_msg, expected_code = { + "message": "No special characters are allowed: !\"#$%&'()*+,./:;<=>?@[\\]^`{|}~. No sequences of '-' characters are allowed" + }, 500 + actual_msg, actual_code = admin_new_notification.add_notification( + admin_new_notification_data.request_json_good + ) + + calls = [] + mock_pgquery.assert_has_calls(calls) + assert actual_msg == expected_msg + assert actual_code == expected_code + + +@patch("api.src.admin_new_email_notification.check_safe_input") +@patch("api.src.admin_new_email_notification.pgquery.write_db") +def test_add_org_generic_exception(mock_pgquery, mock_check_safe_input): + mock_check_safe_input.return_value = True + mock_pgquery.side_effect = Exception("Test") + expected_msg, expected_code = {"message": "Encountered unknown issue"}, 500 + actual_msg, actual_code = admin_new_notification.add_notification( + admin_new_notification_data.request_json_good + ) + + assert actual_msg == expected_msg + assert actual_code == expected_code + + +@patch("api.src.admin_new_email_notification.check_safe_input") +@patch("api.src.admin_new_email_notification.pgquery.write_db") +def test_add_org_sql_exception(mock_pgquery, mock_check_safe_input): + mock_check_safe_input.return_value = True + orig = MagicMock() + orig.args = ({"D": "SQL issue encountered"},) + mock_pgquery.side_effect = sqlalchemy.exc.IntegrityError("", {}, orig) + expected_msg, expected_code = {"message": "SQL issue encountered"}, 500 + actual_msg, actual_code = admin_new_notification.add_notification( + admin_new_notification_data.request_json_good + ) + + assert actual_msg == expected_msg + assert actual_code == expected_code diff --git a/services/api/tests/src/test_contact_support.py b/services/api/tests/src/test_contact_support.py index cb4635db7..147190fd0 100644 --- a/services/api/tests/src/test_contact_support.py +++ b/services/api/tests/src/test_contact_support.py @@ -46,7 +46,6 @@ def test_contact_support_resource_initialization_success(): os.environ["CSM_EMAIL_TO_SEND_FROM"] = contact_support_data.CSM_EMAIL_TO_SEND_FROM os.environ["CSM_EMAIL_APP_USERNAME"] = contact_support_data.CSM_EMAIL_APP_USERNAME os.environ["CSM_EMAIL_APP_PASSWORD"] = contact_support_data.CSM_EMAIL_APP_PASSWORD - os.environ["CSM_EMAILS_TO_SEND_TO"] = contact_support_data.CSM_EMAILS_TO_SEND_TO os.environ[ "CSM_TARGET_SMTP_SERVER_ADDRESS" ] = DEFAULT_CSM_TARGET_SMTP_SERVER_ADDRESS @@ -64,16 +63,11 @@ def test_contact_support_resource_initialization_success(): contactSupportResource.CSM_EMAIL_APP_PASSWORD == contact_support_data.CSM_EMAIL_APP_PASSWORD ) - assert ( - contactSupportResource.CSM_EMAILS_TO_SEND_TO - == contact_support_data.CSM_EMAILS_TO_SEND_TO - ) # cleanup del os.environ["CSM_EMAIL_TO_SEND_FROM"] del os.environ["CSM_EMAIL_APP_USERNAME"] del os.environ["CSM_EMAIL_APP_PASSWORD"] - del os.environ["CSM_EMAILS_TO_SEND_TO"] del os.environ["CSM_TARGET_SMTP_SERVER_ADDRESS"] del os.environ["CSM_TARGET_SMTP_SERVER_PORT"] @@ -82,7 +76,6 @@ def test_contact_support_resource_initialization_no_CSM_EMAIL_TO_SEND_FROM(): # prepare os.environ["CSM_EMAIL_APP_USERNAME"] = contact_support_data.CSM_EMAIL_APP_USERNAME os.environ["CSM_EMAIL_APP_PASSWORD"] = contact_support_data.CSM_EMAIL_APP_PASSWORD - os.environ["CSM_EMAILS_TO_SEND_TO"] = contact_support_data.CSM_EMAILS_TO_SEND_TO os.environ[ "CSM_TARGET_SMTP_SERVER_ADDRESS" ] = DEFAULT_CSM_TARGET_SMTP_SERVER_ADDRESS @@ -101,7 +94,6 @@ def test_contact_support_resource_initialization_no_CSM_EMAIL_TO_SEND_FROM(): # cleanup del os.environ["CSM_EMAIL_APP_USERNAME"] del os.environ["CSM_EMAIL_APP_PASSWORD"] - del os.environ["CSM_EMAILS_TO_SEND_TO"] del os.environ["CSM_TARGET_SMTP_SERVER_ADDRESS"] del os.environ["CSM_TARGET_SMTP_SERVER_PORT"] @@ -109,7 +101,6 @@ def test_contact_support_resource_initialization_no_CSM_EMAIL_TO_SEND_FROM(): def test_contact_support_resource_initialization_no_CSM_EMAIL_APP_PASSWORD(): # prepare os.environ["CSM_EMAIL_TO_SEND_FROM"] = contact_support_data.CSM_EMAIL_TO_SEND_FROM - os.environ["CSM_EMAILS_TO_SEND_TO"] = contact_support_data.CSM_EMAILS_TO_SEND_TO os.environ["CSM_EMAIL_APP_USERNAME"] = contact_support_data.CSM_EMAIL_APP_USERNAME os.environ[ "CSM_TARGET_SMTP_SERVER_ADDRESS" @@ -128,46 +119,16 @@ def test_contact_support_resource_initialization_no_CSM_EMAIL_APP_PASSWORD(): # cleanup del os.environ["CSM_EMAIL_TO_SEND_FROM"] - del os.environ["CSM_EMAILS_TO_SEND_TO"] del os.environ["CSM_EMAIL_APP_USERNAME"] del os.environ["CSM_TARGET_SMTP_SERVER_ADDRESS"] del os.environ["CSM_TARGET_SMTP_SERVER_PORT"] -def test_contact_support_resource_initialization_no_CSM_EMAILS_TO_SEND_TO(): - # prepare - os.environ["CSM_EMAIL_TO_SEND_FROM"] = contact_support_data.CSM_EMAIL_TO_SEND_FROM - os.environ["CSM_EMAIL_APP_USERNAME"] = contact_support_data.CSM_EMAIL_APP_USERNAME - os.environ["CSM_EMAIL_APP_PASSWORD"] = contact_support_data.CSM_EMAIL_APP_PASSWORD - os.environ[ - "CSM_TARGET_SMTP_SERVER_ADDRESS" - ] = DEFAULT_CSM_TARGET_SMTP_SERVER_ADDRESS - os.environ["CSM_TARGET_SMTP_SERVER_PORT"] = str(DEFAULT_CSM_TARGET_SMTP_SERVER_PORT) - - # execute - exceptionOccurred = False - try: - contactSupportResource = contact_support.ContactSupportResource() - except Exception as e: - exceptionOccurred = True - - # assert - assert exceptionOccurred - - # cleanup - del os.environ["CSM_EMAIL_TO_SEND_FROM"] - del os.environ["CSM_EMAIL_APP_USERNAME"] - del os.environ["CSM_EMAIL_APP_PASSWORD"] - del os.environ["CSM_TARGET_SMTP_SERVER_ADDRESS"] - del os.environ["CSM_TARGET_SMTP_SERVER_PORT"] - - def test_options(): # prepare os.environ["CSM_EMAIL_TO_SEND_FROM"] = contact_support_data.CSM_EMAIL_TO_SEND_FROM os.environ["CSM_EMAIL_APP_USERNAME"] = contact_support_data.CSM_EMAIL_APP_USERNAME os.environ["CSM_EMAIL_APP_PASSWORD"] = contact_support_data.CSM_EMAIL_APP_PASSWORD - os.environ["CSM_EMAILS_TO_SEND_TO"] = contact_support_data.CSM_EMAILS_TO_SEND_TO os.environ[ "CSM_TARGET_SMTP_SERVER_ADDRESS" ] = DEFAULT_CSM_TARGET_SMTP_SERVER_ADDRESS @@ -184,7 +145,6 @@ def test_options(): del os.environ["CSM_EMAIL_TO_SEND_FROM"] del os.environ["CSM_EMAIL_APP_USERNAME"] del os.environ["CSM_EMAIL_APP_PASSWORD"] - del os.environ["CSM_EMAILS_TO_SEND_TO"] del os.environ["CSM_TARGET_SMTP_SERVER_ADDRESS"] del os.environ["CSM_TARGET_SMTP_SERVER_PORT"] @@ -194,7 +154,6 @@ def test_post_success(): os.environ["CSM_EMAIL_TO_SEND_FROM"] = contact_support_data.CSM_EMAIL_TO_SEND_FROM os.environ["CSM_EMAIL_APP_USERNAME"] = contact_support_data.CSM_EMAIL_APP_USERNAME os.environ["CSM_EMAIL_APP_PASSWORD"] = contact_support_data.CSM_EMAIL_APP_PASSWORD - os.environ["CSM_EMAILS_TO_SEND_TO"] = contact_support_data.CSM_EMAILS_TO_SEND_TO os.environ[ "CSM_TARGET_SMTP_SERVER_ADDRESS" ] = DEFAULT_CSM_TARGET_SMTP_SERVER_ADDRESS @@ -215,7 +174,6 @@ def test_post_success(): del os.environ["CSM_EMAIL_TO_SEND_FROM"] del os.environ["CSM_EMAIL_APP_USERNAME"] del os.environ["CSM_EMAIL_APP_PASSWORD"] - del os.environ["CSM_EMAILS_TO_SEND_TO"] del os.environ["CSM_TARGET_SMTP_SERVER_ADDRESS"] del os.environ["CSM_TARGET_SMTP_SERVER_PORT"] @@ -225,7 +183,6 @@ def test_post_no_json_body(): os.environ["CSM_EMAIL_TO_SEND_FROM"] = contact_support_data.CSM_EMAIL_TO_SEND_FROM os.environ["CSM_EMAIL_APP_USERNAME"] = contact_support_data.CSM_EMAIL_APP_USERNAME os.environ["CSM_EMAIL_APP_PASSWORD"] = contact_support_data.CSM_EMAIL_APP_PASSWORD - os.environ["CSM_EMAILS_TO_SEND_TO"] = contact_support_data.CSM_EMAILS_TO_SEND_TO os.environ[ "CSM_TARGET_SMTP_SERVER_ADDRESS" ] = DEFAULT_CSM_TARGET_SMTP_SERVER_ADDRESS @@ -248,7 +205,6 @@ def test_post_no_json_body(): del os.environ["CSM_EMAIL_TO_SEND_FROM"] del os.environ["CSM_EMAIL_APP_USERNAME"] del os.environ["CSM_EMAIL_APP_PASSWORD"] - del os.environ["CSM_EMAILS_TO_SEND_TO"] del os.environ["CSM_TARGET_SMTP_SERVER_ADDRESS"] del os.environ["CSM_TARGET_SMTP_SERVER_PORT"] @@ -258,7 +214,6 @@ def test_validate_input(): os.environ["CSM_EMAIL_TO_SEND_FROM"] = contact_support_data.CSM_EMAIL_TO_SEND_FROM os.environ["CSM_EMAIL_APP_USERNAME"] = contact_support_data.CSM_EMAIL_APP_USERNAME os.environ["CSM_EMAIL_APP_PASSWORD"] = contact_support_data.CSM_EMAIL_APP_PASSWORD - os.environ["CSM_EMAILS_TO_SEND_TO"] = contact_support_data.CSM_EMAILS_TO_SEND_TO os.environ[ "CSM_TARGET_SMTP_SERVER_ADDRESS" ] = DEFAULT_CSM_TARGET_SMTP_SERVER_ADDRESS @@ -277,7 +232,6 @@ def test_validate_input(): del os.environ["CSM_EMAIL_TO_SEND_FROM"] del os.environ["CSM_EMAIL_APP_USERNAME"] del os.environ["CSM_EMAIL_APP_PASSWORD"] - del os.environ["CSM_EMAILS_TO_SEND_TO"] del os.environ["CSM_TARGET_SMTP_SERVER_ADDRESS"] del os.environ["CSM_TARGET_SMTP_SERVER_PORT"] diff --git a/services/common/tests/test_email_util.py b/services/common/tests/test_email_util.py new file mode 100644 index 000000000..e5756669e --- /dev/null +++ b/services/common/tests/test_email_util.py @@ -0,0 +1,8 @@ +from mock import patch +from common import email_util + +@patch("common.email_util.query_db") +def test_get_email_list(mock_query_db): + mock_query_db.return_value = [[{"email": "bob@gmail.com"},], [{"email": "bob2@gmail.com"}]] + email_list = email_util.get_email_list("msg_type") + assert email_list == ["bob@gmail.com", "bob2@gmail.com"] \ No newline at end of file From de3d5373ae699e9a3b667ef739b8c0c39f119996 Mon Sep 17 00:00:00 2001 From: Marc Wodahl Date: Wed, 19 Jun 2024 11:45:31 -0600 Subject: [PATCH 079/189] Add Notifications Type --- webapp/src/types/Notifications.d.ts | 6 ++++++ 1 file changed, 6 insertions(+) create mode 100644 webapp/src/types/Notifications.d.ts diff --git a/webapp/src/types/Notifications.d.ts b/webapp/src/types/Notifications.d.ts new file mode 100644 index 000000000..bb77d0192 --- /dev/null +++ b/webapp/src/types/Notifications.d.ts @@ -0,0 +1,6 @@ +export type AdminEmailNotification = { + first_name: string + last_name: string + email: string + email_type: string +} From f53b22cccbc1b865067afe6631ed85237e970566 Mon Sep 17 00:00:00 2001 From: Marc Wodahl Date: Wed, 19 Jun 2024 14:10:31 -0600 Subject: [PATCH 080/189] Remove receive error emails from public.users --- .../sql_scripts/CVManager_CreateTables.sql | 1 - .../sql_scripts/CVManager_SampleData.sql | 4 +- services/api/src/admin_email_notification.py | 2 - services/api/src/admin_new_user.py | 5 +-- services/api/src/admin_user.py | 9 +--- .../api/tests/data/admin_new_user_data.py | 8 +--- services/api/tests/data/admin_user_data.py | 12 ++--- .../AdminFormManager.test.tsx.snap | 44 +------------------ .../features/adminAdduser/AdminAddUser.tsx | 9 ---- .../__snapshots__/AdminAddUser.test.tsx.snap | 22 ---------- .../adminAdduser/adminAddUserSlice.tsx | 1 - .../features/adminEditUser/AdminEditUser.tsx | 11 ----- .../features/adminUserTab/AdminUserTab.tsx | 6 --- .../__snapshots__/AdminUserTab.test.tsx.snap | 44 +------------------ webapp/src/types/UserTypes.d.ts | 1 - 15 files changed, 13 insertions(+), 166 deletions(-) diff --git a/resources/sql_scripts/CVManager_CreateTables.sql b/resources/sql_scripts/CVManager_CreateTables.sql index 4d0ceccab..4c57ff977 100644 --- a/resources/sql_scripts/CVManager_CreateTables.sql +++ b/resources/sql_scripts/CVManager_CreateTables.sql @@ -237,7 +237,6 @@ CREATE TABLE IF NOT EXISTS public.users first_name character varying(128) NOT NULL, last_name character varying(128) NOT NULL, super_user bit(1) NOT NULL, - receive_error_emails bit(1) NOT NULL, CONSTRAINT users_pkey PRIMARY KEY (user_id), CONSTRAINT users_email UNIQUE (email) ); diff --git a/resources/sql_scripts/CVManager_SampleData.sql b/resources/sql_scripts/CVManager_SampleData.sql index 1262cafac..0cb233b36 100644 --- a/resources/sql_scripts/CVManager_SampleData.sql +++ b/resources/sql_scripts/CVManager_SampleData.sql @@ -49,8 +49,8 @@ INSERT INTO public.rsu_organization( -- Replace user with a real gmail to test GCP OAuth2.0 support INSERT INTO public.users( - email, first_name, last_name, super_user, receive_error_emails) - VALUES ('test@gmail.com', 'Test', 'User', '1', '1'); + email, first_name, last_name, super_user) + VALUES ('test@gmail.com', 'Test', 'User', '1'); INSERT INTO public.user_organization( user_id, organization_id, role_id) diff --git a/services/api/src/admin_email_notification.py b/services/api/src/admin_email_notification.py index df51f6552..26385f549 100644 --- a/services/api/src/admin_email_notification.py +++ b/services/api/src/admin_email_notification.py @@ -28,9 +28,7 @@ def get_notification_data(user_email): "email_type": row["email_type"] } - print(notification_dict) notification_list = list(notification_dict.values()) - print(notification_list) # If list is empty and a single user was requested, return empty object if len(notification_list) == 0: return [] diff --git a/services/api/src/admin_new_user.py b/services/api/src/admin_new_user.py index 3590d006b..274e85612 100644 --- a/services/api/src/admin_new_user.py +++ b/services/api/src/admin_new_user.py @@ -90,8 +90,8 @@ def add_user(user_spec): try: user_insert_query = ( - "INSERT INTO public.users(email, first_name, last_name, super_user, receive_error_emails) " - f"VALUES ('{user_spec['email']}', '{user_spec['first_name']}', '{user_spec['last_name']}', '{'1' if user_spec['super_user'] else '0'}', '{'1' if user_spec['receive_error_emails'] else '0'}')" + "INSERT INTO public.users(email, first_name, last_name, super_user) " + f"VALUES ('{user_spec['email']}', '{user_spec['first_name']}', '{user_spec['last_name']}', '{'1' if user_spec['super_user'] else '0'}')" ) pgquery.write_db(user_insert_query) @@ -136,7 +136,6 @@ class AdminNewUserSchema(Schema): first_name = fields.Str(required=True) last_name = fields.Str(required=True) super_user = fields.Bool(required=True) - receive_error_emails = fields.Bool(required=True) organizations = fields.List( fields.Nested(UserOrganizationSchema), required=True, diff --git a/services/api/src/admin_user.py b/services/api/src/admin_user.py index 0e7366a0b..fd2fcb040 100644 --- a/services/api/src/admin_user.py +++ b/services/api/src/admin_user.py @@ -9,7 +9,7 @@ def get_user_data(user_email): query = ( "SELECT to_jsonb(row) " "FROM (" - "SELECT email, first_name, last_name, super_user, receive_error_emails, org.name, roles.name AS role " + "SELECT email, first_name, last_name, super_user, org.name, roles.name AS role " "FROM public.users " "JOIN public.user_organization AS uo ON uo.user_id = users.user_id " "JOIN public.organizations AS org ON org.organization_id = uo.organization_id " @@ -30,9 +30,6 @@ def get_user_data(user_email): "first_name": row["first_name"], "last_name": row["last_name"], "super_user": True if row["super_user"] == "1" else False, - "receive_error_emails": True - if row["receive_error_emails"] == "1" - else False, "organizations": [], } user_dict[row["email"]]["organizations"].append( @@ -101,8 +98,7 @@ def modify_user(user_spec): f"email='{user_spec['email']}', " f"first_name='{user_spec['first_name']}', " f"last_name='{user_spec['last_name']}', " - f"super_user='{'1' if user_spec['super_user'] else '0'}', " - f"receive_error_emails='{'1' if user_spec['receive_error_emails'] else '0'}' " + f"super_user='{'1' if user_spec['super_user'] else '0'}' " f"WHERE email = '{user_spec['orig_email']}'" ) pgquery.write_db(query) @@ -190,7 +186,6 @@ class AdminUserPatchSchema(Schema): first_name = fields.Str(required=True) last_name = fields.Str(required=True) super_user = fields.Bool(required=True) - receive_error_emails = fields.Bool(required=True) organizations_to_add = fields.List( fields.Nested(UserOrganizationSchema), required=True ) diff --git a/services/api/tests/data/admin_new_user_data.py b/services/api/tests/data/admin_new_user_data.py index 8086471e8..cd6ffb8bd 100644 --- a/services/api/tests/data/admin_new_user_data.py +++ b/services/api/tests/data/admin_new_user_data.py @@ -9,7 +9,6 @@ "first_name": "John", "last_name": "Doe", "super_user": True, - "receive_error_emails": True, "organizations": [{"name": "Test Org", "role": "operator"}], } @@ -18,7 +17,6 @@ "first_name": "John", "last_name": "Doe", "super_user": True, - "receive_error_emails": True, "organizations": ["Test Org"], } @@ -29,7 +27,6 @@ "first_name": "John", "last_name": "Doe", "super_user": True, - "receive_error_emails": True, "organizations": [{"name": "Test Org", "role": "operator"}], } @@ -38,13 +35,12 @@ "first_name": "John--", "last_name": "Doe@#", "super_user": True, - "receive_error_emails": True, "organizations": [{"name": "Test Org##", "role": "operator"}], } user_insert_query = ( - "INSERT INTO public.users(email, first_name, last_name, super_user, receive_error_emails) " - "VALUES ('jdoe@example.com', 'John', 'Doe', '1', '1')" + "INSERT INTO public.users(email, first_name, last_name, super_user) " + "VALUES ('jdoe@example.com', 'John', 'Doe', '1')" ) user_org_insert_query = ( diff --git a/services/api/tests/data/admin_user_data.py b/services/api/tests/data/admin_user_data.py index 42640267e..a1762430b 100644 --- a/services/api/tests/data/admin_user_data.py +++ b/services/api/tests/data/admin_user_data.py @@ -14,7 +14,6 @@ "first_name": "test", "last_name": "test", "super_user": True, - "receive_error_emails": True, "organizations_to_add": [{"name": "Test Org3", "role": "admin"}], "organizations_to_modify": [{"name": "Test Org2", "role": "user"}], "organizations_to_remove": [{"name": "Test Org1", "role": "user"}], @@ -26,7 +25,6 @@ "first_name": "test", "last_name": "test", "super_user": True, - "receive_error_emails": True, "organizations_to_add": [{"name": "Test Org3", "role": "admin"}], "organizations_to_remove": [{"name": "Test Org1", "role": "user"}], } @@ -37,7 +35,6 @@ "first_name": "test", "last_name": "tes--t", "super_user": True, - "receive_error_emails": True, "organizations_to_add": [{"name": "Test Org3#@", "role": "adm#!in"}], "organizations_to_modify": [{"name": "Test O%@!rg2", "role": "user"}], "organizations_to_remove": [{"name": "Test O!##rg1", "role": "!#user"}], @@ -52,7 +49,6 @@ "first_name": "test", "last_name": "test", "super_user": "1", - "receive_error_emails": "1", "name": "test org", "role": "admin", }, @@ -65,7 +61,6 @@ "first_name": "test", "last_name": "test", "super_user": True, - "receive_error_emails": True, "organizations": [{"name": "test org", "role": "admin"}], } ] @@ -73,7 +68,7 @@ expected_get_user_query = ( "SELECT to_jsonb(row) " "FROM (" - "SELECT email, first_name, last_name, super_user, receive_error_emails, org.name, roles.name AS role " + "SELECT email, first_name, last_name, super_user, org.name, roles.name AS role " "FROM public.users " "JOIN public.user_organization AS uo ON uo.user_id = users.user_id " "JOIN public.organizations AS org ON org.organization_id = uo.organization_id " @@ -84,7 +79,7 @@ expected_get_user_query_one = ( "SELECT to_jsonb(row) " "FROM (" - "SELECT email, first_name, last_name, super_user, receive_error_emails, org.name, roles.name AS role " + "SELECT email, first_name, last_name, super_user, org.name, roles.name AS role " "FROM public.users " "JOIN public.user_organization AS uo ON uo.user_id = users.user_id " "JOIN public.organizations AS org ON org.organization_id = uo.organization_id " @@ -98,8 +93,7 @@ "email='test@email.com', " "first_name='test', " "last_name='test', " - "super_user='1', " - "receive_error_emails='1' " + "super_user='1' " "WHERE email = 'test@email.com'" ) diff --git a/webapp/src/components/__snapshots__/AdminFormManager.test.tsx.snap b/webapp/src/components/__snapshots__/AdminFormManager.test.tsx.snap index 9e105adeb..1c43c761e 100644 --- a/webapp/src/components/__snapshots__/AdminFormManager.test.tsx.snap +++ b/webapp/src/components/__snapshots__/AdminFormManager.test.tsx.snap @@ -1327,48 +1327,6 @@ exports[`snapshot user 1`] = ` />
- -
- - Rcv Err Emails - - - -
- No records to display diff --git a/webapp/src/features/adminAdduser/AdminAddUser.tsx b/webapp/src/features/adminAdduser/AdminAddUser.tsx index 44ceaf0b2..ffb2b7d73 100644 --- a/webapp/src/features/adminAdduser/AdminAddUser.tsx +++ b/webapp/src/features/adminAdduser/AdminAddUser.tsx @@ -117,15 +117,6 @@ const AdminAddUser = () => { - - - - Organizations
-
-
- - -
-
diff --git a/webapp/src/features/adminAdduser/adminAddUserSlice.tsx b/webapp/src/features/adminAdduser/adminAddUserSlice.tsx index eef29e37a..da7fe40c8 100644 --- a/webapp/src/features/adminAdduser/adminAddUserSlice.tsx +++ b/webapp/src/features/adminAdduser/adminAddUserSlice.tsx @@ -13,7 +13,6 @@ export type AdminUserForm = { super_user: boolean organizations: string[] roles: string[] - receive_error_emails: boolean } const initialState = { diff --git a/webapp/src/features/adminEditUser/AdminEditUser.tsx b/webapp/src/features/adminEditUser/AdminEditUser.tsx index eb22d12c0..ce8cf2dad 100644 --- a/webapp/src/features/adminEditUser/AdminEditUser.tsx +++ b/webapp/src/features/adminEditUser/AdminEditUser.tsx @@ -55,7 +55,6 @@ const AdminEditUser = () => { first_name: '', last_name: '', super_user: '', - receive_error_emails: '', organizations_to_add: [], organizations_to_modify: [], organizations_to_remove: [], @@ -96,7 +95,6 @@ const AdminEditUser = () => { setValue('first_name', apiData.user_data.first_name) setValue('last_name', apiData.user_data.last_name) setValue('super_user', apiData.user_data.super_user.toString()) - setValue('receive_error_emails', apiData.user_data.receive_error_emails.toString()) } console.log('useEffect apiData', email, userTableData, apiData) }, [apiData, setValue]) @@ -167,15 +165,6 @@ const AdminEditUser = () => { - - - - Organizations { id: 3, render: (rowData: AdminUserWithId) => (rowData.super_user ? 'Yes' : 'No'), }, - { - title: 'Rcv Err Emails', - field: 'receive_error_emails', - id: 3, - render: (rowData: AdminUserWithId) => (rowData.receive_error_emails ? 'Yes' : 'No'), - }, ]) const loading = useSelector(selectLoading) diff --git a/webapp/src/features/adminUserTab/__snapshots__/AdminUserTab.test.tsx.snap b/webapp/src/features/adminUserTab/__snapshots__/AdminUserTab.test.tsx.snap index c9a1ea334..9ddc7e13d 100644 --- a/webapp/src/features/adminUserTab/__snapshots__/AdminUserTab.test.tsx.snap +++ b/webapp/src/features/adminUserTab/__snapshots__/AdminUserTab.test.tsx.snap @@ -368,48 +368,6 @@ exports[`should take a snapshot 1`] = ` />
- -
- - Rcv Err Emails - - - -
- No records to display diff --git a/webapp/src/types/UserTypes.d.ts b/webapp/src/types/UserTypes.d.ts index e7ab0e8e1..f70565fbc 100644 --- a/webapp/src/types/UserTypes.d.ts +++ b/webapp/src/types/UserTypes.d.ts @@ -33,7 +33,6 @@ type AdminUser = { name: string role: string }> - receive_error_emails: boolean } type AdminUserWithId = AdminUser & { From 5b30b8b5093f1beb805b7a93be3ffa46a578da43 Mon Sep 17 00:00:00 2001 From: Marc Wodahl Date: Wed, 19 Jun 2024 16:12:14 -0600 Subject: [PATCH 081/189] Update organization dropdown to update correctly on organization add/change --- webapp/src/Dashboard.tsx | 5 +-- .../AdminOrganizationTab.tsx | 23 ++++++++++-- .../AdminOrganizationTabUser.tsx | 16 ++++++++- webapp/src/generalSlices/userSlice.ts | 35 ++++++++++++++++--- webapp/src/managers.tsx | 7 ++-- 5 files changed, 72 insertions(+), 14 deletions(-) diff --git a/webapp/src/Dashboard.tsx b/webapp/src/Dashboard.tsx index a472760d9..f2fd4c62e 100644 --- a/webapp/src/Dashboard.tsx +++ b/webapp/src/Dashboard.tsx @@ -14,7 +14,7 @@ import { // Actions getRsuData, } from './generalSlices/rsuSlice' -import { selectAuthLoginData, selectLoadingGlobal } from './generalSlices/userSlice' +import { selectAuthLoginData, selectLoadingGlobal, selectOrganizationName } from './generalSlices/userSlice' import { SecureStorageManager } from './managers' import { ReactKeycloakProvider } from '@react-keycloak/web' import keycloak from './keycloak-config' @@ -31,6 +31,7 @@ const Dashboard = () => { const dispatch: ThunkDispatch = useDispatch() const authLoginData = useSelector(selectAuthLoginData) const loadingGlobal = useSelector(selectLoadingGlobal) + const organizationName = useSelector(selectOrganizationName) useEffect(() => { keycloak @@ -53,7 +54,7 @@ const Dashboard = () => { dispatch(getRsuData()) }, [authLoginData, dispatch]) - console.log('Auth Role', SecureStorageManager.getUserRole()) + useEffect(() => {}, [organizationName]) return ( { if (activeTab === undefined) { @@ -59,8 +61,19 @@ const AdminOrganizationTab = () => { const errorState = useSelector(selectErrorState) const errorMsg = useSelector(selectErrorMsg) + const defaultOrgName = useSelector(selectOrganizationName) + var defaultOrgData = orgData.find((org) => org.name === defaultOrgName) + useEffect(() => { - dispatch(getOrgData({ orgName: 'all', all: true, specifiedOrg: undefined })) + dispatch(getOrgData({ orgName: 'all', all: true, specifiedOrg: undefined })).then(() => { + if (defaultOrgData) { + const selectedOrg = (orgData ?? []).find( + (organization: AdminOrgSummary) => organization?.name === defaultOrgName + ) + dispatch(setSelectedOrg(selectedOrg)) + defaultOrgData = null + } + }) }, [dispatch]) const updateTableData = (orgName: string) => { @@ -79,6 +92,12 @@ const AdminOrganizationTab = () => { updateTableData(selectedOrgName) } + const handleOrgDelete = (orgName) => { + dispatch(deleteOrg(orgName)) + dispatch(setOrganizationList({ value: { name: orgName }, type: 'delete' })) + dispatch(changeOrganization(orgData[0].name)) + } + return (
@@ -148,7 +167,7 @@ const AdminOrganizationTab = () => { dispatch(deleteOrg(selectedOrgName))} + deleteOrganization={() => handleOrgDelete(selectedOrgName)} selectedOrganization={selectedOrgName} /> diff --git a/webapp/src/features/adminOrganizationTabUser/AdminOrganizationTabUser.tsx b/webapp/src/features/adminOrganizationTabUser/AdminOrganizationTabUser.tsx index cf9660e95..01132647d 100644 --- a/webapp/src/features/adminOrganizationTabUser/AdminOrganizationTabUser.tsx +++ b/webapp/src/features/adminOrganizationTabUser/AdminOrganizationTabUser.tsx @@ -25,7 +25,7 @@ import { setSelectedUserRole, setSelectedUserList, } from './adminOrganizationTabUserSlice' -import { selectLoadingGlobal } from '../../generalSlices/userSlice' +import { selectAuthLoginData, selectLoadingGlobal, setOrganizationList } from '../../generalSlices/userSlice' import { useSelector, useDispatch } from 'react-redux' import '../adminRsuTab/Admin.css' @@ -47,6 +47,7 @@ const AdminOrganizationTabUser = (props: AdminOrganizationTabUserProps) => { const selectedUserList = useSelector(selectSelectedUserList) const availableRoles = useSelector(selectAvailableRoles) const loadingGlobal = useSelector(selectLoadingGlobal) + const authLoginData = useSelector(selectAuthLoginData) const [userColumns] = useState[]>([ { title: 'First Name', @@ -155,6 +156,9 @@ const AdminOrganizationTabUser = (props: AdminOrganizationTabUserProps) => { updateTableData: props.updateTableData, }) ) + if (row.email === authLoginData?.data?.email) { + dispatch(setOrganizationList({ value: { name: props.selectedOrg, role: row.role }, type: 'delete' })) + } } const userMultiDelete = async (rows: AdminOrgUser[]) => { @@ -165,6 +169,11 @@ const AdminOrganizationTabUser = (props: AdminOrganizationTabUserProps) => { updateTableData: props.updateTableData, }) ) + for (let i = 0; i < rows.length; i++) { + if (rows[i].email === authLoginData?.data?.email) { + dispatch(setOrganizationList({ value: { name: props.selectedOrg, role: rows[i].role }, type: 'delete' })) + } + } } const userMultiAdd = async (userList: AdminOrgUser[]) => { @@ -175,6 +184,11 @@ const AdminOrganizationTabUser = (props: AdminOrganizationTabUserProps) => { updateTableData: props.updateTableData, }) ) + for (let i = 0; i < userList.length; i++) { + if (userList[i].email === authLoginData?.data?.email) { + dispatch(setOrganizationList({ value: { name: props.selectedOrg, role: userList[i].role }, type: 'add' })) + } + } } const userBulkEdit = async ( diff --git a/webapp/src/generalSlices/userSlice.ts b/webapp/src/generalSlices/userSlice.ts index dda091e03..240133cdf 100644 --- a/webapp/src/generalSlices/userSlice.ts +++ b/webapp/src/generalSlices/userSlice.ts @@ -65,8 +65,28 @@ export const userSlice = createSlice({ SecureStorageManager.removeUserRole() }, changeOrganization: (state, action) => { - state.value.organization = + var organization = UserManager.getOrganization(state.value.authLoginData, action.payload) ?? state.value.organization + state.value.organization = organization + SecureStorageManager.setUserRole({ name: organization.name, role: organization.role }) + }, + setOrganizationList: (state, action) => { + if (action.payload.type === 'add') { + console.debug('payload: ', [...state.value.authLoginData.data.organizations, action.payload]) + state.value.authLoginData.data.organizations = [ + ...state.value.authLoginData.data.organizations, + action.payload.value, + ] + } else if (action.payload.type === 'delete') { + var index = state.value.authLoginData.data.organizations.findIndex( + (org) => org.name === action.payload.value.name + ) + if (index > -1) { + var updatedOrgList = state.value.authLoginData.data.organizations + updatedOrgList.splice(index, 1) + state.value.authLoginData.data.organizations = [...updatedOrgList] + } + } }, setLoading: (state, action) => { state.loading = action.payload @@ -102,7 +122,7 @@ export const userSlice = createSlice({ state.value.authLoginData = action.payload state.value.organization = action.payload?.data?.organizations?.[0] LocalStorageManager.setAuthData(action.payload) - SecureStorageManager.setUserRole(action.payload) + SecureStorageManager.setUserRole(action.payload['data']['organizations'][0]) }) .addCase(keycloakLogin.rejected, (state, action: PayloadAction) => { console.debug('keycloakLogin.rejected') @@ -115,8 +135,15 @@ export const userSlice = createSlice({ }, }) -export const { logout, changeOrganization, setLoading, setLoginFailure, setKcFailure, setRouteNotFound } = - userSlice.actions +export const { + logout, + changeOrganization, + setOrganizationList, + setLoading, + setLoginFailure, + setKcFailure, + setRouteNotFound, +} = userSlice.actions export const selectAuthLoginData = (state: RootState) => state.user.value.authLoginData export const selectToken = (state: RootState) => state.user.value.authLoginData.token diff --git a/webapp/src/managers.tsx b/webapp/src/managers.tsx index 6b9d22fe5..b9d502d8a 100644 --- a/webapp/src/managers.tsx +++ b/webapp/src/managers.tsx @@ -44,11 +44,8 @@ const SecureStorageManager = { return authData['role'] } }, - setUserRole: (authData) => { - return secureLocalStorage.setItem( - AUTH_DATA_SECURE_STORAGE_KEY, - JSON.stringify(authData['data']['organizations'][0]) - ) + setUserRole: (organization) => { + return secureLocalStorage.setItem(AUTH_DATA_SECURE_STORAGE_KEY, JSON.stringify(organization)) }, removeUserRole: () => { return secureLocalStorage.removeItem(AUTH_DATA_SECURE_STORAGE_KEY) From d694b5c037721d68c114029c07199c5cf3913b7a Mon Sep 17 00:00:00 2001 From: Marc Wodahl Date: Thu, 20 Jun 2024 10:41:06 -0600 Subject: [PATCH 082/189] Add toast package --- webapp/package-lock.json | 30 ++++++++++++++++++++++++++++++ webapp/package.json | 3 ++- webapp/yarn.lock | 24 +++++++++++++++++++++++- 3 files changed, 55 insertions(+), 2 deletions(-) diff --git a/webapp/package-lock.json b/webapp/package-lock.json index 150013f5b..fc740e940 100644 --- a/webapp/package-lock.json +++ b/webapp/package-lock.json @@ -40,6 +40,7 @@ "react-confirm-alert": "^2.8.0", "react-dom": "^17.0.2", "react-hook-form": "^7.41.5", + "react-hot-toast": "^2.4.1", "react-icons": "^4.3.1", "react-map-gl": "^7.0.21", "react-redux": "^8.0.5", @@ -21229,6 +21230,35 @@ "react": "^16.8.0 || ^17 || ^18" } }, + "node_modules/react-hot-toast": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/react-hot-toast/-/react-hot-toast-2.4.1.tgz", + "integrity": "sha512-j8z+cQbWIM5LY37pR6uZR6D4LfseplqnuAO4co4u8917hBUvXlEqyP1ZzqVLcqoyUesZZv/ImreoCeHVDpE5pQ==", + "dependencies": { + "goober": "^2.1.10" + }, + "engines": { + "node": ">=10" + }, + "peerDependencies": { + "react": ">=16", + "react-dom": ">=16" + } + }, + "node_modules/react-hot-toast/node_modules/csstype": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.3.tgz", + "integrity": "sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==", + "peer": true + }, + "node_modules/react-hot-toast/node_modules/goober": { + "version": "2.1.14", + "resolved": "https://registry.npmjs.org/goober/-/goober-2.1.14.tgz", + "integrity": "sha512-4UpC0NdGyAFqLNPnhCT2iHpza2q+RAY3GV85a/mRPdzyPQMsj0KmMMuetdIkzWRbJ+Hgau1EZztq8ImmiMGhsg==", + "peerDependencies": { + "csstype": "^3.0.10" + } + }, "node_modules/react-icons": { "version": "4.3.1", "resolved": "https://registry.npmjs.org/react-icons/-/react-icons-4.3.1.tgz", diff --git a/webapp/package.json b/webapp/package.json index e7b6c8ba5..2cad32714 100644 --- a/webapp/package.json +++ b/webapp/package.json @@ -35,6 +35,7 @@ "react-confirm-alert": "^2.8.0", "react-dom": "^17.0.2", "react-hook-form": "^7.41.5", + "react-hot-toast": "^2.4.1", "react-icons": "^4.3.1", "react-map-gl": "^7.0.21", "react-redux": "^8.0.5", @@ -101,4 +102,4 @@ "worker-loader!mapbox-gl/dist/mapbox-gl-csp-worker": "/mocks/workerLoader.js" } } -} \ No newline at end of file +} diff --git a/webapp/yarn.lock b/webapp/yarn.lock index 4581822ab..fdd3b61d9 100644 --- a/webapp/yarn.lock +++ b/webapp/yarn.lock @@ -4913,6 +4913,11 @@ csstype@^2.5.2: resolved "https://registry.npmjs.org/csstype/-/csstype-2.6.17.tgz" integrity sha512-u1wmTI1jJGzCJzWndZo8mk4wnPTZd1eOIYTYvuEyOQGfmDl3TrabCCfKnOC86FZwW/9djqTl933UF/cS425i9A== +csstype@^3.0.10: + version "3.1.3" + resolved "https://registry.npmjs.org/csstype/-/csstype-3.1.3.tgz" + integrity sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw== + csstype@^3.0.2: version "3.1.1" resolved "https://registry.npmjs.org/csstype/-/csstype-3.1.1.tgz" @@ -6338,6 +6343,11 @@ fs.realpath@^1.0.0: resolved "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz" integrity sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw== +fsevents@^2.1.2, fsevents@^2.3.2, fsevents@~2.3.2: + version "2.3.2" + resolved "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz" + integrity sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA== + function-bind@^1.1.1, function-bind@^1.1.2: version "1.1.2" resolved "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz" @@ -6533,6 +6543,11 @@ globby@^11.0.4, globby@^11.1.0: merge2 "^1.4.1" slash "^3.0.0" +goober@^2.1.10: + version "2.1.14" + resolved "https://registry.npmjs.org/goober/-/goober-2.1.14.tgz" + integrity sha512-4UpC0NdGyAFqLNPnhCT2iHpza2q+RAY3GV85a/mRPdzyPQMsj0KmMMuetdIkzWRbJ+Hgau1EZztq8ImmiMGhsg== + gopd@^1.0.1: version "1.0.1" resolved "https://registry.npmjs.org/gopd/-/gopd-1.0.1.tgz" @@ -10508,7 +10523,7 @@ react-dev-utils@^12.0.0: strip-ansi "^6.0.1" text-table "^0.2.0" -"react-dom@^16.0.0 || ^17.0.0", "react-dom@^16.8 || ^17.0 || ^18.0", "react-dom@^16.8.0 || ^17 || ^18", "react-dom@^16.8.0 || ^17.0.0", "react-dom@^16.8.0 || ^17.0.0 || ^18.0.0", "react-dom@^16.8.5 || ^17.0.0 || ^18.0.0", "react-dom@^17.0.0 || ^18.0.0", react-dom@^17.0.2, "react-dom@^17.0.2 || ^18.0.0", react-dom@<18.0.0, "react-dom@>= 16.8.0", react-dom@>=0.16.0, react-dom@>=16.0.0, react-dom@>=16.14.0, react-dom@>=16.6.0, react-dom@>=16.8, react-dom@>=16.8.0, react-dom@>=16.9.0: +"react-dom@^16.0.0 || ^17.0.0", "react-dom@^16.8 || ^17.0 || ^18.0", "react-dom@^16.8.0 || ^17 || ^18", "react-dom@^16.8.0 || ^17.0.0", "react-dom@^16.8.0 || ^17.0.0 || ^18.0.0", "react-dom@^16.8.5 || ^17.0.0 || ^18.0.0", "react-dom@^17.0.0 || ^18.0.0", react-dom@^17.0.2, "react-dom@^17.0.2 || ^18.0.0", react-dom@<18.0.0, "react-dom@>= 16.8.0", react-dom@>=0.16.0, react-dom@>=16, react-dom@>=16.0.0, react-dom@>=16.14.0, react-dom@>=16.6.0, react-dom@>=16.8, react-dom@>=16.8.0, react-dom@>=16.9.0: version "17.0.2" resolved "https://registry.npmjs.org/react-dom/-/react-dom-17.0.2.tgz" integrity sha512-s4h96KtLDUQlsENhMn1ar8t2bEa+q/YAtj8pPPdIjPDGBDIVNsrD9aXNWqspUe6AzKCIG0C1HZZLqLV7qpOBGA== @@ -10537,6 +10552,13 @@ react-hook-form@^7.0.0, react-hook-form@^7.41.5: resolved "https://registry.npmjs.org/react-hook-form/-/react-hook-form-7.41.5.tgz" integrity sha512-DAKjSJ7X9f16oQrP3TW2/eD9N6HOgrmIahP4LOdFphEWVfGZ2LulFd6f6AQ/YS/0cx/5oc4j8a1PXxuaurWp/Q== +react-hot-toast@^2.4.1: + version "2.4.1" + resolved "https://registry.npmjs.org/react-hot-toast/-/react-hot-toast-2.4.1.tgz" + integrity sha512-j8z+cQbWIM5LY37pR6uZR6D4LfseplqnuAO4co4u8917hBUvXlEqyP1ZzqVLcqoyUesZZv/ImreoCeHVDpE5pQ== + dependencies: + goober "^2.1.10" + react-icons@^4.3.1: version "4.3.1" resolved "https://registry.npmjs.org/react-icons/-/react-icons-4.3.1.tgz" From 7f1c8cf06f9b52e01935b17d5067242a735da8c1 Mon Sep 17 00:00:00 2001 From: Marc Wodahl Date: Thu, 20 Jun 2024 10:49:40 -0600 Subject: [PATCH 083/189] AdminAddOrganization toast notifications --- webapp/src/App.tsx | 8 ++ webapp/src/__snapshots__/App.test.tsx.snap | 3 + .../AdminAddOrganization.tsx | 27 ++---- .../AdminAddOrganization.test.tsx.snap | 1 - .../adminAddOrganizationSlice.test.ts | 87 ++----------------- .../adminAddOrganizationSlice.tsx | 37 +------- 6 files changed, 26 insertions(+), 137 deletions(-) diff --git a/webapp/src/App.tsx b/webapp/src/App.tsx index 290ac2b0c..84c762ff2 100644 --- a/webapp/src/App.tsx +++ b/webapp/src/App.tsx @@ -16,6 +16,7 @@ import Dashboard from './Dashboard' import { NotFound } from './pages/404' import { theme } from './styles' import { ThemeProvider } from '@mui/material' +import { Toaster, ToastOptions } from 'react-hot-toast' const App = () => { const dispatch: ThunkDispatch = useDispatch() @@ -56,6 +57,13 @@ const App = () => { )} + ) } diff --git a/webapp/src/__snapshots__/App.test.tsx.snap b/webapp/src/__snapshots__/App.test.tsx.snap index 520c5f1d2..8668e2e78 100644 --- a/webapp/src/__snapshots__/App.test.tsx.snap +++ b/webapp/src/__snapshots__/App.test.tsx.snap @@ -41,5 +41,8 @@ exports[`should take a snapshot 1`] = `
+
`; diff --git a/webapp/src/features/adminAddOrganization/AdminAddOrganization.tsx b/webapp/src/features/adminAddOrganization/AdminAddOrganization.tsx index 916bf95e5..3dadb46c4 100644 --- a/webapp/src/features/adminAddOrganization/AdminAddOrganization.tsx +++ b/webapp/src/features/adminAddOrganization/AdminAddOrganization.tsx @@ -2,14 +2,11 @@ import React from 'react' import { Form } from 'react-bootstrap' import { useForm } from 'react-hook-form' import { - selectSuccessMsg, - selectErrorState, - selectErrorMsg, - // actions addOrg, } from './adminAddOrganizationSlice' import { useSelector, useDispatch } from 'react-redux' +import toast from 'react-hot-toast' import '../adminRsuTab/Admin.css' import 'react-widgets/styles.css' @@ -22,18 +19,20 @@ export type AdminAddOrgForm = { const AdminAddOrganization = () => { const dispatch: ThunkDispatch = useDispatch() - const successMsg = useSelector(selectSuccessMsg) - const errorState = useSelector(selectErrorState) - const errorMsg = useSelector(selectErrorMsg) + const notifySuccess = (message: string) => toast.success(message) + const notifyError = (message: string) => toast.error(message) const { register, handleSubmit, - reset, formState: { errors }, } = useForm() const onSubmit = (data: AdminAddOrgForm) => { - dispatch(addOrg({ json: data, reset })) + dispatch(addOrg({ json: data })).then((data: any) => { + data.payload.success + ? notifySuccess(data.payload.message) + : notifyError('Failed to add organization due to error: ' + data.payload.message) + }) } return ( @@ -55,16 +54,6 @@ const AdminAddOrganization = () => { )} - {successMsg && ( -

- {successMsg} -

- )} - {errorState && ( -

- Failed to add organization due to error: {errorMsg} -

- )}
-
diff --git a/webapp/src/features/adminAddOrganization/adminAddOrganizationSlice.test.ts b/webapp/src/features/adminAddOrganization/adminAddOrganizationSlice.test.ts index a277e480d..4ad021be2 100644 --- a/webapp/src/features/adminAddOrganization/adminAddOrganizationSlice.test.ts +++ b/webapp/src/features/adminAddOrganization/adminAddOrganizationSlice.test.ts @@ -2,16 +2,9 @@ import reducer from './adminAddOrganizationSlice' import { // async thunks addOrg, - resetMsg, - - // reducers - setSuccessMsg, // selectors selectLoading, - selectSuccessMsg, - selectErrorState, - selectErrorMsg, } from './adminAddOrganizationSlice' import apiHelper from '../../apis/api-helper' import EnvironmentVars from '../../EnvironmentVars' @@ -21,11 +14,6 @@ describe('admin add organization reducer', () => { it('should handle initial state', () => { expect(reducer(undefined, { type: 'unknown' })).toEqual({ loading: false, - value: { - successMsg: '', - errorState: false, - errorMsg: '', - }, }) }) }) @@ -33,11 +21,6 @@ describe('admin add organization reducer', () => { describe('async thunks', () => { const initialState: RootState['adminAddOrganization'] = { loading: null, - value: { - successMsg: null, - errorState: null, - errorMsg: null, - }, } beforeAll(() => { @@ -59,8 +42,7 @@ describe('async thunks', () => { }, }) const json = { data: 'data' } as any - let reset = jest.fn() - let action = addOrg({ json, reset }) + let action = addOrg({ json }) apiHelper._postData = jest.fn().mockReturnValue({ status: 200, message: 'message' }) let resp = await action(dispatch, getState, undefined) @@ -70,13 +52,11 @@ describe('async thunks', () => { token: 'token', body: JSON.stringify(json), }) - expect(reset).toHaveBeenCalled() - expect(dispatch).toHaveBeenCalledTimes(2 + 2) + expect(dispatch).toHaveBeenCalledTimes(1 + 2) // Error Code Other dispatch = jest.fn() - reset = jest.fn() - action = addOrg({ json, reset }) + action = addOrg({ json }) apiHelper._postData = jest.fn().mockReturnValue({ status: 500, message: 'message' }) resp = await action(dispatch, getState, undefined) expect(resp.payload).toEqual({ success: false, message: 'message' }) @@ -85,8 +65,7 @@ describe('async thunks', () => { token: 'token', body: JSON.stringify(json), }) - expect(dispatch).toHaveBeenCalledTimes(1 + 2) - expect(reset).not.toHaveBeenCalled() + expect(dispatch).toHaveBeenCalledTimes(0 + 2) }) it('Updates the state correctly pending', async () => { @@ -97,15 +76,11 @@ describe('async thunks', () => { expect(state).toEqual({ ...initialState, loading, - value: { ...initialState.value }, }) }) it('Updates the state correctly fulfilled', async () => { const loading = false - let successMsg = 'message' - let errorMsg = '' - let errorState = false let state = reducer(initialState, { type: 'adminAddOrganization/addOrg/fulfilled', @@ -115,13 +90,9 @@ describe('async thunks', () => { expect(state).toEqual({ ...initialState, loading, - value: { ...initialState.value, successMsg, errorMsg, errorState }, }) // Error Case - successMsg = '' - errorMsg = 'message' - errorState = true state = reducer(initialState, { type: 'adminAddOrganization/addOrg/fulfilled', @@ -131,7 +102,6 @@ describe('async thunks', () => { expect(state).toEqual({ ...initialState, loading, - value: { ...initialState.value, successMsg, errorMsg, errorState }, }) }) @@ -140,50 +110,7 @@ describe('async thunks', () => { const state = reducer(initialState, { type: 'adminAddOrganization/addOrg/rejected', }) - expect(state).toEqual({ ...initialState, loading, value: { ...initialState.value } }) - }) - }) - - describe('resetMsg', () => { - it('returns and calls the api correctly', async () => { - let dispatch = jest.fn() - const getState = jest.fn().mockReturnValue({ - user: { - value: { - authLoginData: { token: 'token' }, - }, - }, - }) - const action = resetMsg() - - global.setTimeout = jest.fn((cb) => cb()) as any - try { - await action(dispatch, getState, undefined) - expect(setTimeout).toHaveBeenCalledTimes(1) - expect(dispatch).toHaveBeenCalledTimes(1 + 2) - } catch (e) { - ;(global.setTimeout as any).mockClear() - throw e - } - }) - }) -}) - -describe('reducers', () => { - const initialState: RootState['adminAddOrganization'] = { - loading: null, - value: { - successMsg: null, - errorState: null, - errorMsg: null, - }, - } - - it('setSuccessMsg reducer updates state correctly', async () => { - const successMsg = 'successMsg' - expect(reducer(initialState, setSuccessMsg(successMsg))).toEqual({ - ...initialState, - value: { ...initialState.value, successMsg }, + expect(state).toEqual({ ...initialState, loading }) }) }) }) @@ -201,9 +128,5 @@ describe('selectors', () => { it('selectors return the correct value', async () => { expect(selectLoading(state)).toEqual('loading') - - expect(selectSuccessMsg(state)).toEqual('successMsg') - expect(selectErrorState(state)).toEqual('errorState') - expect(selectErrorMsg(state)).toEqual('errorMsg') }) }) diff --git a/webapp/src/features/adminAddOrganization/adminAddOrganizationSlice.tsx b/webapp/src/features/adminAddOrganization/adminAddOrganizationSlice.tsx index 8676b63a9..be14b0885 100644 --- a/webapp/src/features/adminAddOrganization/adminAddOrganizationSlice.tsx +++ b/webapp/src/features/adminAddOrganization/adminAddOrganizationSlice.tsx @@ -6,22 +6,15 @@ import { RootState } from '../../store' import { getOrgData } from '../adminOrganizationTab/adminOrganizationTabSlice' import { AdminAddOrgForm } from './AdminAddOrganization' -const initialState = { - successMsg: '', - errorState: false, - errorMsg: '', -} - export const addOrg = createAsyncThunk( 'adminAddOrganization/addOrg', async ( payload: { json: AdminAddOrgForm - reset: () => void }, { getState, dispatch } ) => { - const { json, reset } = payload + const { json } = payload const currentState = getState() as RootState const token = selectToken(currentState) @@ -32,33 +25,21 @@ export const addOrg = createAsyncThunk( }) switch (data.status) { case 200: - reset() - dispatch(resetMsg()) dispatch(getOrgData({ orgName: 'all', all: true, specifiedOrg: json.name })) return { success: true, message: 'Organization Creation is successful.' } default: - dispatch(resetMsg()) return { success: false, message: data.message } } }, { condition: (_, { getState }) => selectToken(getState() as RootState) != undefined } ) -export const resetMsg = createAsyncThunk('adminAddOrganization/resetMsg', async (_, { dispatch }) => { - setTimeout(() => dispatch(setSuccessMsg('')), 5000) -}) - export const adminAddOrganizationSlice = createSlice({ name: 'adminAddOrganization', initialState: { loading: false, - value: initialState, - }, - reducers: { - setSuccessMsg: (state, action) => { - state.value.successMsg = action.payload - }, }, + reducers: {}, extraReducers: (builder) => { builder .addCase(addOrg.pending, (state) => { @@ -66,15 +47,6 @@ export const adminAddOrganizationSlice = createSlice({ }) .addCase(addOrg.fulfilled, (state, action) => { state.loading = false - if (action.payload.success) { - state.value.successMsg = action.payload.message - state.value.errorMsg = '' - state.value.errorState = false - } else { - state.value.successMsg = '' - state.value.errorMsg = action.payload.message - state.value.errorState = true - } }) .addCase(addOrg.rejected, (state) => { state.loading = false @@ -82,11 +54,6 @@ export const adminAddOrganizationSlice = createSlice({ }, }) -export const { setSuccessMsg } = adminAddOrganizationSlice.actions - export const selectLoading = (state: RootState) => state.adminAddOrganization.loading -export const selectSuccessMsg = (state: RootState) => state.adminAddOrganization.value.successMsg -export const selectErrorState = (state: RootState) => state.adminAddOrganization.value.errorState -export const selectErrorMsg = (state: RootState) => state.adminAddOrganization.value.errorMsg export default adminAddOrganizationSlice.reducer From ef5ce7dae53f05bce2ff49abc67ec105237456eb Mon Sep 17 00:00:00 2001 From: Marc Wodahl Date: Thu, 20 Jun 2024 11:53:39 -0600 Subject: [PATCH 084/189] AdminAddRsu, AdminAddUser toast notifications add --- .../src/features/adminAddRsu/AdminAddRsu.tsx | 32 ++++----- .../__snapshots__/AdminAddRsu.test.tsx.snap | 11 ---- .../adminAddRsu/adminAddRsuSlice.test.ts | 65 ++++--------------- .../features/adminAddRsu/adminAddRsuSlice.tsx | 38 +++-------- .../features/adminAdduser/AdminAddUser.tsx | 27 +++----- .../__snapshots__/AdminAddUser.test.tsx.snap | 1 - .../adminAdduser/adminAddUserSlice.test.ts | 59 ++++------------- .../adminAdduser/adminAddUserSlice.tsx | 45 +++---------- 8 files changed, 66 insertions(+), 212 deletions(-) diff --git a/webapp/src/features/adminAddRsu/AdminAddRsu.tsx b/webapp/src/features/adminAddRsu/AdminAddRsu.tsx index f007afdf5..39175435b 100644 --- a/webapp/src/features/adminAddRsu/AdminAddRsu.tsx +++ b/webapp/src/features/adminAddRsu/AdminAddRsu.tsx @@ -3,9 +3,6 @@ import { Form } from 'react-bootstrap' import { useForm } from 'react-hook-form' import { Multiselect, DropdownList } from 'react-widgets' import { - selectSuccessMsg, - selectErrorState, - selectErrorMsg, selectPrimaryRoutes, selectSelectedRoute, selectOtherRouteDisabled, @@ -36,6 +33,7 @@ import { useSelector, useDispatch } from 'react-redux' import '../adminRsuTab/Admin.css' import { AnyAction, ThunkDispatch } from '@reduxjs/toolkit' import { RootState } from '../../store' +import toast from 'react-hot-toast' export type AdminAddRsuForm = { ip: string @@ -55,9 +53,6 @@ export type AdminAddRsuForm = { const AdminAddRsu = () => { const dispatch: ThunkDispatch = useDispatch() - const successMsg = useSelector(selectSuccessMsg) - const errorState = useSelector(selectErrorState) - const errorMsg = useSelector(selectErrorMsg) const primaryRoutes = useSelector(selectPrimaryRoutes) const selectedRoute = useSelector(selectSelectedRoute) const otherRouteDisabled = useSelector(selectOtherRouteDisabled) @@ -73,6 +68,17 @@ const AdminAddRsu = () => { const selectedOrganizations = useSelector(selectSelectedOrganizations) const submitAttempt = useSelector(selectSubmitAttempt) + const notifySuccess = (message: string) => toast.success(message) + const notifyError = (message: string) => toast.error(message) + + const handleFormSubmit = (data: AdminAddRsuForm) => { + dispatch(submitForm({ data, reset })).then((data: any) => { + data.payload.success + ? notifySuccess(data.payload.message) + : notifyError('Failed to add RSU due to error: ' + data.payload.message) + }) + } + const { register, handleSubmit, @@ -86,7 +92,7 @@ const AdminAddRsu = () => { return (
-
dispatch(submitForm({ data, reset })))}> + handleFormSubmit(data))}> RSU IP { )} - { -

- {successMsg} -

- } - - {errorState && ( -

- Failed to add rsu due to error: {errorMsg} -

- )} -
-

-

diff --git a/webapp/src/features/adminAddRsu/adminAddRsuSlice.test.ts b/webapp/src/features/adminAddRsu/adminAddRsuSlice.test.ts index 76105964c..f2fd06753 100644 --- a/webapp/src/features/adminAddRsu/adminAddRsuSlice.test.ts +++ b/webapp/src/features/adminAddRsu/adminAddRsuSlice.test.ts @@ -11,7 +11,6 @@ import { updateJson, // reducers - setSuccessMsg, updateSelectedRoute, updateSelectedModel, updateSelectedSshGroup, @@ -28,9 +27,6 @@ import { selectSnmpCredentialGroups, selectSnmpVersions, selectOrganizations, - selectSuccessMsg, - selectErrorState, - selectErrorMsg, selectSelectedRoute, selectOtherRouteDisabled, selectSelectedModel, @@ -50,10 +46,7 @@ describe('admin add RSU reducer', () => { expect(reducer(undefined, { type: 'unknown' })).toEqual({ loading: false, value: { - successMsg: '', apiData: {}, - errorState: false, - errorMsg: '', selectedRoute: 'Select Route (Required)', otherRouteDisabled: true, selectedModel: 'Select RSU Model (Required)', @@ -71,10 +64,7 @@ describe('async thunks', () => { const initialState: RootState['adminAddRsu'] = { loading: null, value: { - successMsg: 'successMsg', apiData: null, - errorState: null, - errorMsg: null, selectedRoute: null, otherRouteDisabled: null, selectedModel: null, @@ -119,20 +109,18 @@ describe('async thunks', () => { it('Updates the state correctly pending', async () => { const loading = true - const errorState = false const state = reducer(initialState, { type: 'adminAddRsu/getRsuCreationData/pending', }) expect(state).toEqual({ ...initialState, loading, - value: { ...initialState.value, errorState }, + value: { ...initialState.value }, }) }) it('Updates the state correctly fulfilled', async () => { const loading = false - const errorState = false const apiData = 'apiData' const state = reducer(initialState, { type: 'adminAddRsu/getRsuCreationData/fulfilled', @@ -142,17 +130,16 @@ describe('async thunks', () => { expect(state).toEqual({ ...initialState, loading, - value: { ...initialState.value, errorState, apiData }, + value: { ...initialState.value, apiData }, }) }) it('Updates the state correctly rejected', async () => { const loading = false - const errorState = true const state = reducer(initialState, { type: 'adminAddRsu/getRsuCreationData/rejected', }) - expect(state).toEqual({ ...initialState, loading, value: { ...initialState.value, errorState } }) + expect(state).toEqual({ ...initialState, loading, value: { ...initialState.value } }) }) }) @@ -180,8 +167,7 @@ describe('async thunks', () => { token: 'token', body: JSON.stringify(json), }) - expect(setTimeout).toHaveBeenCalledTimes(1) - expect(dispatch).toHaveBeenCalledTimes(3 + 2) + expect(dispatch).toHaveBeenCalledTimes(2 + 2) expect(reset).toHaveBeenCalledTimes(1) } catch (e) { ;(global.setTimeout as any).mockClear() @@ -213,22 +199,18 @@ describe('async thunks', () => { it('Updates the state correctly pending', async () => { const loading = true - const errorState = false const state = reducer(initialState, { type: 'adminAddRsu/createRsu/pending', }) expect(state).toEqual({ ...initialState, loading, - value: { ...initialState.value, errorState }, + value: { ...initialState.value }, }) }) it('Updates the state correctly fulfilled', async () => { const loading = false - let successMsg = 'RSU Creation is successful.' - let errorMsg = '' - let errorState = false let state = reducer(initialState, { type: 'adminAddRsu/createRsu/fulfilled', @@ -238,13 +220,10 @@ describe('async thunks', () => { expect(state).toEqual({ ...initialState, loading, - value: { ...initialState.value, successMsg, errorMsg, errorState }, + value: { ...initialState.value }, }) // Error Case - successMsg = '' - errorMsg = 'message' - errorState = true state = reducer(initialState, { type: 'adminAddRsu/createRsu/fulfilled', @@ -254,18 +233,16 @@ describe('async thunks', () => { expect(state).toEqual({ ...initialState, loading, - value: { ...initialState.value, successMsg, errorMsg, errorState }, + value: { ...initialState.value }, }) }) it('Updates the state correctly rejected', async () => { const loading = false - const errorState = true - const errorMsg = 'unknown error' const state = reducer(initialState, { type: 'adminAddRsu/createRsu/rejected', }) - expect(state).toEqual({ ...initialState, loading, value: { ...initialState.value, errorState, errorMsg } }) + expect(state).toEqual({ ...initialState, loading, value: { ...initialState.value } }) }) }) @@ -294,7 +271,6 @@ describe('async thunks', () => { let reset = jest.fn() let action = submitForm({ data, reset }) let resp = await action(dispatch, getState, undefined) - expect(resp.payload).toEqual(false) expect(dispatch).toHaveBeenCalledTimes(1 + 2) // invalid checkForm @@ -319,7 +295,11 @@ describe('async thunks', () => { }) action = submitForm({ data, reset }) resp = await action(dispatch, getState, undefined) - expect(resp.payload).toEqual(true) + expect(resp.payload).toEqual({ + message: 'Please fill out all required fields', + submitAttempt: true, + success: false, + }) expect(dispatch).toHaveBeenCalledTimes(0 + 2) }) @@ -328,7 +308,7 @@ describe('async thunks', () => { const state = reducer(initialState, { type: 'adminAddRsu/submitForm/fulfilled', - payload: submitAttempt, + payload: { submitAttempt: 'submitAttempt' }, }) expect(state).toEqual({ @@ -544,10 +524,7 @@ describe('reducers', () => { const initialState: RootState['adminAddRsu'] = { loading: null, value: { - successMsg: null, apiData: null, - errorState: null, - errorMsg: null, selectedRoute: null, otherRouteDisabled: null, selectedModel: null, @@ -559,14 +536,6 @@ describe('reducers', () => { }, } - it('setSuccessMsg reducer updates state correctly', async () => { - const successMsg = 'successMsg' - expect(reducer(initialState, setSuccessMsg(successMsg))).toEqual({ - ...initialState, - value: { ...initialState.value, successMsg }, - }) - }) - it('updateSelectedRoute reducer updates state correctly', async () => { let selectedRoute = 'selectedRoute' let otherRouteDisabled = false @@ -651,7 +620,6 @@ describe('selectors', () => { const initialState = { loading: 'loading', value: { - successMsg: 'successMsg', apiData: { primary_routes: ['I-25', 'I-70'], rsu_models: ['model1', 'model2'], @@ -660,8 +628,6 @@ describe('selectors', () => { snmp_version_groups: ['version_1', 'version_2'], organizations: ['org1', 'org2'], }, - errorState: 'errorState', - errorMsg: 'errorMsg', selectedRoute: 'selectedRoute', otherRouteDisabled: 'otherRouteDisabled', rsuModels: 'rsuModels', @@ -678,7 +644,6 @@ describe('selectors', () => { it('selectors return the correct value', async () => { expect(selectLoading(state)).toEqual('loading') - expect(selectSuccessMsg(state)).toEqual('successMsg') expect(selectApiData(state)).toEqual(initialState.value.apiData) expect(selectPrimaryRoutes(state)).toEqual(initialState.value.apiData.primary_routes) expect(selectRsuModels(state)).toEqual(initialState.value.apiData.rsu_models) @@ -686,8 +651,6 @@ describe('selectors', () => { expect(selectSnmpCredentialGroups(state)).toEqual(initialState.value.apiData.snmp_credential_groups) expect(selectSnmpVersions(state)).toEqual(initialState.value.apiData.snmp_version_groups) expect(selectOrganizations(state)).toEqual(initialState.value.apiData.organizations) - expect(selectErrorState(state)).toEqual('errorState') - expect(selectErrorMsg(state)).toEqual('errorMsg') expect(selectSelectedRoute(state)).toEqual('selectedRoute') expect(selectOtherRouteDisabled(state)).toEqual('otherRouteDisabled') expect(selectSelectedModel(state)).toEqual('selectedModel') diff --git a/webapp/src/features/adminAddRsu/adminAddRsuSlice.tsx b/webapp/src/features/adminAddRsu/adminAddRsuSlice.tsx index 48271fdc7..2222786a5 100644 --- a/webapp/src/features/adminAddRsu/adminAddRsuSlice.tsx +++ b/webapp/src/features/adminAddRsu/adminAddRsuSlice.tsx @@ -44,10 +44,7 @@ export type AdminRsuCreationBody = { } const initialState = { - successMsg: '', apiData: {} as AdminRsuKeyedCreationInfo, - errorState: false, - errorMsg: '', selectedRoute: 'Select Route (Required)', otherRouteDisabled: true, selectedModel: 'Select RSU Model (Required)', @@ -191,7 +188,6 @@ export const createRsu = createAsyncThunk( switch (data.status) { case 200: dispatch(adminAddRsuSlice.actions.resetForm()) - setTimeout(() => dispatch(setSuccessMsg('')), 5000) dispatch(updateRsuTableData()) reset() return { success: true, message: '' } @@ -210,10 +206,14 @@ export const submitForm = createAsyncThunk( const currentState = getState() as RootState if (checkForm(currentState.adminAddRsu)) { let json = updateJson(data, currentState.adminAddRsu) - dispatch(createRsu({ json, reset })) - return false + let res = await dispatch(createRsu({ json, reset })) + if ((res.payload as any).success) { + return { submitAttempt: false, success: true, message: 'RSU Created Successfully' } + } else { + return { submitAttempt: false, success: false, message: (res.payload as any).message } + } } else { - return true + return { submitAttempt: true, success: false, message: 'Please fill out all required fields' } } } ) @@ -225,9 +225,6 @@ export const adminAddRsuSlice = createSlice({ value: initialState, }, reducers: { - setSuccessMsg: (state, action) => { - state.value.successMsg = action.payload - }, updateSelectedRoute: (state, action) => { state.value.selectedRoute = action.payload state.value.otherRouteDisabled = action.payload === 'Other' @@ -261,46 +258,30 @@ export const adminAddRsuSlice = createSlice({ builder .addCase(getRsuCreationData.pending, (state) => { state.loading = true - state.value.errorState = false }) .addCase(getRsuCreationData.fulfilled, (state, action) => { state.loading = false state.value.apiData = action.payload - state.value.errorState = false }) .addCase(getRsuCreationData.rejected, (state) => { state.loading = false - state.value.errorState = true }) .addCase(createRsu.pending, (state) => { state.loading = true - state.value.errorState = false }) .addCase(createRsu.fulfilled, (state, action) => { state.loading = false - if (action.payload.success) { - state.value.successMsg = 'RSU Creation is successful.' - state.value.errorMsg = '' - state.value.errorState = false - } else { - state.value.successMsg = '' - state.value.errorMsg = action.payload.message - state.value.errorState = true - } }) .addCase(createRsu.rejected, (state) => { state.loading = false - state.value.errorState = true - state.value.errorMsg = 'unknown error' }) .addCase(submitForm.fulfilled, (state, action) => { - state.value.submitAttempt = action.payload + state.value.submitAttempt = action.payload.submitAttempt }) }, }) export const { - setSuccessMsg, resetForm, updateSelectedRoute, updateSelectedModel, @@ -320,9 +301,6 @@ export const selectSnmpCredentialGroups = (state: RootState) => export const selectSnmpVersions = (state: RootState) => state.adminAddRsu.value.apiData?.snmp_version_groups ?? [] export const selectOrganizations = (state: RootState) => state.adminAddRsu.value.apiData?.organizations ?? [] -export const selectSuccessMsg = (state: RootState) => state.adminAddRsu.value.successMsg -export const selectErrorState = (state: RootState) => state.adminAddRsu.value.errorState -export const selectErrorMsg = (state: RootState) => state.adminAddRsu.value.errorMsg export const selectSelectedRoute = (state: RootState) => state.adminAddRsu.value.selectedRoute export const selectOtherRouteDisabled = (state: RootState) => state.adminAddRsu.value.otherRouteDisabled export const selectSelectedModel = (state: RootState) => state.adminAddRsu.value.selectedModel diff --git a/webapp/src/features/adminAdduser/AdminAddUser.tsx b/webapp/src/features/adminAdduser/AdminAddUser.tsx index 44ceaf0b2..09e9979ba 100644 --- a/webapp/src/features/adminAdduser/AdminAddUser.tsx +++ b/webapp/src/features/adminAdduser/AdminAddUser.tsx @@ -3,14 +3,11 @@ import { Form } from 'react-bootstrap' import { useForm } from 'react-hook-form' import { Multiselect, DropdownList } from 'react-widgets' import { - selectSuccessMsg, selectSelectedOrganizationNames, selectSelectedOrganizations, selectOrganizationNames, selectAvailableRoles, selectApiData, - selectErrorState, - selectErrorMsg, selectSubmitAttempt, // actions @@ -28,17 +25,15 @@ import '../adminRsuTab/Admin.css' import 'react-widgets/styles.css' import { AnyAction, ThunkDispatch } from '@reduxjs/toolkit' import { RootState } from '../../store' +import toast from 'react-hot-toast' const AdminAddUser = () => { const dispatch: ThunkDispatch = useDispatch() - const successMsg = useSelector(selectSuccessMsg) const selectedOrganizationNames = useSelector(selectSelectedOrganizationNames) const selectedOrganizations = useSelector(selectSelectedOrganizations) const organizationNames = useSelector(selectOrganizationNames) const availableRoles = useSelector(selectAvailableRoles) const apiData = useSelector(selectApiData) - const errorState = useSelector(selectErrorState) - const errorMsg = useSelector(selectErrorMsg) const submitAttempt = useSelector(selectSubmitAttempt) const { register, @@ -56,7 +51,15 @@ const AdminAddUser = () => { dispatch(updateAvailableRolesApiData()) }, [apiData, dispatch]) - const onSubmit = (data: AdminUserForm) => dispatch(submitForm({ data, reset })) + const onSubmit = (data: AdminUserForm) => { + dispatch(submitForm({ data, reset })).then((data: any) => { + if (data.payload.success) { + toast.success('User added successfully') + } else { + toast.error('Failed to add user: ' + data.payload.message) + } + }) + } return (
@@ -174,16 +177,6 @@ const AdminAddUser = () => {

)} - {successMsg && ( -

- {successMsg} -

- )} - {errorState && ( -

- Failed to add user due to error: {errorMsg} -

- )}
-
diff --git a/webapp/src/features/adminAdduser/adminAddUserSlice.test.ts b/webapp/src/features/adminAdduser/adminAddUserSlice.test.ts index db6fb7638..a8b3a7df4 100644 --- a/webapp/src/features/adminAdduser/adminAddUserSlice.test.ts +++ b/webapp/src/features/adminAdduser/adminAddUserSlice.test.ts @@ -10,19 +10,15 @@ import { updateOrganizationNamesApiData, updateAvailableRolesApiData, updateOrganizations, - setSuccessMsg, setSelectedRole, // selectors selectLoading, - selectSuccessMsg, selectSelectedOrganizationNames, selectSelectedOrganizations, selectOrganizationNames, selectAvailableRoles, selectApiData, - selectErrorState, - selectErrorMsg, selectSubmitAttempt, } from './adminAddUserSlice' import apiHelper from '../../apis/api-helper' @@ -34,14 +30,11 @@ describe('admin add User reducer', () => { expect(reducer(undefined, { type: 'unknown' })).toEqual({ loading: false, value: { - successMsg: '', selectedOrganizationNames: [], selectedOrganizations: [], organizationNames: [], availableRoles: [], apiData: {}, - errorState: false, - errorMsg: '', submitAttempt: false, }, }) @@ -52,14 +45,11 @@ describe('async thunks', () => { var initialState: RootState['adminAddUser'] = { loading: null, value: { - successMsg: null, selectedOrganizationNames: null, selectedOrganizations: null, organizationNames: null, availableRoles: null, apiData: null, - errorState: null, - errorMsg: null, submitAttempt: null, }, } @@ -118,8 +108,6 @@ describe('async thunks', () => { it('Updates the state correctly fulfilled', async () => { const loading = false const apiData = 'data' - let errorMsg = '' - let errorState = false let state = reducer(initialState, { type: 'adminAddUser/getUserData/fulfilled', @@ -129,13 +117,10 @@ describe('async thunks', () => { expect(state).toEqual({ ...initialState, loading, - value: { ...initialState.value, errorMsg, errorState, apiData }, + value: { ...initialState.value, apiData }, }) // Error Case - errorMsg = 'message' - errorState = true - state = reducer(initialState, { type: 'adminAddUser/getUserData/fulfilled', payload: { message: 'message', success: false }, @@ -144,7 +129,7 @@ describe('async thunks', () => { expect(state).toEqual({ ...initialState, loading, - value: { ...initialState.value, errorMsg, errorState }, + value: { ...initialState.value }, }) }) @@ -209,9 +194,6 @@ describe('async thunks', () => { it('Updates the state correctly fulfilled', async () => { const loading = false - let successMsg = 'message' - let errorMsg = '' - let errorState = false let state = reducer(initialState, { type: 'adminAddUser/createUser/fulfilled', @@ -221,14 +203,10 @@ describe('async thunks', () => { expect(state).toEqual({ ...initialState, loading, - value: { ...initialState.value, successMsg, errorMsg, errorState }, + value: { ...initialState.value }, }) // Error Case - successMsg = '' - errorMsg = 'message' - errorState = true - state = reducer(initialState, { type: 'adminAddUser/createUser/fulfilled', payload: { message: 'message', success: false }, @@ -237,7 +215,7 @@ describe('async thunks', () => { expect(state).toEqual({ ...initialState, loading, - value: { ...initialState.value, successMsg, errorMsg, errorState }, + value: { ...initialState.value }, }) }) @@ -267,8 +245,7 @@ describe('async thunks', () => { let action = resetForm(reset) await action(dispatch, getState, undefined) expect(reset).toHaveBeenCalledTimes(1) - expect(global.setTimeout).toHaveBeenCalledTimes(1) - expect(dispatch).toHaveBeenCalledTimes(1 + 2) + expect(dispatch).toHaveBeenCalledTimes(0 + 2) } catch (e) { ;(global.setTimeout as any).mockClear() throw e @@ -310,7 +287,6 @@ describe('async thunks', () => { let reset = jest.fn() let action = submitForm({ data, reset }) let resp = await action(dispatch, getState, undefined) - expect(resp.payload).toEqual(false) expect(dispatch).toHaveBeenCalledTimes(1 + 2) // invalid checkForm @@ -329,7 +305,11 @@ describe('async thunks', () => { }) action = submitForm({ data, reset }) resp = await action(dispatch, getState, undefined) - expect(resp.payload).toEqual(true) + expect(resp.payload).toEqual({ + message: 'Please fill out all required fields', + submitAttempt: true, + success: false, + }) expect(dispatch).toHaveBeenCalledTimes(0 + 2) }) @@ -338,7 +318,7 @@ describe('async thunks', () => { const state = reducer(initialState, { type: 'adminAddUser/submitForm/fulfilled', - payload: submitAttempt, + payload: { submitAttempt: 'submitAttempt' }, }) expect(state).toEqual({ @@ -353,14 +333,11 @@ describe('reducers', () => { const initialState: RootState['adminAddUser'] = { loading: null, value: { - successMsg: null, selectedOrganizationNames: null, selectedOrganizations: null, organizationNames: null, availableRoles: null, apiData: null, - errorState: null, - errorMsg: null, submitAttempt: null, }, } @@ -425,14 +402,6 @@ describe('reducers', () => { }) }) - it('setSuccessMsg reducer updates state correctly', async () => { - const successMsg = 'successMsg' - expect(reducer(initialState, setSuccessMsg(successMsg))).toEqual({ - ...initialState, - value: { ...initialState.value, successMsg }, - }) - }) - it('setSelectedRole reducer updates state correctly', async () => { const selectedOrganizations = [ { id: 0, name: 'org1', role: 'role3' }, @@ -458,14 +427,11 @@ describe('selectors', () => { const initialState = { loading: 'loading', value: { - successMsg: 'successMsg', selectedOrganizationNames: 'selectedOrganizationNames', selectedOrganizations: 'selectedOrganizations', organizationNames: 'organizationNames', availableRoles: 'availableRoles', apiData: 'apiData', - errorState: 'errorState', - errorMsg: 'errorMsg', submitAttempt: 'submitAttempt', }, } @@ -474,14 +440,11 @@ describe('selectors', () => { it('selectors return the correct value', async () => { expect(selectLoading(state)).toEqual('loading') - expect(selectSuccessMsg(state)).toEqual('successMsg') expect(selectSelectedOrganizationNames(state)).toEqual('selectedOrganizationNames') expect(selectSelectedOrganizations(state)).toEqual('selectedOrganizations') expect(selectOrganizationNames(state)).toEqual('organizationNames') expect(selectAvailableRoles(state)).toEqual('availableRoles') expect(selectApiData(state)).toEqual('apiData') - expect(selectErrorState(state)).toEqual('errorState') - expect(selectErrorMsg(state)).toEqual('errorMsg') expect(selectSubmitAttempt(state)).toEqual('submitAttempt') }) }) diff --git a/webapp/src/features/adminAdduser/adminAddUserSlice.tsx b/webapp/src/features/adminAdduser/adminAddUserSlice.tsx index eef29e37a..40964533e 100644 --- a/webapp/src/features/adminAdduser/adminAddUserSlice.tsx +++ b/webapp/src/features/adminAdduser/adminAddUserSlice.tsx @@ -17,14 +17,11 @@ export type AdminUserForm = { } const initialState = { - successMsg: '', selectedOrganizationNames: [] as { id: number; name: string; role: string }[], selectedOrganizations: [] as { id: number; name: string; role: string }[], organizationNames: [] as { id: number; name: string }[], availableRoles: [] as { role: string }[], apiData: {} as AdminUserForm, - errorState: false, - errorMsg: '', submitAttempt: false, } @@ -77,7 +74,6 @@ export const createUser = createAsyncThunk( export const resetForm = createAsyncThunk('adminAddUser/resetForm', async (reset: () => void, { dispatch }) => { reset() - setTimeout(() => dispatch(adminAddUserSlice.actions.setSuccessMsg('')), 5000) }) export const submitForm = createAsyncThunk( @@ -93,10 +89,14 @@ export const submitForm = createAsyncThunk( ...data, organizations: submitOrgs, } - dispatch(createUser({ json: tempData, reset })) - return false + let res = await dispatch(createUser({ json: tempData, reset })) + if ((res.payload as any).success) { + return { submitAttempt: false, success: true, message: 'User Created Successfully' } + } else { + return { submitAttempt: false, success: false, message: (res.payload as any).message } + } } else { - return true + return { submitAttempt: true, success: false, message: 'Please fill out all required fields' } } }, { condition: (_, { getState }) => selectToken(getState() as RootState) != undefined } @@ -140,9 +140,6 @@ export const adminAddUserSlice = createSlice({ state.value.selectedOrganizations = newOrganizations state.value.selectedOrganizationNames = action.payload }, - setSuccessMsg: (state, action) => { - state.value.successMsg = action.payload - }, setSelectedRole: ( state, action: { @@ -169,11 +166,6 @@ export const adminAddUserSlice = createSlice({ state.loading = false if (action.payload.success) { state.value.apiData = action.payload.data - state.value.errorMsg = '' - state.value.errorState = false - } else { - state.value.errorMsg = action.payload.message - state.value.errorState = true } }) .addCase(getUserData.rejected, (state) => { @@ -184,15 +176,6 @@ export const adminAddUserSlice = createSlice({ }) .addCase(createUser.fulfilled, (state, action) => { state.loading = false - if (action.payload.success) { - state.value.errorMsg = '' - state.value.errorState = false - state.value.successMsg = action.payload.message - } else { - state.value.errorMsg = action.payload.message - state.value.errorState = true - state.value.successMsg = '' - } }) .addCase(createUser.rejected, (state) => { state.loading = false @@ -202,28 +185,20 @@ export const adminAddUserSlice = createSlice({ state.value.selectedOrganizationNames = [] }) .addCase(submitForm.fulfilled, (state, action) => { - state.value.submitAttempt = action.payload + state.value.submitAttempt = action.payload.submitAttempt }) }, }) -export const { - updateOrganizationNamesApiData, - updateAvailableRolesApiData, - updateOrganizations, - setSuccessMsg, - setSelectedRole, -} = adminAddUserSlice.actions +export const { updateOrganizationNamesApiData, updateAvailableRolesApiData, updateOrganizations, setSelectedRole } = + adminAddUserSlice.actions export const selectLoading = (state: RootState) => state.adminAddUser.loading -export const selectSuccessMsg = (state: RootState) => state.adminAddUser.value.successMsg export const selectSelectedOrganizationNames = (state: RootState) => state.adminAddUser.value.selectedOrganizationNames export const selectSelectedOrganizations = (state: RootState) => state.adminAddUser.value.selectedOrganizations export const selectOrganizationNames = (state: RootState) => state.adminAddUser.value.organizationNames export const selectAvailableRoles = (state: RootState) => state.adminAddUser.value.availableRoles export const selectApiData = (state: RootState) => state.adminAddUser.value.apiData -export const selectErrorState = (state: RootState) => state.adminAddUser.value.errorState -export const selectErrorMsg = (state: RootState) => state.adminAddUser.value.errorMsg export const selectSubmitAttempt = (state: RootState) => state.adminAddUser.value.submitAttempt export default adminAddUserSlice.reducer From 70bb3bb4ca87d5db7db250ae9c62091a23cc9e57 Mon Sep 17 00:00:00 2001 From: Marc Wodahl Date: Thu, 20 Jun 2024 12:16:51 -0600 Subject: [PATCH 085/189] Admin edit menus add toast notifications --- .../AdminEditOrganization.tsx | 24 +++----- .../adminEditOrganizationSlice.test.ts | 20 +----- .../adminEditOrganizationSlice.tsx | 8 --- .../features/adminEditRsu/AdminEditRsu.tsx | 26 +++----- .../adminEditRsu/adminEditRsuSlice.test.ts | 59 ++++-------------- .../adminEditRsu/adminEditRsuSlice.tsx | 37 +++-------- .../features/adminEditUser/AdminEditUser.tsx | 26 +++----- .../adminEditUser/adminEditUserSlice.test.ts | 61 ++++--------------- .../adminEditUser/adminEditUserSlice.tsx | 40 +++--------- 9 files changed, 64 insertions(+), 237 deletions(-) diff --git a/webapp/src/features/adminEditOrganization/AdminEditOrganization.tsx b/webapp/src/features/adminEditOrganization/AdminEditOrganization.tsx index 5959a306d..b17d8fe08 100644 --- a/webapp/src/features/adminEditOrganization/AdminEditOrganization.tsx +++ b/webapp/src/features/adminEditOrganization/AdminEditOrganization.tsx @@ -3,8 +3,6 @@ import { Form } from 'react-bootstrap' import { useForm } from 'react-hook-form' import { selectSuccessMsg, - selectErrorState, - selectErrorMsg, // actions updateStates, @@ -12,6 +10,7 @@ import { setSuccessMsg, } from './adminEditOrganizationSlice' import { useSelector, useDispatch } from 'react-redux' +import toast from 'react-hot-toast' import '../adminRsuTab/Admin.css' import 'react-widgets/styles.css' @@ -26,15 +25,12 @@ import { setSelectedOrg, } from '../adminOrganizationTab/adminOrganizationTabSlice' import { Link, useParams, useNavigate } from 'react-router-dom' -import { ThemeProvider, Typography } from '@mui/material' -import { theme } from '../../styles' +import { Typography } from '@mui/material' const AdminEditOrganization = () => { const dispatch: ThunkDispatch = useDispatch() const successMsg = useSelector(selectSuccessMsg) - const errorState = useSelector(selectErrorState) - const errorMsg = useSelector(selectErrorMsg) const selectedOrg = useSelector(selectSelectedOrg) const orgData = useSelector(selectOrgData) const { @@ -69,7 +65,11 @@ const AdminEditOrganization = () => { }, [setValue, selectedOrg?.name]) const onSubmit = (data: adminOrgPatch) => { - dispatch(editOrganization({ json: data, setValue, selectedOrg: selectedOrg?.name })) + dispatch(editOrganization({ json: data, setValue, selectedOrg: selectedOrg?.name })).then((data: any) => { + data.payload.success + ? toast.success(data.payload.message) + : toast.error('Failed to apply changes to organization due to error: ' + data.payload.message) + }) } useEffect(() => { @@ -97,16 +97,6 @@ const AdminEditOrganization = () => { )} - {successMsg && ( -

- {successMsg} -

- )} - {errorState && ( -

- Failed to apply changes to organization due to error: {errorMsg} -

- )}
-

-

@@ -1151,17 +1140,6 @@ exports[`snapshot refresh 1`] = `
-

-

diff --git a/webapp/src/features/adminRsuTab/adminRsuTabSlice.tsx b/webapp/src/features/adminRsuTab/adminRsuTabSlice.tsx index bc036c6b4..48763250f 100644 --- a/webapp/src/features/adminRsuTab/adminRsuTabSlice.tsx +++ b/webapp/src/features/adminRsuTab/adminRsuTabSlice.tsx @@ -59,16 +59,21 @@ export const deleteRsu = createAsyncThunk( query_params: { rsu_ip }, }) + var return_val = {} + switch (data.status) { case 200: console.debug('Successfully deleted RSU: ' + rsu_ip) + return_val = { success: true, message: 'Successfully deleted RSU: ' + rsu_ip } break default: + return_val = { success: false, message: data.message } break } if (shouldUpdateTableData) { dispatch(updateTableData()) } + return return_val }, { condition: (_, { getState }) => selectToken(getState() as RootState) != undefined } ) @@ -80,7 +85,14 @@ export const deleteMultipleRsus = createAsyncThunk( for (const row of rows) { promises.push(dispatch(deleteRsu({ rsu_ip: row.ip, shouldUpdateTableData: false }))) } - Promise.all(promises).then(() => dispatch(updateTableData())) + var res = await Promise.all(promises) + dispatch(updateTableData()) + for (const r of res) { + if (!r.payload.success) { + return { success: false, message: 'Failed to delete one or more RSU(s)' } + } + } + return { success: true, message: 'RSUs Deleted Successfully' } }, { condition: (_, { getState }) => selectToken(getState() as RootState) != undefined } ) diff --git a/webapp/src/features/adminUserTab/AdminUserTab.tsx b/webapp/src/features/adminUserTab/AdminUserTab.tsx index 872409f5a..c6cabb31e 100644 --- a/webapp/src/features/adminUserTab/AdminUserTab.tsx +++ b/webapp/src/features/adminUserTab/AdminUserTab.tsx @@ -25,6 +25,7 @@ import { RootState } from '../../store' import { Action } from '@material-table/core' import { Route, Routes, useLocation, useNavigate } from 'react-router-dom' import { NotFound } from '../../pages/404' +import toast from 'react-hot-toast' const getTitle = (activeTab: string) => { if (activeTab === undefined) { @@ -74,7 +75,7 @@ const AdminUserTab = () => { const buttons = [ { label: 'Yes', - onClick: () => dispatch(deleteUsers([rowData])), + onClick: () => handleDelete([rowData]), }, { label: 'No', @@ -98,7 +99,7 @@ const AdminUserTab = () => { const buttons = [ { label: 'Yes', - onClick: () => dispatch(deleteUsers(rowData)), + onClick: () => handleDelete(rowData), }, { label: 'No', @@ -115,6 +116,12 @@ const AdminUserTab = () => { }, ] + const handleDelete = (rowData: AdminUserWithId[]) => { + dispatch(deleteUsers(rowData)).then((data: any) => { + data.payload.success ? toast.success('User(s) Deleted Successfully') : toast.error(data.message.payload) + }) + } + const updateTableData = async () => { dispatch(getAvailableUsers()) } diff --git a/webapp/src/features/adminUserTab/adminUserTabSlice.tsx b/webapp/src/features/adminUserTab/adminUserTabSlice.tsx index cc9ff6162..9ff240847 100644 --- a/webapp/src/features/adminUserTab/adminUserTabSlice.tsx +++ b/webapp/src/features/adminUserTab/adminUserTabSlice.tsx @@ -30,10 +30,10 @@ export const deleteUser = async (user_email: string, token: string) => { switch (data.status) { case 200: console.debug(`Successfully deleted User: ${user_email}`) - break + return { success: true, message: 'Successfully deleted User: ' + user_email } default: console.error(data.message) - break + return { success: false, message: data.message } } } @@ -65,8 +65,14 @@ export const deleteUsers = createAsyncThunk( for (const user of data) { promises.push(deleteUser(user.email, token)) } - await Promise.all(promises) + var res = await Promise.all(promises) dispatch(getAvailableUsers()) + for (const r of res) { + if (!r.success) { + return { success: false, message: 'Failed to delete one or more User(s)' } + } + } + return { success: true, message: 'User(s) Deleted Successfully' } }, { condition: (_, { getState }) => selectToken(getState() as RootState) != undefined } ) From e3220213f721f06b4b478fa220924774fc2bbeba Mon Sep 17 00:00:00 2001 From: Marc Wodahl Date: Thu, 20 Jun 2024 15:09:57 -0600 Subject: [PATCH 087/189] AdminOrganizationTab add toast notifications --- .../AdminOrganizationTabRsu.tsx | 25 ++++++++++++-- .../adminOrganizationTabRsuSlice.tsx | 25 +++++++++++--- .../AdminOrganizationTabUser.tsx | 33 ++++++++++++++++--- .../adminOrganizationTabUserSlice.tsx | 32 ++++++++++++++---- 4 files changed, 97 insertions(+), 18 deletions(-) diff --git a/webapp/src/features/adminOrganizationTabRsu/AdminOrganizationTabRsu.tsx b/webapp/src/features/adminOrganizationTabRsu/AdminOrganizationTabRsu.tsx index e04a124e8..3f712511b 100644 --- a/webapp/src/features/adminOrganizationTabRsu/AdminOrganizationTabRsu.tsx +++ b/webapp/src/features/adminOrganizationTabRsu/AdminOrganizationTabRsu.tsx @@ -29,6 +29,7 @@ import { AnyAction, ThunkDispatch } from '@reduxjs/toolkit' import { RootState } from '../../store' import { Action, Column } from '@material-table/core' import { AdminOrgRsu } from '../adminOrganizationTab/adminOrganizationTabSlice' +import toast from 'react-hot-toast' interface AdminOrganizationTabRsuProps { selectedOrg: string @@ -91,15 +92,33 @@ const AdminOrganizationTabRsu = (props: AdminOrganizationTabRsuProps) => { }, [selectedOrg, dispatch]) const rsuOnDelete = async (rsu: AdminOrgRsu) => { - dispatch(rsuDeleteSingle({ rsu, selectedOrg, updateTableData })) + dispatch(rsuDeleteSingle({ rsu, selectedOrg, updateTableData })).then((data) => { + if (!(data.payload as any).success) { + toast.error((data.payload as any).message) + } else { + toast.success((data.payload as any).message) + } + }) } const rsuMultiDelete = async (rows: AdminOrgRsu[]) => { - dispatch(rsuDeleteMultiple({ rows, selectedOrg, updateTableData })) + dispatch(rsuDeleteMultiple({ rows, selectedOrg, updateTableData })).then((data) => { + if (!(data.payload as any).success) { + toast.error((data.payload as any).message) + } else { + toast.success((data.payload as any).message) + } + }) } const rsuMultiAdd = async (rsuList: AdminOrgRsu[]) => { - dispatch(rsuAddMultiple({ rsuList, selectedOrg, updateTableData })) + dispatch(rsuAddMultiple({ rsuList, selectedOrg, updateTableData })).then((data) => { + if (!(data.payload as any).success) { + toast.error((data.payload as any).message) + } else { + toast.success((data.payload as any).message) + } + }) } const accordionTheme = createTheme({ diff --git a/webapp/src/features/adminOrganizationTabRsu/adminOrganizationTabRsuSlice.tsx b/webapp/src/features/adminOrganizationTabRsu/adminOrganizationTabRsuSlice.tsx index 57a29cf19..f7b3f75a0 100644 --- a/webapp/src/features/adminOrganizationTabRsu/adminOrganizationTabRsuSlice.tsx +++ b/webapp/src/features/adminOrganizationTabRsu/adminOrganizationTabRsuSlice.tsx @@ -65,9 +65,14 @@ export const rsuDeleteSingle = createAsyncThunk( 'Cannot remove RSU ' + rsu.ip + ' from ' + selectedOrg + ' because it must belong to at least one organization.' ) } - Promise.all(promises).then(() => { - dispatch(refresh({ selectedOrg, updateTableData })) - }) + var res = await Promise.all(promises) + dispatch(refresh({ selectedOrg, updateTableData })) + + if ((res[0].payload as any).success) { + return { success: true, message: 'RSU deleted successfully' } + } else { + return { success: false, message: 'Failed to delete RSU' } + } }, { condition: (_, { getState }) => selectToken(getState() as RootState) != undefined } ) @@ -93,8 +98,13 @@ export const rsuDeleteMultiple = createAsyncThunk( } } if (invalidRsus.length === 0) { - await dispatch(editOrg(patchJson)) + var res = await dispatch(editOrg(patchJson)) dispatch(refresh({ selectedOrg, updateTableData })) + if ((res.payload as any).success) { + return { success: true, message: 'RSU(s) deleted successfully' } + } else { + return { success: false, message: 'Failed to delete RSU(s)' } + } } else { alert( 'Cannot remove RSU(s) ' + @@ -120,8 +130,13 @@ export const rsuAddMultiple = createAsyncThunk( for (const row of rsuList) { patchJson.rsus_to_add.push(row.ip) } - await dispatch(editOrg(patchJson)) + var res = await dispatch(editOrg(patchJson)) dispatch(refresh({ selectedOrg, updateTableData })) + if ((res.payload as any).success) { + return { success: true, message: 'RSU(s) added successfully' } + } else { + return { success: false, message: 'Failed to add RSU(s)' } + } }, { condition: (_, { getState }) => selectToken(getState() as RootState) != undefined } ) diff --git a/webapp/src/features/adminOrganizationTabUser/AdminOrganizationTabUser.tsx b/webapp/src/features/adminOrganizationTabUser/AdminOrganizationTabUser.tsx index cf9660e95..0a83eca70 100644 --- a/webapp/src/features/adminOrganizationTabUser/AdminOrganizationTabUser.tsx +++ b/webapp/src/features/adminOrganizationTabUser/AdminOrganizationTabUser.tsx @@ -33,6 +33,7 @@ import { RootState } from '../../store' import { AnyAction, ThunkDispatch } from '@reduxjs/toolkit' import { Action, Column } from '@material-table/core' import { AdminOrgUser } from '../adminOrganizationTab/adminOrganizationTabSlice' +import toast from 'react-hot-toast' interface AdminOrganizationTabUserProps { selectedOrg: string @@ -154,7 +155,13 @@ const AdminOrganizationTabUser = (props: AdminOrganizationTabUserProps) => { selectedOrg: props.selectedOrg, updateTableData: props.updateTableData, }) - ) + ).then((data) => { + if (!(data.payload as any).success) { + toast.error((data.payload as any).message) + } else { + toast.success((data.payload as any).message) + } + }) } const userMultiDelete = async (rows: AdminOrgUser[]) => { @@ -164,7 +171,13 @@ const AdminOrganizationTabUser = (props: AdminOrganizationTabUserProps) => { selectedOrg: props.selectedOrg, updateTableData: props.updateTableData, }) - ) + ).then((data) => { + if (!(data.payload as any).success) { + toast.error((data.payload as any).message) + } else { + toast.success((data.payload as any).message) + } + }) } const userMultiAdd = async (userList: AdminOrgUser[]) => { @@ -174,7 +187,13 @@ const AdminOrganizationTabUser = (props: AdminOrganizationTabUserProps) => { selectedOrg: props.selectedOrg, updateTableData: props.updateTableData, }) - ) + ).then((data) => { + if (!(data.payload as any).success) { + toast.error((data.payload as any).message) + } else { + toast.success((data.payload as any).message) + } + }) } const userBulkEdit = async ( @@ -192,7 +211,13 @@ const AdminOrganizationTabUser = (props: AdminOrganizationTabUserProps) => { selectedOrg: props.selectedOrg, updateTableData: props.updateTableData, }) - ) + ).then((data) => { + if (!(data.payload as any).success) { + toast.error((data.payload as any).message) + } else { + toast.success((data.payload as any).message) + } + }) } const accordionTheme = createTheme({ diff --git a/webapp/src/features/adminOrganizationTabUser/adminOrganizationTabUserSlice.tsx b/webapp/src/features/adminOrganizationTabUser/adminOrganizationTabUserSlice.tsx index 73af90a3f..f394679d3 100644 --- a/webapp/src/features/adminOrganizationTabUser/adminOrganizationTabUserSlice.tsx +++ b/webapp/src/features/adminOrganizationTabUser/adminOrganizationTabUserSlice.tsx @@ -105,9 +105,14 @@ export const userDeleteSingle = createAsyncThunk( ' because they must belong to at least one organization.' ) } - Promise.all(promises).then(() => { - dispatch(refresh({ selectedOrg, updateTableData })) - }) + var res = await Promise.all(promises) + dispatch(refresh({ selectedOrg, updateTableData })) + + if ((res[0].payload as any).success) { + return { success: true, message: 'User deleted successfully' } + } else { + return { success: false, message: 'Failed to delete user' } + } }, { condition: (_, { getState }) => selectToken(getState() as RootState) != undefined } ) @@ -134,8 +139,13 @@ export const userDeleteMultiple = createAsyncThunk( } } if (invalidUsers.length === 0) { - await dispatch(editOrg(patchJson)) + var res = await dispatch(editOrg(patchJson)) dispatch(refresh({ selectedOrg, updateTableData })) + if ((res.payload as any).success) { + return { success: true, message: 'User(s) deleted successfully' } + } else { + return { success: false, message: 'Failed to delete user(s)' } + } } else { alert( 'Cannot remove User(s) ' + @@ -162,8 +172,13 @@ export const userAddMultiple = createAsyncThunk( const userRole = { email: user?.email, role: user?.role } patchJson.users_to_add.push(userRole) } - await dispatch(editOrg(patchJson)) + var res = await dispatch(editOrg(patchJson)) dispatch(refresh({ selectedOrg, updateTableData })) + if ((res.payload as any).success) { + return { success: true, message: 'User(s) added successfully' } + } else { + return { success: false, message: 'Failed to add user(s)' } + } }, { condition: (payload: AdminOrgTabUserAddMultiple, { getState }) => @@ -185,8 +200,13 @@ export const userBulkEdit = createAsyncThunk( const userRole = { email: row.newData.email, role: row.newData.role } patchJson.users_to_modify.push(userRole) } - await dispatch(editOrg(patchJson)) + var res = await dispatch(editOrg(patchJson)) dispatch(refresh({ selectedOrg, updateTableData })) + if ((res.payload as any).success) { + return { success: true, message: 'User(s) updated successfully' } + } else { + return { success: false, message: 'Failed to update user(s)' } + } }, { condition: (_, { getState }) => selectToken(getState() as RootState) != undefined } ) From 5601a60acf02cbd6840368e66749c1ab675dd55e Mon Sep 17 00:00:00 2001 From: Marc Wodahl Date: Fri, 21 Jun 2024 08:40:30 -0600 Subject: [PATCH 088/189] Add toast notifications to contact support, snmp config menus --- webapp/src/components/ContactSupportMenu.tsx | 29 ++--------- webapp/src/components/SnmpsetMenu.tsx | 51 ++++++++----------- webapp/src/components/SnmpwalkMenu.tsx | 17 +++---- .../__snapshots__/SnmpsetMenu.test.tsx.snap | 4 -- .../__snapshots__/SnmpwalkMenu.test.tsx.snap | 1 - webapp/src/generalSlices/configSlice.test.ts | 12 ++--- webapp/src/generalSlices/configSlice.ts | 14 +---- 7 files changed, 38 insertions(+), 90 deletions(-) diff --git a/webapp/src/components/ContactSupportMenu.tsx b/webapp/src/components/ContactSupportMenu.tsx index ab8918154..56dd29c6c 100644 --- a/webapp/src/components/ContactSupportMenu.tsx +++ b/webapp/src/components/ContactSupportMenu.tsx @@ -6,12 +6,10 @@ import 'react-widgets/styles.css' import RsuApi from '../apis/rsu-api' import './css/ContactSupportMenu.css' +import toast from 'react-hot-toast' const ContactSupportMenu = () => { const [hidden, setHidden] = useState(true) // hidden by default - const [successMsg, setSuccessMsg] = useState('') - const [errorState, setErrorState] = useState(false) - const [errorMessage, setErrorMessage] = useState('') const { register, handleSubmit, @@ -24,21 +22,13 @@ const ContactSupportMenu = () => { const res = await RsuApi.postContactSupport(data) const status = res.status if (status === 200) { - console.debug('Successfully sent email: ' + status) - setSuccessMsg('Successfully sent email') - setErrorState(false) + toast.success('Successfully sent email') reset() } else { - console.error('Something went wrong: ' + status) - setSuccessMsg('') - setErrorState(true) - setErrorMessage('Something went wrong, please try again later') + toast.error('Something went wrong: ' + status) } } catch (exception_var) { - console.error(exception_var) - setSuccessMsg('') - setErrorState(true) - setErrorMessage('An exception occurred, please try again later') + toast.error('An exception occurred, please try again later') } } @@ -96,17 +86,6 @@ const ContactSupportMenu = () => { /> {errors.message && {errors.message.message}} - - {successMsg && ( -

- {successMsg} -

- )} - {errorState && ( -

- Error: {errorMessage} -

- )}
{type !== 'single_rsu' && ( @@ -83,15 +85,6 @@ const SnmpsetMenu = (props: SnmpsetMenu) => { )} - {changeSuccess ? ( -
-

Successful write to RSU

-
- ) : ( -

- )} - {errorState !== '' ? :
} - {type !== 'single_rsu' ? (

@@ -120,20 +113,18 @@ const SnmpsetMenu = (props: SnmpsetMenu) => { If you are configuring SPaT or MAP forwarding, apply the TX message
filter after your configuration has been applied

- - {snmpFilterMsg !== '' ? ( -
- {snmpFilterErr === true ? ( - - ) : ( -

{snmpFilterMsg}

- )} -
- ) : ( -
- )}
) : (
diff --git a/webapp/src/components/SnmpwalkMenu.tsx b/webapp/src/components/SnmpwalkMenu.tsx index 88c76b1a2..69f7362f5 100644 --- a/webapp/src/components/SnmpwalkMenu.tsx +++ b/webapp/src/components/SnmpwalkMenu.tsx @@ -6,7 +6,6 @@ import { Options } from './AdminDeletionOptions' import { selectRsuManufacturer, selectRsuIpv4 } from '../generalSlices/rsuSlice' import { selectMsgFwdConfig, - selectErrorState, // Actions refreshSnmpFwdConfig, @@ -22,12 +21,12 @@ import { import { IconButton, ThemeProvider, Tooltip, createTheme } from '@mui/material' import { AnyAction, ThunkDispatch } from '@reduxjs/toolkit' import { RootState } from '../store' +import toast from 'react-hot-toast' const SnmpwalkMenu = () => { const dispatch: ThunkDispatch = useDispatch() const msgFwdConfig = useSelector(selectMsgFwdConfig) - const errorState = useSelector(selectErrorState) const rsuIp = useSelector(selectRsuIpv4) const rsuManufacturer = useSelector(selectRsuManufacturer) @@ -48,7 +47,11 @@ const SnmpwalkMenu = () => { snmpMsgType: countsMsgType, destIp: ip, }) - ) + ).then((data: any) => { + data.payload.changeSuccess + ? toast.success('Successfully deleted SNMP forwarding') + : toast.error('Failed to delete SNMP forwarding: ' + data.payload.errorState) + }) }, }, { @@ -148,14 +151,6 @@ const SnmpwalkMenu = () => {
)}
- - {errorState !== '' ? ( - - ) : ( -
- )}
) diff --git a/webapp/src/components/__snapshots__/SnmpsetMenu.test.tsx.snap b/webapp/src/components/__snapshots__/SnmpsetMenu.test.tsx.snap index e60442663..de9cf9f77 100644 --- a/webapp/src/components/__snapshots__/SnmpsetMenu.test.tsx.snap +++ b/webapp/src/components/__snapshots__/SnmpsetMenu.test.tsx.snap @@ -77,10 +77,6 @@ exports[`should take a snapshot 1`] = ` > Delete Forwarding -

-

-
`; diff --git a/webapp/src/generalSlices/configSlice.test.ts b/webapp/src/generalSlices/configSlice.test.ts index bce58511c..84b34f01c 100644 --- a/webapp/src/generalSlices/configSlice.test.ts +++ b/webapp/src/generalSlices/configSlice.test.ts @@ -177,7 +177,7 @@ describe('async thunks', () => { const state = reducer(initialState, { type: 'config/submitSnmpSet/pending', }) - expect(state).toEqual({ loading, value: { ...initialState.value, changeSuccess, errorState } }) + expect(state).toEqual({ loading, value: { ...initialState.value, changeSuccess } }) }) it('Updates the state correctly fulfilled', async () => { @@ -188,7 +188,7 @@ describe('async thunks', () => { type: 'config/submitSnmpSet/fulfilled', payload: { changeSuccess, errorState }, }) - expect(state).toEqual({ loading, value: { ...initialState.value, changeSuccess, errorState } }) + expect(state).toEqual({ loading, value: { ...initialState.value, changeSuccess } }) }) it('Updates the state correctly rejected', async () => { @@ -248,7 +248,7 @@ describe('async thunks', () => { const state = reducer(initialState, { type: 'config/deleteSnmpSet/pending', }) - expect(state).toEqual({ loading, value: { ...initialState.value, changeSuccess, errorState } }) + expect(state).toEqual({ loading, value: { ...initialState.value, changeSuccess } }) }) it('Updates the state correctly fulfilled', async () => { @@ -259,7 +259,7 @@ describe('async thunks', () => { type: 'config/deleteSnmpSet/fulfilled', payload: { changeSuccess, errorState }, }) - expect(state).toEqual({ loading, value: { ...initialState.value, changeSuccess, errorState } }) + expect(state).toEqual({ loading, value: { ...initialState.value, changeSuccess } }) }) it('Updates the state correctly rejected', async () => { @@ -319,7 +319,7 @@ describe('async thunks', () => { it('Updates the state correctly fulfilled', async () => { let loading = false let snmpFilterErr = false - let snmpFilterMsg = 'error' + let snmpFilterMsg = '' const state = reducer(initialState, { type: 'config/filterSnmp/fulfilled', payload: { snmpFilterErr, snmpFilterMsg }, @@ -458,7 +458,7 @@ describe('async thunks', () => { it('Updates the state correctly fulfilled', async () => { let loading = false let snmpFilterErr = false - let snmpFilterMsg = 'error' + let snmpFilterMsg = '' const state = reducer(initialState, { type: 'config/filterSnmp/fulfilled', payload: { snmpFilterErr, snmpFilterMsg }, diff --git a/webapp/src/generalSlices/configSlice.ts b/webapp/src/generalSlices/configSlice.ts index 32a03e823..0cb92e615 100644 --- a/webapp/src/generalSlices/configSlice.ts +++ b/webapp/src/generalSlices/configSlice.ts @@ -292,39 +292,27 @@ export const configSlice = createSlice({ }) .addCase(submitSnmpSet.pending, (state) => { state.loading = true - state.value.errorState = '' - state.value.changeSuccess = false }) .addCase(submitSnmpSet.fulfilled, (state, action) => { state.loading = false - state.value.changeSuccess = action.payload.changeSuccess - state.value.errorState = action.payload.errorState }) .addCase(submitSnmpSet.rejected, (state) => { state.loading = false }) .addCase(deleteSnmpSet.pending, (state) => { state.loading = true - state.value.errorState = '' - state.value.changeSuccess = false }) - .addCase(deleteSnmpSet.fulfilled, (state, action) => { + .addCase(deleteSnmpSet.fulfilled, (state) => { state.loading = false - state.value.changeSuccess = action.payload.changeSuccess - state.value.errorState = action.payload.errorState }) .addCase(deleteSnmpSet.rejected, (state) => { state.loading = false }) .addCase(filterSnmp.pending, (state) => { state.loading = true - state.value.snmpFilterMsg = '' - state.value.snmpFilterErr = false }) .addCase(filterSnmp.fulfilled, (state, action) => { state.loading = false - state.value.snmpFilterMsg = action.payload.snmpFilterMsg - state.value.snmpFilterErr = action.payload.snmpFilterErr }) .addCase(filterSnmp.rejected, (state) => { state.loading = false From 1f4c8ddc649c9746b6f01a28a824afe4dddf4614 Mon Sep 17 00:00:00 2001 From: Marc Wodahl Date: Mon, 24 Jun 2024 10:14:30 -0600 Subject: [PATCH 089/189] Update org dropdown list on user-org edit --- .../AdminOrganizationTabUser.tsx | 9 ++++++++- .../AdminOrganizationTabUserTypes.d.ts | 1 + .../adminOrganizationTabUserSlice.test.ts | 3 ++- .../adminOrganizationTabUserSlice.tsx | 11 +++++++++-- webapp/src/generalSlices/userSlice.ts | 8 +++++++- 5 files changed, 27 insertions(+), 5 deletions(-) diff --git a/webapp/src/features/adminOrganizationTabUser/AdminOrganizationTabUser.tsx b/webapp/src/features/adminOrganizationTabUser/AdminOrganizationTabUser.tsx index 01132647d..74882b0ad 100644 --- a/webapp/src/features/adminOrganizationTabUser/AdminOrganizationTabUser.tsx +++ b/webapp/src/features/adminOrganizationTabUser/AdminOrganizationTabUser.tsx @@ -25,7 +25,12 @@ import { setSelectedUserRole, setSelectedUserList, } from './adminOrganizationTabUserSlice' -import { selectAuthLoginData, selectLoadingGlobal, setOrganizationList } from '../../generalSlices/userSlice' +import { + selectAuthLoginData, + selectEmail, + selectLoadingGlobal, + setOrganizationList, +} from '../../generalSlices/userSlice' import { useSelector, useDispatch } from 'react-redux' import '../adminRsuTab/Admin.css' @@ -48,6 +53,7 @@ const AdminOrganizationTabUser = (props: AdminOrganizationTabUserProps) => { const availableRoles = useSelector(selectAvailableRoles) const loadingGlobal = useSelector(selectLoadingGlobal) const authLoginData = useSelector(selectAuthLoginData) + const userEmail = useSelector(selectEmail) const [userColumns] = useState[]>([ { title: 'First Name', @@ -203,6 +209,7 @@ const AdminOrganizationTabUser = (props: AdminOrganizationTabUserProps) => { dispatch( userBulkEditAction({ json, + selectedUser: userEmail, selectedOrg: props.selectedOrg, updateTableData: props.updateTableData, }) diff --git a/webapp/src/features/adminOrganizationTabUser/AdminOrganizationTabUserTypes.d.ts b/webapp/src/features/adminOrganizationTabUser/AdminOrganizationTabUserTypes.d.ts index 33d85b83a..d925cb344 100644 --- a/webapp/src/features/adminOrganizationTabUser/AdminOrganizationTabUserTypes.d.ts +++ b/webapp/src/features/adminOrganizationTabUser/AdminOrganizationTabUserTypes.d.ts @@ -18,5 +18,6 @@ export type AdminOrgUserDeleteMultiple = { export type AdminOrgTabUserBulkEdit = { json: { [key: string]: { newData: AdminOrgTabUser } } selectedOrg: string + selectedUser: string updateTableData: (org: string) => void } diff --git a/webapp/src/features/adminOrganizationTabUser/adminOrganizationTabUserSlice.test.ts b/webapp/src/features/adminOrganizationTabUser/adminOrganizationTabUserSlice.test.ts index e9491fcfd..13f5be0e2 100644 --- a/webapp/src/features/adminOrganizationTabUser/adminOrganizationTabUserSlice.test.ts +++ b/webapp/src/features/adminOrganizationTabUser/adminOrganizationTabUserSlice.test.ts @@ -378,10 +378,11 @@ describe('async thunks', () => { obj2: { newData: { email: 'test2@gmail.com', role: 'role2' } }, } const selectedOrg = 'selectedOrg' + const selectedUser = 'selectedUser' const fetchPatchOrganization = jest.fn() const updateTableData = jest.fn() - const action = userBulkEdit({ json, selectedOrg, updateTableData }) + const action = userBulkEdit({ json, selectedOrg, selectedUser, updateTableData }) await action(dispatch, getState, undefined) expect(dispatch).toHaveBeenCalledTimes(2 + 2) diff --git a/webapp/src/features/adminOrganizationTabUser/adminOrganizationTabUserSlice.tsx b/webapp/src/features/adminOrganizationTabUser/adminOrganizationTabUserSlice.tsx index 73af90a3f..11bf51687 100644 --- a/webapp/src/features/adminOrganizationTabUser/adminOrganizationTabUserSlice.tsx +++ b/webapp/src/features/adminOrganizationTabUser/adminOrganizationTabUserSlice.tsx @@ -1,5 +1,5 @@ import { createAsyncThunk, createSlice } from '@reduxjs/toolkit' -import { selectToken } from '../../generalSlices/userSlice' +import { selectToken, setOrganizationList } from '../../generalSlices/userSlice' import EnvironmentVars from '../../EnvironmentVars' import apiHelper from '../../apis/api-helper' import { RootState } from '../../store' @@ -174,19 +174,26 @@ export const userAddMultiple = createAsyncThunk( export const userBulkEdit = createAsyncThunk( 'adminOrganizationTabUser/userBulkEdit', async (payload: AdminOrgTabUserBulkEdit, { dispatch }) => { - const { json, selectedOrg, updateTableData } = payload + const { json, selectedOrg, selectedUser, updateTableData } = payload const patchJson: adminOrgPatch = { name: selectedOrg, users_to_modify: [], } const rows = Object.values(json) + var orgUpdateVal = {} for (var row of rows) { + if (row.newData.email === selectedUser) { + orgUpdateVal = { name: selectedOrg, role: row.newData.role } + } const userRole = { email: row.newData.email, role: row.newData.role } patchJson.users_to_modify.push(userRole) } await dispatch(editOrg(patchJson)) dispatch(refresh({ selectedOrg, updateTableData })) + if (Object.keys(orgUpdateVal).length > 0) { + dispatch(setOrganizationList({ value: orgUpdateVal, orgName: selectedOrg, type: 'update' })) + } }, { condition: (_, { getState }) => selectToken(getState() as RootState) != undefined } ) diff --git a/webapp/src/generalSlices/userSlice.ts b/webapp/src/generalSlices/userSlice.ts index 240133cdf..e65c04fab 100644 --- a/webapp/src/generalSlices/userSlice.ts +++ b/webapp/src/generalSlices/userSlice.ts @@ -72,7 +72,6 @@ export const userSlice = createSlice({ }, setOrganizationList: (state, action) => { if (action.payload.type === 'add') { - console.debug('payload: ', [...state.value.authLoginData.data.organizations, action.payload]) state.value.authLoginData.data.organizations = [ ...state.value.authLoginData.data.organizations, action.payload.value, @@ -86,6 +85,13 @@ export const userSlice = createSlice({ updatedOrgList.splice(index, 1) state.value.authLoginData.data.organizations = [...updatedOrgList] } + } else if (action.payload.type === 'update') { + var index = state.value.authLoginData.data.organizations.findIndex((org) => org.name === action.payload.orgName) + if (index > -1) { + var updatedOrgList = state.value.authLoginData.data.organizations + updatedOrgList[index] = action.payload.value + state.value.authLoginData.data.organizations = [...updatedOrgList] + } } }, setLoading: (state, action) => { From 716a70815628c3eb9a4975df11a92fa49c483bc3 Mon Sep 17 00:00:00 2001 From: Marc Wodahl Date: Mon, 24 Jun 2024 11:12:02 -0600 Subject: [PATCH 090/189] Update admin edit pages to populate values correctly --- .../src/features/adminEditRsu/AdminEditRsu.tsx | 17 +++++++++-------- .../features/adminEditUser/AdminEditUser.tsx | 15 ++++++++------- 2 files changed, 17 insertions(+), 15 deletions(-) diff --git a/webapp/src/features/adminEditRsu/AdminEditRsu.tsx b/webapp/src/features/adminEditRsu/AdminEditRsu.tsx index 63e35b54f..d2f49ac52 100644 --- a/webapp/src/features/adminEditRsu/AdminEditRsu.tsx +++ b/webapp/src/features/adminEditRsu/AdminEditRsu.tsx @@ -123,14 +123,15 @@ const AdminEditRsu = () => { }, [dispatch, rsuIp, rsuTableData]) useEffect(() => { - if (apiData && Object.keys(apiData).length !== 0) { - setValue('orig_ip', apiData.rsu_data.ip) - setValue('ip', apiData.rsu_data.ip) - setValue('geo_position.latitude', apiData.rsu_data.geo_position.latitude.toString()) - setValue('geo_position.longitude', apiData.rsu_data.geo_position.longitude.toString()) - setValue('milepost', String(apiData.rsu_data.milepost)) - setValue('serial_number', apiData.rsu_data.serial_number) - setValue('scms_id', apiData.rsu_data.scms_id) + const currRsu = (rsuTableData ?? []).find((rsu: AdminRsu) => rsu.ip === rsuIp) + if (currRsu) { + setValue('orig_ip', currRsu.ip) + setValue('ip', currRsu.ip) + setValue('geo_position.latitude', currRsu.geo_position.latitude.toString()) + setValue('geo_position.longitude', currRsu.geo_position.longitude.toString()) + setValue('milepost', String(currRsu.milepost)) + setValue('serial_number', currRsu.serial_number) + setValue('scms_id', currRsu.scms_id) } }, [apiData, setValue]) diff --git a/webapp/src/features/adminEditUser/AdminEditUser.tsx b/webapp/src/features/adminEditUser/AdminEditUser.tsx index eb22d12c0..ddc227f6b 100644 --- a/webapp/src/features/adminEditUser/AdminEditUser.tsx +++ b/webapp/src/features/adminEditUser/AdminEditUser.tsx @@ -90,13 +90,14 @@ const AdminEditUser = () => { }, [dispatch]) useEffect(() => { - if (apiData && Object.keys(apiData).length !== 0) { - setValue('orig_email', apiData.user_data.email) - setValue('email', apiData.user_data.email) - setValue('first_name', apiData.user_data.first_name) - setValue('last_name', apiData.user_data.last_name) - setValue('super_user', apiData.user_data.super_user.toString()) - setValue('receive_error_emails', apiData.user_data.receive_error_emails.toString()) + const currUser = (userTableData ?? []).find((user: AdminUserWithId) => user.email === email) + if (currUser) { + setValue('orig_email', currUser.email) + setValue('email', currUser.email) + setValue('first_name', currUser.first_name) + setValue('last_name', currUser.last_name) + setValue('super_user', currUser.super_user.toString()) + setValue('receive_error_emails', currUser.receive_error_emails.toString()) } console.log('useEffect apiData', email, userTableData, apiData) }, [apiData, setValue]) From b5494cda1d38df8a5181d225ddccb8627314826a Mon Sep 17 00:00:00 2001 From: Marc Wodahl Date: Tue, 25 Jun 2024 08:03:25 -0600 Subject: [PATCH 091/189] Update comments --- services/addons/images/count_metric/sample.env | 1 - services/api/src/admin_email_notification.py | 1 - services/api/src/contact_support.py | 1 - .../api/tests/src/test_admin_new_email_notification.py | 8 ++++---- .../adminNotificationTab/AdminNotificationTab.tsx | 1 - 5 files changed, 4 insertions(+), 8 deletions(-) diff --git a/services/addons/images/count_metric/sample.env b/services/addons/images/count_metric/sample.env index b796d3861..4927c0ff9 100644 --- a/services/addons/images/count_metric/sample.env +++ b/services/addons/images/count_metric/sample.env @@ -21,7 +21,6 @@ SMTP_SERVER_IP = '' SMTP_USERNAME = '' SMTP_PASSWORD = '' SMTP_EMAIL = '' -# Multiple emails can be delimited by a ',' # --------------------------------------------------------------------- # If ENABLE_EMAILER is 'False', set the following environment variables diff --git a/services/api/src/admin_email_notification.py b/services/api/src/admin_email_notification.py index 26385f549..e7a7adb4e 100644 --- a/services/api/src/admin_email_notification.py +++ b/services/api/src/admin_email_notification.py @@ -96,7 +96,6 @@ def modify_notification(notification_spec): def delete_notification(user_email, email_type): - # Delete user-to-organization relationships notification_remove_query = ( "DELETE FROM public.user_email_notification WHERE " f"user_id IN (SELECT user_id FROM public.users WHERE email = '{user_email}') " diff --git a/services/api/src/contact_support.py b/services/api/src/contact_support.py index 4e5e5b4e4..256c0eb22 100644 --- a/services/api/src/contact_support.py +++ b/services/api/src/contact_support.py @@ -76,7 +76,6 @@ def post(self): message = request.json["message"] email_addresses = get_email_list("Contact Support") - logging.info(f"Email addresses: {email_addresses}") for email_address in email_addresses: emailSender = EmailSender( self.CSM_TARGET_SMTP_SERVER_ADDRESS, diff --git a/services/api/tests/src/test_admin_new_email_notification.py b/services/api/tests/src/test_admin_new_email_notification.py index 33614643f..111a30700 100644 --- a/services/api/tests/src/test_admin_new_email_notification.py +++ b/services/api/tests/src/test_admin_new_email_notification.py @@ -59,7 +59,7 @@ def test_check_safe_input_bad(): @patch("api.src.admin_new_email_notification.check_safe_input") @patch("api.src.admin_new_email_notification.pgquery.write_db") -def test_add_org_success_commsignia(mock_pgquery, mock_check_safe_input): +def test_add_notification_success(mock_pgquery, mock_check_safe_input): mock_check_safe_input.return_value = True expected_msg, expected_code = { "message": "New email notification successfully added" @@ -76,7 +76,7 @@ def test_add_org_success_commsignia(mock_pgquery, mock_check_safe_input): @patch("api.src.admin_new_email_notification.check_safe_input") @patch("api.src.admin_new_email_notification.pgquery.write_db") -def test_add_org_safety_fail(mock_pgquery, mock_check_safe_input): +def test_add_notification_safety_fail(mock_pgquery, mock_check_safe_input): mock_check_safe_input.return_value = False expected_msg, expected_code = { "message": "No special characters are allowed: !\"#$%&'()*+,./:;<=>?@[\\]^`{|}~. No sequences of '-' characters are allowed" @@ -93,7 +93,7 @@ def test_add_org_safety_fail(mock_pgquery, mock_check_safe_input): @patch("api.src.admin_new_email_notification.check_safe_input") @patch("api.src.admin_new_email_notification.pgquery.write_db") -def test_add_org_generic_exception(mock_pgquery, mock_check_safe_input): +def test_add_notification_generic_exception(mock_pgquery, mock_check_safe_input): mock_check_safe_input.return_value = True mock_pgquery.side_effect = Exception("Test") expected_msg, expected_code = {"message": "Encountered unknown issue"}, 500 @@ -107,7 +107,7 @@ def test_add_org_generic_exception(mock_pgquery, mock_check_safe_input): @patch("api.src.admin_new_email_notification.check_safe_input") @patch("api.src.admin_new_email_notification.pgquery.write_db") -def test_add_org_sql_exception(mock_pgquery, mock_check_safe_input): +def test_add_notification_sql_exception(mock_pgquery, mock_check_safe_input): mock_check_safe_input.return_value = True orig = MagicMock() orig.args = ({"D": "SQL issue encountered"},) diff --git a/webapp/src/features/adminNotificationTab/AdminNotificationTab.tsx b/webapp/src/features/adminNotificationTab/AdminNotificationTab.tsx index 22d92589a..f8a6344a4 100644 --- a/webapp/src/features/adminNotificationTab/AdminNotificationTab.tsx +++ b/webapp/src/features/adminNotificationTab/AdminNotificationTab.tsx @@ -5,7 +5,6 @@ import { AiOutlinePlusCircle } from 'react-icons/ai' import { confirmAlert } from 'react-confirm-alert' import { Options } from '../../components/AdminDeletionOptions' import { selectLoading } from '../../generalSlices/rsuSlice' -// Need to import the authData to get current user email import { selectTableData, getUserNotifications, From 9a09d2f576d409b7af3f9a2091b9dc2697b19f71 Mon Sep 17 00:00:00 2001 From: Drew Johnston <31270488+drewjj@users.noreply.github.com> Date: Thu, 27 Jun 2024 17:50:42 -0600 Subject: [PATCH 092/189] Add missing field checks --- .../iss_health_check/iss_health_checker.py | 64 +++++++++++++------ 1 file changed, 46 insertions(+), 18 deletions(-) diff --git a/services/addons/images/iss_health_check/iss_health_checker.py b/services/addons/images/iss_health_check/iss_health_checker.py index ec2d5a3d4..71dd6c8fe 100644 --- a/services/addons/images/iss_health_check/iss_health_checker.py +++ b/services/addons/images/iss_health_check/iss_health_checker.py @@ -11,6 +11,7 @@ # Set up logging logger = logging.getLogger(__name__) + @dataclass class RsuDataWrapper: rsu_data: Dict[str, Dict[str, str]] = field(default_factory=dict) @@ -23,7 +24,7 @@ def get_dict(self): def set_provisioner_company(self, scms_id, provisioner_company): self.rsu_data[scms_id]["provisionerCompany"] = provisioner_company - + def set_entity_type(self, scms_id, entity_type): self.rsu_data[scms_id]["entityType"] = entity_type @@ -39,7 +40,7 @@ def set_expiration(self, scms_id, expiration): def get_rsu_data() -> RsuDataWrapper: """Get RSU data from PostgreSQL and return it in a wrapper object""" - + result = {} query = ( "SELECT jsonb_build_object('rsu_id', rsu_id, 'iss_scms_id', iss_scms_id) " @@ -89,14 +90,21 @@ def get_scms_status_data(): for enrollment_status in enrollment_list: es_id = enrollment_status["_id"] if es_id in rsu_data.get_dict(): - rsu_data.set_provisioner_company(es_id, enrollment_status["provisionerCompany_id"]) + rsu_data.set_provisioner_company( + es_id, enrollment_status["provisionerCompany_id"] + ) rsu_data.set_entity_type(es_id, enrollment_status["entityType"]) rsu_data.set_project_id(es_id, enrollment_status["project_id"]) rsu_data.set_device_health(es_id, enrollment_status["deviceHealth"]) # If the device has yet to download its first set of certs, set the expiration time to when it was enrolled if "authorizationCertInfo" in enrollment_status["enrollments"][0]: - rsu_data.set_expiration(es_id, enrollment_status["enrollments"][0]["authorizationCertInfo"]["expireTimeOfLatestDownloadedCert"]) + rsu_data.set_expiration( + es_id, + enrollment_status["enrollments"][0]["authorizationCertInfo"][ + "expireTimeOfLatestDownloadedCert" + ], + ) else: rsu_data.set_expiration(es_id, None) @@ -119,24 +127,34 @@ def insert_scms_data(data): if validate_scms_data(value) is False: continue - health = "1" if value["deviceHealth"] == "Healthy" else "0" - if value["expiration"]: - query = ( - query - + f" ('{now_ts}', '{health}', '{value['expiration']}', {value['rsu_id']})," - ) + # If deviceHealth isn't included in the device's SCMS profile, assume unhealthy + if "deviceHealth" in value: + health = "1" if value["deviceHealth"] == "Healthy" else "0" else: - query = query + f" ('{now_ts}', '{health}', NULL, {value['rsu_id']})," + health = "0" + + # If expiration isn't included in the device's SCMS profile, assume None + if "expiration" in value: + # Check if the expiration field is None + if value["expiration"]: + expiration = f"'{value["expiration"]}'" + else: + expiration = "NULL" + else: + expiration = "NULL" + + query = query + f" ('{now_ts}', '{health}', {expiration}, {value['rsu_id']})," - query = query[:-1] # remove comma + query = query[:-1] # remove comma pgquery.write_db(query) logger.info( "SCMS data inserted {} messages into PostgreSQL...".format(len(data.values())) ) + def validate_scms_data(value): """Validate the SCMS data - + Args: value (dict): SCMS data """ @@ -144,21 +162,31 @@ def validate_scms_data(value): try: value["rsu_id"] except KeyError as e: - logger.warning("rsu_id not found in data, is it real data? exception: {}".format(e)) + logger.warning( + "rsu_id not found in data, is it real data? exception: {}".format(e) + ) return False try: value["deviceHealth"] except KeyError as e: - logger.warning("deviceHealth not found in data for RSU with id {}, is it real data? exception: {}".format(value["rsu_id"], e)) + logger.warning( + "deviceHealth not found in data for RSU with id {}, is it real data? exception: {}".format( + value["rsu_id"], e + ) + ) return False try: value["expiration"] except KeyError as e: - logger.warning("expiration not found in data for RSU with id {}, is it real data? exception: {}".format(value["rsu_id"], e)) + logger.warning( + "expiration not found in data for RSU with id {}, is it real data? exception: {}".format( + value["rsu_id"], e + ) + ) return False - + return True @@ -170,4 +198,4 @@ def validate_scms_data(value): logging.basicConfig(format="%(levelname)s:%(message)s", level=log_level) scms_statuses = get_scms_status_data() - insert_scms_data(scms_statuses) \ No newline at end of file + insert_scms_data(scms_statuses) From 9ae0319f6783b5f376e1ad7ed384ea2caa2b2e96 Mon Sep 17 00:00:00 2001 From: Marc Wodahl Date: Tue, 2 Jul 2024 08:18:23 -0600 Subject: [PATCH 093/189] add useEffect comment for Admin Org tab --- .../src/features/adminOrganizationTab/AdminOrganizationTab.tsx | 2 ++ 1 file changed, 2 insertions(+) diff --git a/webapp/src/features/adminOrganizationTab/AdminOrganizationTab.tsx b/webapp/src/features/adminOrganizationTab/AdminOrganizationTab.tsx index d7c7114c1..e09874d03 100644 --- a/webapp/src/features/adminOrganizationTab/AdminOrganizationTab.tsx +++ b/webapp/src/features/adminOrganizationTab/AdminOrganizationTab.tsx @@ -66,6 +66,8 @@ const AdminOrganizationTab = () => { useEffect(() => { dispatch(getOrgData({ orgName: 'all', all: true, specifiedOrg: undefined })).then(() => { + // on first render set the default organization in the admin + // organization tab to the currently selected organization if (defaultOrgData) { const selectedOrg = (orgData ?? []).find( (organization: AdminOrgSummary) => organization?.name === defaultOrgName From 0e8ee4bdfe40f4b1cd891ea184046e7917934c24 Mon Sep 17 00:00:00 2001 From: Marc Wodahl Date: Tue, 2 Jul 2024 10:49:00 -0600 Subject: [PATCH 094/189] README Updates --- services/addons/images/count_metric/README.md | 2 ++ services/addons/images/firmware_manager/README.md | 1 + services/api/README.md | 5 +++++ 3 files changed, 8 insertions(+) diff --git a/services/addons/images/count_metric/README.md b/services/addons/images/count_metric/README.md index 2e2b15328..71edd42ea 100644 --- a/services/addons/images/count_metric/README.md +++ b/services/addons/images/count_metric/README.md @@ -8,6 +8,8 @@ It is important to note that the count_metric service assumes Map and TIM messag Specifically includes the following message types: ["BSM", "TIM", "Map", "SPaT", "SRM", "SSM"] +Please note that the daily emailer relies on the user_email_notification PostgreSQL table to pull in the list of users that are subscribed to receive these emails. + To run this service, the following environment variables must be set: LOGGING_LEVEL: The logging level of the deployment. Options are: 'critical', 'error', 'warning', 'info' and 'debug'. If not specified, will default to 'info'. Refer to Python's documentation for more info: [Python logging](https://docs.python.org/3/howto/logging.html). diff --git a/services/addons/images/firmware_manager/README.md b/services/addons/images/firmware_manager/README.md index be1be2acc..ee6721dd0 100644 --- a/services/addons/images/firmware_manager/README.md +++ b/services/addons/images/firmware_manager/README.md @@ -57,6 +57,7 @@ The firmware_manager microservice expects the following environment variables to - LOGGING_LEVEL (optional, defaults to 'info') The Firmware Manager is capable of sending an email to the support team in the event that an online RSU experiences a firmware upgrade failure. +This functionality relies on the user_email_notification PostgreSQL table to pull in the list of users that are subscribed to receive these emails. To do so the following environment variables must be set: - SMTP_EMAIL - Email to send from. diff --git a/services/api/README.md b/services/api/README.md index d8abe5bf1..135ca6b1b 100644 --- a/services/api/README.md +++ b/services/api/README.md @@ -32,6 +32,11 @@ Example return value: - {"name": "John Doe", "email": "jdoe@gmail.com", "role": "admin"} +### /contact-support (POST) + +Sends a support request email to all users subscribed to 'Support Requests' in the cv-manager. Please note that this functionality +relies on the user_email_notification table in PostgreSQL to pull in all users subscribed to receive these notifications. + ### /rsuinfo (GET) Returns all basic data for RSUs in the GCP Cloud SQL database. It performs a basic select all query from a table named "RsuData" that is located in a database specified by the environments variables. Returns single JSON object. From c87087d40fc71180874791330ad283c407bf43c4 Mon Sep 17 00:00:00 2001 From: Marc Wodahl Date: Tue, 2 Jul 2024 10:49:22 -0600 Subject: [PATCH 095/189] Update email type names --- resources/sql_scripts/CVManager_SampleData.sql | 2 +- services/addons/images/firmware_manager/upgrader.py | 2 +- services/api/src/contact_support.py | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/resources/sql_scripts/CVManager_SampleData.sql b/resources/sql_scripts/CVManager_SampleData.sql index 0cb233b36..cb4238084 100644 --- a/resources/sql_scripts/CVManager_SampleData.sql +++ b/resources/sql_scripts/CVManager_SampleData.sql @@ -72,4 +72,4 @@ INSERT INTO public.snmp_msgfwd_config( INSERT INTO public.email_type( email_type) - VALUES ('Contact Support'), ('Firmware Upgrade Failure'), ('Daily Message Counts'); \ No newline at end of file + VALUES ('Support Requests'), ('Firmware Upgrade Failures'), ('Daily Message Counts'); \ No newline at end of file diff --git a/services/addons/images/firmware_manager/upgrader.py b/services/addons/images/firmware_manager/upgrader.py index 94ddcf8cd..2378a8b10 100644 --- a/services/addons/images/firmware_manager/upgrader.py +++ b/services/addons/images/firmware_manager/upgrader.py @@ -98,7 +98,7 @@ def check_online(self): def send_error_email(self, type="Firmware Upgrader", err=""): try: - email_addresses = get_email_list("Firmware Upgrade Failure") + email_addresses = get_email_list("Firmware Upgrade Failures") subject = ( f"{self.rsu_ip} Firmware Upgrader Failure" diff --git a/services/api/src/contact_support.py b/services/api/src/contact_support.py index 256c0eb22..b31a74b2a 100644 --- a/services/api/src/contact_support.py +++ b/services/api/src/contact_support.py @@ -75,7 +75,7 @@ def post(self): subject = request.json["subject"] message = request.json["message"] - email_addresses = get_email_list("Contact Support") + email_addresses = get_email_list("Support Requests") for email_address in email_addresses: emailSender = EmailSender( self.CSM_TARGET_SMTP_SERVER_ADDRESS, From d0ceefe00b4c773f426f4c24b4ad7bd95b8f1f73 Mon Sep 17 00:00:00 2001 From: Marc Wodahl Date: Tue, 2 Jul 2024 11:06:05 -0600 Subject: [PATCH 096/189] Update admin add form, add sql update script --- .../email_notification_update.sql | 18 ++++++++++++++++++ .../AdminAddNotification.tsx | 8 ++++++-- 2 files changed, 24 insertions(+), 2 deletions(-) create mode 100644 resources/sql_scripts/update_scripts/email_notification_update.sql diff --git a/resources/sql_scripts/update_scripts/email_notification_update.sql b/resources/sql_scripts/update_scripts/email_notification_update.sql new file mode 100644 index 000000000..840b2ab69 --- /dev/null +++ b/resources/sql_scripts/update_scripts/email_notification_update.sql @@ -0,0 +1,18 @@ +-- Run this SQL update script if you already have a deployed CV Manager PostgreSQL database +-- This file assumes that the email_type and user_email_notification tables exist, and that the email_type +-- table has an entry for 'Support Requests'. + +DO $$ +DECLARE + error_email_type_id INT; +BEGIN + SELECT email_type_id INTO error_email_type_id FROM public.email_type WHERE email_type = 'Support Requests'; + + INSERT INTO public.user_email_notification (user_id, email_type_id) + SELECT user_id, error_email_type_id + FROM public.users + WHERE receive_error_emails = B'1'; + + ALTER TABLE public.users DROP Column receive_error_emails; + +END $$; \ No newline at end of file diff --git a/webapp/src/features/adminAddNotification/AdminAddNotification.tsx b/webapp/src/features/adminAddNotification/AdminAddNotification.tsx index ec6ad3b24..c0c2d21ed 100644 --- a/webapp/src/features/adminAddNotification/AdminAddNotification.tsx +++ b/webapp/src/features/adminAddNotification/AdminAddNotification.tsx @@ -51,14 +51,18 @@ const AdminAddNotification = () => { dispatch(updateEmailTypesApiData()) }, [apiData, dispatch]) - const onSubmit = (data: AdminNotificationForm) => dispatch(submitForm({ data, reset })) + const onSubmit = (data: AdminNotificationForm) => { + data.email = userEmail + dispatch(submitForm({ data, reset })) + } return (
Email - +
+

{userEmail}

From 3a1de82236f4f8b2802c00f6165e337678c39651 Mon Sep 17 00:00:00 2001 From: Marc Wodahl Date: Wed, 3 Jul 2024 07:51:16 -0600 Subject: [PATCH 097/189] Add snmp_msgfwd remove query --- services/api/src/admin_rsu.py | 7 +++++++ services/api/tests/data/admin_rsu_data.py | 1 + services/api/tests/src/test_admin_rsu.py | 1 + 3 files changed, 9 insertions(+) diff --git a/services/api/src/admin_rsu.py b/services/api/src/admin_rsu.py index 937db08d4..ed7f720c7 100644 --- a/services/api/src/admin_rsu.py +++ b/services/api/src/admin_rsu.py @@ -156,6 +156,13 @@ def delete_rsu(rsu_ip): ) pgquery.write_db(scms_remove_query) + # Delete snmp message forward config data + msg_config_remove_query = ( + "DELETE FROM public.snmp_msgfwd_config WHERE " + f"rsu_id=(SELECT rsu_id FROM public.rsus WHERE ipv4_address = '{rsu_ip}')" + ) + pgquery.write_db(msg_config_remove_query) + # Delete RSU data rsu_remove_query = "DELETE FROM public.rsus WHERE " f"ipv4_address = '{rsu_ip}'" pgquery.write_db(rsu_remove_query) diff --git a/services/api/tests/data/admin_rsu_data.py b/services/api/tests/data/admin_rsu_data.py index 353d1b232..72b86ff30 100644 --- a/services/api/tests/data/admin_rsu_data.py +++ b/services/api/tests/data/admin_rsu_data.py @@ -146,5 +146,6 @@ "DELETE FROM public.rsu_organization WHERE rsu_id=(SELECT rsu_id FROM public.rsus WHERE ipv4_address = '10.11.81.12')", "DELETE FROM public.ping WHERE rsu_id=(SELECT rsu_id FROM public.rsus WHERE ipv4_address = '10.11.81.12')", "DELETE FROM public.scms_health WHERE rsu_id=(SELECT rsu_id FROM public.rsus WHERE ipv4_address = '10.11.81.12')", + "DELETE FROM public.snmp_msgfwd_config WHERE rsu_id=(SELECT rsu_id FROM public.rsus WHERE ipv4_address = '10.11.81.12')", "DELETE FROM public.rsus WHERE ipv4_address = '10.11.81.12'", ] diff --git a/services/api/tests/src/test_admin_rsu.py b/services/api/tests/src/test_admin_rsu.py index 0cc0c2555..5dd4a8713 100644 --- a/services/api/tests/src/test_admin_rsu.py +++ b/services/api/tests/src/test_admin_rsu.py @@ -279,6 +279,7 @@ def test_delete_rsu(mock_write_db): call(admin_rsu_data.delete_rsu_calls[1]), call(admin_rsu_data.delete_rsu_calls[2]), call(admin_rsu_data.delete_rsu_calls[3]), + call(admin_rsu_data.delete_rsu_calls[4]) ] mock_write_db.assert_has_calls(calls) assert actual_result == expected_result From a20671c8cfb3091e53b3f9db20a741ea31932ab1 Mon Sep 17 00:00:00 2001 From: Michael7371 <40476797+Michael7371@users.noreply.github.com> Date: Wed, 3 Jul 2024 10:01:33 -0600 Subject: [PATCH 098/189] fixes from test deployment --- resources/ota/nginx/nginx-ssl.conf | 4 ++-- resources/ota/nginx/ssl/README.md | 24 ++++++++++++++++++- .../addons/images/obu_ota_server/README.md | 2 +- ...nia_manifest.py => commsignia_manifest.py} | 0 .../images/obu_ota_server/obu_ota_server.py | 15 ++++++------ .../addons/images/obu_ota_server/sample.env | 4 ++-- .../test_commsignia_manifest.py | 2 +- .../obu_ota_server/test_obu_ota_server.py | 18 +++++++------- 8 files changed, 46 insertions(+), 23 deletions(-) rename services/addons/images/obu_ota_server/{commsginia_manifest.py => commsignia_manifest.py} (100%) diff --git a/resources/ota/nginx/nginx-ssl.conf b/resources/ota/nginx/nginx-ssl.conf index ffa10bfd5..976b0fb7b 100644 --- a/resources/ota/nginx/nginx-ssl.conf +++ b/resources/ota/nginx/nginx-ssl.conf @@ -38,7 +38,7 @@ http { server { listen 80; - server_name http://$SERVER_HOST; + server_name $SERVER_HOST; location / { return 301 https://$host$request_uri; @@ -53,7 +53,7 @@ http { server { listen 443 default_server ssl; - server_name https://$SERVER_HOST; + server_name $SERVER_HOST; ssl_certificate /etc/ssl/certs/ota_server.crt; ssl_certificate_key /etc/ssl/private/ota_server.key; diff --git a/resources/ota/nginx/ssl/README.md b/resources/ota/nginx/ssl/README.md index 39172c5ed..c91acd246 100644 --- a/resources/ota/nginx/ssl/README.md +++ b/resources/ota/nginx/ssl/README.md @@ -1,3 +1,25 @@ # SSL -This folder is used as an attached volume to the NGINX service and must include the server's .crt and .key file. All .crt and .key files are not tracked through git. \ No newline at end of file +This folder is used as an attached volume to the NGINX service and must include the server's `.crt` and `.key` file. All `.crt` and `.key` files are not tracked through git. + +The `.crt` file must be in the following multi-line format: + +```.crt +-----BEGIN CERTIFICATE----- +EC_KEY_BASED_CERTIFICATE +-----END CERTIFICATE----- +-----BEGIN CERTIFICATE----- +CA_INTERMEDIATE_CERTIFICATE +-----END CERTIFICATE----- +-----BEGIN CERTIFICATE----- +CA_ROOT_CERTIFICATE +-----END CERTIFICATE----- +``` + +The `.key` file must be in the following multi-line format: + +```.key +-----BEGIN EC PRIVATE KEY----- +EC_PRIVATE_KEY_DATA +-----END EC PRIVATE KEY----- +``` diff --git a/services/addons/images/obu_ota_server/README.md b/services/addons/images/obu_ota_server/README.md index 776a719b4..402d6c41a 100644 --- a/services/addons/images/obu_ota_server/README.md +++ b/services/addons/images/obu_ota_server/README.md @@ -44,7 +44,7 @@ The following environmental variables must be set: SERVER_HOST: The base URL of the OTA server, this must be resolvable from the OBU. Generally this should be set to the IP address of the server or the DNS name. -BLOB_STORAGE_PROVIDER: Set to either "LOCAL" or "GCP" depending on deployment environment. +BLOB_STORAGE_PROVIDER: Set to either "DOCKER" or "GCP" depending on deployment environment. OTA_USERNAME: Username to be used with basic authentication. diff --git a/services/addons/images/obu_ota_server/commsginia_manifest.py b/services/addons/images/obu_ota_server/commsignia_manifest.py similarity index 100% rename from services/addons/images/obu_ota_server/commsginia_manifest.py rename to services/addons/images/obu_ota_server/commsignia_manifest.py diff --git a/services/addons/images/obu_ota_server/obu_ota_server.py b/services/addons/images/obu_ota_server/obu_ota_server.py index 915f7ff30..14d02bca1 100644 --- a/services/addons/images/obu_ota_server/obu_ota_server.py +++ b/services/addons/images/obu_ota_server/obu_ota_server.py @@ -2,7 +2,7 @@ from fastapi.security import HTTPBasic, HTTPBasicCredentials from fastapi.middleware.cors import CORSMiddleware from common import gcs_utils -import commsginia_manifest +import commsignia_manifest import os import glob import aiofiles @@ -41,13 +41,13 @@ async def read_root(request: Request): def get_firmware_list(): - blob_storage_provider = os.getenv("BLOB_STORAGE_PROVIDER", "LOCAL") + blob_storage_provider = os.getenv("BLOB_STORAGE_PROVIDER", "DOCKER") files = [] file_extension = ".tar.sig" - if blob_storage_provider.upper() == "LOCAL": + if blob_storage_provider.upper() == "DOCKER": files = glob.glob(f"/firmwares/*{file_extension}") elif blob_storage_provider.upper() == "GCP": - blob_storage_path = os.getenv("BLOB_STORAGE_PATH", "LOCAL") + blob_storage_path = os.getenv("BLOB_STORAGE_PATH", "DOCKER") files = gcs_utils.list_gcs_blobs(blob_storage_path, file_extension) return files @@ -56,8 +56,9 @@ def get_firmware_list(): async def get_manifest(request: Request): try: files = get_firmware_list() + logging.debug(f"get_manifest :: Files: {files}") host_name = os.getenv("SERVER_HOST", "localhost") - response_manifest = commsginia_manifest.add_contents(host_name, files) + response_manifest = commsignia_manifest.add_contents(host_name, files) return response_manifest except Exception as e: raise HTTPException(status_code=500, detail=str(e)) @@ -65,11 +66,11 @@ async def get_manifest(request: Request): def get_firmware(firmware_id: str, local_file_path: str): try: - blob_storage_provider = os.getenv("BLOB_STORAGE_PROVIDER", "LOCAL") + blob_storage_provider = os.getenv("BLOB_STORAGE_PROVIDER", "DOCKER") # checks if firmware exists locally if not os.path.exists(local_file_path): # If configured to only use local storage, return False as firmware does not exist - if blob_storage_provider.upper() == "LOCAL": + if blob_storage_provider.upper() == "DOCKER": return False # If configured to use GCP storage, download the firmware from GCP elif blob_storage_provider.upper() == "GCP": diff --git a/services/addons/images/obu_ota_server/sample.env b/services/addons/images/obu_ota_server/sample.env index 898befda8..2f70e2385 100644 --- a/services/addons/images/obu_ota_server/sample.env +++ b/services/addons/images/obu_ota_server/sample.env @@ -5,8 +5,8 @@ LOGGING_LEVEL="INFO" SERVER_HOST = "localhost" # Blob storage variables -# For local deployments set the BLOB_STORAGE_PROVIDER to "LOCAL" -BLOB_STORAGE_PROVIDER="LOCAL" +# For local deployments set the BLOB_STORAGE_PROVIDER to "DOCKER" +BLOB_STORAGE_PROVIDER="DOCKER" # For users using GCP cloud storage BLOB_STORAGE_BUCKET="" diff --git a/services/addons/tests/obu_ota_server/test_commsignia_manifest.py b/services/addons/tests/obu_ota_server/test_commsignia_manifest.py index 999f7b657..f15add07e 100644 --- a/services/addons/tests/obu_ota_server/test_commsignia_manifest.py +++ b/services/addons/tests/obu_ota_server/test_commsignia_manifest.py @@ -1,6 +1,6 @@ import pytest import unittest.mock as mock -from addons.images.obu_ota_server.commsginia_manifest import add_contents +from addons.images.obu_ota_server.commsignia_manifest import add_contents def test_add_contents(): diff --git a/services/addons/tests/obu_ota_server/test_obu_ota_server.py b/services/addons/tests/obu_ota_server/test_obu_ota_server.py index 9d8fb44aa..cae568760 100644 --- a/services/addons/tests/obu_ota_server/test_obu_ota_server.py +++ b/services/addons/tests/obu_ota_server/test_obu_ota_server.py @@ -17,12 +17,12 @@ @patch("os.getenv") @patch("glob.glob") def test_get_firmware_list_local(mock_glob, mock_getenv): - mock_getenv.return_value = "LOCAL" + mock_getenv.return_value = "DOCKER" mock_glob.return_value = ["/firmwares/test1.tar.sig", "/firmwares/test2.tar.sig"] result = get_firmware_list() - mock_getenv.assert_called_once_with("BLOB_STORAGE_PROVIDER", "LOCAL") + mock_getenv.assert_called_once_with("BLOB_STORAGE_PROVIDER", "DOCKER") mock_glob.assert_called_once_with("/firmwares/*.tar.sig") assert result == ["/firmwares/test1.tar.sig", "/firmwares/test2.tar.sig"] @@ -38,7 +38,7 @@ def test_get_firmware_list_gcs(mock_list_gcs_blobs, mock_getenv): result = get_firmware_list() - # mock_getenv.assert_called_once_with("BLOB_STORAGE_PROVIDER", "LOCAL") + # mock_getenv.assert_called_once_with("BLOB_STORAGE_PROVIDER", "DOCKER") mock_list_gcs_blobs.assert_called_once_with("GCP", ".tar.sig") assert result == ["/firmwares/test1.tar.sig", "/firmwares/test2.tar.sig"] @@ -47,14 +47,14 @@ def test_get_firmware_list_gcs(mock_list_gcs_blobs, mock_getenv): @patch("os.path.exists") @patch("common.gcs_utils.list_gcs_blobs") def test_get_firmware_local_fail(mock_gcs_utils, mock_os_path_exists, mock_os_getenv): - mock_os_getenv.return_value = "LOCAL" + mock_os_getenv.return_value = "DOCKER" mock_os_path_exists.return_value = False firmware_id = "test_firmware_id" local_file_path = "test_local_file_path" result = get_firmware(firmware_id, local_file_path) - mock_os_getenv.assert_called_once_with("BLOB_STORAGE_PROVIDER", "LOCAL") + mock_os_getenv.assert_called_once_with("BLOB_STORAGE_PROVIDER", "DOCKER") mock_os_path_exists.assert_called_once_with(local_file_path) mock_gcs_utils.assert_not_called() @@ -67,14 +67,14 @@ def test_get_firmware_local_fail(mock_gcs_utils, mock_os_path_exists, mock_os_ge def test_get_firmware_local_success( mock_gcs_utils, mock_os_path_exists, mock_os_getenv ): - mock_os_getenv.return_value = "LOCAL" + mock_os_getenv.return_value = "DOCKER" mock_os_path_exists.return_value = True firmware_id = "test_firmware_id" local_file_path = "test_local_file_path" result = get_firmware(firmware_id, local_file_path) - mock_os_getenv.assert_called_once_with("BLOB_STORAGE_PROVIDER", "LOCAL") + mock_os_getenv.assert_called_once_with("BLOB_STORAGE_PROVIDER", "DOCKER") mock_os_path_exists.assert_called_once_with(local_file_path) mock_gcs_utils.assert_not_called() @@ -95,7 +95,7 @@ def test_get_firmware_gcs_success( local_file_path = "test_local_file_path" result = get_firmware(firmware_id, local_file_path) - mock_os_getenv.assert_called_with("BLOB_STORAGE_PROVIDER", "LOCAL") + mock_os_getenv.assert_called_with("BLOB_STORAGE_PROVIDER", "DOCKER") mock_os_path_exists.assert_called_with(local_file_path) mock_download_gcp_blob.assert_called_once_with(firmware_id, local_file_path) @@ -116,7 +116,7 @@ def test_get_firmware_gcs_failure( local_file_path = "test_local_file_path" result = get_firmware(firmware_id, local_file_path) - mock_os_getenv.assert_called_with("BLOB_STORAGE_PROVIDER", "LOCAL") + mock_os_getenv.assert_called_with("BLOB_STORAGE_PROVIDER", "DOCKER") mock_os_path_exists.assert_called_with(local_file_path) mock_download_gcp_blob.assert_called_once_with(firmware_id, local_file_path) From 5c934d631d01a40299f07eb1b3e9ea5e907625ca Mon Sep 17 00:00:00 2001 From: Michael English Date: Wed, 3 Jul 2024 10:09:07 -0600 Subject: [PATCH 099/189] updating gen_dhparam.sh permissions to be executable --- resources/ota/nginx/gen_dhparam.sh | 0 1 file changed, 0 insertions(+), 0 deletions(-) mode change 100644 => 100755 resources/ota/nginx/gen_dhparam.sh diff --git a/resources/ota/nginx/gen_dhparam.sh b/resources/ota/nginx/gen_dhparam.sh old mode 100644 new mode 100755 From 2d98b6371cce7a0b281f437bad81dae3f1598f1e Mon Sep 17 00:00:00 2001 From: Michael7371 <40476797+Michael7371@users.noreply.github.com> Date: Wed, 3 Jul 2024 10:16:37 -0600 Subject: [PATCH 100/189] fixing unit tests with updated file name --- services/addons/tests/obu_ota_server/test_obu_ota_server.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/services/addons/tests/obu_ota_server/test_obu_ota_server.py b/services/addons/tests/obu_ota_server/test_obu_ota_server.py index cae568760..ac8a8357c 100644 --- a/services/addons/tests/obu_ota_server/test_obu_ota_server.py +++ b/services/addons/tests/obu_ota_server/test_obu_ota_server.py @@ -201,13 +201,13 @@ async def test_read_root(): @patch.dict("os.environ", {"OTA_USERNAME": "username", "OTA_PASSWORD": "password"}) @pytest.mark.anyio @patch("addons.images.obu_ota_server.obu_ota_server.get_firmware_list") -@patch("addons.images.obu_ota_server.obu_ota_server.commsginia_manifest.add_contents") -async def test_get_manifest(mock_commsginia_manifest, mock_get_firmware_list): +@patch("addons.images.obu_ota_server.obu_ota_server.commsignia_manifest.add_contents") +async def test_get_manifest(mock_commsignia_manifest, mock_get_firmware_list): mock_get_firmware_list.return_value = [ "/firmwares/test1.tar.sig", "/firmwares/test2.tar.sig", ] - mock_commsginia_manifest.return_value = {"json": "data"} + mock_commsignia_manifest.return_value = {"json": "data"} async with AsyncClient(app=app, base_url="http://test") as ac: response = await ac.get( "/firmwares/commsignia", auth=BasicAuth("username", "password") From c7ec6ee5c2909100295ac3bff2cf01625f598408 Mon Sep 17 00:00:00 2001 From: Michael7371 <40476797+Michael7371@users.noreply.github.com> Date: Wed, 3 Jul 2024 10:21:42 -0600 Subject: [PATCH 101/189] updated host name reference in nginx-plain --- resources/ota/nginx/nginx-plain.conf | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/resources/ota/nginx/nginx-plain.conf b/resources/ota/nginx/nginx-plain.conf index d47854cc6..b8d9f0092 100644 --- a/resources/ota/nginx/nginx-plain.conf +++ b/resources/ota/nginx/nginx-plain.conf @@ -17,7 +17,7 @@ http { server { listen 80; - server_name http://$SERVER_HOST; + server_name $SERVER_HOST; client_max_body_size 300M; From 7cf775e95dceedabc09fa391b3bd528cf0a300f5 Mon Sep 17 00:00:00 2001 From: Marc Wodahl Date: Mon, 8 Jul 2024 14:50:17 -0600 Subject: [PATCH 102/189] Add sample data script --- README.md | 2 + docker-compose-mongo.yml | 2 + mongo_scripts/insert_data.js | 212 +++++++++++++++++++++++++++++++++++ mongo_scripts/setup_mongo.sh | 7 +- sample.env | 5 +- 5 files changed, 226 insertions(+), 2 deletions(-) create mode 100644 mongo_scripts/insert_data.js diff --git a/README.md b/README.md index 49a4254d1..70064f228 100644 --- a/README.md +++ b/README.md @@ -222,6 +222,8 @@ For the "Debug Solution" to run properly on Windows 10/11 using WSL, the followi - MONGO_PORT: Port number of MongoDB (default is 27017) - MONGO_COLLECTION_TTL: Number of days documents will be kept in a MongoDB collection +- INSERT_SAMPLE_DATA: If true, sample data will be inserted in the CVCounts, V2XGeoJson, and OdeSsmJson collections + Keycloak Variables - KEYCLOAK_DOMAIN: Domain name that Keycloak will be served on. diff --git a/docker-compose-mongo.yml b/docker-compose-mongo.yml index 7a3b255e6..360c7cf93 100644 --- a/docker-compose-mongo.yml +++ b/docker-compose-mongo.yml @@ -44,10 +44,12 @@ services: MONGO_CV_MANAGER_DB_USER: ${MONGO_CV_MANAGER_DB_USER} MONGO_CV_MANAGER_DB_PASS: ${MONGO_CV_MANAGER_DB_PASS} MONGO_COLLECTION_TTL: ${MONGO_COLLECTION_TTL} + INSERT_SAMPLE_DATA: ${INSERT_SAMPLE_DATA} entrypoint: ['/bin/bash', 'setup_mongo.sh'] volumes: - ./mongo_scripts/setup_mongo.sh:/setup_mongo.sh - ./mongo_scripts/create_indexes.js:/create_indexes.js + - ./mongo_scripts/insert_data.js:/insert_data.js volumes: mongo_data: diff --git a/mongo_scripts/insert_data.js b/mongo_scripts/insert_data.js new file mode 100644 index 000000000..ec4ab10af --- /dev/null +++ b/mongo_scripts/insert_data.js @@ -0,0 +1,212 @@ +const cv_manager_user = process.env.MONGO_ADMIN_DB_USER +const cv_manager_pass = process.env.MONGO_ADMIN_DB_PASS + +db = connect('mongodb://' + cv_manager_user + ':' + cv_manager_pass + '@mongo:27017/admin') +if (db === null) { + print('Error connecting to the MongoDB instance') +} else { + print('Successfully connected to the MongDB instance') +} + +var currentDate = new Date() +var currentDateIso = currentDate.toISOString().slice(0, -5) + +var datePreviousHour = new Date() +datePreviousHour.setHours(currentDate.getHours() - 1) +var datePreviousHourIso = datePreviousHour.toISOString().slice(0, -5) + +ConflictMonitor = db.getSiblingDB('ConflictMonitor') + +// Insert CVCounts documents +ConflictMonitor.CVCounts.insertMany([ + { + messageType: 'Map', + rsuIp: '10.0.0.180', + timestamp: new Date(currentDateIso.slice(0, -5) + '00:00Z'), + count: 153, + }, + { + messageType: 'Map', + rsuIp: '10.0.0.78', + timestamp: new Date(currentDateIso.slice(0, -5) + '00:00Z'), + count: 12, + }, + { + messageType: 'SPaT', + rsuIp: '10.0.0.180', + timestamp: new Date(currentDateIso.slice(0, -5) + '00:00Z'), + count: 24, + }, + { + messageType: 'SPaT', + rsuIp: '10.0.0.78', + timestamp: new Date(currentDateIso.slice(0, -5) + '00:00Z'), + count: 15, + }, + { + messageType: 'SPaT', + rsuIp: '10.0.0.180', + timestamp: new Date(datePreviousHourIso.slice(0, -5) + '00:00Z'), + count: 17, + }, + { + messageType: 'SPaT', + rsuIp: '10.0.0.78', + timestamp: new Date(datePreviousHourIso.slice(0, -5) + '00:00Z'), + count: 12, + }, + { + messageType: 'Map', + rsuIp: '10.0.0.180', + timestamp: new Date(datePreviousHourIso.slice(0, -5) + '00:00Z'), + count: 145, + }, + { + messageType: 'Map', + rsuIp: '10.0.0.78', + timestamp: new Date(datePreviousHourIso.slice(0, -5) + '00:00Z'), + count: 10, + }, +]) + +// insert V2XGeoJson documents +ConflictMonitor.V2XGeoJson.insertMany([ + { + type: 'Feature', + geometry: { + type: 'Point', + coordinates: [-104.997663, 39.708309], + }, + properties: { + id: '10.0.0.180', + timestamp: new Date(currentDateIso.slice(0, -5) + '15:00Z'), + msg_type: 'Bsm', + }, + }, + { + type: 'Feature', + geometry: { + type: 'Point', + coordinates: [-105.014169, 39.740033], + }, + properties: { + id: '10.0.0.180', + timestamp: new Date(currentDateIso.slice(0, -5) + '00:00Z'), + msg_type: 'Bsm', + }, + }, + { + type: 'Feature', + geometry: { + type: 'Point', + coordinates: [-104.990622, 39.7718], + }, + properties: { + id: '10.0.0.180', + timestamp: new Date(datePreviousHourIso.slice(0, -5) + '45:00Z'), + msg_type: 'Bsm', + }, + }, + { + type: 'Feature', + geometry: { + type: 'Point', + coordinates: [-104.982867, 39.817387], + }, + properties: { + id: '10.0.0.78', + timestamp: new Date(datePreviousHourIso.slice(0, -5) + '30:00Z'), + msg_type: 'Bsm', + }, + }, + { + type: 'Feature', + geometry: { + type: 'Point', + coordinates: [-104.987132, 39.856097], + }, + properties: { + id: '10.0.0.78', + timestamp: new Date(datePreviousHourIso.slice(0, -5) + '15:00Z'), + msg_type: 'Bsm', + }, + }, + { + type: 'Feature', + geometry: { + type: 'Point', + coordinates: [-104.98752, 39.882289], + }, + properties: { + id: '10.0.0.78', + timestamp: new Date(datePreviousHourIso.slice(0, -5) + '00:00Z'), + msg_type: 'Bsm', + }, + }, +]) + +// insert OdeSsmJson document +ConflictMonitor.OdeSsmJson.insertOne({ + metadata: { + logFileName: '', + recordType: 'ssmTx', + securityResultCode: 'success', + receivedMessageDetails: { rxSource: 'NA' }, + payloadType: 'us.dot.its.jpo.ode.model.OdeSsmPayload', + serialId: { + streamId: 'daccfc21-e356-4481-be37-5963a056bfc0', + bundleSize: 1, + bundleId: 0, + recordId: 0, + serialNumber: 0, + }, + odeReceivedAt: currentDate, + schemaVersion: 6, + maxDurationTime: 0, + recordGeneratedAt: '', + recordGeneratedBy: 'RSU', + sanitized: false, + odePacketID: '', + odeTimStartDateTime: '', + originIp: '10.0.0.78', + ssmSource: 'RSU', + }, + payload: { + data: { + second: 0, + status: { + signalStatus: [ + { + sequenceNumber: 0, + id: { + id: 12110, + }, + sigStatus: { + signalStatusPackage: [ + { + requester: { + id: { + stationID: 2366845094, + }, + request: 3, + sequenceNumber: 0, + typeData: { + role: 'publicTransport', + }, + }, + inboundOn: { + lane: 2, + }, + status: 'granted', + }, + ], + }, + }, + ], + }, + }, + dataType: 'us.dot.its.jpo.ode.plugin.j2735.J2735SSM', + }, +}) + +print('Successfully inserted sample data') diff --git a/mongo_scripts/setup_mongo.sh b/mongo_scripts/setup_mongo.sh index d8ae35a0a..85bc67caa 100644 --- a/mongo_scripts/setup_mongo.sh +++ b/mongo_scripts/setup_mongo.sh @@ -8,4 +8,9 @@ echo "MongoDB is up and running!" cd / -mongosh -u $MONGO_ADMIN_DB_USER -p $MONGO_ADMIN_DB_PASS --authenticationDatabase admin --host mongo:27017 /create_indexes.js \ No newline at end of file +mongosh -u $MONGO_ADMIN_DB_USER -p $MONGO_ADMIN_DB_PASS --authenticationDatabase admin --host mongo:27017 /create_indexes.js + +if [ "$INSERT_SAMPLE_DATA" = "true" ]; then + echo "Inserting sample data" + mongosh -u $MONGO_ADMIN_DB_USER -p $MONGO_ADMIN_DB_PASS --authenticationDatabase admin --host mongo:27017 /insert_data.js +fi \ No newline at end of file diff --git a/sample.env b/sample.env index da960e05a..545bf2ac1 100644 --- a/sample.env +++ b/sample.env @@ -106,4 +106,7 @@ MONGO_DB_USER=root MONGO_DB_PASS=password MONGO_PORT=27017 MONGO_DB_URI=mongodb://${MONGO_DB_USER}:${MONGO_DB_PASS}@${MONGO_IP}:${MONGO_PORT}/ -MONGO_COLLECTION_TTL=7 # days \ No newline at end of file +MONGO_COLLECTION_TTL=7 # days + +# If true sample data will be inserted into the MongoDB CVCounts, V2XGeoJson, and OdeSsmJson collections. +INSERT_SAMPLE_DATA=true \ No newline at end of file From c6ab1b72bcba96852f209b42edce43bf2d66e460 Mon Sep 17 00:00:00 2001 From: Marc Wodahl Date: Tue, 16 Jul 2024 14:25:03 -0600 Subject: [PATCH 103/189] README, sample.env updates and move mongo_scripts to resources --- README.md | 10 +++++ .../mongo_scripts}/create_indexes.js | 0 .../mongo_scripts}/insert_data.js | 0 .../mongo_scripts}/setup_mongo.sh | 0 sample.env | 38 ++++++++++--------- 5 files changed, 30 insertions(+), 18 deletions(-) rename {mongo_scripts => resources/mongo_scripts}/create_indexes.js (100%) rename {mongo_scripts => resources/mongo_scripts}/insert_data.js (100%) rename {mongo_scripts => resources/mongo_scripts}/setup_mongo.sh (100%) diff --git a/README.md b/README.md index 4da306dbe..6b68eb220 100644 --- a/README.md +++ b/README.md @@ -210,6 +210,16 @@ For the "Debug Solution" to run properly on Windows 10/11 using WSL, the followi MongoDB Variables +#### For Windows Users Only + +If running on Windows, please make sure that your global git config is set up to not convert end-of-line characters during checkout. + +Disable `git core.autocrlf` (One Time Only) + +```bash +git config --global core.autocrlf false +``` + - MONGO_DB_URI: URI for the MongoDB connections. - MONGO_DB_NAME: Database name for RSU counts. - MONGO_ADMIN_DB_USER: Admin Username for MongoDB diff --git a/mongo_scripts/create_indexes.js b/resources/mongo_scripts/create_indexes.js similarity index 100% rename from mongo_scripts/create_indexes.js rename to resources/mongo_scripts/create_indexes.js diff --git a/mongo_scripts/insert_data.js b/resources/mongo_scripts/insert_data.js similarity index 100% rename from mongo_scripts/insert_data.js rename to resources/mongo_scripts/insert_data.js diff --git a/mongo_scripts/setup_mongo.sh b/resources/mongo_scripts/setup_mongo.sh similarity index 100% rename from mongo_scripts/setup_mongo.sh rename to resources/mongo_scripts/setup_mongo.sh diff --git a/sample.env b/sample.env index fca86e53e..3d620dae4 100644 --- a/sample.env +++ b/sample.env @@ -53,6 +53,25 @@ GOOGLE_APPLICATION_CREDENTIALS='./resources/google/sample_gcp_service_account.js MONGO_DB_URI= MONGO_DB_NAME="ode" +# Necessary Variables if running MongoDB locally with docker-compose-mongo.yml +MONGO_ADMIN_DB_USER=root +MONGO_ADMIN_DB_PASS=password +MONGO_CV_MANAGER_DB_USER=root +MONGO_CV_MANAGER_DB_PASS=password + +MONGO_IP=${DOCKER_HOST_IP} +MONGO_DB_USER=root +# This is the default password for the MongoDB container - MUST BE CHANGED FOR DEPLOYMENTS +MONGO_DB_PASS=password +MONGO_PORT=27017 + +# If running MongoDB locally with docker-compose-mongo.yml uncomment the following line +# MONGO_DB_URI=mongodb://${MONGO_DB_USER}:${MONGO_DB_PASS}@${MONGO_IP}:${MONGO_PORT}/ +MONGO_COLLECTION_TTL=7 # days + +# If true sample data will be inserted into the MongoDB CVCounts, V2XGeoJson, and OdeSsmJson collections. +INSERT_SAMPLE_DATA=true + # Set these variables if using either "MONGODB" or "BIGQUERY" # COUNTS_MSG_TYPES: Comma seperated list of message types. # COUNTS_MSG_TYPES must be set for the counts menu to correctly populate when building an image for deployment @@ -209,21 +228,4 @@ RSU_STATUS_LOGGING_LEVEL="INFO" COUNTS_LOGGING_LEVEL="INFO" OBU_OTA_LOGGING_LEVEL="INFO" # Supported log levels are "ALL", "DEBUG", "ERROR", "FATAL", "INFO", "OFF", "TRACE" and "WARN" -KC_LOGGING_LEVEL="INFO" - -# Necessary Variables if using docker-compose-mongo.yml -MONGO_ADMIN_DB_USER=root -MONGO_ADMIN_DB_PASS=password -MONGO_CV_MANAGER_DB_USER=root -MONGO_CV_MANAGER_DB_PASS=password - -MONGO_IP=${DOCKER_HOST_IP} -MONGO_DB_USER=root -# This is the default password for the MongoDB container - MUST BE CHANGED FOR DEPLOYMENTS -MONGO_DB_PASS=password -MONGO_PORT=27017 -MONGO_DB_URI=mongodb://${MONGO_DB_USER}:${MONGO_DB_PASS}@${MONGO_IP}:${MONGO_PORT}/ -MONGO_COLLECTION_TTL=7 # days - -# If true sample data will be inserted into the MongoDB CVCounts, V2XGeoJson, and OdeSsmJson collections. -INSERT_SAMPLE_DATA=true \ No newline at end of file +KC_LOGGING_LEVEL="INFO" \ No newline at end of file From fbda0a350f5e4750ef9c809f5c1f2afc80ed69d7 Mon Sep 17 00:00:00 2001 From: Marc Wodahl Date: Tue, 16 Jul 2024 15:26:11 -0600 Subject: [PATCH 104/189] Update map on org change --- webapp/src/pages/Map.tsx | 2 ++ 1 file changed, 2 insertions(+) diff --git a/webapp/src/pages/Map.tsx b/webapp/src/pages/Map.tsx index 7f52908f0..9efa325d6 100644 --- a/webapp/src/pages/Map.tsx +++ b/webapp/src/pages/Map.tsx @@ -35,6 +35,7 @@ import { // actions selectRsu, + getRsuData, toggleMapDisplay, getIssScmsStatus, getMapData, @@ -212,6 +213,7 @@ function MapPage(props: MapPageProps) { // useEffects for RSU layer useEffect(() => { + dispatch(getRsuData()) dispatch(selectRsu(null)) dispatch(clearFirmware()) }, [organization, dispatch]) From 5b861b3af5bdfb3f4111b0d85c5e082ab7cc0746 Mon Sep 17 00:00:00 2001 From: Drew Johnston <31270488+drewjj@users.noreply.github.com> Date: Wed, 17 Jul 2024 17:48:17 -0600 Subject: [PATCH 105/189] Reduce ping requirements for a successful ping --- docker-compose.yml | 2 -- services/addons/images/rsu_status_check/rsu_pinger.py | 2 +- 2 files changed, 1 insertion(+), 3 deletions(-) diff --git a/docker-compose.yml b/docker-compose.yml index c2a5117e2..b68095da7 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -50,8 +50,6 @@ services: TIMEZONE: ${TIMEZONE} LOGGING_LEVEL: ${API_LOGGING_LEVEL} - volumes: - - ${GOOGLE_APPLICATION_CREDENTIALS}:/google/gcp_credentials.json logging: options: max-size: '10m' diff --git a/services/addons/images/rsu_status_check/rsu_pinger.py b/services/addons/images/rsu_status_check/rsu_pinger.py index 76c839cc2..4f06334ce 100644 --- a/services/addons/images/rsu_status_check/rsu_pinger.py +++ b/services/addons/images/rsu_status_check/rsu_pinger.py @@ -23,7 +23,7 @@ def ping_rsu_ips(rsu_list): for rsu in rsu_list: # id: rsu_id # key: process pinging the RSU's ipv4_address - p[rsu[0]] = Popen(["ping", "-n", "-w5", "-c3", rsu[1]], stdout=DEVNULL) + p[rsu[0]] = Popen(["ping", "-n", "-w20", "-c1", rsu[1]], stdout=DEVNULL) ping_data = {} while p: From 144d3ffd815ec2e873b7c9ee2e92a02acc4ffd35 Mon Sep 17 00:00:00 2001 From: Drew Johnston <31270488+drewjj@users.noreply.github.com> Date: Wed, 17 Jul 2024 23:02:06 -0600 Subject: [PATCH 106/189] Update unit tests to reflect the changes --- services/addons/tests/rsu_status_check/test_rsu_pinger.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/services/addons/tests/rsu_status_check/test_rsu_pinger.py b/services/addons/tests/rsu_status_check/test_rsu_pinger.py index 1066fb02e..b056dfbab 100644 --- a/services/addons/tests/rsu_status_check/test_rsu_pinger.py +++ b/services/addons/tests/rsu_status_check/test_rsu_pinger.py @@ -36,8 +36,8 @@ def test_ping_rsu_ips_online(mock_Popen): expected_result = {1: "1", 2: "1"} mock_Popen.assert_has_calls( [ - call(["ping", "-n", "-w5", "-c3", "1.1.1.1"], stdout=DEVNULL), - call(["ping", "-n", "-w5", "-c3", "2.2.2.2"], stdout=DEVNULL), + call(["ping", "-n", "-w20", "-c1", "1.1.1.1"], stdout=DEVNULL), + call(["ping", "-n", "-w20", "-c1", "2.2.2.2"], stdout=DEVNULL), ] ) assert mock_p.poll.call_count == len(rsu_list) # 2 @@ -59,8 +59,8 @@ def test_ping_rsu_ips_offline(mock_Popen): expected_result = {1: "0", 2: "0"} mock_Popen.assert_has_calls( [ - call(["ping", "-n", "-w5", "-c3", "1.1.1.1"], stdout=DEVNULL), - call(["ping", "-n", "-w5", "-c3", "2.2.2.2"], stdout=DEVNULL), + call(["ping", "-n", "-w20", "-c1", "1.1.1.1"], stdout=DEVNULL), + call(["ping", "-n", "-w20", "-c1", "2.2.2.2"], stdout=DEVNULL), ] ) assert mock_p.poll.call_count == len(rsu_list) # 2 From b917dae21f43d477600cd53a88123b8f40c7ea74 Mon Sep 17 00:00:00 2001 From: Drew Johnston <31270488+drewjj@users.noreply.github.com> Date: Wed, 17 Jul 2024 23:23:39 -0600 Subject: [PATCH 107/189] Change the tx flag to on for TIM message forwarding --- services/common/rsufwdsnmpset.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/services/common/rsufwdsnmpset.py b/services/common/rsufwdsnmpset.py index 4501ff208..7ee833731 100644 --- a/services/common/rsufwdsnmpset.py +++ b/services/common/rsufwdsnmpset.py @@ -493,7 +493,7 @@ def config_init( "47900", index, "8003", - raw=True + raw=True, ) else: return ( @@ -502,6 +502,7 @@ def config_init( ) elif snmp_version == "1218": # Based on message type, choose the right port + # rsu_ip, snmp_creds, dest_ip, udp_port, rsu_index, psid, tx if msg_type.lower() == "bsm": return config_txrxmsg( rsu_ip, snmp_creds, dest_ip, "46800", index, "20", False @@ -524,7 +525,7 @@ def config_init( ) if msg_type.lower() == "tim": return config_txrxmsg( - rsu_ip, snmp_creds, dest_ip, "47900", index, "8003", False + rsu_ip, snmp_creds, dest_ip, "47900", index, "8003", True ) else: return ( From d364202dbd9ba8f87992f9f0811be97247177129 Mon Sep 17 00:00:00 2001 From: Michael7371 <40476797+Michael7371@users.noreply.github.com> Date: Thu, 18 Jul 2024 10:07:16 -0600 Subject: [PATCH 108/189] update from working context --- .../sql_scripts/CVManager_CreateTables.sql | 13 +++++ .../images/obu_ota_server/obu_ota_server.py | 55 +++++++++++++------ 2 files changed, 50 insertions(+), 18 deletions(-) diff --git a/resources/sql_scripts/CVManager_CreateTables.sql b/resources/sql_scripts/CVManager_CreateTables.sql index da02cf1d4..0a2312486 100644 --- a/resources/sql_scripts/CVManager_CreateTables.sql +++ b/resources/sql_scripts/CVManager_CreateTables.sql @@ -401,4 +401,17 @@ CREATE TABLE IF NOT EXISTS public.snmp_msgfwd_config ON DELETE NO ACTION ); +-- Create obu_ota_requests table +CREATE TABLE public.obu_ota_requests ( + obu_sn int4 NOT NULL, + request_datetime timestamp NOT NULL, + origin_ip inet NOT NULL, + obu_firmware_version varchar(128) NOT NULL, + requested_firmware_version varchar(128) NOT NULL, + error_status bit(1) NOT NULL, + error_message varchar(128) NOT NULL, + manufacturer int4 NOT NULL, + CONSTRAINT fk_manufacturer FOREIGN KEY (manufacturer) REFERENCES public.manufacturers(manufacturer_id) +); + CREATE SCHEMA IF NOT EXISTS keycloak; \ No newline at end of file diff --git a/services/addons/images/obu_ota_server/obu_ota_server.py b/services/addons/images/obu_ota_server/obu_ota_server.py index 14d02bca1..94d32fdc1 100644 --- a/services/addons/images/obu_ota_server/obu_ota_server.py +++ b/services/addons/images/obu_ota_server/obu_ota_server.py @@ -1,19 +1,17 @@ from fastapi import FastAPI, Request, Response, HTTPException, Depends from fastapi.security import HTTPBasic, HTTPBasicCredentials -from fastapi.middleware.cors import CORSMiddleware -from common import gcs_utils +from common import gcs_utils, pgquery import commsignia_manifest import os import glob import aiofiles from starlette.responses import Response import logging +from datetime import datetime app = FastAPI() log_level = "INFO" if "LOGGING_LEVEL" not in os.environ else os.environ["LOGGING_LEVEL"] - logging.basicConfig(format="%(levelname)s:%(message)s", level=log_level) - security = HTTPBasic() @@ -113,25 +111,46 @@ async def read_file(file_path, start, end): raise HTTPException(status_code=500, detail="Error reading file") +async def log_request( + manufacturer: str, + request: Request, + firmware_id: str, + error_status: int, + error_message: str, +): + + current_dt = datetime.now().strftime("%Y-%m-%d %H:%M:%S") + query = f"INSERT INTO public.obu_ota_requests (obu_sn, manufacturer, request_datetime, origin_ip, error_status, error_message) VALUES\ + , B'{online_status}', {rsu_id}, '{current_dt}', '{ip}', {error_status}, '{error_message}')" + + # Run query + pgquery.write_db(query) + + @app.get( "/firmwares/commsignia/{firmware_id}", dependencies=[Depends(authenticate_user)] ) async def get_fw(request: Request, firmware_id: str): - file_path = f"/firmwares/{firmware_id}" + try: + file_path = f"/firmwares/{firmware_id}" - # Checks if firmware exists locally or downloads it from GCS - if not get_firmware(firmware_id, file_path): - raise HTTPException(status_code=404, detail="Firmware not found") + # Checks if firmware exists locally or downloads it from GCS + if not get_firmware(firmware_id, file_path): + raise HTTPException(status_code=404, detail="Firmware not found") - header_start, header_end = parse_range_header(request.headers.get("Range")) - data, file_size, end = await read_file(file_path, header_start, header_end) + header_start, header_end = parse_range_header(request.headers.get("Range")) + data, file_size, end = await read_file(file_path, header_start, header_end) - headers = { - "Content-Range": f"bytes {header_start}-{end-1}/{file_size}", - "Content-Length": str(end - header_start), - "Accept-Ranges": "bytes", - } + headers = { + "Content-Range": f"bytes {header_start}-{end-1}/{file_size}", + "Content-Length": str(end - header_start), + "Accept-Ranges": "bytes", + } - return Response( - content=data, media_type="application/octet-stream", headers=headers - ) + await log_request("commsignia", request, firmware_id, 0, "") + return Response( + content=data, media_type="application/octet-stream", headers=headers + ) + except HTTPException as e: + await log_request("commsignia", request, firmware_id, 1, e.detail) + raise From 1a92c01d699276e560077f428e46d7fed5583922 Mon Sep 17 00:00:00 2001 From: Marc Wodahl Date: Fri, 19 Jul 2024 14:55:57 -0600 Subject: [PATCH 109/189] Fix snmpset toast notification bug --- webapp/src/components/SnmpsetMenu.tsx | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/webapp/src/components/SnmpsetMenu.tsx b/webapp/src/components/SnmpsetMenu.tsx index 486a5c118..abf406425 100644 --- a/webapp/src/components/SnmpsetMenu.tsx +++ b/webapp/src/components/SnmpsetMenu.tsx @@ -59,11 +59,11 @@ const SnmpsetMenu = (props: SnmpsetMenu) => { @@ -170,7 +170,7 @@ exports[`snapshot organization 1`] = ` viewBox="0 0 24 24" > @@ -417,7 +417,7 @@ exports[`snapshot rsu 1`] = ` viewBox="0 0 24 24" >
@@ -495,7 +495,7 @@ exports[`snapshot rsu 1`] = ` style="width: 48px;" > @@ -592,7 +592,7 @@ exports[`snapshot rsu 1`] = ` viewBox="0 0 24 24" > @@ -634,7 +634,7 @@ exports[`snapshot rsu 1`] = ` viewBox="0 0 24 24" > @@ -676,7 +676,7 @@ exports[`snapshot rsu 1`] = ` viewBox="0 0 24 24" > @@ -718,7 +718,7 @@ exports[`snapshot rsu 1`] = ` viewBox="0 0 24 24" > @@ -788,20 +788,21 @@ exports[`snapshot rsu 1`] = ` />

Rows per page: