Merge branch 'develop' into release/0.0.3

This commit is contained in:
jwu-fisker
2022-09-28 20:12:23 -07:00
11 changed files with 231 additions and 104 deletions

View File

@@ -3329,7 +3329,6 @@ exports[`App Route /packages authenticated 1`] = `
/>
</svg>
</a>
<div />
</div>
</td>
</tr>
@@ -3460,6 +3459,7 @@ exports[`App Route /packages authenticated 1`] = `
</tr>
</tfoot>
</table>
<div />
</div>
</div>
</main>

View File

@@ -76,10 +76,11 @@ export const CarUpdatesProvider = ({ children }) => {
result = await api.getCarUpdates(search, token);
if (result.error)
throw new Error(`Get car updates error. ${result.message}`);
result.data.forEach((item) => {
item.progress = 0;
result.data.forEach((item, i) => {
item.msg = item.status;
applyProgressStatus(item, item);
item.progress = 0;
applyProgressStatus(item);
result.data[i] = Object.assign(result.data[i], item);
});
setCarUpdates(result.data);
if (search && search.offset === 0 && result.total) {
@@ -119,28 +120,27 @@ export const CarUpdatesProvider = ({ children }) => {
return 0;
};
const applyProgressStatus = (item, status) => {
if (validateStatusMessage(status)) {
if (status.msg === "downloading") {
item.progress = getDownloadProgress(status);
const applyProgressStatus = (item) => {
if (validateStatusMessage(item)) {
if (item.msg === "downloading") {
item.progress = getDownloadProgress(item);
item.status = `${item.ecu || ""} downloading ${item.progress}%`.trim();
return;
} else if (status.msg === "package_download_complete") {
} else if (item.msg === "package_download_complete") {
item.progress = 100;
item.status = "download complete";
item.status = `${item.ecu || ""} download complete`.trim();
return;
} else if (status.msg === "installing") {
item.progress = getInstallProgress(status);
} else if (item.msg === "installing") {
item.progress = getInstallProgress(item);
item.status = `${item.ecu || ""} installing ${item.progress}%`.trim();
return;
} else if (status.msg === "package_install_complete") {
} else if (item.msg === "package_install_complete") {
item.progress = 100;
item.status = "install complete";
item.status = `${item.ecu || ""} install complete`.trim();
return;
}
}
delete item.progress;
item.status = status.msg;
};
const applyProgressStatuses = (statuses) => {

View File

@@ -45,6 +45,7 @@ export const VehicleProvider = ({ children }) => {
}
cars.forEach((car) => {
car.connected = result[car.vin] || false;
car.connectedHMI = result[`2:${car.vin}`] || false;
});
} catch (e) {
logger.error(e.stack);

View File

@@ -13,7 +13,7 @@ import { StatusProvider, useStatusContext } from "./StatusContext";
const checkVehicleResult = (error, busy, vehicle) => {
checkBaseResults(error, busy);
expect(screen.getByTestId("vehicle").innerHTML).toEqual(vehicle);
}
};
const checkVehiclesResult = (error, busy, vehicles) => {
checkBaseResults(error, busy);
@@ -195,12 +195,22 @@ describe("VehicleContext", () => {
<>
<div data-testid="error">{message}</div>
<div data-testid="busy">{busy.toString()}</div>
<button data-testid="updateVehicleNull" onClick={() => update(null)} />
<button data-testid="updateVehicleNoVIN" onClick={() => update({})} />
<button
data-testid="updateVehicleNull"
onClick={() => update(null)}
/>
<button
data-testid="updateVehicleNoVIN"
onClick={() => update({})}
/>
<button
data-testid="updateVehicle"
onClick={() =>
update({ vin: "3C4PDCBG0ET127145", log_level: "warn", canbus: { enabled: false } })
update({
vin: "3C4PDCBG0ET127145",
log_level: "warn",
canbus: { enabled: false },
})
}
/>
</>
@@ -265,13 +275,17 @@ describe("VehicleContext", () => {
<>
<div data-testid="error">{message}</div>
<div data-testid="busy">{busy.toString()}</div>
<button data-testid="deleteVehicleNull" onClick={() => deleteV(null)} />
<button data-testid="deleteVehicleNonexistent" onClick={() => deleteV("11111111111111111")} />
<button
data-testid="deleteVehicleNull"
onClick={() => deleteV(null)}
/>
<button
data-testid="deleteVehicleNonexistent"
onClick={() => deleteV("11111111111111111")}
/>
<button
data-testid="deleteVehicle"
onClick={() =>
deleteV("3C4PDCBG0ET127145")
}
onClick={() => deleteV("3C4PDCBG0ET127145")}
/>
</>
);
@@ -321,7 +335,7 @@ describe("VehicleContext", () => {
describe("sendCommand", () => {
beforeEach(async () => {
const TestComp = () => {
const {busy, sendCommand} = useVehicleContext();
const { busy, sendCommand } = useVehicleContext();
const { message, setMessage } = useStatusContext();
const sendC = async (vin, command) => {
@@ -336,25 +350,38 @@ describe("VehicleContext", () => {
<>
<div data-testid="error">{message}</div>
<div data-testid="busy">{busy.toString()}</div>
<button data-testid="sendCommandNullVin" onClick={() => sendC(null, {command:"doors_lock"})} />
<button data-testid="sendCommandNonexistentVin" onClick={() => sendC(["11111111111111111"], {command:"doors_lock"})} />
<button
data-testid="sendCommandNullVin"
onClick={() => sendC(null, { command: "doors_lock" })}
/>
<button
data-testid="sendCommandNonexistentVin"
onClick={() =>
sendC(["11111111111111111"], { command: "doors_lock" })
}
/>
<button
data-testid="sendCommandVin"
onClick={() => sendC("3C4PDCBG0ET127145", {command:"doors_lock"})}
onClick={() =>
sendC("3C4PDCBG0ET127145", { command: "doors_lock" })
}
/>
<button
data-testid="sendCommandWrongCommand"
onClick={() => sendC("3C4PDCBG0ET127145", {command:"noSuchCommand"})}
onClick={() =>
sendC("3C4PDCBG0ET127145", { command: "noSuchCommand" })
}
/>
</>
);}
render(
<StatusProvider>
<VehicleProvider>
<TestComp />
</VehicleProvider>
</StatusProvider>
</>
);
};
render(
<StatusProvider>
<VehicleProvider>
<TestComp />
</VehicleProvider>
</StatusProvider>
);
});
afterEach(() => {
@@ -364,24 +391,23 @@ describe("VehicleContext", () => {
it("initial state", () => {
checkBaseResults("", "false");
});
})
});
});
const expectedFilters = [
{
can_id: "123-456",
interval: 789
interval: 789,
},
{
can_id: "1",
interval: 1000
interval: 1000,
},
{
can_id: "1000",
interval: 1
}
]
interval: 1,
},
];
const expectedVehicleData = {
vin: "3C4PDCBG0ET127145",
@@ -390,9 +416,16 @@ const expectedVehicleData = {
trim: "Basic",
ecu_list: "ECUA 2.0.0, ECUB 2.1.1",
log_level: "info",
canbus: { enabled: true, data_logger_enabled: true, max_mem_buffer_size: 1, max_disk_buffer_size: 2, filters: expectedFilters },
canbus: {
enabled: true,
data_logger_enabled: true,
max_mem_buffer_size: 1,
max_disk_buffer_size: 2,
filters: expectedFilters,
},
connected: true,
}
connectedHMI: false,
};
const expectedVehiclesData = [
{
@@ -402,19 +435,44 @@ const expectedVehiclesData = [
trim: "Basic",
ecu_list: "ECUA 2.0.0, ECUB 2.1.1",
log_level: "info",
canbus: { enabled: true, data_logger_enabled: true, max_mem_buffer_size: 1, max_disk_buffer_size: 2, filters: expectedFilters },
canbus: {
enabled: true,
data_logger_enabled: true,
max_mem_buffer_size: 1,
max_disk_buffer_size: 2,
filters: expectedFilters,
},
connected: true,
connectedHMI: false,
},
{ vin: "1G1FP87S3GN100062", connected: true, connectedHMI: false },
{
vin: "1HGCG325XYA062256",
year: 2021,
connected: true,
connectedHMI: false,
},
{
vin: "1J4GZ78YXWC160024",
year: 2021,
model: "Ocean",
connected: true,
connectedHMI: false,
},
{
vin: "2C3CCAAG8CH222800",
model: "Ocean",
trim: "Basic",
connected: true,
connectedHMI: false,
},
{ vin: "1G1FP87S3GN100062", connected: true },
{ vin: "1HGCG325XYA062256", year: 2021, connected: true },
{ vin: "1J4GZ78YXWC160024", year: 2021, model: "Ocean", connected: true },
{ vin: "2C3CCAAG8CH222800", model: "Ocean", trim: "Basic", connected: true },
{
vin: "KNADM4A39C6028108",
year: 2021,
model: "Ocean",
trim: "Basic",
connected: true,
connectedHMI: false,
},
{
vin: "1G11C5SL9FF153507",
@@ -422,5 +480,6 @@ const expectedVehiclesData = [
model: "Ocean",
trim: "Basic",
connected: true,
connectedHMI: false,
},
];

View File

@@ -19,7 +19,7 @@ import TableHeaderSortable from "../../Table/HeaderSortable";
import { logger } from "../../../services/monitoring";
import ConnectedIcon from "../../Controls/ConnectedIcon";
import ECUList from "../../Controls/ECUList";
import {useLocalStorage} from "../../useLocalStorage";
import { useLocalStorage } from "../../useLocalStorage";
const tableColumns = [
{
@@ -163,7 +163,8 @@ const CarSelectionTable = (props) => {
<TableCell align="center">
<ConnectedIcon
connected={row.connected}
style={{ marginRight: 5 }}
connectedHMI={row.connectedHMI}
style={{ marginRight: 3 }}
/>
<Link to={`/vehicle-status/${row.vin}`}>{row.vin}</Link>
{row.ecu_list && (

View File

@@ -1,5 +1,6 @@
import React, { useEffect, useState } from "react";
import {
LinearProgress,
Table,
TableBody,
TableCell,
@@ -57,14 +58,21 @@ const MainForm = ({ vin, token }) => {
const [pageIndex, setPageIndex] = useState(0);
const [orderBy, setOrderBy] = useState("id");
const [order, setOrder] = useState("desc");
const { cancelUpdate, getCarUpdates, carUpdates, totalCarUpdates } =
useCarUpdatesContext();
const {
cancelUpdate,
getCarUpdates,
carUpdates,
totalCarUpdates,
startMonitor,
stopMonitor,
} = useCarUpdatesContext();
const { setMessage } = useStatusContext();
useEffect(() => {
(async () => {
try {
if (!vin || !token) return;
stopMonitor();
await getCarUpdates(
{
vin,
@@ -82,6 +90,20 @@ const MainForm = ({ vin, token }) => {
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [vin, token, pageIndex, pageSize, orderBy, order]);
useEffect(() => {
try {
if (carUpdates.length === 0) return;
startMonitor(token);
} catch (e) {
setMessage(e.message);
logger.warn(e.stack);
}
return () => {
stopMonitor();
};
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [carUpdates]);
const handleChangePageIndex = (event, newIndex) => {
setPageIndex(newIndex);
};
@@ -141,7 +163,12 @@ const MainForm = ({ vin, token }) => {
{updateName(row)}
</Link>
</TableCell>
<TableCell align="center">{row.status}</TableCell>
<TableCell align="center">
{row.status}
{row.progress > -1 && (
<LinearProgress variant="determinate" value={row.progress} />
)}
</TableCell>
<TableCell align="center">
{LocalDateTimeString(row.created)}
</TableCell>

View File

@@ -1,12 +1,27 @@
import React from "react";
import PropTypes from "prop-types";
import CheckCircleIcon from "@material-ui/icons/CheckCircle";
import CheckBoxIcon from "@material-ui/icons/CheckBox";
import { Tooltip } from "@material-ui/core";
const ConnectedIcon = (props) => {
if (props.connected) {
if (props.connected || props.connectedHMI) {
return (
<span style={props.style}>
<CheckCircleIcon style={{ color: "Green", fontSize: 12 }} />
{props.connected && (
<Tooltip title="TBOX">
<CheckCircleIcon
style={{ color: "Green", fontSize: 12, marginRight: 3 }}
/>
</Tooltip>
)}
{props.connectedHMI && (
<Tooltip title="ICC">
<CheckBoxIcon
style={{ color: "Blue", fontSize: 12, marginRight: 3 }}
/>
</Tooltip>
)}
</span>
);
}

View File

@@ -9,30 +9,30 @@ import {
FormLabel,
Radio,
RadioGroup,
TextField
TextField,
} from "@material-ui/core";
import useStyles from "../../useStyles";
import {
useFleetContext,
FleetProvider
} from "../../Contexts/FleetContext";
import { useFleetContext, FleetProvider } from "../../Contexts/FleetContext";
import { useStatusContext } from "../../Contexts/StatusContext";
import { useUserContext } from "../../Contexts/UserContext";
import { logger } from "../../../services/monitoring";
const MainForm = () => {
const queries = new URLSearchParams(useLocation().search);
const { fleet, getFleet, updateFleet, busy } = useFleetContext();
const { token: { idToken: { jwtToken: token } } } = useUserContext();
const {
token: {
idToken: { jwtToken: token },
},
} = useUserContext();
const { setMessage, setTitle, setSitePath } = useStatusContext();
const [redirect, setRedirect] = useState(null);
const classes = useStyles();
const [name, setName] = useState(queries.get("name") ?? "");
const [oldName, ] = useState(name);
const [oldName] = useState(name);
const [selectedLogLevel, setSelectedLogLevel] = useState("info");
const [canbusEnabled, setCANBusEnabled] = useState(true);
const [dataLoggerEnabled, setDataLoggerEnabled] = useState(false);
@@ -71,36 +71,40 @@ const MainForm = () => {
if (fleet.canbus) {
setCANBusEnabled(fleet.canbus.enabled ?? canbusEnabled);
setDataLoggerEnabled(fleet.canbus.data_logger_enabled ?? dataLoggerEnabled);
setDataLoggerEnabled(
fleet.canbus.data_logger_enabled ?? dataLoggerEnabled
);
setMaxMemBufferSize(fleet.canbus.max_mem_buffer_size ?? maxMemBufferSize);
setMaxDiskBufferSize(fleet.canbus.max_disk_buffer_size ?? maxDiskBufferSize);
setMaxDiskBufferSize(
fleet.canbus.max_disk_buffer_size ?? maxDiskBufferSize
);
}
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [fleet]);
const onNameChange = (event) => {
setName(event.target.value);
}
};
const onLogLevelChange = (event) => {
setSelectedLogLevel(event.target.value);
}
};
const onCANBusChange = (event) => {
setCANBusEnabled(event.target.checked);
}
};
const onDataLoggerChange = (event) => {
setDataLoggerEnabled(event.target.checked);
}
};
const onMaxMemBufferSizeChange = (event) => {
setMaxMemBufferSize(event.target.value);
}
};
const onMaxDiskBufferSizeChange = (event) => {
setMaxDiskBufferSize(event.target.value);
}
};
const onSubmit = async (event) => {
try {
@@ -112,12 +116,13 @@ const MainForm = () => {
enabled: canbusEnabled,
data_logger_enabled: canbusEnabled ? dataLoggerEnabled : false,
max_mem_buffer_size: canbusEnabled ? parseInt(maxMemBufferSize) : 0,
max_disk_buffer_size: canbusEnabled && dataLoggerEnabled ? parseInt(maxDiskBufferSize) : 0
}
max_disk_buffer_size:
canbusEnabled && dataLoggerEnabled
? parseInt(maxDiskBufferSize)
: 0,
},
};
console.log(oldName);
const result = await updateFleet(oldName, formData, token);
if (!result || result.error) return;
@@ -164,20 +169,24 @@ const MainForm = () => {
<FormControlLabel value="info" control={<Radio />} label="Info" />
<FormControlLabel value="warn" control={<Radio />} label="Warn" />
<FormControlLabel value="error" control={<Radio />} label="Error" />
<FormControlLabel value="critical" control={<Radio />} label="Critical" />
<FormControlLabel
value="critical"
control={<Radio />}
label="Critical"
/>
</RadioGroup>
<FormLabel id="demo-row-radio-buttons-group-label">CAN Bus</FormLabel>
<FormGroup>
<FormControlLabel control={
<Checkbox
checked={canbusEnabled}
onChange={onCANBusChange}
/>
} label="CAN Bus Enabled" />
<FormControlLabel
control={
<Checkbox checked={canbusEnabled} onChange={onCANBusChange} />
}
label="CAN Bus Enabled"
/>
<TextField
id="max_mem_buffer_size"
name="max_mem_buffer_size"
label='Max Memory Buffer Size (0 uses default size)'
label="Max Memory Buffer Size (0 uses default size)"
value={maxMemBufferSize}
onChange={onMaxMemBufferSizeChange}
variant="outlined"
@@ -190,18 +199,21 @@ const MainForm = () => {
required
fullWidth
/>
<FormControlLabel control={
<Checkbox
checked={dataLoggerEnabled}
onChange={onDataLoggerChange}
disabled={!canbusEnabled}
/>
} label="Data Logger Enabled" />
<FormControlLabel
control={
<Checkbox
checked={dataLoggerEnabled}
onChange={onDataLoggerChange}
disabled={!canbusEnabled}
/>
}
label="Data Logger Enabled"
/>
</FormGroup>
<TextField
id="max_disk_buffer_size"
name="max_disk_buffer_size"
label='Max Disk Buffer Size (0 uses default size)'
label="Max Disk Buffer Size (0 uses default size)"
value={maxDiskBufferSize}
onChange={onMaxDiskBufferSizeChange}
variant="outlined"
@@ -236,4 +248,4 @@ const FleetUpdateForm = (props) => (
</FleetProvider>
);
export default FleetUpdateForm;
export default FleetUpdateForm;

View File

@@ -68,7 +68,11 @@ const MainForm = () => {
const [orderBy, setOrderBy] = useState("id");
const [order, setOrder] = useState("asc");
const [search, setSearch] = useState("");
const [showDeleteModal, setShowDeleteModal] = useState(false);
const [deleteId, setDeleteId] = useState("");
const [deleteRowName, setDeleteRowName] = useState("");
const { getManifests, deleteManifest, manifests, totalManifests } =
useManifestsContext();
const { setMessage, setTitle, setSitePath } = useStatusContext();
@@ -133,6 +137,12 @@ const MainForm = () => {
setSearch(query);
};
const setDeletePopup = (id, row) => {
setDeleteId(id);
setDeleteRowName(`${row.name} ${row.version}`);
setShowDeleteModal(true);
}
const onDelete = async (manifest_id) => {
try {
await deleteManifest(parseInt(manifest_id), token);
@@ -182,16 +192,10 @@ const MainForm = () => {
return (
<div>
<Tooltip key={`delete-${action.id}`} title={action.tip}>
<Link to="#" onClick={() => onDelete(action.id)}>
<Link to="#" onClick={() => setDeletePopup(action.id, row)}>
{action.icon}
</Link>
</Tooltip>
<DeleteConfirmation
message={action.id}
open={showDeleteModal}
close={() => setShowDeleteModal(false)}
deleteFunction={() => onDelete(action.id)}
/>
</div>
);
}
@@ -261,6 +265,11 @@ const MainForm = () => {
</TableRow>
</TableFooter>
</Table>
<DeleteConfirmation
message={deleteRowName}
open={showDeleteModal}
close={() => setShowDeleteModal(false)}
deleteFunction={() => onDelete(deleteId)} />
</div>
);
};

View File

@@ -151,7 +151,6 @@ const Component = () => {
url="https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png"
/>
<CenterFocus center={center} zoom={zoom} />
{markers.map((marker) => (
<Marker
icon={getCarIcon(marker[2])}
@@ -184,6 +183,7 @@ const Component = () => {
key={carState.vin}
vin={carState.vin}
online={carState.online}
onlineHMI={carState.online_hmi}
battery={carState.battery}
doors={carState.doors}
location={carState.location}

View File

@@ -13,7 +13,7 @@ import CANSignals from "../Cars/CANSignals";
const VehiclePopUp = (props) => {
const classes = useStyles();
const [viewCAN, setViewCAN] = useState(false);
const { vin, online, onClose } = props;
const { vin, online, onClose, onlineHMI } = props;
const toggleView = (e) => {
e.preventDefault();
@@ -43,6 +43,9 @@ const VehiclePopUp = (props) => {
<p>
<b>Connected</b>: {online.toString()}
</p>
<p>
<b>ICC Connected</b>: {onlineHMI?.toString()}
</p>
{viewCAN && <CANSignals vin={vin} />}
{!viewCAN && <DigitalTwin {...props} />}
</div>