Files
ota-admin-portal/src/components/Controls/SendDiagnosticCommand/index.jsx
2024-01-18 13:25:41 -08:00

305 lines
9.3 KiB
JavaScript

import clsx from "clsx";
import { Button, FormControl, InputLabel, Select, Grid, Switch } from "@material-ui/core";
import React, { useEffect, useState } from "react";
import { useStatusContext } from "../../Contexts/StatusContext";
import { logger } from "../../../services/monitoring";
import smsAPI from "../../../services/smsAPI";
import cmp from "semver-compare";
import PropTypes from 'prop-types';
import {
useVehicleContext
} from "../../Contexts/VehicleContext";
const DIAGNOSTIC_COMMANDS = [
{ displayname: "Reset", val: "remote_reset" },
{ displayname: "Set CAN Network State", val: "can_network" },
{ displayname: "Set Remote Ignition", val: "remote_ignition" },
{ displayname: "Send Wake Up SMS", val: "sms" },
// { displayname: "Update SecOC keys", val: "write_secoc_key" },
{ displayname: "Read ECU versions", val: "read_ecu_versions" },
]
const SendDiagnosticCommand = ({ vin, token, classes }) => {
const { vehicle, getVehicle, getState, sendDiagnosticCommand, getECUs } = useVehicleContext();
const [carState, setCarState] = useState(null);
const [ecus, setEcus] = useState([]);
const { setMessage } = useStatusContext();
const [currentCommand, setCurrentCommand] = useState(DIAGNOSTIC_COMMANDS[0].val);
const [currentECU, setCurrentECU] = useState("");
const [canNetState, setCanNetState] = useState(false);
const [ignitionState, setIgnitionState] = useState(false);
const [seconds, setSeconds] = useState(180);
const changeCommandHandler = (e) => {
setCurrentCommand(e.target.value);
};
//Update online/offline state
useEffect(() => {
if (!vin) return;
getCarState();
const interval = setInterval(getCarState, 5000);
return () => { clearInterval(interval); }
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [vin]);
//Get ECUs
useEffect(() => {
(async () => {
if (!vin) return;
const unique = true;
const result = await getECUs({ vin, unique }, token)
sortECUs(result.data)
result.data.push({ ecu: "TBOX" })
setCurrentECU(result.data[0].ecu)
setEcus(result.data)
await getVehicle(vin, token);
})();
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [vin]);
const getCarState = async () => {
try {
const result = await getState(token, vin);
setCarState(result.data);
} catch (e) {
setMessage(e.message);
logger.warn(e.stack);
}
};
const isOnline = () => {
return carState && carState?.online;
};
const sortECUs = (ecus) => {
return ecus.sort((a, b) => { return a.ecu < b.ecu ? -1 : a.ecu > b.ecu ? 1 : 0 })
}
const TREX_MIN_VER = "1.1.141";
const isRemoteResetSupported = () => {
return !carState?.trex_version ? true
: cmp(carState.trex_version, TREX_MIN_VER) >= 0
|| carState.trex_version.includes("dev")
|| carState.trex_version.includes("0.0.0");
};
const clickHandler = async (_) => {
try {
if (currentCommand === "remote_reset") {
await sendDiagnosticCommand([vin], { command: currentCommand, ecu_name: currentECU }, token);
}
else if (currentCommand === "can_network") {
await sendDiagnosticCommand([vin], { command: currentCommand, can_net_action: canNetState ? "on" : "off", timeout: seconds }, token);
} else if (currentCommand === "remote_ignition") {
await sendDiagnosticCommand([vin], { command: currentCommand, ignition_action: ignitionState ? "on" : "off", timeout: seconds }, token);
} else if (currentCommand === "sms") {
const res = await smsAPI.send({ ICCID: vehicle.iccid, await: true, messageText: "remote_diagnostic" }, token)
if (res.error) {
setMessage(`Failed to wake up the car: ${res.error}`)
return;
}
} else if (currentCommand === "read_ecu_versions") {
await sendDiagnosticCommand([vin], { command: currentCommand, ecu_name: currentECU }, token);
} else if (currentCommand === "write_secoc_key") {
await sendDiagnosticCommand([vin], { command: currentCommand }, token);
}
setMessage(`Sent diagnostic command to ${vin}`);
} catch (error) {
setMessage(error.message);
logger.error(error.stack);
}
};
return (
<div className={clsx(classes.paper, classes.tableSize)}>
{
currentCommand === "remote_reset"
?
<AllECUsCommand classes={classes} ecus={ecus} currentECU={currentECU} setCurrentECU={setCurrentECU} />
: currentCommand === "can_network"
?
<CanNetCommand canNetState={canNetState} setCanNetState={setCanNetState} seconds={seconds} setSeconds={setSeconds} />
: currentCommand === "remote_ignition"
?
<RemoteIgnitionCommand ignitionState={ignitionState} setIgnitionState={setIgnitionState} seconds={seconds} setSeconds={setSeconds} />
: currentCommand === "read_ecu_versions"
?
<AllECUsCommand classes={classes} ecus={ecus.concat({ ecu: "*" })} currentECU={currentECU} setCurrentECU={setCurrentECU} />
: null
}
<p></p>
<FormControl
className={classes.formControl}
variant="outlined"
size="small"
>
<InputLabel htmlFor="send-command" className={classes.whiteBackground}>
Diagnostic Command
</InputLabel>
<Select
native
variant="outlined"
inputProps={{
name: "send-command",
id: "send-command",
}}
onChange={changeCommandHandler}
>
{DIAGNOSTIC_COMMANDS.map((command, index) => (
<option key={index} value={command.val}>
{command.displayname}
</option>
))}
</Select>
</FormControl>
<Button
type="submit"
aria-label="send command"
fullWidth
variant="contained"
color="primary"
className={classes.submit}
onClick={clickHandler}
disabled={(!isOnline() || !isRemoteResetSupported()) && currentCommand !== "sms"}
>
Send
</Button>
<div>
<b>
{isOnline() ? "ONLINE" : "OFFLINE"}
</b>
</div>
<div>
<b>
{!isRemoteResetSupported() ? `Remote Reset supported from ${TREX_MIN_VER}, current version ${carState.trex_version}` : ""}
</b>
</div>
</div >
);
};
export const AllECUsCommand = ({ classes, ecus, currentECU, setCurrentECU }) => {
return (
<FormControl
className={classes.formControl}
variant="outlined"
size="small"
margin="normal"
>
<InputLabel className={classes.whiteBackground}>
ECU
</InputLabel>
<Select
native
variant="outlined"
value={currentECU}
onChange={(event) => {
setCurrentECU(event.target.value)
}}
>
{ecus.map((row, index) => (
<option key={index} value={row.ecu}>
{row.ecu}
</option>
))}
</Select>
</FormControl>
)
}
AllECUsCommand.propTypes = {
classes: PropTypes.object.isRequired,
ecus: PropTypes.array.isRequired,
currentECU: PropTypes.string.isRequired,
setCurrentECU: PropTypes.func.isRequired,
};
const CanNetCommand = ({ canNetState, setCanNetState, seconds, setSeconds }) => {
return (
<div>
<Grid component="label" container alignItems="center" spacing={1}>
<Grid item>Off</Grid>
<Grid item>
<Switch checked={canNetState} onChange={(event) => {
setCanNetState(event.target.checked)
}} />
</Grid>
<Grid item>On</Grid>
</Grid>
{
canNetState ?
<label>
Enter amount of seconds:
<input
type="number"
value={seconds}
min={0} // Minimum value
step={10} // Increment/decrement step
onChange={(event) => {
const value = event.target.value;
setSeconds(parseInt(value));
}}
/>
</label>
: null
}
</div>
)
}
CanNetCommand.propTypes = {
setCanNetState: PropTypes.func.isRequired,
seconds: PropTypes.number.isRequired,
setSeconds: PropTypes.func.isRequired,
};
const RemoteIgnitionCommand = ({ ignitionState, setIgnitionState, setSeconds, seconds }) => {
return (
<div>
<Grid component="label" container alignItems="center" spacing={1}>
<Grid item>Off</Grid>
<Grid item>
<Switch checked={ignitionState} onChange={(event) => {
setIgnitionState(event.target.checked)
}} />
</Grid>
<Grid item>On</Grid>
</Grid>
{
ignitionState ?
<label>
Enter amount of seconds:
<input
type="number"
value={seconds}
min={0} // Minimum value
step={10} // Increment/decrement step
onChange={(event) => {
const value = event.target.value;
setSeconds(parseInt(value));
}}
/>
</label>
: null
}
</div>
)
}
RemoteIgnitionCommand.propTypes = {
ignitionState: PropTypes.bool.isRequired,
setIgnitionState: PropTypes.func.isRequired,
setSeconds: PropTypes.func.isRequired,
seconds: PropTypes.number.isRequired,
};
export default SendDiagnosticCommand;