Merge pull request #480 from Fisker-Inc/CEC-5356

CEC-5356: use `after_utc` to get can signals
This commit is contained in:
Alexander Andrews
2023-11-08 09:24:33 -08:00
committed by GitHub
4 changed files with 104 additions and 35 deletions

View File

@@ -5,6 +5,11 @@ import {
TableCell, TableCell,
TableHead, TableHead,
TableRow, TableRow,
Box,
FormControl,
InputLabel,
Select,
MenuItem,
} from "@material-ui/core"; } from "@material-ui/core";
import { import {
@@ -13,7 +18,7 @@ import {
} from "../../Contexts/CANSignalsContext"; } from "../../Contexts/CANSignalsContext";
const Main = ({ vin }) => { const Main = ({ vin }) => {
const { signals, setVIN } = useCANSignalContext(); const { signals, setVIN, queryDate, delays, delayIndex, setDelayIndex } = useCANSignalContext();
useEffect(() => { useEffect(() => {
setVIN(vin); setVIN(vin);
@@ -26,24 +31,31 @@ const Main = ({ vin }) => {
if (!signals || signals.length === 0) return <h3>Loading...</h3>; if (!signals || signals.length === 0) return <h3>Loading...</h3>;
return ( return (
<Table> <>
<TableHead> <Box sx={{ display: "flex", alignItems: "baseline", gap: "8px" }}>
<TableRow> <span>Searching every</span>
<TableCell>Timestamp</TableCell> <DelayController delays={delays} delayIndex={delayIndex} setDelayIndex={setDelayIndex} />
<TableCell>Signal</TableCell> <span>seconds for signals sent after {queryDate}.</span>
<TableCell>Value</TableCell> </Box>
</TableRow> <Table>
</TableHead> <TableHead>
<TableBody> <TableRow>
{signals.map((signal, i) => ( <TableCell>Timestamp</TableCell>
<TableRow key={i}> <TableCell>Signal</TableCell>
<TableCell>{signal.timestamp}</TableCell> <TableCell>Value</TableCell>
<TableCell>{signal.signal}</TableCell>
<TableCell>{signal.value}</TableCell>
</TableRow> </TableRow>
))} </TableHead>
</TableBody> <TableBody>
</Table> {signals.map((signal, i) => (
<TableRow key={i}>
<TableCell>{signal.timestamp}</TableCell>
<TableCell>{signal.signal}</TableCell>
<TableCell>{signal.value}</TableCell>
</TableRow>
))}
</TableBody>
</Table>
</>
); );
}; };
@@ -53,4 +65,33 @@ const CANSignals = (props) => (
</CANSignalProvider> </CANSignalProvider>
); );
const DelayController = ({
delays,
delayIndex,
setDelayIndex
}) => {
const label = "Delay";
const handleChange = (event) => {
setDelayIndex(event.target.value);
}
return (
<FormControl variant="standard" sx={{ m: 1, minWidth: 120 }}>
<InputLabel id="can-signal-delay">{label}</InputLabel>
<Select
labelId="can-signal-delay"
onChange={handleChange}
value={delayIndex}
label={label}
>
{delays.map((delay, i) => {
return (
<MenuItem value={i} selected={i === delayIndex} key={delay}>{delay / 1000}</MenuItem>
);
})}
</Select>
</FormControl>
)
}
export default CANSignals; export default CANSignals;

View File

@@ -1,8 +1,8 @@
import React, {useContext, useState} from "react"; import React, { useContext, useState } from "react";
import api from "../../services/vehiclesAPI"; import api from "../../services/vehiclesAPI";
import { LocalDateTimeString } from "../../utils/dates"; import { LocalDateTimeString } from "../../utils/dates";
import {useInterval} from "usehooks-ts"; import { useInterval } from "usehooks-ts";
const CANSignalContext = React.createContext(); const CANSignalContext = React.createContext();
@@ -26,31 +26,43 @@ const transformSignals = (signals) =>
.flat(); .flat();
export const CANSignalProvider = ({ token, children }) => { export const CANSignalProvider = ({ token, children }) => {
// auto scale polling if not getting response
const delays = [500, 1500, 3000, 6000];
const [delayIndex, setDelayIndex] = useState(2);
const [vin, setVIN] = useState(null); const [vin, setVIN] = useState(null);
const [signals, setSignals] = useState([]); const [signals, setSignals] = useState([]);
const [delay, setDelay] = useState(500); const [utc, setUtc] = useState(undefined);
useInterval( useInterval(
() => { () => {
getCANSignals() getCANSignals()
}, vin?delay:null }, vin ? delays[delayIndex] : null
) );
const getCANSignals = async () => { const getCANSignals = async () => {
try { try {
if (!vin) return; if (!vin) return;
const result = await api.getCANSignals(vin, token); const result = await api.getCANSignals(vin, {
if (result.error) after_utc: utc,
}, token);
if (result.error) {
throw new Error(`Get CAN signals error. ${result.message}`); throw new Error(`Get CAN signals error. ${result.message}`);
}
const mostRecentTimestamp = result.data?.[0]?.timestamp;
if (mostRecentTimestamp) {
setUtc(new Date(mostRecentTimestamp).getTime() - 50); // apply slight offset to ensure last CAN Signals sent before sleep are returned.
} else {
setUtc(new Date().getTime());
}
const items = transformSignals(result.data); const items = transformSignals(result.data);
if (items.length > 0) { if (items.length > 0) {
setDelay(500);
setSignals(items); setSignals(items);
} else { } else {
setDelay(1000);
setSignals([BlankSignal("No signals")]); setSignals([BlankSignal("No signals")]);
} }
} catch (e) { } catch (e) {
@@ -61,8 +73,12 @@ export const CANSignalProvider = ({ token, children }) => {
return ( return (
<CANSignalContext.Provider <CANSignalContext.Provider
value={{ value={{
queryDate: new Date(utc).toLocaleTimeString(),
signals, signals,
setVIN, setVIN,
delays,
delayIndex,
setDelayIndex,
}} }}
> >
{children} {children}

View File

@@ -8,7 +8,7 @@ import {
waitFor, waitFor,
act, act,
} from "@testing-library/react"; } from "@testing-library/react";
import {CANSignalProvider, useCANSignalContext} from "./CANSignalsContext"; import { CANSignalProvider, useCANSignalContext } from "./CANSignalsContext";
const checkSignalsResults = (filters) => { const checkSignalsResults = (filters) => {
expect(screen.getByTestId("signals").innerHTML).toEqual(filters); expect(screen.getByTestId("signals").innerHTML).toEqual(filters);
@@ -19,14 +19,15 @@ describe("CANSignalsContext", () => {
beforeEach(() => { beforeEach(() => {
jest.useFakeTimers("setInterval"); jest.useFakeTimers("setInterval");
const TestComp = () => { const TestComp = () => {
const { signals, setVIN } = useCANSignalContext(); const { signals, setVIN, setDelayIndex } = useCANSignalContext();
setDelayIndex(0);
return ( return (
<> <>
<div data-testid="signals">{JSON.stringify(signals)}</div> <div data-testid="signals">{JSON.stringify(signals)}</div>
<button <button
data-testid="getSignals" data-testid="getSignals"
onClick={()=>{setVIN("TESTVIN1234567890")}} onClick={() => { setVIN("TESTVIN1234567890") }}
/> />
</> </>
); );
@@ -54,12 +55,12 @@ describe("CANSignalsContext", () => {
await waitFor(() => await waitFor(() =>
expect(screen.getByTestId("signals").innerHTML).toBe("[]") expect(screen.getByTestId("signals").innerHTML).toBe("[]")
); );
jest.advanceTimersByTime(501); jest.advanceTimersByTime(3001);
}) })
await waitFor(() => { await waitFor(() => {
return expect(screen.getByTestId("signals").innerHTML).not.toBe("[]"); return expect(screen.getByTestId("signals").innerHTML).not.toBe("[]");
} ); });
checkSignalsResults(JSON.stringify(expectedSignalsData)); checkSignalsResults(JSON.stringify(expectedSignalsData));
}); });

View File

@@ -213,8 +213,18 @@ const vehiclesAPI = {
.then(fetchRespHandler) .then(fetchRespHandler)
.catch(errorHandler), .catch(errorHandler),
getCANSignals: async (vin, token) => /**
fetch(`${API_ENDPOINT}/cansignals/${vin}`, { *
* @param {String} vin The VIN of the car
* @param {Object} filter
* @param {Integer} filter.after_utc Point in time to begin signal response
* @param {Integer} filter.limit Max number of CAN Signals to return
* @param {String} token
* @returns {Array}
*/
getCANSignals: async (vin, filter = {}, token) => {
const url = addQueryParams(`${API_ENDPOINT}/cansignals/${vin}`, filter);
return fetch(url, {
method: "GET", method: "GET",
headers: Object.assign( headers: Object.assign(
{ "Content-Type": "application/json" }, { "Content-Type": "application/json" },
@@ -222,7 +232,8 @@ const vehiclesAPI = {
), ),
}) })
.then(fetchRespHandler) .then(fetchRespHandler)
.catch(errorHandler), .catch(errorHandler);
},
getTRexLogs: async (vin, date, offset, count, direction, token, controller) => getTRexLogs: async (vin, date, offset, count, direction, token, controller) =>
fetch(`${API_ENDPOINT}/vehicle/${vin}/trex-logs?date=${date}&offset=${offset}&count=${count}&direction=${direction}`, { fetch(`${API_ENDPOINT}/vehicle/${vin}/trex-logs?date=${date}&offset=${offset}&count=${count}&direction=${direction}`, {