frontend/src/device/autoDetect/ScannedI2C.tsx

175 lines
No EOL
6.6 KiB
TypeScript

import { Box, Button, Checkbox, FormControlLabel, Grid2, MenuItem, Radio, RadioGroup, TextField, Tooltip, Typography } from "@mui/material";
import { Component } from "models";
import { getAddressTypes, getFriendlyName, GetSecondaryComponents, getSubtypes, Subtype } from "pbHelpers/ComponentType";
import { ComponentAvailabilityMap, DevicePositions } from "pbHelpers/DeviceAvailability";
import { GetProductAvailability } from "products/DeviceProduct";
import { pond } from "protobuf-ts/pond";
import { quack } from "protobuf-ts/quack";
import React, { useEffect, useState } from "react";
interface Props {
addressData: quack.AddressData
deviceProduct: pond.DeviceProduct
componentSelectionCallback: (component: Component[], checked: boolean) => void
availablePositions: DevicePositions
sensorNum?: number
}
export default function ScannedI2C(props: Props){
const {addressData, deviceProduct, componentSelectionCallback, availablePositions, sensorNum} = props
const [types, setTypes] = useState<quack.ComponentType[]>([])
const [subtypeOptions, setSubtypeOptions] = useState<Array<Subtype>>([])
const [selectedPrimaryType, setSelectedPrimaryType] = useState<quack.ComponentType>(quack.ComponentType.COMPONENT_TYPE_INVALID)
const [selectedPrimarySubtype, setSelectedPrimarySubtype] = useState(0)
const [added, setAdded] = useState(false)
const [addressInUse, setAddressInUse] = useState(false)
useEffect(()=>{
let types: quack.ComponentType[] = []
//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)
if(productAvailability){
let i2cMap = productAvailability.get(quack.AddressType.ADDRESS_TYPE_I2C)
if(i2cMap){
i2cMap.forEach((val, key) => {
let addresses = val as number[] ?? []
if(addresses.length > 0 && addresses.includes(addressData.address)){
types.push(key)
}
})
}
}
//this if can be taken out if we dont want to select the first component type by default
if(types.length > 0) {
setSelectedPrimaryType(types[0])
buildSubtypeOptions(types[0])
checkAddressAvailable(types[0])
}
setTypes(types)
},[addressData, deviceProduct])
const buildSubtypeOptions = (compType: quack.ComponentType) => {
let subtypes = getSubtypes(compType)
let validSub: Subtype[] = []
//have to exclude subtypes that are not I2C
subtypes.forEach((sub,i) => {
let addrTypes = getAddressTypes(compType, sub.key)
if (addrTypes.includes(quack.AddressType.ADDRESS_TYPE_I2C)){
validSub.push(sub)
}
})
//set the selected on to be the first in the valid selection
if(validSub.length > 0){
setSelectedPrimarySubtype(validSub[0].key)
}
setSubtypeOptions(validSub)
}
const checkAddressAvailable = (compType: quack.ComponentType) => {
let i2cMap = availablePositions as ComponentAvailabilityMap
let compAddresses = i2cMap.get(compType) ?? []
if (!compAddresses.includes(addressData.address)){
setAddressInUse(true)
}else{
setAddressInUse(false)
}
}
const componentSelected = (event: React.ChangeEvent<HTMLInputElement>, checked: boolean) => {
let toAdd: Component[] = []
setAdded(checked)
//build a component with given information and defaults for the rest
let primaryComponent = Component.create()
primaryComponent.settings.type = selectedPrimaryType
primaryComponent.settings.subtype = selectedPrimarySubtype
primaryComponent.settings.addressType = quack.AddressType.ADDRESS_TYPE_I2C
primaryComponent.settings.address = addressData.address
primaryComponent.settings.expansionLine = addressData.expansionLine
primaryComponent.settings.muxLine = addressData.muxLine
primaryComponent.settings.name = getFriendlyName(selectedPrimaryType, selectedPrimarySubtype)
toAdd.push(primaryComponent)
GetSecondaryComponents(selectedPrimaryType, selectedPrimarySubtype).forEach(secComp => {
let component = Component.create()
component.settings = secComp
toAdd.push(component)
})
//return the component and the checked status to the parent
componentSelectionCallback(toAdd, checked)
}
return (
<Box sx={{marginY: 1}}>
{sensorNum && <Typography>Sensor {sensorNum}</Typography>}
<Typography variant="caption">{addressData.expansionLine !== 0 && "Expansion Line: " + addressData.expansionLine}</Typography>
{/* may also want to specify the mux line, not sure if the sensors will have a mux line though */}
{types.length > 0 ?
<React.Fragment>
<Grid2 container direction="row" alignItems="center" justifyContent="space-between">
<Grid2 size={3}>
<RadioGroup
value={selectedPrimaryType}
onChange={(_, value) => {
let v = parseInt(value)
if (!isNaN(v)){
buildSubtypeOptions(v)
checkAddressAvailable(v)
}
}}>
{types.map((type, i) => {
return (
<FormControlLabel
disabled={added}
key={i}
value={type}
control={<Radio />}
label={getFriendlyName(type)}
/>
)
})}
</RadioGroup>
</Grid2>
<Grid2 size={8}>
{subtypeOptions.length > 0 &&
<TextField
disabled={added}
value={selectedPrimarySubtype}
fullWidth
onChange={(event)=>{
let val = +event.target.value
setSelectedPrimarySubtype(val)
}}
select>
{subtypeOptions.map(s => (
<MenuItem key={s.key} value={s.key}>{s.friendlyName}</MenuItem>
))}
</TextField>
}
</Grid2>
<Grid2 size={1}>
<Tooltip
title="Add to list to be added to the device"
children={
<Checkbox
value={added}
onChange={componentSelected}
disabled={addressInUse}
/>
}
/>
</Grid2>
</Grid2>
</React.Fragment>
:
<Typography>Sensor Not Supported By Product</Typography>
}
</Box>
)
}