Skip to content

Commit

Permalink
Embedding decoder map in page
Browse files Browse the repository at this point in the history
  • Loading branch information
jacob6838 committed May 29, 2024
1 parent be6c302 commit d2bbbec
Show file tree
Hide file tree
Showing 5 changed files with 124 additions and 69 deletions.
22 changes: 18 additions & 4 deletions gui/src/components/decoder/decoder-entry.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ import MapRoundedIcon from "@mui/icons-material/MapRounded";
import DeleteIcon from "@mui/icons-material/Delete";
import DownloadIcon from "@mui/icons-material/Download";
import CircularProgress from "@mui/material/CircularProgress";
import { getTimestamp } from "../map/map-component";

// type DecoderApiResponseGeneric = {
// type: DECODER_MESSAGE_TYPE;
Expand All @@ -37,8 +38,19 @@ type DecoderEntryProps = {
};

export const DecoderEntry = (props: DecoderDataEntry & DecoderEntryProps) => {
const { id, status, selected, text, type, isGreyedOut, decodedResponse, onSelected, onTextChanged, onDeleted } =
props;
const {
id,
status,
selected,
text,
type,
isGreyedOut,
decodedResponse,
timestamp,
onSelected,
onTextChanged,
onDeleted,
} = props;

const getIntersectionId = (decodedResponse: DecoderApiResponseGeneric | undefined) => {
if (!decodedResponse) {
Expand Down Expand Up @@ -108,7 +120,7 @@ export const DecoderEntry = (props: DecoderDataEntry & DecoderEntryProps) => {
return "yellow";
case "COMPLETED":
if (isGreyedOut) {
return "#566454";
return "#51634e";
} else {
return "#448b3b";
}
Expand All @@ -122,7 +134,9 @@ export const DecoderEntry = (props: DecoderDataEntry & DecoderEntryProps) => {
return (
<TableRow>
<TableCell style={{ backgroundColor: getCellColor() }}>
{type == "MAP" && <Checkbox checked={selected} onChange={handleCheckboxChange} />}
{type == "MAP" && text != "" && <Checkbox checked={selected} onChange={handleCheckboxChange} />}
{timestamp && <Typography variant="caption">{format(new Date(timestamp), "yyyy-MM-dd HH:mm:ss")}</Typography>}
<br></br>
<TextField value={text} placeholder="Paste data here" onChange={handleTextChange} sx={{ width: 160 }} />
<IconButton aria-label="delete" onClick={handleDeleteClick}>
<DeleteIcon />
Expand Down
7 changes: 5 additions & 2 deletions gui/src/components/decoder/decoder-tables.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -78,9 +78,9 @@ export const DecoderTables = (props: DecoderTableProps) => {
}
};

const downloadJsonFile = (contents: any, name: string) => {
const downloadJsonFile = (contents: any, name: string, alreadyStringified = false) => {
const element = document.createElement("a");
const file = new Blob([JSON.stringify(contents)], {
const file = new Blob([alreadyStringified ? contents : JSON.stringify(contents)], {
type: "text/plain",
});
element.href = URL.createObjectURL(file);
Expand Down Expand Up @@ -128,6 +128,7 @@ export const DecoderTables = (props: DecoderTableProps) => {
isGreyedOut={entry.id !== selectedMapMessageId}
decodedResponse={entry.decodedResponse}
selected={entry.id == selectedMapMessageId}
timestamp={entry.timestamp}
onSelected={onItemSelected}
onTextChanged={(id, text) => onTextChanged(id, text, "MAP")}
onDeleted={onItemDeleted}
Expand Down Expand Up @@ -173,6 +174,7 @@ export const DecoderTables = (props: DecoderTableProps) => {
isGreyedOut={isGreyedOut(getIntersectionId(entry.decodedResponse))}
decodedResponse={entry.decodedResponse}
selected={false}
timestamp={entry.timestamp}
onSelected={onItemSelected}
onTextChanged={(id, text) => onTextChanged(id, text, "SPAT")}
onDeleted={onItemDeleted}
Expand Down Expand Up @@ -218,6 +220,7 @@ export const DecoderTables = (props: DecoderTableProps) => {
isGreyedOut={isGreyedOutIp(getIntersectionId(entry.decodedResponse))}
decodedResponse={entry.decodedResponse}
selected={false}
timestamp={entry.timestamp}
onSelected={onItemSelected}
onTextChanged={(id, text) => onTextChanged(id, text, "BSM")}
onDeleted={onItemDeleted}
Expand Down
19 changes: 10 additions & 9 deletions gui/src/components/map/map-component.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -176,7 +176,7 @@ const markerLayer: LayerProps = {
},
};

const getTimestamp = (dt: any): number => {
export const getTimestamp = (dt: any): number => {
try {
const dtFromString = Date.parse(dt as any as string);
if (isNaN(dtFromString)) {
Expand Down Expand Up @@ -247,6 +247,7 @@ const generateQueryParams = (
case "exact":
let startDate = undefined as number | undefined;
let endDate = undefined as number | undefined;

for (const map of (source as { map: ProcessedMap[] }).map) {
if (!startDate || getTimestamp(map.properties.odeReceivedAt) < startDate) {
startDate = getTimestamp(map.properties.odeReceivedAt);
Expand All @@ -256,11 +257,11 @@ const generateQueryParams = (
}
}
for (const spat of (source as { spat: ProcessedSpat[] }).spat) {
if (!startDate || spat.utcTimeStamp < startDate) {
startDate = spat.utcTimeStamp;
if (!startDate || getTimestamp(spat.utcTimeStamp) < startDate) {
startDate = getTimestamp(spat.utcTimeStamp);
}
if (!endDate || spat.utcTimeStamp > endDate) {
endDate = spat.utcTimeStamp;
if (!endDate || getTimestamp(spat.utcTimeStamp) > endDate) {
endDate = getTimestamp(spat.utcTimeStamp);
}
}
for (const bsm of (source as { bsm: OdeBsmData[] }).bsm) {
Expand All @@ -274,7 +275,7 @@ const generateQueryParams = (
return {
startDate: new Date(startDate ?? Date.now()),
endDate: new Date(endDate ?? Date.now() + 1),
eventDate: new Date((startDate ?? Date.now(), endDate ?? Date.now() + 1) / 2),
eventDate: new Date((startDate ?? Date.now()) / 2 + (endDate ?? Date.now() + 1) / 2),
vehicleId: undefined,
};
default:
Expand Down Expand Up @@ -961,7 +962,7 @@ const MapTab = (props: MyProps) => {
setMapData(latestMapMessage);
setMapSpatTimes((prevValue) => ({
...prevValue,
mapTime: latestMapMessage.properties.odeReceivedAt as unknown as number,
mapTime: getTimestamp(latestMapMessage.properties.odeReceivedAt) / 1000,
}));
setMapSignalGroups(mapSignalGroupsLocal);
if (latestMapMessage != null) {
Expand All @@ -977,7 +978,7 @@ const MapTab = (props: MyProps) => {
setSpatSignalGroups(spatSignalGroupsLocal);

// ######################### BSMs #########################
if (!importedMessageData) {
if (!importedMessageData && props.sourceDataType != "exact") {
const rawBsmPromise = MessageMonitorApi.getBsmMessages({
token: session?.accessToken,
vehicleId: queryParams.vehicleId,
Expand Down Expand Up @@ -1020,7 +1021,7 @@ const MapTab = (props: MyProps) => {
setMapData(latestMapMessage);
setMapSpatTimes((prevValue) => ({
...prevValue,
mapTime: latestMapMessage.properties.odeReceivedAt as unknown as number,
mapTime: getTimestamp(latestMapMessage.properties.odeReceivedAt) / 1000,
}));
setMapSignalGroups(mapSignalGroupsLocal);
if (latestMapMessage != null) {
Expand Down
1 change: 1 addition & 0 deletions gui/src/models/decoder.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ type DecoderDataEntry = {
status: DECODER_PROGRESS_TYPE;
selected: boolean;
isGreyedOut: boolean;
timestamp?: number | undefined;
text: string;
decodedResponse: DecoderApiResponseGeneric | undefined;
};
144 changes: 90 additions & 54 deletions gui/src/pages/decoder.tsx
Original file line number Diff line number Diff line change
@@ -1,16 +1,13 @@
import { useEffect, useState } from "react";
import Head from "next/head";
import { Box, Button, Container } from "@mui/material";
import { Box, Button, Container, Grid, Typography } from "@mui/material";
import DecoderApi from "../apis/decoder-api";
import { DashboardLayout } from "../components/dashboard-layout";
import { useDashboardContext } from "../contexts/dashboard-context";
import { useSession } from "next-auth/react";
import MapDialog from "../components/intersection-selector/intersection-selector-dialog";
import { DecoderTables } from "../components/decoder/decoder-tables";
import { v4 as uuidv4 } from "uuid";
import { DecoderMapDialog } from "../components/decoder/decoder-map-dialog";
import { Plus as PlusIcon } from "../icons/plus";
import MapIcon from "@mui/icons-material/Map";
import MapTab, { getTimestamp } from "../components/map/map-component";

const DecoderPage = () => {
const { data: session } = useSession();
Expand Down Expand Up @@ -61,6 +58,7 @@ const DecoderPage = () => {
[id]: {
...prevData[id],
decodedResponse: response,
timestamp: getTimestampFromType(type, response),
status: text == "" ? "NOT_STARTED" : response == undefined ? "ERROR" : "COMPLETED",
},
};
Expand Down Expand Up @@ -112,32 +110,45 @@ const DecoderPage = () => {
}
};

const getTimestampFromType = (type: DECODER_MESSAGE_TYPE, decodedResponse: DecoderApiResponseGeneric | undefined) => {
switch (type) {
case "MAP":
return getTimestamp(decodedResponse?.processedMap?.properties.odeReceivedAt);
case "SPAT":
return getTimestamp(decodedResponse?.processedSpat?.utcTimeStamp);
case "BSM":
return getTimestamp(decodedResponse?.bsm?.metadata.odeReceivedAt);
}
};

const onFileUploaded = (contents: string[], type: DECODER_MESSAGE_TYPE) => {
setData((prevData) => {
const textToIds: { [text: string]: string } = {};
contents.forEach((text) =>
contents.forEach((text) => {
const id = uuidv4();
textToIds[text] = id;
submitDecoderRequest(text, type)?.then((response) => {
const id = uuidv4();
textToIds[text] = id;
setData((prevData) => {
return {
...prevData,
[id]: {
...prevData[text],
...prevData[id],
decodedResponse: response,
timestamp: getTimestampFromType(type, response),
status: text == "" ? "NOT_STARTED" : response == undefined ? "ERROR" : "COMPLETED",
},
};
});
})
);
});
});
let newEntries = {};
contents.forEach((text) => {
newEntries[textToIds[text]] = {
id: textToIds[text],
type: type,
status: "IN_PROGRESS",
text: text,
timestamp: undefined,
selected: false,
isGreyedOut: false,
decodedResponse: undefined,
Expand All @@ -150,6 +161,32 @@ const DecoderPage = () => {
});
};

const getIntersectionId = (decodedResponse: DecoderApiResponseGeneric | undefined) => {
if (!decodedResponse) {
return undefined;
}

switch (decodedResponse.type) {
case "MAP":
const mapPayload = decodedResponse.processedMap;
return mapPayload?.properties?.intersectionId;
case "SPAT":
const spatPayload = decodedResponse.processedSpat;
return spatPayload?.intersectionId;
case "BSM":
const bsmPayload = decodedResponse.bsm;
return bsmPayload?.metadata.originIp;
}
};

const isGreyedOut = (intersectionId: number | undefined) => {
return selectedMapMessage?.intersectionId === undefined || intersectionId !== selectedMapMessage?.intersectionId;
};

const isGreyedOutIp = (rsuIp: string | undefined) => {
return (selectedMapMessage?.rsuIp === undefined || rsuIp !== selectedMapMessage?.rsuIp) && rsuIp != "";
};

return (
<>
<Head>
Expand All @@ -163,41 +200,57 @@ const DecoderPage = () => {
py: 8,
}}
>
{/* <Container maxWidth={false}>
<Container maxWidth={false}>
<Box
sx={{
alignItems: "center",
display: "flex",
overflow: "hidden",
justifyContent: "space-between",
flexWrap: "wrap",
m: -1,
}}
>
<div>
<Typography noWrap variant="h4">
Query
</Typography>
</div>
<Grid container justifyContent="space-between" spacing={3}>
<Grid item>
<Typography sx={{ m: 1, mb: 2 }} variant="h4">
ASN.1 Decoder
</Typography>
</Grid>
</Grid>
</Box>
<Box mt={3}>
<DataSelectorEditForm
onQuery={query}
onVisualize={onVisualize}
roadRegulatorIntersectionIds={roadRegulatorIntersectionIds}
dbIntersectionId={intersectionId}
/>
</Box>
</Container> */}
<Container sx={{ mt: 5, alignItems: "center", display: "flex" }}>
<Button
color="primary"
variant="contained"
onClick={() => {
setOpenMapDialog(true);
<Box
sx={{
alignItems: "center",
display: "flex",
overflow: "hidden",
height: "50vh",
}}
startIcon={<MapIcon fontSize="medium" />}
sx={{ m: 1 }}
>
Display Selected Data On Map
</Button>
<MapTab
sourceData={{
map: Object.values(data)
.filter((v) => v.type === "MAP" && v.status == "COMPLETED" && v.id == selectedMapMessage?.id)
.map((v) => v.decodedResponse?.processedMap!),
spat: Object.values(data)
.filter(
(v) =>
v.type === "SPAT" && v.status == "COMPLETED" && !isGreyedOut(getIntersectionId(v.decodedResponse))
)
.map((v) => v.decodedResponse?.processedSpat!),
bsm: Object.values(data)
.filter(
(v) =>
v.type === "BSM" &&
v.status == "COMPLETED" &&
!isGreyedOutIp(getIntersectionId(v.decodedResponse))
)
.map((v) => v.decodedResponse?.bsm!),
}}
sourceDataType={"exact"}
intersectionId={-1}
roadRegulatorId={-1}
/>
</Box>
</Container>
<Container sx={{ mt: 5, alignItems: "center", display: "flex" }}>
<DecoderTables
Expand All @@ -212,23 +265,6 @@ const DecoderPage = () => {
/>
</Container>
</Box>
<DecoderMapDialog
open={openMapDialog}
onClose={() => {
setOpenMapDialog(false);
}}
intersectionId={-1}
roadRegulatorId={-1}
map={Object.values(data)
.filter((v) => v.type === "MAP" && v.status == "COMPLETED")
.map((v) => v.decodedResponse?.processedMap!)}
spat={Object.values(data)
.filter((v) => v.type === "SPAT" && v.status == "COMPLETED")
.map((v) => v.decodedResponse?.processedSpat!)}
bsm={Object.values(data)
.filter((v) => v.type === "BSM" && v.status == "COMPLETED")
.map((v) => v.decodedResponse?.bsm!)}
/>
</>
);
};
Expand Down

0 comments on commit d2bbbec

Please sign in to comment.