finished the card to detect i2c components and select ones to add
This commit is contained in:
parent
eebb2407f4
commit
c8105ef624
4 changed files with 232 additions and 72 deletions
|
|
@ -238,6 +238,7 @@ export default function ComponentSettings(props: Props) {
|
||||||
componentType: quack.ComponentType
|
componentType: quack.ComponentType
|
||||||
): number[] => {
|
): number[] => {
|
||||||
let positions = availablePositions.get(addressType);
|
let positions = availablePositions.get(addressType);
|
||||||
|
console.log(positions)
|
||||||
if (!positions) return [];
|
if (!positions) return [];
|
||||||
switch (addressType) {
|
switch (addressType) {
|
||||||
case quack.AddressType.ADDRESS_TYPE_I2C:
|
case quack.AddressType.ADDRESS_TYPE_I2C:
|
||||||
|
|
@ -412,6 +413,7 @@ export default function ComponentSettings(props: Props) {
|
||||||
for (let i = 0; i < addressTypes.length; i++) {
|
for (let i = 0; i < addressTypes.length; i++) {
|
||||||
let addressType = addressTypes[i];
|
let addressType = addressTypes[i];
|
||||||
let availablePositions = getAvailablePositions(addressType, type);
|
let availablePositions = getAvailablePositions(addressType, type);
|
||||||
|
console.log(availablePositions)
|
||||||
|
|
||||||
availableMenuItemPositions.push(
|
availableMenuItemPositions.push(
|
||||||
<MenuItem key={addressType} divider disabled>
|
<MenuItem key={addressType} divider disabled>
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
import { Box, Card, DialogContent, FormControlLabel, Radio, RadioGroup, Step, StepLabel, Stepper } from "@mui/material";
|
import { Box, Button, Card, DialogActions, DialogContent, DialogTitle, FormControlLabel, Grid2, Radio, RadioGroup, Step, StepLabel, Stepper, Typography } from "@mui/material";
|
||||||
import { cloneDeep } from "lodash";
|
import { cloneDeep } from "lodash";
|
||||||
import { getSubtypes, Subtype } from "pbHelpers/ComponentType";
|
import { getSubtypes, Subtype } from "pbHelpers/ComponentType";
|
||||||
import { pond, quack } from "protobuf-ts/pond";
|
import { pond, quack } from "protobuf-ts/pond";
|
||||||
|
|
@ -7,10 +7,16 @@ import { useEffect, useState } from "react";
|
||||||
import ScannedI2C from "./ScannedI2C";
|
import ScannedI2C from "./ScannedI2C";
|
||||||
import ComponentForm from "component/ComponentForm";
|
import ComponentForm from "component/ComponentForm";
|
||||||
import { Component, Device } from "models";
|
import { Component, Device } from "models";
|
||||||
|
import { DeviceAvailabilityMap, OffsetAvailabilityMap } from "pbHelpers/DeviceAvailability";
|
||||||
|
import ResponsiveDialog from "common/ResponsiveDialog";
|
||||||
|
import { useComponentAPI, useSnackbar } from "hooks";
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
scannedComponents: pond.ComponentAddressMap
|
scannedComponents: pond.ComponentAddressMap
|
||||||
device: Device
|
device: Device
|
||||||
|
availablePositions: DeviceAvailabilityMap
|
||||||
|
availableOffsets: OffsetAvailabilityMap
|
||||||
|
refreshCallback: () => void
|
||||||
}
|
}
|
||||||
|
|
||||||
interface CompStep {
|
interface CompStep {
|
||||||
|
|
@ -19,12 +25,15 @@ interface CompStep {
|
||||||
}
|
}
|
||||||
|
|
||||||
export default function DeviceScannedComponents(props: Props){
|
export default function DeviceScannedComponents(props: Props){
|
||||||
const {scannedComponents, device} = props
|
const {scannedComponents, device, availablePositions, availableOffsets, refreshCallback} = props
|
||||||
|
const compAPI = useComponentAPI();
|
||||||
const [scannedI2C, setScannedI2C] = useState<pond.DetectI2C>()
|
const [scannedI2C, setScannedI2C] = useState<pond.DetectI2C>()
|
||||||
const [components, setComponents] = useState<Component[]>([])
|
const [components, setComponents] = useState<Component[]>([])
|
||||||
const [steps, setSteps] = useState<CompStep[]>([]);
|
const [steps, setSteps] = useState<CompStep[]>([]);
|
||||||
|
const { error, success } = useSnackbar();
|
||||||
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);
|
||||||
|
|
||||||
useEffect(()=>{
|
useEffect(()=>{
|
||||||
if (scannedComponents.i2c) setScannedI2C(scannedComponents.i2c)
|
if (scannedComponents.i2c) setScannedI2C(scannedComponents.i2c)
|
||||||
|
|
@ -38,6 +47,7 @@ export default function DeviceScannedComponents(props: Props){
|
||||||
completed: false
|
completed: false
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
setSteps(steps)
|
||||||
},[components])
|
},[components])
|
||||||
|
|
||||||
const stepper = () => {
|
const stepper = () => {
|
||||||
|
|
@ -57,8 +67,28 @@ export default function DeviceScannedComponents(props: Props){
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
const addComponentContent = () => {
|
const addComponents = () => {
|
||||||
|
let c: pond.MultiComponentSettings = pond.MultiComponentSettings.create();
|
||||||
|
components.forEach(component => {
|
||||||
|
c.components.push(component.settings);
|
||||||
|
});
|
||||||
|
compAPI
|
||||||
|
.addMultiComponents(device.id(), c)
|
||||||
|
.then(resp => {
|
||||||
|
success("Components added to Device");
|
||||||
|
})
|
||||||
|
.catch(err => {
|
||||||
|
error("One or more component failed to add");
|
||||||
|
})
|
||||||
|
.finally(() => {
|
||||||
|
refreshCallback();
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
const addComponentDialog = () => {
|
||||||
return (
|
return (
|
||||||
|
<ResponsiveDialog open={openDialog} onClose={()=>{setOpenDialog(false)}}>
|
||||||
|
<DialogTitle>Adding Components</DialogTitle>
|
||||||
<DialogContent>
|
<DialogContent>
|
||||||
{stepper()}
|
{stepper()}
|
||||||
<ComponentForm
|
<ComponentForm
|
||||||
|
|
@ -71,23 +101,102 @@ export default function DeviceScannedComponents(props: Props){
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
</DialogContent>
|
</DialogContent>
|
||||||
|
<DialogActions>
|
||||||
|
<Grid2 container direction="row" justifyContent="space-between">
|
||||||
|
<Grid2>
|
||||||
|
<Button
|
||||||
|
variant="contained"
|
||||||
|
color="primary"
|
||||||
|
onClick={() => {
|
||||||
|
close();
|
||||||
|
}}>
|
||||||
|
Cancel
|
||||||
|
</Button>
|
||||||
|
</Grid2>
|
||||||
|
<Grid2>
|
||||||
|
{currentStep !== 0 && (
|
||||||
|
<Button
|
||||||
|
sx={{marginX: 1}}
|
||||||
|
variant="contained"
|
||||||
|
color="primary"
|
||||||
|
onClick={() => {
|
||||||
|
setCurrentStep(currentStep - 1);
|
||||||
|
}}>
|
||||||
|
Back
|
||||||
|
</Button>
|
||||||
|
)}
|
||||||
|
{currentStep !== steps.length - 1 && (
|
||||||
|
<React.Fragment>
|
||||||
|
<Button
|
||||||
|
sx={{marginX: 1}}
|
||||||
|
variant="contained"
|
||||||
|
color="primary"
|
||||||
|
onClick={() => {
|
||||||
|
setCurrentStep(currentStep + 1);
|
||||||
|
}}>
|
||||||
|
Next
|
||||||
|
</Button>
|
||||||
|
</React.Fragment>
|
||||||
|
)}
|
||||||
|
<Button
|
||||||
|
sx={{marginX: 1}}
|
||||||
|
variant="contained"
|
||||||
|
color="primary"
|
||||||
|
onClick={() => {
|
||||||
|
//setConfirmationDialogOpen(true)
|
||||||
|
setOpenDialog(false)
|
||||||
|
addComponents();
|
||||||
|
}}>
|
||||||
|
Confirm
|
||||||
|
</Button>
|
||||||
|
</Grid2>
|
||||||
|
</Grid2>
|
||||||
|
</DialogActions>
|
||||||
|
</ResponsiveDialog>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const componentSelection = (newComponent: Component, checked: boolean) => {
|
||||||
|
let cloneList = cloneDeep(components)
|
||||||
|
if(checked){
|
||||||
|
//add the component to the list
|
||||||
|
cloneList.push(newComponent)
|
||||||
|
}else{
|
||||||
|
//remove any components that match the component location: (type)-(addressType)-(address)
|
||||||
|
components.forEach((comp, i) => {
|
||||||
|
//check the components location against the one in the list
|
||||||
|
if(comp.locationString() === newComponent.locationString()){
|
||||||
|
cloneList.splice(i, 1)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
setComponents(cloneList)
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Card>
|
|
||||||
{scannedI2C &&
|
|
||||||
<React.Fragment>
|
<React.Fragment>
|
||||||
<Box>I2C Components Found</Box>
|
<Card raised sx={{padding: 1}}>
|
||||||
|
{scannedI2C !== undefined &&
|
||||||
|
<React.Fragment>
|
||||||
|
<Box>
|
||||||
|
<Typography sx={{fontWeight: 650}}>
|
||||||
|
I2C Components Found
|
||||||
|
</Typography>
|
||||||
|
</Box>
|
||||||
{scannedI2C?.settings?.foundAddresses.map((addr, index) => {
|
{scannedI2C?.settings?.foundAddresses.map((addr, index) => {
|
||||||
return (
|
return (
|
||||||
<ScannedI2C key={index} decimalAddress={addr} deviceProduct={device.settings.product} updateComponentList={()=>{}} />
|
<ScannedI2C key={index} decimalAddress={addr} deviceProduct={device.settings.product} componentSelectionCallback={componentSelection} availablePositions={availablePositions.get(quack.AddressType.ADDRESS_TYPE_I2C) ?? []}/>
|
||||||
)
|
)
|
||||||
})}
|
})}
|
||||||
</React.Fragment>
|
</React.Fragment>
|
||||||
}
|
}
|
||||||
|
<Box display="flex" flexDirection="row-reverse" marginTop={2}>
|
||||||
|
<Button disabled={components.length === 0} variant="contained" color="primary" onClick={()=>{setOpenDialog(true)}}>Add Components</Button>
|
||||||
|
</Box>
|
||||||
</Card>
|
</Card>
|
||||||
|
{addComponentDialog()}
|
||||||
|
</React.Fragment>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
@ -1,5 +1,7 @@
|
||||||
import { Box, Button, FormControlLabel, MenuItem, Radio, RadioGroup, TextField } from "@mui/material";
|
import { Box, Button, Checkbox, FormControlLabel, Grid2, MenuItem, Radio, RadioGroup, TextField, Tooltip } from "@mui/material";
|
||||||
|
import { Component } from "models";
|
||||||
import { getAddressTypes, getFriendlyName, getSubtypes, Subtype } from "pbHelpers/ComponentType";
|
import { getAddressTypes, getFriendlyName, getSubtypes, Subtype } from "pbHelpers/ComponentType";
|
||||||
|
import { ComponentAvailabilityMap, DevicePositions } from "pbHelpers/DeviceAvailability";
|
||||||
import { GetProductAvailability } from "products/DeviceProduct";
|
import { GetProductAvailability } from "products/DeviceProduct";
|
||||||
import { pond } from "protobuf-ts/pond";
|
import { pond } from "protobuf-ts/pond";
|
||||||
import { quack } from "protobuf-ts/quack";
|
import { quack } from "protobuf-ts/quack";
|
||||||
|
|
@ -8,29 +10,29 @@ import { useEffect, useState } from "react";
|
||||||
interface Props {
|
interface Props {
|
||||||
decimalAddress: number
|
decimalAddress: number
|
||||||
deviceProduct: pond.DeviceProduct
|
deviceProduct: pond.DeviceProduct
|
||||||
updateComponentList: () => void
|
componentSelectionCallback: (component: Component, checked: boolean) => void
|
||||||
|
availablePositions: DevicePositions
|
||||||
}
|
}
|
||||||
|
|
||||||
export default function ScannedI2C(props: Props){
|
export default function ScannedI2C(props: Props){
|
||||||
const {decimalAddress, deviceProduct} = props
|
const {decimalAddress, deviceProduct, componentSelectionCallback, availablePositions} = props
|
||||||
const [types, setTypes] = useState<quack.ComponentType[]>([])
|
const [types, setTypes] = useState<quack.ComponentType[]>([])
|
||||||
const [subtypeOptions, setSubtypeOptions] = useState<Array<Subtype>>([])
|
const [subtypeOptions, setSubtypeOptions] = useState<Array<Subtype>>([])
|
||||||
const [selectedType, setSelectedType] = useState<quack.ComponentType>(quack.ComponentType.COMPONENT_TYPE_INVALID)
|
const [selectedType, setSelectedType] = useState<quack.ComponentType>(quack.ComponentType.COMPONENT_TYPE_INVALID)
|
||||||
const [selectedSubtype, setSelectedSubtype] = useState(0)
|
const [selectedSubtype, setSelectedSubtype] = useState(0)
|
||||||
|
const [addressInUse, setAddressInUse] = useState(false)
|
||||||
|
|
||||||
useEffect(()=>{
|
useEffect(()=>{
|
||||||
//convert the address to hex
|
|
||||||
let hexAddress = parseInt(decimalAddress.toString(16))
|
|
||||||
hexAddress = 0x2a //hardcoding for testing
|
|
||||||
let types: quack.ComponentType[] = []
|
let types: quack.ComponentType[] = []
|
||||||
//find what component types use that address
|
//find what component types use that address, this is different than the available positions passed in
|
||||||
|
//this is meant to get all possibilities according to product type and the given address, availablePositions is the restriction of what is available
|
||||||
let productAvailability = GetProductAvailability(deviceProduct)
|
let productAvailability = GetProductAvailability(deviceProduct)
|
||||||
if(productAvailability){
|
if(productAvailability){
|
||||||
let i2cMap = productAvailability.get(quack.AddressType.ADDRESS_TYPE_I2C)
|
let i2cMap = productAvailability.get(quack.AddressType.ADDRESS_TYPE_I2C)
|
||||||
if(i2cMap){
|
if(i2cMap){
|
||||||
i2cMap.forEach((val, key) => {
|
i2cMap.forEach((val, key) => {
|
||||||
let addresses = val as number[] ?? []
|
let addresses = val as number[] ?? []
|
||||||
if(addresses.length > 0 && addresses.includes(hexAddress)){
|
if(addresses.length > 0 && addresses.includes(decimalAddress)){
|
||||||
types.push(key)
|
types.push(key)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
@ -40,6 +42,7 @@ export default function ScannedI2C(props: Props){
|
||||||
if(types.length > 0) {
|
if(types.length > 0) {
|
||||||
setSelectedType(types[0])
|
setSelectedType(types[0])
|
||||||
buildSubtypeOptions(types[0])
|
buildSubtypeOptions(types[0])
|
||||||
|
checkAddressAvailable(types[0])
|
||||||
}
|
}
|
||||||
setTypes(types)
|
setTypes(types)
|
||||||
},[decimalAddress, deviceProduct])
|
},[decimalAddress, deviceProduct])
|
||||||
|
|
@ -61,18 +64,43 @@ export default function ScannedI2C(props: Props){
|
||||||
setSubtypeOptions(validSub)
|
setSubtypeOptions(validSub)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const checkAddressAvailable = (compType: quack.ComponentType) => {
|
||||||
|
let i2cMap = availablePositions as ComponentAvailabilityMap
|
||||||
|
let compAddresses = i2cMap.get(compType) ?? []
|
||||||
|
if (!compAddresses.includes(decimalAddress)){
|
||||||
|
setAddressInUse(true)
|
||||||
|
}else{
|
||||||
|
setAddressInUse(false)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
const componentSelected = (event: React.ChangeEvent<HTMLInputElement>, checked: boolean) => {
|
||||||
|
//build a component with given information and defaults for the rest
|
||||||
|
let component = Component.create()
|
||||||
|
component.settings.type = selectedType
|
||||||
|
component.settings.subtype = selectedSubtype
|
||||||
|
component.settings.addressType = quack.AddressType.ADDRESS_TYPE_I2C
|
||||||
|
component.settings.address = decimalAddress
|
||||||
|
component.settings.name = getFriendlyName(selectedType, selectedSubtype)
|
||||||
|
//return the component and the checked status to the parent
|
||||||
|
componentSelectionCallback(component, checked)
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Box>
|
<Box>
|
||||||
Sensor 1
|
Sensor 1
|
||||||
|
<Grid2 container direction="row" alignItems="center" justifyContent="space-between">
|
||||||
|
<Grid2 size={3}>
|
||||||
<RadioGroup
|
<RadioGroup
|
||||||
value={selectedType}
|
value={selectedType}
|
||||||
onChange={(_, value) => {
|
onChange={(_, value) => {
|
||||||
console.log(value)
|
|
||||||
let v = parseInt(value)
|
let v = parseInt(value)
|
||||||
if (!isNaN(v)){
|
if (!isNaN(v)){
|
||||||
buildSubtypeOptions(v)
|
buildSubtypeOptions(v)
|
||||||
|
checkAddressAvailable(v)
|
||||||
}
|
}
|
||||||
}}>
|
}}>
|
||||||
{types.map((type, i) => {
|
{types.map((type, i) => {
|
||||||
|
|
@ -86,13 +114,14 @@ export default function ScannedI2C(props: Props){
|
||||||
)
|
)
|
||||||
})}
|
})}
|
||||||
</RadioGroup>
|
</RadioGroup>
|
||||||
|
</Grid2>
|
||||||
|
<Grid2 size={8}>
|
||||||
{subtypeOptions.length > 0 &&
|
{subtypeOptions.length > 0 &&
|
||||||
<TextField
|
<TextField
|
||||||
value={selectedSubtype}
|
value={selectedSubtype}
|
||||||
fullWidth
|
fullWidth
|
||||||
onChange={(event)=>{
|
onChange={(event)=>{
|
||||||
let val = +event.target.value
|
let val = +event.target.value
|
||||||
console.log(event.target.value)
|
|
||||||
setSelectedSubtype(val)
|
setSelectedSubtype(val)
|
||||||
}}
|
}}
|
||||||
select>
|
select>
|
||||||
|
|
@ -101,7 +130,20 @@ export default function ScannedI2C(props: Props){
|
||||||
))}
|
))}
|
||||||
</TextField>
|
</TextField>
|
||||||
}
|
}
|
||||||
<Button>Add Components</Button>
|
</Grid2>
|
||||||
|
<Grid2 size={1}>
|
||||||
|
<Tooltip
|
||||||
|
title="Add to list to be added to the device"
|
||||||
|
children={
|
||||||
|
<Checkbox
|
||||||
|
onChange={componentSelected}
|
||||||
|
disabled={addressInUse}
|
||||||
|
/>
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
|
||||||
|
</Grid2>
|
||||||
|
</Grid2>
|
||||||
</Box>
|
</Box>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
@ -167,7 +167,9 @@ export default function DevicePage() {
|
||||||
const loadPortScan = () => {
|
const loadPortScan = () => {
|
||||||
deviceAPI.listFoundComponents(device.id())
|
deviceAPI.listFoundComponents(device.id())
|
||||||
.then(resp => {
|
.then(resp => {
|
||||||
|
if (resp.data.foundComponents?.i2c){
|
||||||
setScannedAddresses(resp.data.foundComponents ?? undefined)
|
setScannedAddresses(resp.data.foundComponents ?? undefined)
|
||||||
|
}
|
||||||
})
|
})
|
||||||
.catch(err => {
|
.catch(err => {
|
||||||
console.log(err)
|
console.log(err)
|
||||||
|
|
@ -427,7 +429,12 @@ export default function DevicePage() {
|
||||||
</Grid>
|
</Grid>
|
||||||
{scannedAddresses &&
|
{scannedAddresses &&
|
||||||
<Grid size={{ xs: 6, sm: 6, lg: 4, xl: 4 }}>
|
<Grid size={{ xs: 6, sm: 6, lg: 4, xl: 4 }}>
|
||||||
<DeviceScannedComponents scannedComponents={scannedAddresses} device={device}/>
|
<DeviceScannedComponents
|
||||||
|
scannedComponents={scannedAddresses}
|
||||||
|
device={device}
|
||||||
|
availablePositions={availablePositions}
|
||||||
|
availableOffsets={availableOffsets}
|
||||||
|
refreshCallback={loadDevice}/>
|
||||||
</Grid>
|
</Grid>
|
||||||
}
|
}
|
||||||
{diagnosticComponents.length > 0 &&
|
{diagnosticComponents.length > 0 &&
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue