Merge branch 'onewire_detect' into dev_environment

This commit is contained in:
csawatzky 2025-11-04 14:31:48 -06:00
commit 25bce682ad
7 changed files with 154 additions and 34 deletions

2
package-lock.json generated
View file

@ -10953,7 +10953,7 @@
}, },
"node_modules/protobuf-ts": { "node_modules/protobuf-ts": {
"version": "1.0.0", "version": "1.0.0",
"resolved": "git+https://gitlab+deploy-token-50627:hv8mB4WkyvtjBpJKU1rN@gitlab.com/brandx/protobuf-ts.git#b230f4b2779d21ca7b502798b1472f667f8bbdc0", "resolved": "git+https://gitlab+deploy-token-50627:hv8mB4WkyvtjBpJKU1rN@gitlab.com/brandx/protobuf-ts.git#873cef415993fc91a18e8b3ff80cdc6531f4e78d",
"dependencies": { "dependencies": {
"protobufjs": "^6.8.8" "protobufjs": "^6.8.8"
} }

View file

@ -142,8 +142,8 @@ export default function DeviceWizard(props: Props) {
</Tooltip> </Tooltip>
)} )}
{selectedPort && {selectedPort &&
// selectedPort.addressType === quack.AddressType.ADDRESS_TYPE_CONFIGURABLE_PIN_ARRAY && selectedPort.addressType === quack.AddressType.ADDRESS_TYPE_CONFIGURABLE_PIN_ARRAY &&
// device.featureSupported("detectOneWire") && device.featureSupported("detectOneWire") &&
selectedPort.autoDetectable && ( selectedPort.autoDetectable && (
<Tooltip title="Used to tell the Device to scan this port for any plugged in sensors"> <Tooltip title="Used to tell the Device to scan this port for any plugged in sensors">
<MenuItem <MenuItem

View file

@ -41,6 +41,7 @@ export default function DeviceScannedComponents(props: Props){
const [currentStep, setCurrentStep] = useState(0) const [currentStep, setCurrentStep] = useState(0)
const [settingsValid, setSettingsValid] = useState(false); const [settingsValid, setSettingsValid] = useState(false);
const [openDialog, setOpenDialog] = useState(false); const [openDialog, setOpenDialog] = useState(false);
const [clearOpen, setClearOpen] = useState(false);
useEffect(()=>{ useEffect(()=>{
if (scannedComponents.i2c) setScannedI2C(scannedComponents.i2c) if (scannedComponents.i2c) setScannedI2C(scannedComponents.i2c)
@ -207,8 +208,8 @@ export default function DeviceScannedComponents(props: Props){
setComponents(cloneList) setComponents(cloneList)
} }
const removeScan = (key: string) => { const removeScan = (key: string, type: pond.ObjectType) => {
deviceAPI.removeFoundComponents(device.settings.deviceId, key) deviceAPI.removeFoundComponents(device.settings.deviceId, key, type)
.then(resp => { .then(resp => {
console.log("Cleared this scan") console.log("Cleared this scan")
}).catch(err => { }).catch(err => {
@ -216,18 +217,45 @@ export default function DeviceScannedComponents(props: Props){
}) })
} }
const removeAllScans = () => {
deviceAPI.removeAllFoundComponents(device.settings.deviceId)
.then(resp => {
console.log("Cleared all scans")
}).catch(err => {
console.log("There was a problem clearing scans")
})
}
const clearWarning = () => {
return (
<ResponsiveDialog open={clearOpen} onClose={()=>{setClearOpen(false)}}>
<DialogTitle>Clear All Scans</DialogTitle>
<DialogContent>This action will clear all scans for this device.</DialogContent>
<DialogActions>
<Button variant="contained" onClick={()=>{setClearOpen(false)}}>Close</Button>
<Button variant="contained" color="primary" onClick={()=>{removeAllScans()}}>Continue</Button>
</DialogActions>
</ResponsiveDialog>
)
}
return ( return (
<React.Fragment> <React.Fragment>
{clearWarning()}
<Card raised sx={{padding: 1}}> <Card raised sx={{padding: 1}}>
<Box display="flex" flexDirection="row" justifyContent="space-between" alignItems="center">
<Typography sx={{fontWeight: 650, fontSize: 25}}>Sensor Scan</Typography>
<Button variant="contained" color="primary" onClick={()=>{setClearOpen(true)}}>Clear</Button>
</Box>
{scannedI2C !== undefined && {scannedI2C !== undefined &&
<React.Fragment> <Box marginBottom={5}>
<Box justifyContent="space-between" display="flex"> <Box justifyContent="space-between" display="flex">
<Typography sx={{fontWeight: 650}}> <Typography sx={{fontWeight: 650}}>
I2C Sensors I2C Sensors
</Typography> </Typography>
<Button variant="contained" color="primary" onClick={()=>{removeScan(scannedI2C.key)}}>Clear Scan</Button> <Button variant="contained" color="primary" onClick={()=>{removeScan(scannedI2C.key, pond.ObjectType.OBJECT_TYPE_DETECT_I2C)}}>Clear Scan</Button>
</Box> </Box>
{validI2CCompAddresses.length > 0 ? validI2CCompAddresses.map((addr, index) => { {validI2CCompAddresses.length > 0 ? validI2CCompAddresses.map((addr, index) => {
return ( return (
@ -244,10 +272,10 @@ export default function DeviceScannedComponents(props: Props){
</Typography> </Typography>
</Box> </Box>
} }
</React.Fragment> </Box>
} }
{scannedOneWire.length > 0 && {scannedOneWire.length > 0 &&
<Box marginTop={10}> <Box>
<Box justifyContent="space-between" display="flex"> <Box justifyContent="space-between" display="flex">
<Typography sx={{fontWeight: 650}}> <Typography sx={{fontWeight: 650}}>
Pin Port Sensors Pin Port Sensors
@ -265,11 +293,11 @@ export default function DeviceScannedComponents(props: Props){
} }
}) })
return( return(
<Box key={i} padding={2} paddingBottom={0}> <Box key={i} paddingX={2}>
<Box display="flex" justifyContent="space-between"> <Box display="flex" justifyContent="space-between" alignItems={"center"}>
<Typography>Port: {portLabel}</Typography> <Typography>Port: {portLabel}</Typography>
<Button variant="contained" color="primary" onClick={()=>{ <Button variant="contained" color="primary" onClick={()=>{
removeScan(scan.key) removeScan(scan.key, pond.ObjectType.OBJECT_TYPE_DETECT_ONEWIRE)
}}>Clear Port Scan</Button> }}>Clear Port Scan</Button>
</Box> </Box>
<ScannedOneWirePort scan={scan} componentSelectionCallback={componentSelection}/> <ScannedOneWirePort scan={scan} componentSelectionCallback={componentSelection}/>

View file

@ -1,4 +1,5 @@
import { Box, Checkbox, MenuItem, TextField, Tooltip } from "@mui/material" import { Box, Checkbox, Grid2, MenuItem, TextField, Tooltip, Typography } from "@mui/material"
import { useMobile } from "hooks"
import { getFriendlyName, getSubtypes, Subtype } from "pbHelpers/ComponentType" import { getFriendlyName, getSubtypes, Subtype } from "pbHelpers/ComponentType"
import { quack } from "protobuf-ts/quack" import { quack } from "protobuf-ts/quack"
import React, { useEffect, useState } from "react" import React, { useEffect, useState } from "react"
@ -11,20 +12,21 @@ interface Props {
export default function PortComponent(props: Props){ export default function PortComponent(props: Props){
const {compData, componentSelect} = props const {compData, componentSelect} = props
// const [selectedSubtype, setSelectedSubtype] = useState<number>(0) // const [selectedSubtype, setSelectedSubtype] = useState<number>(0)
const [subtypeVal, setSubtypeVal] = useState("none")//note that this is the friendly name and not the key or the value because it needs to be different from the other alias options const [subtypeVal, setSubtypeVal] = useState("no subtype")//note that this is the friendly name and not the key or the value because it needs to be different from the other alias options
const [subtypeMap, setSubtypeMap] = useState<Map<string, number>>(new Map()) const [subtypeMap, setSubtypeMap] = useState<Map<string, number>>(new Map())
const [subtypeOptions, setSubtypeOptions] = useState<Subtype[]>([]) const [subtypeOptions, setSubtypeOptions] = useState<Subtype[]>([])
const [added, setAdded] = useState(false) const [added, setAdded] = useState(false)
const isMobile = useMobile()
useEffect(()=>{ useEffect(()=>{
// setSelectedSubtype(compData.subtype) // setSelectedSubtype(compData.subtype)
let validSubtypes: Subtype[] = [] let validSubtypes: Subtype[] = []
let subMap: Map<string, number> = new Map() let subMap: Map<string, number> = new Map()
let subVal: string = "none" let subVal: string = "no subtype"
getSubtypes(compData.type).forEach(option => { getSubtypes(compData.type).forEach(option => {
if(compData.subtype === option.key || compData.subtype === 0){ if(compData.subtype === option.key || compData.subtype === 0){
validSubtypes.push(option) validSubtypes.push(option)
if(subVal === "none"){ if(subVal === "no subtype"){
subVal = option.friendlyName subVal = option.friendlyName
} }
} }
@ -40,13 +42,14 @@ export default function PortComponent(props: Props){
<TextField <TextField
value={subtypeVal} value={subtypeVal}
disabled={added} disabled={added}
fullWidth
onChange={(event) => { onChange={(event) => {
setSubtypeVal(event.target.value) setSubtypeVal(event.target.value)
// setSelectedSubtype(subtypeMap.get(event.target.value) ?? 0) // setSelectedSubtype(subtypeMap.get(event.target.value) ?? 0)
compData.subtype = subtypeMap.get(event.target.value) ?? 0 compData.subtype = subtypeMap.get(event.target.value) ?? 0
}} }}
select> select>
<MenuItem key={-1} value={"none"}>Select the Component Subtype</MenuItem> <MenuItem key={-1} value={"no subtype"}>Select the Component Subtype</MenuItem>
{subtypeOptions.map((s, i) => ( {subtypeOptions.map((s, i) => (
<MenuItem key={i} value={s.friendlyName}>{s.friendlyName}</MenuItem> <MenuItem key={i} value={s.friendlyName}>{s.friendlyName}</MenuItem>
))} ))}
@ -71,15 +74,77 @@ export default function PortComponent(props: Props){
) )
} }
const desktopDisplay = () => {
return (
<Box padding={2}>
<Grid2 container spacing={2} direction="row" wrap="nowrap" alignContent="center" alignItems="center" justifyContent="space-between">
<Grid2 size={3}>
<Typography>
{getFriendlyName(compData.type)}
</Typography>
</Grid2>
<Grid2 size={3}>
{subtypeOptions.length < 2
?
<Typography>
{subtypeVal}
</Typography>
:
subtypeSelector()}
</Grid2>
<Grid2 size={2}>
<Typography>
Cable ID: {compData.cableId}
</Typography>
</Grid2>
<Grid2 size={2}>
<Typography>
Nodes: {compData.nodeCount}
</Typography>
</Grid2>
<Grid2 size={1}>
{selectCompCheckbox()}
</Grid2>
</Grid2>
</Box>
)
}
const mobileDisplay = () => {
return(
<Box padding={2}>
<Grid2 container spacing={2} direction="row" wrap="nowrap" alignContent="center" alignItems="center" justifyContent="space-between">
<Grid2 size={5}>
<Typography>
{getFriendlyName(compData.type)}
</Typography>
<Typography>
Cable ID: {compData.cableId}
</Typography>
<Typography>
Nodes: {compData.nodeCount}
</Typography>
</Grid2>
<Grid2 size={5}>
{subtypeOptions.length < 2
?
<Typography>
{subtypeVal}
</Typography>
:
subtypeSelector()}
</Grid2>
<Grid2 size={2}>
{selectCompCheckbox()}
</Grid2>
</Grid2>
</Box>
)
}
return ( return (
<React.Fragment> <React.Fragment>
<Box> {isMobile ? mobileDisplay() : desktopDisplay()}
{getFriendlyName(compData.type)}
Cable ID: {compData.cableId}
Number of nodes: {compData.nodeCount}
{subtypeOptions.length < 2 ? subtypeVal : subtypeSelector()}
{selectCompCheckbox()}
</Box>
</React.Fragment> </React.Fragment>
) )
} }

View file

@ -1,4 +1,4 @@
import { Box, MenuItem, TextField } from "@mui/material" import { Box, MenuItem, TextField, Typography } from "@mui/material"
import { extension, getFriendlyName, getSubtypes, Subtype } from "pbHelpers/ComponentType" import { extension, getFriendlyName, getSubtypes, Subtype } from "pbHelpers/ComponentType"
import { pond } from "protobuf-ts/pond" import { pond } from "protobuf-ts/pond"
import { quack } from "protobuf-ts/quack" import { quack } from "protobuf-ts/quack"
@ -42,13 +42,16 @@ export default function ScannedOneWirePort(props: Props){
return ( return (
<React.Fragment> <React.Fragment>
{portComponents.map((compData, i) => { {portComponents.length > 0 ? portComponents.map((compData, i) => {
return ( return (
<PortComponent key={i} compData={compData} componentSelect={(checked, component) => { <PortComponent key={i} compData={compData} componentSelect={(checked, component) => {
componentSelected(checked, component) componentSelected(checked, component)
}}/> }}/>
) )
})} }):
<Box>
<Typography>No Components Found</Typography>
</Box>}
</React.Fragment> </React.Fragment>
) )
} }

View file

@ -170,7 +170,7 @@ export default function DevicePage() {
deviceAPI.listFoundComponents(parseInt(deviceID)) deviceAPI.listFoundComponents(parseInt(deviceID))
.then(resp => { .then(resp => {
console.log(resp.data) console.log(resp.data)
if (resp.data.foundComponents){ if (resp.data.foundComponents?.i2c || resp.data.foundComponents?.oneWire){
setScannedAddresses(pond.ListFoundComponentsResponse.fromObject(resp.data).foundComponents ?? undefined) setScannedAddresses(pond.ListFoundComponentsResponse.fromObject(resp.data).foundComponents ?? undefined)
} }
}) })
@ -412,7 +412,16 @@ export default function DevicePage() {
/> />
</Grid> </Grid>
{/* add grid card here for I2C detected components */} {/* add grid card here for I2C detected components */}
{(scannedAddresses?.i2c || scannedAddresses?.oneWire) &&
<Grid size={{ xs: 12 }}>
<DeviceScannedComponents
scannedComponents={scannedAddresses}
device={device}
availablePositions={availablePositions}
availableOffsets={availableOffsets}
refreshCallback={loadDevice}/>
</Grid>
}
{diagnosticComponents.map(comp => ( {diagnosticComponents.map(comp => (
<Grid size={{ xs: 12 }} key={comp.key()}> <Grid size={{ xs: 12 }} key={comp.key()}>
<ComponentDiagnostics <ComponentDiagnostics

View file

@ -149,7 +149,8 @@ export interface IDeviceAPIContext {
keys?: string[], keys?: string[],
types?: string[] types?: string[]
) => Promise<any>; ) => Promise<any>;
removeFoundComponents: (id: number, key: string, otherTeam?: string) => Promise<AxiosResponse<pond.RemoveFoundComponentsResponse>>; removeAllFoundComponents: (id: number,otherTeam?: string) => Promise<AxiosResponse<pond.RemoveAllFoundComponentsResponse>>;
removeFoundComponents: (id: number, key: string, type: pond.ObjectType, otherTeam?: string) => Promise<AxiosResponse<pond.RemoveFoundComponentsResponse>>;
} }
export const DeviceAPIContext = createContext<IDeviceAPIContext>({} as IDeviceAPIContext); export const DeviceAPIContext = createContext<IDeviceAPIContext>({} as IDeviceAPIContext);
@ -989,10 +990,10 @@ export default function DeviceProvider(props: PropsWithChildren<Props>) {
}) })
} }
const removeFoundComponents = (id: number, key: string, otherTeam?: string) => { const removeFoundComponents = (id: number, key: string, type: pond.ObjectType, otherTeam?: string) => {
let url = "/devices/" + id + "/removeFoundComponents/" + key; let url = "/devices/" + id + "/removeFoundComponents/" + key + "?scanType=" + type;
const view = otherTeam ? otherTeam : as const view = otherTeam ? otherTeam : as
if(view) url = url + "?as=" + view if(view) url = url + "&as=" + view
return new Promise<AxiosResponse<pond.RemoveFoundComponentsResponse>>((resolve, reject) => { return new Promise<AxiosResponse<pond.RemoveFoundComponentsResponse>>((resolve, reject) => {
del<pond.RemoveFoundComponentsResponse>(pondURL(url)).then(resp => { del<pond.RemoveFoundComponentsResponse>(pondURL(url)).then(resp => {
return resolve(resp) return resolve(resp)
@ -1002,6 +1003,19 @@ export default function DeviceProvider(props: PropsWithChildren<Props>) {
}) })
} }
const removeAllFoundComponents = (id: number, otherTeam?: string) => {
let url = "/devices/" + id + "/removeAllFoundComponents";
const view = otherTeam ? otherTeam : as
if(view) url = url + "?as=" + view
return new Promise<AxiosResponse<pond.RemoveAllFoundComponentsResponse>>((resolve, reject) => {
del<pond.RemoveAllFoundComponentsResponse>(pondURL(url)).then(resp => {
return resolve(resp)
}).catch(err => {
return reject(err)
})
})
}
return ( return (
<DeviceAPIContext.Provider <DeviceAPIContext.Provider
value={{ value={{
@ -1051,7 +1065,8 @@ export default function DeviceProvider(props: PropsWithChildren<Props>) {
detectI2C, detectI2C,
detectOneWire, detectOneWire,
listFoundComponents, listFoundComponents,
removeFoundComponents removeFoundComponents,
removeAllFoundComponents
}}> }}>
{children} {children}
</DeviceAPIContext.Provider> </DeviceAPIContext.Provider>