From ea5d9db7906d94cf387aeb908f0245fee83be01d Mon Sep 17 00:00:00 2001 From: Tristan Timblin Date: Thu, 18 Jan 2024 13:25:41 -0800 Subject: [PATCH 1/2] CEC-5618: add state and timeout toggles (#495) --- .../BulkActions/actions/Diagnostic.jsx | 111 +++++++++++++++--- .../Controls/SendDiagnosticCommand/index.jsx | 5 +- 2 files changed, 98 insertions(+), 18 deletions(-) diff --git a/src/components/BulkActions/actions/Diagnostic.jsx b/src/components/BulkActions/actions/Diagnostic.jsx index 2120abb..a124957 100644 --- a/src/components/BulkActions/actions/Diagnostic.jsx +++ b/src/components/BulkActions/actions/Diagnostic.jsx @@ -1,17 +1,31 @@ import { forwardRef, useImperativeHandle, useState, useEffect } from "react"; import { + Box, FormControl, + FormControlLabel, + Grid, + TextField, InputLabel, Select, + Switch, } from "@material-ui/core"; import api from "../../../services/vehiclesAPI"; import TaskRunner from "../../../utils/taskRunner"; -import { AllECUsCommand, DIAGNOSTIC_COMMANDS } from "../../Controls/SendDiagnosticCommand"; +import { AllECUsCommand } from "../../Controls/SendDiagnosticCommand"; import useStyles from "../../useStyles"; import { useStatusContext } from "../../Contexts/StatusContext"; import { useUserContext } from "../../Contexts/UserContext"; import unionIntersect from "../../../utils/unionIntersect"; +const DIAGNOSTIC_COMMANDS = [ + { displayname: "Reset", val: "remote_reset", ecu_name: true }, + { displayname: "Set CAN Network State", val: "can_network", state: "can_net_action" }, + { displayname: "Set Remote Ignition", val: "remote_ignition", state: "can_net_action" }, + { displayname: "Read ECU versions", val: "read_ecu_versions", allowAll: true, ecu_name: true }, +]; + +const DEFAULT_SECONDS = 180; + async function getECUsByVINs(vins, token) { return new Promise((resolve, reject) => { const taskRunner = new TaskRunner(30, vins.length); @@ -46,6 +60,8 @@ export default forwardRef(({ const [currentECU, setCurrentECU] = useState(""); const [validateECUs, setValidateECUs] = useState(false); const [command, setCommand] = useState(DIAGNOSTIC_COMMANDS[0]); + const [checked, setChecked] = useState(false); + const [seconds, setSeconds] = useState(DEFAULT_SECONDS); const classes = useStyles(); const { setMessage } = useStatusContext(); const { token: { idToken: { jwtToken: token } } } = useUserContext(); @@ -56,15 +72,12 @@ export default forwardRef(({ return Promise.reject("Invalid ECUs found, cannot submit"); } - return api.sendDiagnosticCommand(ids, { - command: command.val, - ecu_name: currentECU, - }, token) + return api.sendDiagnosticCommand(ids, createDiagnosticPayload(command, checked, currentECU, seconds), token) .then(() => { - setMessage(`Sent ${command} command to ${ids.length} vehicles.`); + setMessage(`Sent ${command.val} command to ${ids.length} vehicles.`); }) .catch(() => { - setMessage(`Failed to send ${command} command.`); + setMessage(`Failed to send ${command.val} command.`); }); } })); @@ -73,6 +86,17 @@ export default forwardRef(({ setCommand(DIAGNOSTIC_COMMANDS[e.target.value]); }; + const handleSwitch = (e) => { + setChecked(e.target.checked); + } + + const handleSecondsChange = (e) => { + const value = Number(e.target.value); + if (value > 0) { + setSeconds(); + } + } + useEffect(() => { async function fetchData() { setValidateECUs(false); @@ -82,22 +106,26 @@ export default forwardRef(({ fetchData(); }, [ids, token]); + useEffect(() => { + if (!checked) { + setSeconds(DEFAULT_SECONDS); + } + }, [checked, setSeconds]); + + useEffect(() => { + setChecked(false); + }, [command, setChecked]); + useEffect(() => { setValidateECUs(true); setCurrentECU(ecus[0].ecu); }, [ecus]); return ( -
+

Attempt to send a vehicle diagnostic command to the following VINs: {idCSV}.

- @@ -117,6 +145,57 @@ export default forwardRef(({ ))} -
+ + {["remote_reset", "read_ecu_versions"].includes(command.val) && } + + {["can_network", "remote_ignition"].includes(command.val) && ( + +
+ } + /> +
+ +
+ )} + ); -}); \ No newline at end of file +}); + +const hasOwnProperty = (obj, key) => { + return Object.prototype.hasOwnProperty.call(obj, key); +} + +const createDiagnosticPayload = (command = {}, checked, ecu, seconds) => { + const payload = { + command: command.val, + }; + + if (hasOwnProperty(command, "ecu_name")) { + payload.ecu_name = ecu; + } + + if (hasOwnProperty(command, "state")) { + payload[command.state] = checked ? "on" : "off"; + payload.timeout = seconds; + } + + return payload; +} \ No newline at end of file diff --git a/src/components/Controls/SendDiagnosticCommand/index.jsx b/src/components/Controls/SendDiagnosticCommand/index.jsx index 68c878b..cdcfc6c 100644 --- a/src/components/Controls/SendDiagnosticCommand/index.jsx +++ b/src/components/Controls/SendDiagnosticCommand/index.jsx @@ -13,13 +13,13 @@ import { useVehicleContext } from "../../Contexts/VehicleContext"; -export const DIAGNOSTIC_COMMANDS = [ +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", allowAll: true }, + { displayname: "Read ECU versions", val: "read_ecu_versions" }, ] const SendDiagnosticCommand = ({ vin, token, classes }) => { @@ -195,6 +195,7 @@ export const AllECUsCommand = ({ classes, ecus, currentECU, setCurrentECU }) => className={classes.formControl} variant="outlined" size="small" + margin="normal" > ECU From 40c3ec94b0057f990608a97dd07d043075763f6e Mon Sep 17 00:00:00 2001 From: Tristan Timblin Date: Thu, 18 Jan 2024 13:26:06 -0800 Subject: [PATCH 2/2] CEC-5591: strip out special characters in vehicle search (#496) --- src/components/Cars/List/useQuery.js | 2 ++ src/components/Cars/List/useQuery.test.jsx | 16 ++++++++++++++++ 2 files changed, 18 insertions(+) diff --git a/src/components/Cars/List/useQuery.js b/src/components/Cars/List/useQuery.js index 48d353b..186a8e3 100644 --- a/src/components/Cars/List/useQuery.js +++ b/src/components/Cars/List/useQuery.js @@ -14,6 +14,8 @@ function parseVin(vin = "") { return [true, vin.replace(prefix, "")]; } + vin = vin.replace(/[^\p{L}\d]/gu, ''); + var re = new RegExp("^[A-HJ-NPR-Z0-9]{8}[0-9X][A-HJ-NPR-Z0-9]{2}[0-9]{6}$"); return [vin.match(re), vin]; } diff --git a/src/components/Cars/List/useQuery.test.jsx b/src/components/Cars/List/useQuery.test.jsx index fbf2e1b..04b8b7e 100644 --- a/src/components/Cars/List/useQuery.test.jsx +++ b/src/components/Cars/List/useQuery.test.jsx @@ -89,6 +89,22 @@ describe("useQuery", () => { ["test", "VCF1EBE2008016235,VCF1EBE20PG001002,VCF1EBE20PG001162", "test vin:VCF1EBE2008016235 vin:VCF1EBE20PG001002 vin:VCF1EBE20PG001162"] ], + [ + "parses a space separated search query with special characters", + "[ocean] pe@r a!aska r*nin", + ["[ocean] pe@r a!aska r*nin", "", "[ocean] pe@r a!aska r*nin"] + ], + [ + "parses a space separated vin query with special characters", + "[\"VCF1EBE2008016235\", \"VCF1EBE20PG001002\", \"VCF1EBE20PG001162\"]", + ["", "VCF1EBE2008016235,VCF1EBE20PG001002,VCF1EBE20PG001162", "vin:VCF1EBE2008016235 vin:VCF1EBE20PG001002 vin:VCF1EBE20PG001162"] + ], + [ + "parses a space separated mixed search and vin query with special characters", + "te$t VCF1EBE20@*(08016235 [VCF1EBE2]0PG001002 VCF*1EBE20PG001162", + ["te$t", "VCF1EBE2008016235,VCF1EBE20PG001002,VCF1EBE20PG001162", "te$t vin:VCF1EBE2008016235 vin:VCF1EBE20PG001002 vin:VCF1EBE20PG001162"] + ], + [ "trims extraneous values from search", "ocean,, , ,,,,pear,,, ",