CEC-2211 CAN signals view (#176)

* CEC-2211 CAN signals view

* Correct stg link
This commit is contained in:
John Wu
2022-08-05 10:58:06 -07:00
committed by GitHub
parent 260a8033bb
commit 34271bc658
11 changed files with 225 additions and 4 deletions

View File

@@ -2,4 +2,4 @@ REACT_APP_CERT_SERVICE_URL=https://dev-gw.cloud.fiskerinc.com/certificate
REACT_APP_AUTH_SERVICE_URL=https://dev-gw.cloud.fiskerinc.com/compute_auth REACT_APP_AUTH_SERVICE_URL=https://dev-gw.cloud.fiskerinc.com/compute_auth
REACT_APP_UPLOAD_SERVICE_URL=https://dev-gw.cloud.fiskerinc.com/ota_update REACT_APP_UPLOAD_SERVICE_URL=https://dev-gw.cloud.fiskerinc.com/ota_update
REACT_APP_AUTH_CALLBACK_URL=https://dev-ota-admin.cloud.fiskerinc.com REACT_APP_AUTH_CALLBACK_URL=https://dev-ota-admin.cloud.fiskerinc.com
REACT_APP_SUPERSET_URL=http://superset-dev.fiskercloud.internal/superset/dashboard/8/?native_filters_key=KPnPthpLQ8rT--6PUdsPzQAcwnleRGHk_3dg0PVYfrXc3SE6zZ2x0p7JuerAZ0Pg REACT_APP_SUPERSET_URL=https://superset-dev.cloud.fiskerinc.com/superset/dashboard/8/?native_filters_key=KPnPthpLQ8rT--6PUdsPzQAcwnleRGHk_3dg0PVYfrXc3SE6zZ2x0p7JuerAZ0Pg

View File

@@ -2,4 +2,4 @@ REACT_APP_AUTH_SERVICE_URL=http://localhost/compute_auth
REACT_APP_CERT_SERVICE_URL=http://localhost/certificate REACT_APP_CERT_SERVICE_URL=http://localhost/certificate
REACT_APP_UPLOAD_SERVICE_URL=http://localhost/ota_update REACT_APP_UPLOAD_SERVICE_URL=http://localhost/ota_update
REACT_APP_AUTH_CALLBACK_URL=http://localhost:3000 REACT_APP_AUTH_CALLBACK_URL=http://localhost:3000
REACT_APP_SUPERSET_URL=http://superset-dev.fiskercloud.internal/superset/dashboard/8/?native_filters_key=KPnPthpLQ8rT--6PUdsPzQAcwnleRGHk_3dg0PVYfrXc3SE6zZ2x0p7JuerAZ0Pg REACT_APP_SUPERSET_URL=https://superset-dev.cloud.fiskerinc.com/superset/dashboard/8/?native_filters_key=KPnPthpLQ8rT--6PUdsPzQAcwnleRGHk_3dg0PVYfrXc3SE6zZ2x0p7JuerAZ0Pg

View File

@@ -2,4 +2,4 @@ REACT_APP_AUTH_SERVICE_URL=https://gw.cloud.fiskerinc.com/compute_auth
REACT_APP_CERT_SERVICE_URL=https://gw.cloud.fiskerinc.com/certificate REACT_APP_CERT_SERVICE_URL=https://gw.cloud.fiskerinc.com/certificate
REACT_APP_UPLOAD_SERVICE_URL=https://gw.cloud.fiskerinc.com/ota_update REACT_APP_UPLOAD_SERVICE_URL=https://gw.cloud.fiskerinc.com/ota_update
REACT_APP_AUTH_CALLBACK_URL=https://ota-admin.cloud.fiskerinc.com REACT_APP_AUTH_CALLBACK_URL=https://ota-admin.cloud.fiskerinc.com
REACT_APP_SUPERSET_URL=https://superset.cloud.fiskerinc.com REACT_APP_SUPERSET_URL=https://superset.cloud.fiskerinc.com/superset/dashboard/9/?native_filters_key=mfJ1VjGTcLUKz7gQs_DgClZhjcdNucYMrPruNibcyDnhkDwdHbAumBRVTpA5tFH_

View File

@@ -2,4 +2,4 @@ REACT_APP_AUTH_SERVICE_URL=https://stg-gw.cloud.fiskerinc.com/compute_auth
REACT_APP_CERT_SERVICE_URL=https://stg-gw.cloud.fiskerinc.com/certificate REACT_APP_CERT_SERVICE_URL=https://stg-gw.cloud.fiskerinc.com/certificate
REACT_APP_UPLOAD_SERVICE_URL=https://stg-gw.cloud.fiskerinc.com/ota_update REACT_APP_UPLOAD_SERVICE_URL=https://stg-gw.cloud.fiskerinc.com/ota_update
REACT_APP_AUTH_CALLBACK_URL=https://stg-ota-admin.cloud.fiskerinc.com REACT_APP_AUTH_CALLBACK_URL=https://stg-ota-admin.cloud.fiskerinc.com
REACT_APP_SUPERSET_URL=http://superset-dev.fiskercloud.internal/superset/dashboard/8/?native_filters_key=KPnPthpLQ8rT--6PUdsPzQAcwnleRGHk_3dg0PVYfrXc3SE6zZ2x0p7JuerAZ0Pg REACT_APP_SUPERSET_URL=https://stg-superset.cloud.fiskerinc.com/superset/dashboard/6/?native_filters_key=XBwRgJIvmxhqBhqlz45kuTnXc1iUY_M_ovzXCzXy5_l-AOFAXEaGLWpYIsfrEHGR

View File

@@ -8298,6 +8298,24 @@ exports[`App Route /vehicle-status authenticated 1`] = `
class="MuiTouchRipple-root" class="MuiTouchRipple-root"
/> />
</button> </button>
<button
aria-controls="tabpanel-4"
aria-selected="false"
class="MuiButtonBase-root MuiTab-root MuiTab-textColorInherit"
id="tab-4"
role="tab"
tabindex="-1"
type="button"
>
<span
class="MuiTab-wrapper"
>
CAN Signals
</span>
<span
class="MuiTouchRipple-root"
/>
</button>
</div> </div>
<span <span
class="PrivateTabIndicator-root-1509 PrivateTabIndicator-colorSecondary-1511 MuiTabs-indicator" class="PrivateTabIndicator-root-1509 PrivateTabIndicator-colorSecondary-1511 MuiTabs-indicator"
@@ -8454,6 +8472,13 @@ exports[`App Route /vehicle-status authenticated 1`] = `
id="tabpanel-3" id="tabpanel-3"
role="tabpanel" role="tabpanel"
/> />
<div
aria-labelledby="tab-4"
class="makeStyles-fullWidth-0"
hidden=""
id="tabpanel-4"
role="tabpanel"
/>
</div> </div>
</main> </main>
</main> </main>

View File

@@ -0,0 +1,104 @@
import {
Table,
TableBody,
TableCell,
TableHead,
TableRow,
} from "@material-ui/core";
import React, { useEffect, useState } from "react";
import { logger } from "../../../services/monitoring";
import { LocalDateTimeString } from "../../../utils/dates";
import { useVehicleContext } from "../../Contexts/VehicleContext";
const BlankSignal = (msg) => ({
timestamp: "",
signal: msg,
value: "",
});
const transformSignals = (signals) =>
signals
.map((signal) => {
const { Timestamp, ...Settings } = signal;
const keys = Object.keys(Settings);
return keys.map((key) => ({
timestamp: LocalDateTimeString(Timestamp),
signal: key,
value: Settings[key],
}));
})
.flat();
const CANSignals = (props) => {
const { vin, token } = props;
const { getCANSignals } = useVehicleContext();
const [signals, setSignals] = useState([]);
const delay = 500;
let timer = 0;
const stopTimer = async () => {
if (timer === 0) return;
clearTimeout(timer);
timer = 0;
};
const startTimer = () => {
stopTimer();
timer = setTimeout(() => {
updateSignals();
}, delay);
};
const updateSignals = async () => {
try {
const result = await getCANSignals(vin, token);
const items = transformSignals(result.data);
if (items.length > 0) {
setSignals(items);
} else {
setSignals(BlankSignal("No signals"));
}
if (delay > 0) startTimer();
} catch (e) {
setSignals(BlankSignal(e.message));
logger.warn(e.stack);
}
};
useEffect(() => {
startTimer();
return () => {
stopTimer();
};
// eslint-disable-next-line react-hooks/exhaustive-deps
}, []);
if (!signals || signals.length === 0) return <h3>Loading...</h3>;
return (
<Table>
<TableHead>
<TableRow>
<TableCell>Timestamp</TableCell>
<TableCell>Signal</TableCell>
<TableCell>Value</TableCell>
</TableRow>
</TableHead>
<TableBody>
{signals.map((signal, i) => (
<TableRow key={i}>
<TableCell>{signal.timestamp}</TableCell>
<TableCell>{signal.signal}</TableCell>
<TableCell>{signal.value}</TableCell>
</TableRow>
))}
</TableBody>
</Table>
);
};
export default CANSignals;

View File

@@ -0,0 +1,35 @@
import React from "react";
import clsx from "clsx";
import { Typography } from "@material-ui/core";
import useStyles from "../../useStyles";
import { useUserContext } from "../../Contexts/UserContext";
import CANSignals from "../CANSignals";
import { VehicleProvider } from "../../Contexts/VehicleContext";
const Main = (props) => {
const {
token: {
idToken: { jwtToken: token },
},
} = useUserContext();
const classes = useStyles();
const { vin } = props;
return (
<div className={clsx(classes.paper, classes.tableSize)}>
<Typography variant="h6" style={{ paddingBottom: "10px" }}>
CAN Signals
</Typography>
<CANSignals vin={vin} token={token} />
</div>
);
};
const CANSignalsTab = (props) => (
<VehicleProvider>
<Main {...props} />
</VehicleProvider>
);
export default CANSignalsTab;

View File

@@ -101,6 +101,24 @@ exports[`CarStatus Render 1`] = `
class="MuiTouchRipple-root" class="MuiTouchRipple-root"
/> />
</button> </button>
<button
aria-controls="tabpanel-4"
aria-selected="false"
class="MuiButtonBase-root MuiTab-root MuiTab-textColorInherit"
id="tab-4"
role="tab"
tabindex="-1"
type="button"
>
<span
class="MuiTab-wrapper"
>
CAN Signals
</span>
<span
class="MuiTouchRipple-root"
/>
</button>
</div> </div>
<span <span
class="PrivateTabIndicator-root-64 PrivateTabIndicator-colorSecondary-66 MuiTabs-indicator" class="PrivateTabIndicator-root-64 PrivateTabIndicator-colorSecondary-66 MuiTabs-indicator"
@@ -203,6 +221,13 @@ exports[`CarStatus Render 1`] = `
id="tabpanel-3" id="tabpanel-3"
role="tabpanel" role="tabpanel"
/> />
<div
aria-labelledby="tab-4"
class="makeStyles-fullWidth-0"
hidden=""
id="tabpanel-4"
role="tabpanel"
/>
</div> </div>
</div> </div>
</div> </div>

View File

@@ -11,6 +11,7 @@ import DigitalTwinTab from "./DigitalTwinTab";
import TabPanel from "../../Controls/TabPanel"; import TabPanel from "../../Controls/TabPanel";
import { useStatusContext } from "../../Contexts/StatusContext"; import { useStatusContext } from "../../Contexts/StatusContext";
import useStyles from "../../useStyles"; import useStyles from "../../useStyles";
import CANSignalsTab from "./CANSignalsTab";
const tabHashes = ["details", "updates", "filters"]; const tabHashes = ["details", "updates", "filters"];
@@ -62,6 +63,7 @@ const CarStatus = () => {
<Tab label="Car Updates" {...tabProps(1)} /> <Tab label="Car Updates" {...tabProps(1)} />
<Tab label="CAN Filters" {...tabProps(2)} /> <Tab label="CAN Filters" {...tabProps(2)} />
<Tab label="Digital Twin" {...tabProps(3)} /> <Tab label="Digital Twin" {...tabProps(3)} />
<Tab label="CAN Signals" {...tabProps(4)} />
</Tabs> </Tabs>
</Box> </Box>
@@ -80,6 +82,10 @@ const CarStatus = () => {
<TabPanel value={tabIndex} index={3}> <TabPanel value={tabIndex} index={3}>
<DigitalTwinTab vin={vin} /> <DigitalTwinTab vin={vin} />
</TabPanel> </TabPanel>
<TabPanel value={tabIndex} index={4} className={classes.fullWidth}>
<CANSignalsTab vin={vin} />
</TabPanel>
</div> </div>
); );
}; };

View File

@@ -205,6 +205,20 @@ export const VehicleProvider = ({ children }) => {
} }
}; };
const getCANSignals = async (vin, token) => {
try {
setBusy(true);
validateVIN(vin);
const result = await api.getCANSignals(vin, token);
if (result.error)
throw new Error(`Get CAN signals error. ${result.message}`);
return result;
} finally {
setBusy(false);
}
};
return ( return (
<VehicleContext.Provider <VehicleContext.Provider
value={{ value={{
@@ -217,6 +231,7 @@ export const VehicleProvider = ({ children }) => {
addVehicle, addVehicle,
deleteVehicle, deleteVehicle,
getConnections, getConnections,
getCANSignals,
getECUs, getECUs,
getLocations, getLocations,
getModels, getModels,

View File

@@ -152,6 +152,17 @@ const vehiclesAPI = {
}) })
.then(fetchRespHandler) .then(fetchRespHandler)
.catch(errorHandler), .catch(errorHandler),
getCANSignals: async (vin, token) =>
fetch(`${API_ENDPOINT}/cansignals/${vin}`, {
method: "GET",
headers: Object.assign(
{ "Content-Type": "application/json" },
getAuthHeaderOptions(token)
),
})
.then(fetchRespHandler)
.catch(errorHandler),
}; };
export default vehiclesAPI; export default vehiclesAPI;