From ab7a74b624904cbcd8e4d5522a842b7c010c62c3 Mon Sep 17 00:00:00 2001 From: jacob6838 Date: Tue, 24 Sep 2024 08:54:12 -0600 Subject: [PATCH 1/3] Fixing missing BSMs and removing prints --- gui/package-lock.json | 1 - gui/package.json | 1 - gui/src/components/map/map-component.tsx | 24 +++++++++++++++++----- gui/src/components/map/utilities/colors.ts | 17 --------------- 4 files changed, 19 insertions(+), 24 deletions(-) diff --git a/gui/package-lock.json b/gui/package-lock.json index fe92a8f..d443a9b 100644 --- a/gui/package-lock.json +++ b/gui/package-lock.json @@ -27,7 +27,6 @@ "@types/react-dom": "^18.0.10", "@zalter/identity-js": "1.2.1", "color": "^4.2.3", - "color-convert": "^2.0.1", "cookie": "^0.5.0", "date-fns": "2.29.3", "dayjs": "^1.11.7", diff --git a/gui/package.json b/gui/package.json index b6758b7..ce8f2e7 100644 --- a/gui/package.json +++ b/gui/package.json @@ -33,7 +33,6 @@ "@types/react-dom": "^18.0.10", "@zalter/identity-js": "1.2.1", "color": "^4.2.3", - "color-convert": "^2.0.1", "cookie": "^0.5.0", "date-fns": "2.29.3", "dayjs": "^1.11.7", diff --git a/gui/src/components/map/map-component.tsx b/gui/src/components/map/map-component.tsx index f14deb3..f302725 100644 --- a/gui/src/components/map/map-component.tsx +++ b/gui/src/components/map/map-component.tsx @@ -1077,7 +1077,7 @@ const MapTab = forwardRef( currentSpatData: ProcessedSpat[], currentBsmData: BsmFeatureCollection ) => { - if (props.sourceDataType == "exact") { + if (props.sourceDataType == "exact" && currentMapData.length == 0) { const uniqueIds = new Set(currentBsmData.features.map((bsm) => bsm.properties?.id).sort()); // generate equally spaced unique colors for each uniqueId const colors = generateColorDictionary(uniqueIds); @@ -1092,8 +1092,9 @@ const MapTab = forwardRef( paint: { ...prevValue.paint, "circle-color": bsmLayerStyle }, })); setBsmData(currentBsmData); + return; } - if (currentMapData.length == 0) { + else if (currentMapData.length == 0) { console.log("Did not attempt to render map, no map messages available:", currentMapData); return; } @@ -1120,6 +1121,21 @@ const MapTab = forwardRef( const spatSignalGroupsLocal = parseSpatSignalGroups(currentSpatData); setSpatSignalGroups(spatSignalGroupsLocal); + + const uniqueIds = new Set(currentBsmData.features.map((bsm) => bsm.properties?.id).sort()); + // generate equally spaced unique colors for each uniqueId + const colors = generateColorDictionary(uniqueIds); + setMapLegendColors((prevValue) => ({ + ...prevValue, + bsmColors: colors, + })); + // add color to each feature + const bsmLayerStyle = generateMapboxStyleExpression(colors); + setBsmLayerStyle((prevValue) => ({ + ...prevValue, + paint: { ...prevValue.paint, "circle-color": bsmLayerStyle }, + })); + setBsmData(currentBsmData); // ######################### Message Data ######################### rawData["map"] = currentMapData; @@ -1410,8 +1426,7 @@ const MapTab = forwardRef( if (props.timeFilterBsms !== false) { // retrieve filtered BSMs const filteredBsms: BsmFeature[] = bsmData?.features?.filter( - (feature) => - feature.properties?.odeReceivedAt >= renderTimeInterval[0] && + (feature) => feature.properties?.odeReceivedAt >= renderTimeInterval[0] && feature.properties?.odeReceivedAt <= renderTimeInterval[1] ); const sortedBsms = filteredBsms.sort((a, b) => b.properties.odeReceivedAt - a.properties.odeReceivedAt); @@ -1464,7 +1479,6 @@ const MapTab = forwardRef( const filteredStartTime = startTime + sliderValue / 10 - timeWindowSeconds; const filteredEndTime = startTime + sliderValue / 10; - console.log("Filtered Time Interval:", filteredStartTime, filteredEndTime, sliderValue, timeWindowSeconds); setRenderTimeInterval([filteredStartTime, filteredEndTime]); }, [sliderValue, queryParams, timeWindowSeconds]); diff --git a/gui/src/components/map/utilities/colors.ts b/gui/src/components/map/utilities/colors.ts index 056d3b0..80153b9 100644 --- a/gui/src/components/map/utilities/colors.ts +++ b/gui/src/components/map/utilities/colors.ts @@ -1,5 +1,4 @@ import { sha256 } from "js-sha256"; -import { hsl, rgb } from "color-convert"; import * as Color from "color"; const DISALLOWED_COLORS = new Set( @@ -18,7 +17,6 @@ const DISALLOWED_COLORS = new Set( ]) // 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; @@ -38,20 +36,16 @@ for (let i = 0; i < OUTPUT_COLOR_SPACE_LENGTH; i += 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; } @@ -75,21 +69,12 @@ export function generateColorDictionary(inputSet: Set): { [key: string]: // 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; } @@ -102,5 +87,3 @@ export function generateMapboxStyleExpression(colors: { [key: string]: string }) layerStyle.push("#000000"); // other return layerStyle; } - -// console.log(generateColorDictionary(new Set(["0139C942", "13906CFE", "37BDF36C", "5C6FA267"]))); From e71e15a00052f3204a274f2e63bc09fdbda10507 Mon Sep 17 00:00:00 2001 From: jacob6838 Date: Wed, 25 Sep 2024 10:40:51 -0600 Subject: [PATCH 2/3] Updating BSM timing to incorporate the secMark --- gui/src/components/map/map-component.tsx | 53 +++++++++++++++++++++--- gui/src/components/map/popup.tsx | 3 +- gui/src/components/map/side-panel.tsx | 2 +- gui/src/pages/decoder.tsx | 5 --- 4 files changed, 50 insertions(+), 13 deletions(-) diff --git a/gui/src/components/map/map-component.tsx b/gui/src/components/map/map-component.tsx index f302725..b0a68e0 100644 --- a/gui/src/components/map/map-component.tsx +++ b/gui/src/components/map/map-component.tsx @@ -175,6 +175,7 @@ const markerLayer: LayerProps = { }, }; +// TS in milliseconds export const getTimestamp = (dt: any): number => { try { const dtFromString = Date.parse(dt as any as string); @@ -193,6 +194,44 @@ export const getTimestamp = (dt: any): number => { } }; +// algorithm: +// 1. Convert odeReceivedAt to a unix timestamp, in milliseconds +// 2. Convert the secMarkMs to seconds +// 3. Pull out the second and minute from the odeReceivedAt timestamp +// case 1: odeReceivedAtSecondOfMinute < secMarkSecondOfMinute +// - decrement the minute by 1 +// - set the second to secMarkSecond +// # Logic: Assume clocks are relatively in sync, so if the secMark is in the future, it must be in the next minute +// case 2: Math.abs((odeReceivedAtSecondOfMinute - 60) - secMarkSecondOfMinute) < 30 +// - increment the minute by 1 +// - set the second to secMarkSecond +// # Logic: We know the odeReceivedAt is +export const getTimestampWithSecMark = (odeReceivedAt: any, secMarkMs: number): number => { + const dt: number = getTimestamp(odeReceivedAt); + const odeReceivedAtDate = new Date(dt); + const odeReceivedAtSecondOfMinute = odeReceivedAtDate.getUTCSeconds(); + const secMarkSecondOfMinute = Math.floor(secMarkMs / 1000); + + const diff = odeReceivedAtSecondOfMinute - secMarkSecondOfMinute; // delay, in seconds + + let adjustedDate = new Date(dt); + + if (diff < 0) { + adjustedDate.setUTCMinutes(adjustedDate.getUTCMinutes() - 1); + } + + adjustedDate.setUTCSeconds(secMarkSecondOfMinute); + return adjustedDate.getTime(); +}; +// test cases +// odeReceivedAtSecondOfMinute = 10, secMarkSecondOfMinute = 15 -> odeReceivedAt is 55 seconds ahead. Subtract 1 minute and set seconds to secMarkSecondOfMinute +// odeReceivedAtSecondOfMinute = 10, secMarkSecondOfMinute = 5 -> odeReceivedAt is 5 seconds behind. Set seconds to secMarkSecondOfMinute +// odeReceivedAtSecondOfMinute = 5, secMarkSecondOfMinute = 59 -> odeReceivedAt is 6 seconds behind. Set seconds to secMarkSecondOfMinute +// odeReceivedAtSecondOfMinute = 59, secMarkSecondOfMinute = 55 -> odeReceivedAt is 4 seconds ahead. Add 1 minute and set seconds to secMarkSecondOfMinute + +// const diff_normal = odeReceivedAtSecondOfMinute - secMarkSecondOfMinute // delay, in seconds +// const diff_ode = (odeReceivedAtSecondOfMinute + 60) - (secMarkSecondOfMinute) // delay, in seconds + const generateQueryParams = ( source: | MessageMonitor.Notification @@ -694,7 +733,8 @@ const MapTab = forwardRef( type: "Feature", properties: { ...bsm.payload.data.coreData, - odeReceivedAt: new Date(bsm.metadata.odeReceivedAt as string).getTime() / 1000, + odeReceivedAt: + getTimestampWithSecMark(bsm.metadata.odeReceivedAt, bsm.payload.data.coreData.secMark) / 1000, }, geometry: { type: "Point", @@ -885,7 +925,8 @@ const MapTab = forwardRef( ...bsm, metadata: { ...bsm.metadata, - odeReceivedAt: getTimestamp(bsm.metadata.odeReceivedAt), + odeReceivedAt: + getTimestampWithSecMark(bsm.metadata.odeReceivedAt, bsm.payload.data.coreData.secMark) / 1000, }, })); } else if (queryParams.default == true) { @@ -1093,8 +1134,7 @@ const MapTab = forwardRef( })); setBsmData(currentBsmData); return; - } - else if (currentMapData.length == 0) { + } else if (currentMapData.length == 0) { console.log("Did not attempt to render map, no map messages available:", currentMapData); return; } @@ -1121,7 +1161,7 @@ const MapTab = forwardRef( const spatSignalGroupsLocal = parseSpatSignalGroups(currentSpatData); setSpatSignalGroups(spatSignalGroupsLocal); - + const uniqueIds = new Set(currentBsmData.features.map((bsm) => bsm.properties?.id).sort()); // generate equally spaced unique colors for each uniqueId const colors = generateColorDictionary(uniqueIds); @@ -1426,7 +1466,8 @@ const MapTab = forwardRef( if (props.timeFilterBsms !== false) { // retrieve filtered BSMs const filteredBsms: BsmFeature[] = bsmData?.features?.filter( - (feature) => feature.properties?.odeReceivedAt >= renderTimeInterval[0] && + (feature) => + feature.properties?.odeReceivedAt >= renderTimeInterval[0] && feature.properties?.odeReceivedAt <= renderTimeInterval[1] ); const sortedBsms = filteredBsms.sort((a, b) => b.properties.odeReceivedAt - a.properties.odeReceivedAt); diff --git a/gui/src/components/map/popup.tsx b/gui/src/components/map/popup.tsx index 77b9c49..60fb9d0 100644 --- a/gui/src/components/map/popup.tsx +++ b/gui/src/components/map/popup.tsx @@ -5,6 +5,7 @@ import { Container, Col } from "reactstrap"; import { Paper, Box, Typography } from "@mui/material"; import { CustomTable } from "./custom-table"; +import { format } from "date-fns"; export const getSelectedLayerPopupContent = (feature: any) => { switch (feature?.layer?.id) { @@ -18,7 +19,7 @@ export const getSelectedLayerPopupContent = (feature: any) => { data={[ ["Id", bsm.id], ["Message Count", bsm.msgCnt], - ["Time", bsm.secMark / 1000], + ["Time", format(bsm.odeReceivedAt * 1000, "MM/dd/yyyy HH:mm:ss.SSS")], ["Speed", bsm.speed], ["Heading", bsm.heading], ]} diff --git a/gui/src/components/map/side-panel.tsx b/gui/src/components/map/side-panel.tsx index b8e07ef..11526a5 100644 --- a/gui/src/components/map/side-panel.tsx +++ b/gui/src/components/map/side-panel.tsx @@ -227,7 +227,7 @@ export const SidePanel = (props: SidePanelProps) => { headers={["Time", "Vehicle ID", "Speed", "Heading"]} data={ bsms?.features.map((bsm) => [ - bsm.properties.secMark / 1000, + format(bsm.properties.odeReceivedAt * 1000, "MM/dd/yyyy HH:mm:ss.SSS"), bsm.properties.id, bsm.properties.speed, bsm.properties.heading, diff --git a/gui/src/pages/decoder.tsx b/gui/src/pages/decoder.tsx index 09ad4df..41c98f6 100644 --- a/gui/src/pages/decoder.tsx +++ b/gui/src/pages/decoder.tsx @@ -22,8 +22,6 @@ const DecoderPage = () => { const [currentBsms, setCurrentBsms] = useState([] as OdeBsmData[]); - console.log("Data", data); - useEffect(() => { const freshData = [] as DecoderDataEntry[]; for (let i = 0; i < 3; i++) { @@ -64,7 +62,6 @@ const DecoderPage = () => { if (type == "BSM") { setSelectedBsms((prevBsms) => [...prevBsms, id]); } - console.log("Response", response); setData((prevData) => { return { ...prevData, @@ -227,7 +224,6 @@ const DecoderPage = () => { .filter((v) => v.type === "BSM" && v.status === "COMPLETED" && selectedBsms.includes(v.id)) .map((v) => v.decodedResponse?.bsm); setCurrentBsms(newBsmData.filter((v) => v !== undefined) as OdeBsmData[]); - console.log("Current BSMs", newBsmData.length); }, [data, selectedBsms]); return ( @@ -314,7 +310,6 @@ const DecoderPage = () => { selectedBsms={selectedBsms} setSelectedBsms={setSelectedBsms} centerMapOnLocation={(lat: number, long: number) => { - console.log("Centering map on location", lat, long, mapRef.current); if (mapRef.current) { mapRef.current?.centerMapOnPoint({ latitude: lat, longitude: long }); } From 8250a37e958893175b5d9ca866871e848c52f845 Mon Sep 17 00:00:00 2001 From: jacob6838 Date: Thu, 26 Sep 2024 10:14:00 -0600 Subject: [PATCH 3/3] Updating mapbox style url --- .../intersection-selector/intersection-selection-map.tsx | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/gui/src/components/intersection-selector/intersection-selection-map.tsx b/gui/src/components/intersection-selector/intersection-selection-map.tsx index a184a02..de6655d 100644 --- a/gui/src/components/intersection-selector/intersection-selection-map.tsx +++ b/gui/src/components/intersection-selector/intersection-selection-map.tsx @@ -3,6 +3,7 @@ import Map, { Layer, MapRef, Marker, Popup, Source, SymbolLayer } from "react-ma import { Container, Col } from "reactstrap"; import getConfig from "next/config"; +import mbStyle from "../../intersectionMapStyle.json"; const { publicRuntimeConfig } = getConfig(); @@ -146,7 +147,7 @@ const IntersectionMap = (props: Props) => { © CDOT']}